@superdoc/mcp 1.7.0-next.13 → 1.7.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 +214 -8
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -31600,7 +31600,7 @@ var init_host = __esm(() => {
31600
31600
  });
31601
31601
 
31602
31602
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
31603
- var SDK_VERSION = "2.20.0-next.13";
31603
+ var SDK_VERSION = "2.20.0-next.15";
31604
31604
 
31605
31605
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31606
31606
  import { accessSync, chmodSync, constants, existsSync, statSync } from "node:fs";
@@ -202834,6 +202834,18 @@ function commitTracked(input, view) {
202834
202834
  newText: op.newText,
202835
202835
  allocateWIdBlock: allocateLocalizedWIdBlock
202836
202836
  }, inlineContext, namespaceBindings);
202837
+ const refinementStart = compileOp.on.coordinateSpace === "tracked" ? projectTrackedParagraphOffsetToVisible(paragraphXml, compileOp.on.startOffset) : compileOp.on.startOffset;
202838
+ const refinementEnd = compileOp.on.coordinateSpace === "tracked" ? projectTrackedParagraphOffsetToVisible(paragraphXml, compileOp.on.endOffset) : compileOp.on.endOffset;
202839
+ const ownLinkedRefinement = refinementStart !== null && refinementEnd !== null ? refineOwnLinkedInsertion(inlineModel, refinementStart, refinementEnd, op.newText, stamping.author, state) : null;
202840
+ if (ownLinkedRefinement) {
202841
+ localized = {
202842
+ ok: true,
202843
+ bytes: TE17.encode(ownLinkedRefinement.xml),
202844
+ emittedBothSides: false,
202845
+ foldedInsRsid: ownLinkedRefinement.rsid,
202846
+ requiresCollaborationSessionSync: true
202847
+ };
202848
+ }
202837
202849
  const canSpanningDelete = compileOp.on.coordinateSpace !== "tracked" && op.newText.length === 0 && compileOp.on.startOffset < compileOp.on.endOffset;
202838
202850
  const spanningDeleteWIds = [];
202839
202851
  let usedSpanningDelete = false;
@@ -202971,6 +202983,17 @@ function commitTracked(input, view) {
202971
202983
  },
202972
202984
  primaryBytes: localized.bytes,
202973
202985
  rewrite: (paragraphBytes2, paragraphWindow) => {
202986
+ if (ownLinkedRefinement) {
202987
+ const counterpartXml = TD42.decode(paragraphBytes2.subarray(paragraphWindow.byteStart, paragraphWindow.byteEnd));
202988
+ const refinement = refineOwnLinkedInsertion(parseParagraphInlineMap(counterpartXml, namespaceBindings), refinementStart, refinementEnd, op.newText, stamping.author, state);
202989
+ return refinement ? {
202990
+ ok: true,
202991
+ bytes: TE17.encode(refinement.xml),
202992
+ emittedBothSides: false,
202993
+ foldedInsRsid: refinement.rsid,
202994
+ requiresCollaborationSessionSync: true
202995
+ } : { ok: false, reason: "textbox-counterpart-ambiguous" };
202996
+ }
202974
202997
  let additionalWIdIndex = 0;
202975
202998
  const base2 = rewriteLocalizedTracked(paragraphBytes2, paragraphWindow, compileOp, {
202976
202999
  rsid: op.rsid,
@@ -206222,6 +206245,80 @@ function containsOnlyTrackedWrappersAndZeroWidth(xml) {
206222
206245
  return false;
206223
206246
  return containsOnlyZeroWidthParagraphTail(withoutWrappers);
206224
206247
  }
206248
+ function refineOwnLinkedInsertion(model, start, end, newText, author, state) {
206249
+ if (newText.includes("\t"))
206250
+ return null;
206251
+ const wrapper = model.pieces.find((piece) => piece.kind === "tracked" && piece.trackedKind === "ins" && piece.visibleStart <= start && piece.visibleEnd >= end);
206252
+ if (!wrapper || wrapper.kind !== "tracked" || !wrapper.children.some((piece) => piece.kind === "container" && piece.containerKind === "complexField") || !wrapper.rsid || !collectSessionCreatedTrackedChangeRsids(state).has(wrapper.rsid) || wrapper.revisionId === null || !collectSessionCreatedTrackedChangeWIds(state).has(Number(wrapper.revisionId)) || classifyInsertionAuthoringOwnership(buildReviewIdentity(author), buildReviewIdentity({
206253
+ name: wrapper.author,
206254
+ email: wrapper.email,
206255
+ initials: wrapper.initials
206256
+ })) !== "same-user")
206257
+ return null;
206258
+ const fields = [];
206259
+ const texts = [];
206260
+ for (const child of wrapper.children) {
206261
+ if (child.kind === "text" && child.textRole === "visible")
206262
+ texts.push(child);
206263
+ else if (child.kind === "container" && child.containerKind === "complexField" && child.resultCharStart !== undefined) {
206264
+ const instructions = child.children.filter((piece) => piece.kind === "text" && piece.textRole === "instruction");
206265
+ if (!/^HYPERLINK\s/i.test(instructions.map((piece) => piece.kind === "text" ? piece.decodedText : "").join("").trim()))
206266
+ return null;
206267
+ for (const piece of child.children) {
206268
+ if (piece.kind === "text") {
206269
+ if (piece.textRole === "visible")
206270
+ texts.push(piece);
206271
+ else if (piece.textRole !== "instruction")
206272
+ return null;
206273
+ } else if (piece.kind !== "marker" || piece.role !== "field")
206274
+ return null;
206275
+ }
206276
+ fields.push(child);
206277
+ } else
206278
+ return null;
206279
+ }
206280
+ if (!fields.length)
206281
+ return null;
206282
+ const paragraphXml = model.paragraphXml;
206283
+ if (start === wrapper.visibleStart && end === wrapper.visibleEnd && newText === "") {
206284
+ return { xml: paragraphXml.slice(0, wrapper.charStart) + paragraphXml.slice(wrapper.charEnd), rsid: wrapper.rsid };
206285
+ }
206286
+ const selected = start === end ? texts.filter((piece) => piece.visibleStart <= start && piece.visibleEnd >= start) : texts.filter((piece) => piece.visibleStart < end && piece.visibleEnd > start);
206287
+ const fieldFor = (piece) => fields.find((field2) => piece.charStart >= field2.resultCharStart && piece.charEnd <= field2.resultCharEnd);
206288
+ if (start === end) {
206289
+ const ordinary = selected.find((piece) => !fieldFor(piece));
206290
+ const interior = selected.find((piece) => piece.visibleStart < start && piece.visibleEnd > start);
206291
+ const chosen = ordinary ?? interior;
206292
+ if (!chosen)
206293
+ return null;
206294
+ selected.splice(0, selected.length, chosen);
206295
+ }
206296
+ const first = selected[0];
206297
+ const last = selected.at(-1);
206298
+ if (!first || !last || first.visibleStart > start || last.visibleEnd < end)
206299
+ return null;
206300
+ const field = fieldFor(first);
206301
+ if (selected.some((piece) => fieldFor(piece) !== field))
206302
+ return null;
206303
+ const patches = [];
206304
+ for (const [index, piece] of selected.entries()) {
206305
+ const tag = /^<([\w.-]+):t\b([^>]*)>[\s\S]*?<\/\1:t>$/.exec(piece.rawXml);
206306
+ if (!tag)
206307
+ return null;
206308
+ const localStart = Math.max(0, start - piece.visibleStart);
206309
+ const localEnd = Math.min(piece.decodedText.length, end - piece.visibleStart);
206310
+ const text = piece.decodedText.slice(0, localStart) + (index === 0 ? newText : "") + piece.decodedText.slice(localEnd);
206311
+ patches.push({
206312
+ start: piece.charStart,
206313
+ end: piece.charEnd,
206314
+ xml: `<${tag[1]}:t${preserveTextElementAttributes(tag[2], text)}>${escapeXmlText(text)}</${tag[1]}:t>`
206315
+ });
206316
+ }
206317
+ let rewritten = paragraphXml;
206318
+ for (const patch of patches.reverse())
206319
+ rewritten = rewritten.slice(0, patch.start) + patch.xml + rewritten.slice(patch.end);
206320
+ return { xml: rewritten, rsid: wrapper.rsid };
206321
+ }
206225
206322
  function spliceWithinSameAuthorInsertion(paragraphXml, startOffset, endOffset, newText, author) {
206226
206323
  const createdVisibleTextSpan = startOffset === endOffset && newText.length > 0 ? {
206227
206324
  startOffset,
@@ -207290,7 +207387,7 @@ function isolateSimpleTextRangeInParagraphXml(input) {
207290
207387
  if (input.rangeStart < 0 || input.rangeEnd <= input.rangeStart) {
207291
207388
  return { ok: false, reason: "invalid-range" };
207292
207389
  }
207293
- const contextualRejection = rejectSimpleTextRangeContext(input.paragraphXml, input.rangeStart, input.rangeEnd, input.allowDisjointTracked ?? false);
207390
+ const contextualRejection = rejectSimpleTextRangeContext(input.paragraphXml, input.rangeStart, input.rangeEnd, input.allowDisjointTracked ?? false, input.allowDisjointFields ?? false);
207294
207391
  if (contextualRejection)
207295
207392
  return { ok: false, reason: contextualRejection };
207296
207393
  const paragraphOpen = /^<([\w.-]+):p\b[^>]*>/.exec(input.paragraphXml);
@@ -207303,7 +207400,7 @@ function isolateSimpleTextRangeInParagraphXml(input) {
207303
207400
  }
207304
207401
  const runs = [];
207305
207402
  const runRe = /<([\w.-]+):r\b[^>]*>[\s\S]*?<\/\1:r>/g;
207306
- const ordinaryRunStarts = input.allowDisjointTracked ? new Map : null;
207403
+ const ordinaryRunStarts = input.allowDisjointTracked || input.allowDisjointFields ? new Map : null;
207307
207404
  if (ordinaryRunStarts) {
207308
207405
  for (const piece of parseParagraphInlineMap(input.paragraphXml).leaves()) {
207309
207406
  if (piece.kind === "text" && piece.textRole === "visible" && piece.containerStack.length === 0 && !ordinaryRunStarts.has(piece.runCharStart)) {
@@ -207378,9 +207475,11 @@ function renderSimpleTextRunSlice(slice, options = {}) {
207378
207475
  }).join("");
207379
207476
  return `${slice.runOpenTag}${options.rPrXml ?? slice.rPrXml}${textXml}${slice.runCloseTag}`;
207380
207477
  }
207381
- function rejectSimpleTextRangeContext(paragraphXml, rangeStart, rangeEnd, allowDisjointTracked) {
207382
- if (/<[\w.-]+:(?:fldSimple|fldChar|instrText|delInstrText)\b/.test(paragraphXml))
207383
- return "field";
207478
+ function rejectSimpleTextRangeContext(paragraphXml, rangeStart, rangeEnd, allowDisjointTracked, allowDisjointFields) {
207479
+ if (/<[\w.-]+:(?:fldSimple|fldChar|instrText|delInstrText)\b/.test(paragraphXml)) {
207480
+ if (!allowDisjointFields || !hasOnlyDisjointFields(paragraphXml, rangeStart, rangeEnd))
207481
+ return "field";
207482
+ }
207384
207483
  if (/<[\w.-]+:(?:commentRangeStart|commentRangeEnd|commentReference)\b/.test(paragraphXml)) {
207385
207484
  return "comment";
207386
207485
  }
@@ -207402,6 +207501,34 @@ function rejectSimpleTextRangeContext(paragraphXml, rangeStart, rangeEnd, allowD
207402
207501
  return "drawing";
207403
207502
  return null;
207404
207503
  }
207504
+ function hasOnlyDisjointFields(paragraphXml, rangeStart, rangeEnd) {
207505
+ const fields = [];
207506
+ function visit(pieces) {
207507
+ for (const piece of pieces) {
207508
+ if (piece.kind !== "container")
207509
+ continue;
207510
+ if (piece.containerKind === "complexField" || piece.containerKind === "fldSimple") {
207511
+ if (rangeStart < piece.visibleEnd && rangeEnd > piece.visibleStart)
207512
+ return false;
207513
+ if (piece.containerKind === "complexField" && piece.resultCharStart === undefined)
207514
+ return false;
207515
+ fields.push({ start: piece.charStart, end: piece.charEnd });
207516
+ } else if (!visit(piece.children))
207517
+ return false;
207518
+ }
207519
+ return true;
207520
+ }
207521
+ if (!visit(parseParagraphInlineMap(paragraphXml).pieces))
207522
+ return false;
207523
+ let outsideFields = "";
207524
+ let cursor = 0;
207525
+ for (const field of fields) {
207526
+ outsideFields += paragraphXml.slice(cursor, field.start);
207527
+ cursor = field.end;
207528
+ }
207529
+ outsideFields += paragraphXml.slice(cursor);
207530
+ return !/<[\w.-]+:(?:fldSimple|fldChar|instrText|delInstrText)\b/.test(outsideFields);
207531
+ }
207405
207532
  function hasTrackedOverlap(pieces, rangeStart, rangeEnd) {
207406
207533
  for (const piece of pieces) {
207407
207534
  if (piece.kind === "tracked" && rangeStart < piece.visibleEnd && rangeEnd > piece.visibleStart) {
@@ -247417,6 +247544,80 @@ function runHyperlinkWrapCommit(input) {
247417
247544
  if (!bytes)
247418
247545
  return rejected19(input.txId, "hyperlink-target-not-found");
247419
247546
  const paraXml = TD71.decode(bytes.subarray(paragraph.byteStart, paragraph.byteEnd));
247547
+ const overlappingInsertions = parseParagraphInlineMap(paraXml).pieces.filter((piece) => piece.kind === "tracked" && piece.trackedKind === "ins" && piece.visibleStart < op.on.endOffset && piece.visibleEnd > op.on.startOffset);
247548
+ if (overlappingInsertions.length > 0) {
247549
+ if (overlappingInsertions.length !== 1)
247550
+ return rejected19(input.txId, "hyperlink-tracked-or-comment-unsupported");
247551
+ const insertion = overlappingInsertions[0];
247552
+ const revisionId = Number(insertion.revisionId);
247553
+ if (insertion.visibleStart > op.on.startOffset || insertion.visibleEnd < op.on.endOffset || !Number.isSafeInteger(revisionId) || !collectSessionCreatedTrackedChangeWIds(input.state).has(revisionId) || !insertion.rsid || !collectSessionCreatedTrackedChangeRsids(input.state).has(insertion.rsid) || classifyInsertionAuthoringOwnership(buildReviewIdentity(stamping.author), buildReviewIdentity({ name: insertion.author, email: insertion.email, initials: insertion.initials })) !== "same-user")
247554
+ return rejected19(input.txId, "hyperlink-tracked-or-comment-unsupported");
247555
+ const textbox = input.state.textboxRegistry.byStoryId.get(op.on.storyId);
247556
+ if (textbox && textbox.alternateContentCounterparts.length > 1) {
247557
+ return rejected19(input.txId, "hyperlink-text-range-unsupported");
247558
+ }
247559
+ const openingEnd = paraXml.indexOf(">", insertion.charStart) + 1;
247560
+ const closingStart = paraXml.lastIndexOf("</", insertion.charEnd);
247561
+ if (openingEnd <= insertion.charStart || closingStart < openingEnd) {
247562
+ return rejected19(input.txId, "hyperlink-tracked-or-comment-unsupported");
247563
+ }
247564
+ const innerXml = paraXml.slice(openingEnd, closingStart);
247565
+ const isolatedInner = isolateSimpleTextRangeInParagraphXml({
247566
+ paragraphXml: `<w:p>${innerXml}</w:p>`,
247567
+ rangeStart: op.on.startOffset - insertion.visibleStart,
247568
+ rangeEnd: op.on.endOffset - insertion.visibleStart,
247569
+ allowDisjointFields: true
247570
+ });
247571
+ if (!isolatedInner.ok)
247572
+ return rejected19(input.txId, mapSimpleTextRangeIsolationReason(isolatedInner.reason));
247573
+ const selectedRuns = isolatedInner.value.selectedRuns.map((run) => renderSimpleTextRunSlice(run, { rPrXml: ensureHyperlinkRunStyle(run, characterStyleId) })).join("");
247574
+ const generatedReplacementXml = isolatedInner.value.beforeRunXml + buildHyperlinkFieldRunsXml({ target: op.target, metadata: op.metadata, resultRunsXml: selectedRuns }) + isolatedInner.value.afterRunXml;
247575
+ const replacementStart = openingEnd + isolatedInner.value.replaceStart - "<w:p>".length;
247576
+ const replacementEnd = openingEnd + isolatedInner.value.replaceEnd - "<w:p>".length;
247577
+ const replacementByteStart = paragraph.byteStart + utf8ByteLength6(paraXml, replacementStart);
247578
+ const replacementXml2 = closeNamespaceScope(generatedReplacementXml, {
247579
+ w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
247580
+ r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
247581
+ }, namespaceBindingsAtPosition(bytes, replacementByteStart), { referencedOnly: true });
247582
+ if (replacementXml2 === null)
247583
+ return rejected19(input.txId, "hyperlink-text-range-unsupported");
247584
+ const documentPatches2 = [{
247585
+ kind: "replaceBytes",
247586
+ order: 0,
247587
+ startByte: replacementByteStart,
247588
+ endByte: paragraph.byteStart + utf8ByteLength6(paraXml, replacementEnd),
247589
+ bytes: TE38.encode(replacementXml2)
247590
+ }];
247591
+ const perPart2 = {
247592
+ [paragraph.partUri]: { kind: "modify", patches: documentPatches2 },
247593
+ ...styleDependencies.value.perPart
247594
+ };
247595
+ return finalize6({
247596
+ input,
247597
+ storyId: op.on.storyId,
247598
+ storyPartUri: paragraph.partUri,
247599
+ sessionEffects: [{ after: compilerHyperlinkCarrier(paragraph.partUri, replacementByteStart, replacementXml2, true) }],
247600
+ perPart: perPart2,
247601
+ preconditions: [
247602
+ buildLocalizedByteWindowPrecondition({ partUri: paragraph.partUri, bytes, patches: documentPatches2 }),
247603
+ ...styleDependencies.value.preconditions
247604
+ ],
247605
+ extraPatchMaps: {},
247606
+ createdRefs: [],
247607
+ updatedRefs: [
247608
+ { kind: "trackedChange", entityId: insertion.rsid, storyId: op.on.storyId },
247609
+ { kind: "block", nodeId: op.on.paraId, storyId: op.on.storyId },
247610
+ { kind: "text", storyId: op.on.storyId, blockId: op.on.paraId, range: { start: op.on.startOffset, end: op.on.endOffset } }
247611
+ ],
247612
+ indexRefreshScope: {
247613
+ perPart: { [paragraph.partUri]: {
247614
+ beforeWindow: { startByte: paragraph.byteStart, endByte: paragraph.byteEnd },
247615
+ kind: "paragraph",
247616
+ blockIds: [op.on.paraId]
247617
+ } }
247618
+ }
247619
+ });
247620
+ }
247420
247621
  const isolated = isolateSimpleTextRangeInParagraphXml({
247421
247622
  paragraphXml: paraXml,
247422
247623
  rangeStart: op.on.startOffset,
@@ -247970,6 +248171,9 @@ function buildHyperlinkRunXml(input) {
247970
248171
  return `<w:hyperlink${attrSegment}>` + runXml + `</w:hyperlink>`;
247971
248172
  }
247972
248173
  function buildTrackedHyperlinkFieldXml(input) {
248174
+ return buildInsOpenTag2(input.stamp) + buildHyperlinkFieldRunsXml(input) + "</w:ins>";
248175
+ }
248176
+ function buildHyperlinkFieldRunsXml(input) {
247973
248177
  const instruction = buildHyperlinkFieldInstruction({
247974
248178
  href: input.target.kind === "external" ? input.target.url : undefined,
247975
248179
  anchor: input.target.kind === "anchor" ? input.target.anchor : undefined,
@@ -247977,7 +248181,7 @@ function buildTrackedHyperlinkFieldXml(input) {
247977
248181
  tooltip: input.metadata?.tooltip,
247978
248182
  targetFrame: input.metadata?.targetFrame
247979
248183
  });
247980
- return buildInsOpenTag2(input.stamp) + '<w:r><w:fldChar w:fldCharType="begin"/></w:r>' + `<w:r><w:instrText>${escapeText(instruction)}</w:instrText></w:r>` + '<w:r><w:fldChar w:fldCharType="separate"/></w:r>' + input.resultRunsXml + '<w:r><w:fldChar w:fldCharType="end"/></w:r>' + "</w:ins>";
248184
+ return '<w:r><w:fldChar w:fldCharType="begin"/></w:r>' + `<w:r><w:instrText>${escapeText(instruction)}</w:instrText></w:r>` + '<w:r><w:fldChar w:fldCharType="separate"/></w:r>' + input.resultRunsXml + '<w:r><w:fldChar w:fldCharType="end"/></w:r>';
247981
248185
  }
247982
248186
  function buildDefaultHyperlinkResultRunXml(text, characterStyleId) {
247983
248187
  return `<w:r><w:rPr><w:rStyle w:val="${escapeAttr7(characterStyleId)}"/></w:rPr><w:t xml:space="preserve">${escapeText(text)}</w:t></w:r>`;
@@ -248120,7 +248324,7 @@ function finalize6(args) {
248120
248324
  backendExtensions: {
248121
248325
  storySurface: {
248122
248326
  family: "storyHyperlink",
248123
- storyLocal: opFromInput.kind === "hyperlinkWrap",
248327
+ storyLocal: opFromInput.kind === "hyperlinkWrap" && Object.keys(args.perPart).every((partUri) => partUri === args.storyPartUri),
248124
248328
  changeMode: opFromInput.kind === "hyperlinkInsert" || opFromInput.kind === "hyperlinkWrap" ? opFromInput.changeMode ?? "direct" : "direct",
248125
248329
  targetBlockIds: args.updatedRefs.flatMap((ref3) => ref3.kind === "block" ? [ref3.nodeId] : []),
248126
248330
  atomCarrierContract: SD_DEFAULT_ATOM_CARRIER_CONTRACT
@@ -248178,8 +248382,10 @@ var init_compile_hyperlink_ops = __esm(() => {
248178
248382
  init_tracked_hyperlink_target();
248179
248383
  init_tracked_hyperlink_removal();
248180
248384
  init_xml_namespace_scope();
248385
+ init_paragraph_inline_model();
248181
248386
  init_paragraph_para_id_attrs();
248182
248387
  init_revision_wid_allocator();
248388
+ init_export_projection();
248183
248389
  init_semantic_style_dependencies();
248184
248390
  init_simple_text_run_split();
248185
248391
  TD71 = new TextDecoder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.7.0-next.13",
3
+ "version": "1.7.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.20.0-next.13",
27
+ "@superdoc/sdk": "2.20.0-next.15",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.23.0-next.13",
33
- "@superdoc/docx-engine": "0.22.0-next.13",
32
+ "superdoc": "2.23.0-next.15",
33
+ "@superdoc/docx-engine": "0.22.0-next.15",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",