@superdoc/mcp 1.9.0-next.6 → 1.9.0-next.8

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 +168 -25
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -31620,7 +31620,7 @@ var init_host = __esm(() => {
31620
31620
  });
31621
31621
 
31622
31622
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
31623
- var SDK_VERSION = "2.22.0-next.6";
31623
+ var SDK_VERSION = "2.22.0-next.8";
31624
31624
 
31625
31625
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31626
31626
  import { accessSync, chmodSync, constants, existsSync, statSync } from "node:fs";
@@ -127497,7 +127497,7 @@ function contextKey(value) {
127497
127497
  }
127498
127498
  function memoCanonicalXmlAnalysis(input, compute) {
127499
127499
  const frame = activeFrame;
127500
- if (!frame || frame.cold || frame.retained?.released || input.xml.length > MAX_WINDOW_CHARACTERS)
127500
+ if (!frame || frame.cold || frame.retained?.released || input.xml.length > MAX_WINDOW_CHARACTERS || input.kind === "paragraph-inline" && input.xml.length > MAX_INLINE_WINDOW_CHARACTERS)
127501
127501
  return compute(input.namespaceBindings);
127502
127502
  const namespaceKey = issuedNamespaceKeys.get(input.namespaceBindings);
127503
127503
  if (namespaceKey === undefined)
@@ -127658,7 +127658,7 @@ function memoRetainedAnalysis(cache, key, xml, compute) {
127658
127658
  cache.outputUnits += resources.units;
127659
127659
  return detached;
127660
127660
  }
127661
- var activeFrame = null, MAX_WINDOW_CHARACTERS, MAX_RETAINED_CHARACTERS, MAX_ENTRIES = 32, MAX_RETAINED_OUTPUT_UNITS = 262144, MAX_RETAINED_OUTPUT_BYTES, issuedNamespaceKeys, retainedAnalyses, releasedOwners, CANONICAL_XML_EMPTY_NAMESPACE_BINDINGS;
127661
+ var activeFrame = null, MAX_WINDOW_CHARACTERS, MAX_INLINE_WINDOW_CHARACTERS = 512, MAX_RETAINED_CHARACTERS, MAX_ENTRIES = 32, MAX_RETAINED_OUTPUT_UNITS = 262144, MAX_RETAINED_OUTPUT_BYTES, issuedNamespaceKeys, retainedAnalyses, releasedOwners, CANONICAL_XML_EMPTY_NAMESPACE_BINDINGS;
127662
127662
  var init_canonical_xml_analysis_frame = __esm(() => {
127663
127663
  MAX_WINDOW_CHARACTERS = 512 * 1024;
127664
127664
  MAX_RETAINED_CHARACTERS = 2 * 1024 * 1024;
@@ -194569,6 +194569,16 @@ function issueStructuralTrackedCarrierPartition(state, input) {
194569
194569
  const ownership = readResidentParagraphOwnership(state, paragraph.blockId);
194570
194570
  if (!ownership || paragraph.storyId !== state.mainStoryId || input.resultParagraphs.length !== 2 || input.beforeXml.length > 512 * 1024 || input.afterXml.length > 512 * 1024)
194571
194571
  return null;
194572
+ const local = buildTrackedChangeCatalogFromPartXml({
194573
+ scanScope: "bounded-source-window",
194574
+ xml: input.beforeXml,
194575
+ partUri: paragraph.partUri,
194576
+ storyId: paragraph.storyId,
194577
+ namespaceBindings: ownership.namespaceBindings
194578
+ });
194579
+ const sourceKeys2 = [...local.byRsid.keys()];
194580
+ if (!sourceKeys2.length)
194581
+ return null;
194572
194582
  const inlineMaps = new Map;
194573
194583
  const readInline = (xml) => {
194574
194584
  let value = inlineMaps.get(xml);
@@ -194597,16 +194607,6 @@ function issueStructuralTrackedCarrierPartition(state, input) {
194597
194607
  const renderedPayloads = renderedParagraphs.map((xml) => inlinePayload(xml, ownership.namespaceBindings, readInline, lineage, input.authoredMarkWordId));
194598
194608
  if (!before || resultPayloads.some((payload) => !payload) || JSON.stringify(compactPayload(before)) !== JSON.stringify(compactPayload(resultPayloads.flatMap((payload) => payload))) || renderedPayloads.some((payload) => !payload) || JSON.stringify(compactPayload(renderedPayloads.flatMap((payload) => payload))) !== JSON.stringify(compactPayload(before)) || !proveClosedCommentAnchorPreservation(input.beforeXml, input.afterXml, ownership.namespaceBindings))
194599
194609
  return null;
194600
- const local = buildTrackedChangeCatalogFromPartXml({
194601
- scanScope: "bounded-source-window",
194602
- xml: input.beforeXml,
194603
- partUri: paragraph.partUri,
194604
- storyId: paragraph.storyId,
194605
- namespaceBindings: ownership.namespaceBindings
194606
- });
194607
- const sourceKeys2 = [...local.byRsid.keys()];
194608
- if (!sourceKeys2.length)
194609
- return null;
194610
194610
  for (const mode of ["paired", "independent"]) {
194611
194611
  const view = readResidentReviewCatalog(state, mode, sourceKeys2, true);
194612
194612
  if (!view || !componentFits(view, paragraph.byteStart, paragraph.byteEnd, paragraph.partUri, paragraph.storyId))
@@ -253967,7 +253967,7 @@ function parseSimpleRunTokens(xml) {
253967
253967
  const tMatch = /^\s*<([\w.-]+):t\b([^>]*)>([\s\S]*?)<\/\1:t>\s*$/.exec(afterRPr);
253968
253968
  if (!tMatch)
253969
253969
  return null;
253970
- const text = decodeCompareXmlText(tMatch[3] ?? "");
253970
+ const text = decodeXmlTextPreservingInvalidNumericEntities(tMatch[3] ?? "");
253971
253971
  tokens.push({
253972
253972
  rawXml: match[0],
253973
253973
  openTag: `<${prefix}:r${runAttrs}>${rPrXml}`,
@@ -254027,7 +254027,9 @@ function equalWordTokensPreserveFormatting(oldTokens, targetTokens) {
254027
254027
  let targetIndex = 0;
254028
254028
  for (const op of ops) {
254029
254029
  if (op === "equal") {
254030
- if (oldWords[oldIndex]?.openTag !== targetWords[targetIndex]?.openTag)
254030
+ const oldFormat = oldWords[oldIndex].openTag;
254031
+ const targetFormat = targetWords[targetIndex].openTag;
254032
+ if (oldFormat !== targetFormat)
254031
254033
  return false;
254032
254034
  oldIndex += 1;
254033
254035
  targetIndex += 1;
@@ -254041,24 +254043,165 @@ function equalWordTokensPreserveFormatting(oldTokens, targetTokens) {
254041
254043
  }
254042
254044
  return true;
254043
254045
  }
254044
- function renderWordLevelTrackedRunSegment(oldSegment, targetSegment, getStamp, prefix) {
254045
- const oldTokens = parseSimpleRunTokens(oldSegment);
254046
- const targetTokens = parseSimpleRunTokens(targetSegment);
254047
- if (!oldTokens || !targetTokens)
254048
- return null;
254046
+ function unchangedCompareRunRangesPreserveFormatting(oldTokens, targetTokens, ops, prefix) {
254047
+ const formats = (tokens) => tokens.map((token) => ({
254048
+ end: token.startOffset + token.text.length,
254049
+ signature: compareRunCarrierSignature(`${token.openTag}</${token.runPrefix}:r>`, prefix)
254050
+ }));
254051
+ const oldFormats = formats(oldTokens), targetFormats = formats(targetTokens);
254052
+ let oldIndex = 0, targetIndex = 0;
254053
+ const equalRange = (oldFrom, targetFrom, length) => {
254054
+ while (length > 0) {
254055
+ while (oldFormats[oldIndex].end <= oldFrom)
254056
+ oldIndex += 1;
254057
+ while (targetFormats[targetIndex].end <= targetFrom)
254058
+ targetIndex += 1;
254059
+ const old = oldFormats[oldIndex], target = targetFormats[targetIndex];
254060
+ if (old.signature !== target.signature)
254061
+ return false;
254062
+ const matched = Math.min(length, old.end - oldFrom, target.end - targetFrom);
254063
+ oldFrom += matched;
254064
+ targetFrom += matched;
254065
+ length -= matched;
254066
+ }
254067
+ return true;
254068
+ };
254069
+ let oldCursor = 0, targetCursor = 0;
254070
+ for (const op of ops) {
254071
+ const start = op.type === "insert" ? op.insertAt : op.oldFrom;
254072
+ const retained = start - oldCursor;
254073
+ if (!equalRange(oldCursor, targetCursor, retained))
254074
+ return false;
254075
+ oldCursor = op.type === "insert" ? start : op.oldTo;
254076
+ targetCursor += retained + (op.type === "delete" ? 0 : op.newText.length);
254077
+ }
254078
+ const oldLength = oldFormats.at(-1)?.end ?? 0;
254079
+ return equalRange(oldCursor, targetCursor, oldLength - oldCursor);
254080
+ }
254081
+ function prepareCompareWordSegment(oldTokens, targetTokens, checkDensity, prefix) {
254049
254082
  const oldText = oldTokens.map((token) => token.text).join("");
254050
254083
  const targetText = targetTokens.map((token) => token.text).join("");
254051
254084
  if (oldText === targetText)
254052
254085
  return null;
254053
- if (necessarilyExceedsWordDiffRatio(oldText, targetText, WORD_DIFF_FALLBACK_RATIO))
254086
+ if (oldText.includes("\r") || targetText.includes("\r"))
254087
+ return null;
254088
+ if (checkDensity && necessarilyExceedsWordDiffRatio(oldText, targetText, WORD_DIFF_FALLBACK_RATIO))
254054
254089
  return null;
254055
- if (!equalWordTokensPreserveFormatting(oldTokens, targetTokens))
254090
+ if (!prefix && !equalWordTokensPreserveFormatting(oldTokens, targetTokens))
254056
254091
  return null;
254057
254092
  const ops = getWordChanges(oldText, targetText);
254058
- if (ops.length === 0)
254093
+ if (ops.length === 0 || checkDensity && shouldFallBackFromWordDiff(ops, oldText.length, targetText.length))
254094
+ return null;
254095
+ if (prefix && !unchangedCompareRunRangesPreserveFormatting(oldTokens, targetTokens, ops, prefix))
254059
254096
  return null;
254060
- if (shouldFallBackFromWordDiff(ops, oldText.length, targetText.length))
254097
+ return { oldTokens, targetTokens, oldText, ops };
254098
+ }
254099
+ function compareRunCarrierSignature(xml, prefix) {
254100
+ const canonical = canonicalizeXmlForSignature(`<root xmlns:${prefix}="${NS_W}">${xml}</root>`);
254101
+ return canonical.replace(/<w:r\b[^>]*>/g, (run) => run.replace(/\s+w:rsid\w*="[^"]*"/g, ""));
254102
+ }
254103
+ function splitCompareManualBreakSegments(xml, prefix) {
254104
+ const parsed = collectXmlElementRanges(xml, { fragment: true, additionalNamespaces: { [prefix]: NS_W } });
254105
+ if (parsed.errors.length > 0)
254061
254106
  return null;
254107
+ const ranges = parsed.ranges.sort((left, right) => left.start - right.start);
254108
+ const spans = [""];
254109
+ const breaks = [];
254110
+ let cursor = 0;
254111
+ for (let index = 0;index < ranges.length; ) {
254112
+ const run = ranges[index++];
254113
+ if (xml.slice(cursor, run.start).trim() || run.tag.namespaceUri !== NS_W || run.tag.localName !== "r" || run.tag.prefix !== prefix || run.tag.isSelfClosing)
254114
+ return null;
254115
+ const children = [];
254116
+ let childCursor = run.openEnd;
254117
+ while (index < ranges.length && ranges[index].start < run.closeStart) {
254118
+ const child = ranges[index++];
254119
+ if (child.start < childCursor)
254120
+ continue;
254121
+ if (xml.slice(childCursor, child.start).trim() || child.end > run.closeStart)
254122
+ return null;
254123
+ children.push(child);
254124
+ childCursor = child.end;
254125
+ }
254126
+ if (xml.slice(childCursor, run.closeStart).trim())
254127
+ return null;
254128
+ const rPr = children[0]?.tag.namespaceUri === NS_W && children[0].tag.localName === "rPr" ? children.shift() : undefined;
254129
+ const openTag = xml.slice(run.start, run.openEnd) + (rPr ? xml.slice(rPr.start, rPr.end) : "");
254130
+ if (children.length === 0)
254131
+ return null;
254132
+ for (const child of children) {
254133
+ if (child.tag.namespaceUri !== NS_W || child.tag.prefix !== prefix)
254134
+ return null;
254135
+ const childXml = xml.slice(child.start, child.end);
254136
+ const carrier = children.length === 1 ? xml.slice(run.start, run.end) : `${openTag}${childXml}</${prefix}:r>`;
254137
+ if (child.tag.localName === "t") {
254138
+ if (child.tag.isSelfClosing || xml.slice(child.openEnd, child.closeStart).includes("<") || child.tag.attributes.some((attr3) => !(attr3.namespaceUri === "http://www.w3.org/XML/1998/namespace" && attr3.localName === "space")))
254139
+ return null;
254140
+ spans[spans.length - 1] += carrier;
254141
+ } else if (child.tag.localName === "br") {
254142
+ if (xml.slice(child.openEnd, child.closeStart).trim() || child.tag.attributes.some((attr3) => attr3.namespaceUri !== NS_W || (attr3.localName === "type" ? attr3.value !== "textWrapping" : attr3.localName !== "clear" || !["none", "left", "right", "all"].includes(attr3.value))))
254143
+ return null;
254144
+ breaks.push(carrier);
254145
+ spans.push("");
254146
+ } else
254147
+ return null;
254148
+ }
254149
+ cursor = run.end;
254150
+ }
254151
+ if (xml.slice(cursor).trim() || breaks.length === 0)
254152
+ return null;
254153
+ return { spans, breaks };
254154
+ }
254155
+ function renderWordLevelTrackedRunSegment(oldSegment, targetSegment, getStamp, prefix) {
254156
+ const simpleOld = parseSimpleRunTokens(oldSegment);
254157
+ const simpleTarget = parseSimpleRunTokens(targetSegment);
254158
+ if (simpleOld && simpleTarget && !/<[\w.-]+:br\b/.test(oldSegment + targetSegment)) {
254159
+ const prepared2 = prepareCompareWordSegment(simpleOld, simpleTarget, true);
254160
+ return prepared2 ? { xml: renderPreparedCompareWordSegment(prepared2, getStamp, prefix) } : null;
254161
+ }
254162
+ const old = splitCompareManualBreakSegments(oldSegment, prefix);
254163
+ const target = splitCompareManualBreakSegments(targetSegment, prefix);
254164
+ if (!old || !target || old.breaks.length !== target.breaks.length || old.breaks.some((br, index) => compareRunCarrierSignature(br, prefix) !== compareRunCarrierSignature(target.breaks[index], prefix)))
254165
+ return null;
254166
+ const spans = [];
254167
+ for (let index = 0;index < old.spans.length; index += 1) {
254168
+ const oldSpan = old.spans[index];
254169
+ const targetSpan = target.spans[index];
254170
+ const oldTokens = parseSimpleRunTokens(oldSpan);
254171
+ const targetTokens = parseSimpleRunTokens(targetSpan);
254172
+ if (!oldTokens || !targetTokens)
254173
+ return null;
254174
+ if (oldTokens.some((token) => token.text.length === 0) || targetTokens.some((token) => token.text.length === 0))
254175
+ return null;
254176
+ const oldText2 = oldTokens.map((token) => token.text).join("");
254177
+ const targetText2 = targetTokens.map((token) => token.text).join("");
254178
+ spans.push({ oldSpan, oldTokens, targetTokens, oldText: oldText2, targetText: targetText2 });
254179
+ }
254180
+ const oldText = spans.map((span) => span.oldText).join(`
254181
+ `);
254182
+ const targetText = spans.map((span) => span.targetText).join(`
254183
+ `);
254184
+ if (necessarilyExceedsWordDiffRatio(oldText, targetText, WORD_DIFF_FALLBACK_RATIO))
254185
+ return null;
254186
+ const prepared = [];
254187
+ for (const { oldSpan, oldTokens, targetTokens, oldText: oldText2, targetText: targetText2 } of spans) {
254188
+ if (oldText2 === targetText2) {
254189
+ if (!unchangedCompareRunRangesPreserveFormatting(oldTokens, targetTokens, [], prefix))
254190
+ return null;
254191
+ prepared.push(oldSpan);
254192
+ } else {
254193
+ const span = prepareCompareWordSegment(oldTokens, targetTokens, false, prefix);
254194
+ if (!span)
254195
+ return null;
254196
+ prepared.push(span);
254197
+ }
254198
+ }
254199
+ const ops = prepared.flatMap((span) => typeof span === "string" ? [] : span.ops);
254200
+ if (ops.length === 0 || shouldFallBackFromWordDiff(ops, oldText.length, targetText.length))
254201
+ return null;
254202
+ return { xml: prepared.map((span, index) => (typeof span === "string" ? span : renderPreparedCompareWordSegment(span, getStamp, prefix)) + (old.breaks[index] ?? "")).join("") };
254203
+ }
254204
+ function renderPreparedCompareWordSegment({ oldTokens, targetTokens, oldText, ops }, getStamp, prefix) {
254062
254205
  let xml = "";
254063
254206
  let oldCursor = 0;
254064
254207
  let newCursor = 0;
@@ -254087,7 +254230,7 @@ function renderWordLevelTrackedRunSegment(oldSegment, targetSegment, getStamp, p
254087
254230
  if (oldCursor < oldText.length) {
254088
254231
  xml += renderCompareRunRange(oldTokens, oldCursor, oldText.length, "passthrough");
254089
254232
  }
254090
- return { xml };
254233
+ return xml;
254091
254234
  }
254092
254235
  function runCompareBodyFormatEditCommit(input, storyId, step2, changeMode, options, pushHistory, referenceSteps) {
254093
254236
  const { state, txId } = input;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.9.0-next.6",
3
+ "version": "1.9.0-next.8",
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.22.0-next.6",
27
+ "@superdoc/sdk": "2.22.0-next.8",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.25.0-next.6",
33
- "@superdoc/docx-engine": "0.24.0-next.6",
32
+ "superdoc": "2.25.0-next.8",
33
+ "@superdoc/docx-engine": "0.24.0-next.8",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",