@superdoc/mcp 1.2.0-next.4 → 1.2.0-next.6

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 +197 -33
  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.4";
31317
+ var SDK_VERSION = "2.15.0-next.6";
31318
31318
 
31319
31319
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31320
31320
  import { chmodSync, existsSync } from "node:fs";
@@ -133951,6 +133951,50 @@ var init_note_xml_validation = __esm(() => {
133951
133951
  });
133952
133952
 
133953
133953
  // ../../../v2/document-compare/src/replayability.ts
133954
+ function preservedBookmarkRunTokens(xml) {
133955
+ const markerTags = [...xml.matchAll(/<([\w.-]+:)?bookmark(Start|End)\b[^<>]*?\/>/g)];
133956
+ const markerOpenings = [...xml.matchAll(/<(?:[\w.-]+:)?bookmark(?:Start|End)\b/g)];
133957
+ if (markerTags.length !== 2 || markerOpenings.length !== 2 || markerTags[0][2] !== "Start" || markerTags[1][2] !== "End")
133958
+ return null;
133959
+ let valid = true;
133960
+ let depth = 0;
133961
+ const markers = [];
133962
+ const parser = new import_saxes3.SaxesParser({
133963
+ xmlns: true,
133964
+ additionalNamespaces: replayAdditionalNamespaces(xml)
133965
+ });
133966
+ parser.on("error", () => {
133967
+ valid = false;
133968
+ });
133969
+ parser.on("opentag", (tag) => {
133970
+ depth += 1;
133971
+ if (tag.local !== "bookmarkStart" && tag.local !== "bookmarkEnd")
133972
+ return;
133973
+ const attributes = Object.values(tag.attributes);
133974
+ const id = attributes.find((attribute) => attribute.uri === W_NS && attribute.local === "id")?.value;
133975
+ const name = attributes.find((attribute) => attribute.uri === W_NS && attribute.local === "name")?.value ?? null;
133976
+ if (tag.uri !== W_NS || !tag.isSelfClosing || depth !== 2 || !id || !/^\d+$/.test(id) || tag.local === "bookmarkStart" && !name || tag.local === "bookmarkEnd" && name !== null)
133977
+ valid = false;
133978
+ markers.push({ kind: tag.local, id: id ?? "", name });
133979
+ });
133980
+ parser.on("closetag", () => {
133981
+ depth -= 1;
133982
+ });
133983
+ try {
133984
+ parser.write(`<replay>${xml}</replay>`).close();
133985
+ } catch {
133986
+ valid = false;
133987
+ }
133988
+ if (!valid || depth !== 0 || markers.length !== 2 || markers[0].kind !== "bookmarkStart" || markers[1].kind !== "bookmarkEnd" || markers[0].id !== markers[1].id)
133989
+ return null;
133990
+ return {
133991
+ startXml: markerTags[0][0],
133992
+ endXml: markerTags[1][0],
133993
+ leadingXml: xml.slice(0, markerTags[0].index),
133994
+ trailingXml: xml.slice(markerTags[1].index + markerTags[1][0].length),
133995
+ strippedXml: xml.slice(0, markerTags[0].index) + xml.slice(markerTags[0].index + markerTags[0][0].length, markerTags[1].index) + xml.slice(markerTags[1].index + markerTags[1][0].length)
133996
+ };
133997
+ }
133954
133998
  function replayAdditionalNamespaces(xml) {
133955
133999
  const implicitNamespaces = { w: W_NS };
133956
134000
  const firstElement = /<([A-Za-z_][\w.-]*)(?::[A-Za-z_][\w.-]*)?(?=[\s/>])/.exec(xml);
@@ -134078,6 +134122,13 @@ function parseDirectRunSequence(xml, allowRevisions) {
134078
134122
  originalText += value;
134079
134123
  }
134080
134124
  };
134125
+ const isProofingBoundary = (tag) => {
134126
+ if (tag.uri !== W_NS || tag.local !== "proofErr" || !tag.isSelfClosing)
134127
+ return false;
134128
+ const attributes = Object.values(tag.attributes);
134129
+ const typeAttributes = attributes.filter((attribute) => typeof attribute !== "string" && attribute.uri === W_NS && attribute.local === "type");
134130
+ return typeAttributes.length === 1 && typeof typeAttributes[0] !== "string" && PROOFING_BOUNDARY_TYPES.has(typeAttributes[0].value) && attributes.every((attribute) => typeof attribute !== "string" && (attribute === typeAttributes[0] || attribute.uri === "http://www.w3.org/2000/xmlns/" && (attribute.local === tag.prefix || attribute.local === "w" || tag.prefix === "" && attribute.local === "xmlns") && attribute.value === W_NS));
134131
+ };
134081
134132
  const parser = new import_saxes3.SaxesParser({
134082
134133
  xmlns: true,
134083
134134
  additionalNamespaces: replayAdditionalNamespaces(xml)
@@ -134094,6 +134145,10 @@ function parseDirectRunSequence(xml, allowRevisions) {
134094
134145
  return;
134095
134146
  }
134096
134147
  if (parent === "wrapper") {
134148
+ if (isProofingBoundary(tag)) {
134149
+ stack.push("control");
134150
+ return;
134151
+ }
134097
134152
  rootCount += 1;
134098
134153
  if (tag.uri === W_NS && tag.local === "r") {
134099
134154
  stack.push("run");
@@ -134124,6 +134179,10 @@ function parseDirectRunSequence(xml, allowRevisions) {
134124
134179
  return;
134125
134180
  }
134126
134181
  if (parent === "revision") {
134182
+ if (isProofingBoundary(tag)) {
134183
+ stack.push("control");
134184
+ return;
134185
+ }
134127
134186
  if (tag.uri !== W_NS || tag.local !== "r")
134128
134187
  valid = false;
134129
134188
  activeRevisionRunCount += 1;
@@ -134550,13 +134609,14 @@ function validateRunContents(xml, options) {
134550
134609
  if (xml === "")
134551
134610
  return { ok: true };
134552
134611
  const replayXml = stripAllowedNoteReferences(xml, options);
134553
- if (replayXml === null || UNSUPPORTED_XML.test(replayXml) || RELATIONSHIP_REFERENCE.test(replayXml)) {
134612
+ const withoutBookmarks = replayXml !== null && /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/.test(replayXml) ? preservedBookmarkRunTokens(replayXml)?.strippedXml ?? null : replayXml;
134613
+ if (withoutBookmarks === null || UNSUPPORTED_XML.test(withoutBookmarks) || RELATIONSHIP_REFERENCE.test(withoutBookmarks)) {
134554
134614
  return { ok: false, reason: "target runs require unsupported structural or relationship replay" };
134555
134615
  }
134556
- const namespaceResult = validateReplayNamespaces(replayXml, "target runs use namespace prefixes that cannot be replayed safely");
134616
+ const namespaceResult = validateReplayNamespaces(withoutBookmarks.replace(PROOFING_BOUNDARY_TAG_RE, ""), "target runs use namespace prefixes that cannot be replayed safely");
134557
134617
  if (!namespaceResult.ok)
134558
134618
  return namespaceResult;
134559
- if (!parseDirectRunSequence(replayXml, false)) {
134619
+ if (!parseDirectRunSequence(withoutBookmarks, false)) {
134560
134620
  return { ok: false, reason: "target runs contain unsupported inline markup" };
134561
134621
  }
134562
134622
  return { ok: true };
@@ -134580,7 +134640,7 @@ function validateCompareReplayPayload(payload, options = {}) {
134580
134640
  if (payload.targetRevisionReplay.policy !== "preserve-target-revision-set" || payload.targetRunXml === undefined) {
134581
134641
  return { ok: false, reason: "target revision replay policy is incomplete" };
134582
134642
  }
134583
- const namespaceResult = validateReplayNamespaces(payload.targetRunXml, "target revision runs use namespace prefixes that cannot be replayed safely");
134643
+ const namespaceResult = validateReplayNamespaces(payload.targetRunXml.replace(PROOFING_BOUNDARY_TAG_RE, ""), "target revision runs use namespace prefixes that cannot be replayed safely");
134584
134644
  if (!namespaceResult.ok)
134585
134645
  return namespaceResult;
134586
134646
  const inspected = inspectDirectParagraphRevisionRuns(payload.targetRunXml);
@@ -134600,12 +134660,14 @@ function validateCompareReplayPayload(payload, options = {}) {
134600
134660
  }
134601
134661
  return { ok: true };
134602
134662
  }
134603
- var import_saxes3, UNSUPPORTED_XML, RELATIONSHIP_REFERENCE, W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", DIRECT_RUN_PROPERTY_ELEMENTS;
134663
+ var import_saxes3, UNSUPPORTED_XML, RELATIONSHIP_REFERENCE, W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main", PROOFING_BOUNDARY_TYPES, PROOFING_BOUNDARY_TAG_RE, DIRECT_RUN_PROPERTY_ELEMENTS;
134604
134664
  var init_replayability = __esm(() => {
134605
134665
  init_note_xml_validation();
134606
134666
  import_saxes3 = __toESM(require_saxes(), 1);
134607
- UNSUPPORTED_XML = /<(?:[\w.-]+:)?(?:hyperlink|drawing|pict|object|fldSimple|fldChar|instrText|commentReference|commentRangeStart|commentRangeEnd|bookmarkStart|bookmarkEnd|permStart|permEnd|sdt|customXml|oMath|oMathPara|footnoteReference|endnoteReference|ins|del|moveFrom|moveTo|pPrChange|rPrChange|sectPr)\b/i;
134667
+ UNSUPPORTED_XML = /<(?:[\w.-]+:)?(?:hyperlink|drawing|pict|object|fldSimple|fldChar|instrText|commentReference|commentRangeStart|commentRangeEnd|permStart|permEnd|sdt|customXml|oMath|oMathPara|footnoteReference|endnoteReference|ins|del|moveFrom|moveTo|pPrChange|rPrChange|sectPr)\b/i;
134608
134668
  RELATIONSHIP_REFERENCE = /\s(?:r:id|r:embed|r:link)\s*=/i;
134669
+ PROOFING_BOUNDARY_TYPES = new Set(["spellStart", "spellEnd", "gramStart", "gramEnd"]);
134670
+ PROOFING_BOUNDARY_TAG_RE = /<(?:[A-Za-z_][\w.-]*:)?proofErr\b(?:[^>"']|"[^"]*"|'[^']*')*\/>/g;
134609
134671
  DIRECT_RUN_PROPERTY_ELEMENTS = new Set([
134610
134672
  "rStyle",
134611
134673
  "rFonts",
@@ -134745,7 +134807,10 @@ function canonicalName(namespace, localName2) {
134745
134807
  }
134746
134808
  function computeRunSignature(runXml) {
134747
134809
  const canonicalXml = canonicalizeXmlForSignature(runXml);
134748
- const normalized = normalizeRunXmlForSignature(canonicalXml.replace(REVISION_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) => {
134810
+ const normalized = normalizeRunXmlForSignature(canonicalXml.replace(REVISION_ATTR_RE, (attribute, offset, xml) => {
134811
+ const precedingTag = xml.slice(xml.lastIndexOf("<", offset), offset);
134812
+ return /^<w:proofErr(?:\s|$)/.test(precedingTag) ? attribute : "";
134813
+ }).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) => {
134749
134814
  const name = nestedName ?? standaloneName;
134750
134815
  const attributes = nestedAttributes ?? standaloneAttributes;
134751
134816
  if (!isWordprocessingElement3(name, attributes, canonicalXml))
@@ -134772,7 +134837,7 @@ function normalizeRunXmlForSignature(runXml) {
134772
134837
  continue;
134773
134838
  const index = match.index ?? 0;
134774
134839
  if (index > lastIndex) {
134775
- const interstitial = runXml.slice(lastIndex, index);
134840
+ const interstitial = stripProofingBoundaries(runXml.slice(lastIndex, index));
134776
134841
  if (interstitial.trim().length > 0) {
134777
134842
  pieces.push(interstitial);
134778
134843
  activeTextProperties = "";
@@ -134801,12 +134866,15 @@ function normalizeRunXmlForSignature(runXml) {
134801
134866
  if (!sawRun)
134802
134867
  return runXml;
134803
134868
  if (lastIndex < runXml.length) {
134804
- const trailing = runXml.slice(lastIndex);
134869
+ const trailing = stripProofingBoundaries(runXml.slice(lastIndex));
134805
134870
  if (trailing.trim().length > 0)
134806
134871
  pieces.push(trailing);
134807
134872
  }
134808
134873
  return pieces.join("");
134809
134874
  }
134875
+ function stripProofingBoundaries(xml) {
134876
+ return xml.replace(PROOFING_BOUNDARY_MARKUP_RE, "");
134877
+ }
134810
134878
  function normalizeRunControls(content, namespaceContext) {
134811
134879
  return [...content.matchAll(RUN_CONTROL_RE)].filter((match) => isWordprocessingElement3(match[1], match[3], namespaceContext)).map((match) => {
134812
134880
  const attributes = [...match[3].matchAll(ATTRIBUTE_RE)].filter((attribute) => attribute[1] !== "xmlns" && !attribute[1].startsWith("xmlns:")).map((attribute) => {
@@ -134839,10 +134907,13 @@ function isWordprocessingElement3(name, attributes, namespaceContext) {
134839
134907
  return true;
134840
134908
  return (declaration[2] ?? declaration[3] ?? "") === WORDPROCESSINGML_NAMESPACE2;
134841
134909
  }
134842
- var REVISION_ATTR_RE, COMMENT_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;
134910
+ 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;
134843
134911
  var init_run_signature = __esm(() => {
134844
- REVISION_ATTR_RE = /\s+(?:w:rsid\w*|w14:paraId|w14:textId|w:id)="[^"]*"/g;
134912
+ REVISION_ATTR_RE = /\s+(?:w:rsid\w*|w14:paraId|w14:textId)="[^"]*"/g;
134913
+ REVISION_ELEMENT_RE = /<w:(?:ins|del|moveFrom|moveTo)\b[^>]*>/g;
134914
+ REVISION_ID_ATTR_RE = /\s+w:id="[^"]*"/g;
134845
134915
  COMMENT_MARKUP_RE = /<((?:[\w.-]+:)?(?:commentRangeStart|commentRangeEnd|commentReference))\b([^>]*)\/>/g;
134916
+ PROOFING_BOUNDARY_MARKUP_RE = /<w:proofErr w:type="(?:spellStart|spellEnd|gramStart|gramEnd)"\/>/g;
134846
134917
  NOTE_REFERENCE_MARKUP_RE = /<((?:[\w.-]+:)?r)\b([^>]*)>(?:<((?:[\w.-]+:)?rPr)\b[^>]*(?:\/>|>[\s\S]*?<\/\3>))?<((?:[\w.-]+:)?(?:footnoteReference|endnoteReference))\b([^>]*)\/><\/\1>|<((?:[\w.-]+:)?(?:footnoteReference|endnoteReference))\b([^>]*)\/>/g;
134847
134918
  RUN_RE = /<((?:[\w.-]+:)?r)\b([^>]*)>([\s\S]*?)<\/\1>/g;
134848
134919
  RUN_PROPERTIES_RE = /<((?:[\w.-]+:)?rPr)\b([^>]*)(?:\/>|>[\s\S]*?<\/\1>)/g;
@@ -219948,15 +220019,31 @@ function canonicalizeCompareBodyRunXml(xml, referenceSteps) {
219948
220019
  let result = "";
219949
220020
  let cursor = 0;
219950
220021
  for (const token of tokens) {
219951
- const canonicalGap = canonicalizeWordprocessingReplayXml(xml.slice(cursor, token.start));
220022
+ const canonicalGap = canonicalizeProofingRunGap(xml.slice(cursor, token.start));
219952
220023
  if (canonicalGap === null)
219953
220024
  return null;
219954
220025
  result += canonicalGap + token.xml;
219955
220026
  cursor = token.end;
219956
220027
  }
219957
- const canonicalTail = canonicalizeWordprocessingReplayXml(xml.slice(cursor));
220028
+ const canonicalTail = canonicalizeProofingRunGap(xml.slice(cursor));
219958
220029
  return canonicalTail === null ? null : result + canonicalTail;
219959
220030
  }
220031
+ function canonicalizeProofingRunGap(xml) {
220032
+ const markerRe = /<(?:[A-Za-z_][\w.-]*:)?proofErr\b(?:[^>"']|"[^"]*"|'[^']*')*\/>/g;
220033
+ let result = "";
220034
+ let cursor = 0;
220035
+ for (const marker of xml.matchAll(markerRe)) {
220036
+ const index = marker.index ?? 0;
220037
+ const before = canonicalizeWordprocessingReplayXml(xml.slice(cursor, index));
220038
+ const canonicalMarker = canonicalizeWordprocessingReplayXml(marker[0]);
220039
+ if (before === null || canonicalMarker === null)
220040
+ return null;
220041
+ result += before + canonicalMarker;
220042
+ cursor = index + marker[0].length;
220043
+ }
220044
+ const trailing = canonicalizeWordprocessingReplayXml(xml.slice(cursor));
220045
+ return trailing === null ? null : result + trailing;
220046
+ }
219960
220047
  function collectProtectedReferenceTokens(xml, referenceSteps) {
219961
220048
  const tokens = [];
219962
220049
  for (let index = 0;index < referenceSteps.length; index += 1) {
@@ -220762,6 +220849,26 @@ function runComparePayloadInsertCommit(input, storyId, step2, changeMode, option
220762
220849
  });
220763
220850
  return withParagraphInsertionCatalogRefs(input, receipt2, paragraph.wordIds, storyId);
220764
220851
  }
220852
+ function unchangedBookmarkReferenceSteps(oldRuns, targetRuns) {
220853
+ const oldHasMarker = /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/.test(oldRuns);
220854
+ const targetHasMarker = /<(?:[\w.-]+:)?bookmark(?:Start|End)\b/.test(targetRuns);
220855
+ if (!oldHasMarker && !targetHasMarker)
220856
+ return [];
220857
+ const oldBookmark = preservedBookmarkRunTokens(oldRuns);
220858
+ const targetBookmark = preservedBookmarkRunTokens(targetRuns);
220859
+ if (!oldBookmark || !targetBookmark || oldBookmark.startXml !== targetBookmark.startXml || oldBookmark.endXml !== targetBookmark.endXml || oldBookmark.leadingXml !== targetBookmark.leadingXml || oldBookmark.trailingXml !== targetBookmark.trailingXml)
220860
+ return null;
220861
+ const startBindings = replayFragmentNamespaceBindings(oldBookmark.startXml);
220862
+ const endBindings = replayFragmentNamespaceBindings(oldBookmark.endXml);
220863
+ const bookmarkBindings = { ...startBindings, ...endBindings };
220864
+ return [{
220865
+ kind: "bookmark-start",
220866
+ beforeXml: oldBookmark.startXml,
220867
+ afterXml: oldBookmark.startXml,
220868
+ beforeNamespaceBindings: bookmarkBindings,
220869
+ afterNamespaceBindings: bookmarkBindings
220870
+ }];
220871
+ }
220765
220872
  function runCompareBodyEditCommit(input, storyId, step2, changeMode, options, pushHistory, referenceSteps) {
220766
220873
  const { state, txId } = input;
220767
220874
  const partUri = storyPartUriFromStoryId2(storyId);
@@ -220779,6 +220886,14 @@ function runCompareBodyEditCommit(input, storyId, step2, changeMode, options, pu
220779
220886
  const pPrXml = findFirstElement(paraXml, "pPr")?.xml ?? "";
220780
220887
  const targetParagraphPropertiesXml = step2.targetParagraphPropertiesXml === undefined ? undefined : rewriteCanonicalWordprocessingPrefix(step2.targetParagraphPropertiesXml, paragraphWrapper.wordprocessingPrefix);
220781
220888
  const newRuns = rewriteCanonicalWordprocessingPrefix(step2.targetRunXml ?? renderParagraphRuns(step2.text), paragraphWrapper.wordprocessingPrefix);
220889
+ const openEnd = paragraphWrapper.contentStart;
220890
+ const closeStart = paraXml.lastIndexOf("</");
220891
+ const innerXml = closeStart > openEnd ? paraXml.slice(openEnd, closeStart) : "";
220892
+ const oldRunsRaw = stripElements(innerXml, "pPr");
220893
+ const stableBookmarkSteps = unchangedBookmarkReferenceSteps(oldRunsRaw, newRuns);
220894
+ if (stableBookmarkSteps === null && referenceSteps.length === 0)
220895
+ return rejected14(txId, "unsupported-context");
220896
+ const protectedReferenceSteps = [...referenceSteps, ...stableBookmarkSteps ?? []];
220782
220897
  let newParaXml;
220783
220898
  const createdRefs = [];
220784
220899
  if (changeMode === "tracked") {
@@ -220794,12 +220909,10 @@ function runCompareBodyEditCommit(input, storyId, step2, changeMode, options, pu
220794
220909
  storyId
220795
220910
  });
220796
220911
  }
220797
- const openEnd = paragraphWrapper.contentStart;
220798
- const closeStart = paraXml.lastIndexOf("</");
220799
- const innerXml = closeStart > openEnd ? paraXml.slice(openEnd, closeStart) : "";
220800
- const oldRunsRaw = stripElements(innerXml, "pPr");
220801
220912
  const oldRunsDel = oldRunsRaw.replace(/<((?:[\w.-]+:)?)t(\b[^>]*)>/g, "<$1delText$2>").replace(/<\/((?:[\w.-]+:)?)t>/g, "</$1delText>");
220802
- const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, newRuns, referenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
220913
+ const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, newRuns, protectedReferenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
220914
+ if (!protectedRuns && stableBookmarkSteps?.length)
220915
+ return rejected14(txId, "unsupported-context");
220803
220916
  const delXml = protectedRuns?.xml ?? (oldRunsDel ? wrapTracked("del", oldRunsDel, stamps.del, "rsidDel", paragraphWrapper.wordprocessingPrefix) : "");
220804
220917
  const insXml = protectedRuns ? "" : newRuns ? wrapTracked("ins", newRuns, stamps.ins, "rsidR", paragraphWrapper.wordprocessingPrefix) : "";
220805
220918
  const trackedPPrXml = buildTrackedParagraphProperties(targetParagraphPropertiesXml, pPrXml, stamps.paragraphProperties ?? stamps.ins, paragraphWrapper.wordprocessingPrefix);
@@ -220883,6 +220996,10 @@ function runCompareBodyFormatEditCommit(input, storyId, step2, changeMode, optio
220883
220996
  const innerXml = closeStart > openEnd ? paraXml.slice(openEnd, closeStart) : "";
220884
220997
  const oldRunsRaw = stripElements(innerXml, "pPr");
220885
220998
  const paragraphPropertiesChanged = !equivalentParagraphProperties(targetPPrXml, pPrXml);
220999
+ const stableBookmarkSteps = unchangedBookmarkReferenceSteps(oldRunsRaw, targetRunXml);
221000
+ if (stableBookmarkSteps === null && referenceSteps.length === 0)
221001
+ return rejected14(txId, "unsupported-context");
221002
+ const protectedReferenceSteps = [...referenceSteps, ...stableBookmarkSteps ?? []];
220886
221003
  const baseSimpleRuns = scanSimpleRunFormatting(oldRunsRaw);
220887
221004
  const targetSimpleRuns = scanSimpleRunFormatting(targetRunXml);
220888
221005
  const paragraphPropertiesOnly = paragraphPropertiesChanged && baseSimpleRuns?.text === targetSimpleRuns?.text && computeRunSignature(targetRunXml) === computeRunSignature(oldRunsRaw);
@@ -220946,7 +221063,9 @@ function runCompareBodyFormatEditCommit(input, storyId, step2, changeMode, optio
220946
221063
  if (!stamps)
220947
221064
  return rejected14(txId, "no-author-configured");
220948
221065
  const oldRunsDel = oldRunsRaw.replace(/<((?:[\w.-]+:)?)t(\b[^>]*)>/g, "<$1delText$2>").replace(/<\/((?:[\w.-]+:)?)t>/g, "</$1delText>");
220949
- const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, targetRunXml, referenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
221066
+ const protectedRuns = renderTrackedRunsWithProtectedReferences(oldRunsRaw, targetRunXml, protectedReferenceSteps, stamps, paragraphWrapper.wordprocessingPrefix);
221067
+ if (!protectedRuns && stableBookmarkSteps?.length)
221068
+ return rejected14(txId, "unsupported-context");
220950
221069
  const delXml = protectedRuns?.xml ?? (oldRunsDel ? wrapTracked("del", oldRunsDel, stamps.del, "rsidDel", paragraphWrapper.wordprocessingPrefix) : "");
220951
221070
  const insXml = protectedRuns ? "" : targetRunXml ? wrapTracked("ins", targetRunXml, stamps.ins, "rsidR", paragraphWrapper.wordprocessingPrefix) : "";
220952
221071
  if (delXml || insXml) {
@@ -239395,7 +239514,7 @@ function toRoman(ordinal) {
239395
239514
  }
239396
239515
  function extractParagraphText(paraXml) {
239397
239516
  let out = "";
239398
- for (const match of paraXml.matchAll(/<[\w.-]+:t\b[^>]*>([\s\S]*?)<\/[\w.-]+:t>/g)) {
239517
+ for (const match of paraXml.matchAll(/<[\w.-]+:t\b[^>]*?(?<!\/)>([\s\S]*?)<\/[\w.-]+:t>/g)) {
239399
239518
  out += decodeXmlText(match[1] ?? "");
239400
239519
  }
239401
239520
  return out;
@@ -246944,7 +247063,7 @@ function prepareHistoryReplayMutation(state, entry, direction, patchGroup) {
246944
247063
  ...contentControlRewrite.addedBlockIds,
246945
247064
  ...contentControlRewrite.contentBlockIds,
246946
247065
  ...contentControlRewrite.leadingBlockIds
246947
- ])] : collectPatchGroupTargetBlockIds(patchGroup);
247066
+ ])] : patchGroup.source.kind === "compareApply" ? [...new Set(patchGroup.source.bodySteps.map((step2) => step2.blockId))] : collectPatchGroupTargetBlockIds(patchGroup);
246948
247067
  const currentBaseRevisions = {
246949
247068
  package: state.packageRevision,
246950
247069
  perPart: {}
@@ -247198,6 +247317,8 @@ function collectPatchGroupTargetBlockIds(patchGroup) {
247198
247317
  return [...blockIds];
247199
247318
  }
247200
247319
  function inferHistoryStorySurfaceFamily(sourceKind) {
247320
+ if (sourceKind === "compareApply")
247321
+ return "compareApply";
247201
247322
  if (sourceKind === "editText")
247202
247323
  return "storyText";
247203
247324
  if (sourceKind.startsWith("list"))
@@ -407350,7 +407471,7 @@ function parseRun2(runXml, textTagLocalName = "t", inheritedNamespaceBindings =
407350
407471
  atomKind: INLINE_ATOM_KIND.noteRef,
407351
407472
  id: attrLocal2(next.openTagInner, "id"),
407352
407473
  xml: next.xml,
407353
- meta: { kind: local },
407474
+ meta: { kind: local, runAttributes: attributes },
407354
407475
  visibleLength: 0
407355
407476
  });
407356
407477
  } else if (local === "sym" || local === "noBreakHyphen") {
@@ -407361,7 +407482,7 @@ function parseRun2(runXml, textTagLocalName = "t", inheritedNamespaceBindings =
407361
407482
  atomKind: INLINE_ATOM_KIND.preserved,
407362
407483
  id: null,
407363
407484
  xml: next.xml,
407364
- meta: { tag: local, container: "run-child" },
407485
+ meta: { tag: local, container: "run-child", runAttributes: attributes },
407365
407486
  visibleLength: 0
407366
407487
  });
407367
407488
  }
@@ -420593,7 +420714,9 @@ function serializeBlockXml(block, extraAtoms = [], context = { inherited: CANONI
420593
420714
  const atomXml = atom.kind === "synthetic-comment-marker" ? `<${w}:${atom.meta.marker} ${w}:id="${xmlEscape(String(atom.meta.commentId))}"/>` : atom.xml;
420594
420715
  const attributedBreakXml = runBackedAttributedBreakXml(atom);
420595
420716
  if (atomEmissionMode(atom) === "run-child") {
420596
- sb.push(serializeInlineRunAtom(attributedBreakXml ?? atomXml, runAttributes, w));
420717
+ const atomRunAttributes = atom.meta.runAttributes;
420718
+ const attributes = atomRunAttributes && typeof atomRunAttributes === "object" && !Array.isArray(atomRunAttributes) ? atomRunAttributes : runAttributes;
420719
+ sb.push(serializeInlineRunAtom(attributedBreakXml ?? atomXml, attributes, w));
420597
420720
  } else {
420598
420721
  sb.push(atomXml);
420599
420722
  }
@@ -424884,7 +425007,7 @@ function hasStrictMirrorPrerequisites(prepared, options) {
424884
425007
  case "storyAnchor":
424885
425008
  return options.session != null;
424886
425009
  case "compareApply":
424887
- return false;
425010
+ return options.session != null;
424888
425011
  default: {
424889
425012
  const exhaustive = extension2.family;
424890
425013
  return exhaustive;
@@ -424982,13 +425105,24 @@ function runFamilyMirror(prepared, extension2, options, directStoryText, tracked
424982
425105
  return mirrorPreparedTrackedDecide(prepared, trackedDecision);
424983
425106
  }
424984
425107
  if (extension2.family === "compareApply") {
424985
- return {
424986
- mirrored: false,
424987
- failure: {
424988
- storyId: prepared.resolvedTargets.stories[0] ?? "",
424989
- message: "story-surface mirror for the compareApply family is not supported (apply-deferred)"
424990
- }
424991
- };
425108
+ if (!options.session) {
425109
+ return {
425110
+ mirrored: false,
425111
+ mirrorPath: "session-sync",
425112
+ failure: {
425113
+ storyId: prepared.resolvedTargets.stories[0] ?? "",
425114
+ message: "compareApply session sync requires an authoritative session"
425115
+ }
425116
+ };
425117
+ }
425118
+ const sessionSync = mirrorStorySurfaceFromSession({
425119
+ session: options.session,
425120
+ prepared,
425121
+ extension: extension2,
425122
+ storyIds: storyIdsRequiringShards(prepared, extension2),
425123
+ resolveStoryShard: options.resolveStoryShard
425124
+ });
425125
+ return { ...sessionSync, mirrorPath: "session-sync" };
424992
425126
  }
424993
425127
  if (extension2.family === "storyAnchor" && prepared.source.kind === "insertComment" && !prepared.source.materialize) {
424994
425128
  return mirrorPreparedCommentInsert(prepared, extension2, options);
@@ -425756,6 +425890,9 @@ function admitPreparedStorySurfaceMutation(prepared, options) {
425756
425890
  }
425757
425891
  if (!touchesOwnedStoryPart(prepared))
425758
425892
  return { ok: true };
425893
+ if (extension2.family === "compareApply") {
425894
+ return validateCompareApplySessionSync(prepared, extension2, options);
425895
+ }
425759
425896
  if (extension2.family === "storyText") {
425760
425897
  if (extension2.historyReplay || extension2.resyncFromSession || extension2.sessionSync) {
425761
425898
  return validateStoryTextSessionSync(prepared, extension2, options);
@@ -425824,6 +425961,32 @@ function admitPreparedStorySurfaceMutation(prepared, options) {
425824
425961
  }
425825
425962
  return { ok: true };
425826
425963
  }
425964
+ function validateCompareApplySessionSync(prepared, extension2, options) {
425965
+ const source = prepared.source;
425966
+ if (source.kind !== "compareApply" || !options.session) {
425967
+ return { ok: false, message: "compareApply session sync requires an authoritative session" };
425968
+ }
425969
+ if (options.isSessionProjectionCurrent?.() !== true) {
425970
+ return { ok: false, message: "compareApply requires a current collaborative session projection" };
425971
+ }
425972
+ const storyId = String(source.storyId);
425973
+ const ownership = prepared.resolvedTargets.storyPartOwnership;
425974
+ if (!extension2.storyLocal || prepared.resolvedTargets.stories.length !== 1 || String(prepared.resolvedTargets.stories[0]) !== storyId || ownership.length !== 1 || String(ownership[0]?.storyId) !== storyId || prepared.resolvedTargets.partUris.length !== 1 || String(prepared.resolvedTargets.partUris[0]) !== String(ownership[0]?.partUri)) {
425975
+ return { ok: false, message: "compareApply collaboration requires one owned story part" };
425976
+ }
425977
+ if (source.bodySteps.length === 0 || source.bodySteps.some((step3) => step3.kind !== "edit") || source.storyGroups?.length || source.storyPackageOperations?.length || source.commentSteps?.length || source.packageRewrites?.length || source.relationshipBackedBodyRewrites?.length || source.headerFooterLifecycle || source.headerFooterSlotSteps?.length || source.tableTopologySteps?.length || source.tableBlockSteps?.length || source.tablePropertySteps?.length || source.sectionReplaySteps?.length || source.referenceReplaySteps?.length) {
425978
+ return { ok: false, message: "compareApply collaboration supports paragraph text edits only" };
425979
+ }
425980
+ const targetBlockIds2 = [...new Set(source.bodySteps.map((step3) => step3.blockId))];
425981
+ if (targetBlockIds2.length !== extension2.targetBlockIds.length || targetBlockIds2.some((blockId) => !extension2.targetBlockIds.includes(blockId))) {
425982
+ return { ok: false, message: "compareApply target blocks do not match its prepared refresh scope" };
425983
+ }
425984
+ const shard = options.resolveStoryShard(storyId);
425985
+ if (!shard || targetBlockIds2.some((blockId) => !findBlockMap(shard, blockId))) {
425986
+ return { ok: false, message: "compareApply target blocks are missing from the story shard" };
425987
+ }
425988
+ return { ok: true };
425989
+ }
425827
425990
  function validateStoryTextSessionSync(prepared, extension2, options) {
425828
425991
  if (!options.session) {
425829
425992
  return { ok: false, message: "storyText session sync requires an authoritative session" };
@@ -427460,6 +427623,7 @@ function createCollabV2SessionProjectionController(options) {
427460
427623
  const storyBackend = createStorySurfaceExecutionBackend({
427461
427624
  delegate: packageBackend,
427462
427625
  session: options.session,
427626
+ isSessionProjectionCurrent: () => !destroyed && !remoteProjectionPending && !deferredSessionAuthoritativeRemotePending && sessionStoryAlignment === "aligned",
427463
427627
  resolveStoryShard(storyId) {
427464
427628
  const handle = options.dispatcherRegistry.findStoryShardByStoryId?.(storyId);
427465
427629
  return handle ? collabUnitTargetOrDoc(handle) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.2.0-next.4",
3
+ "version": "1.2.0-next.6",
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.4",
27
+ "@superdoc/sdk": "2.15.0-next.6",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.18.0-next.4",
33
- "@superdoc/docx-engine": "0.17.0-next.4",
32
+ "superdoc": "2.18.0-next.6",
33
+ "@superdoc/docx-engine": "0.17.0-next.7",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",