@harbour-enterprises/superdoc 1.17.0-next.11 → 1.17.0-next.13

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 (33) hide show
  1. package/dist/chunks/{SuperConverter-D0cZ7DBR.es.js → SuperConverter-0JaBYpRa.es.js} +1 -1
  2. package/dist/chunks/{SuperConverter-DOQaFkjd.cjs → SuperConverter-BgHHB-Z2.cjs} +1 -1
  3. package/dist/chunks/{src-DQKyE7kq.cjs → src-ByCDwKn7.cjs} +458 -129
  4. package/dist/chunks/{src-bsYwK29H.es.js → src-C52EYA_H.es.js} +452 -130
  5. package/dist/super-editor/converter.cjs +1 -1
  6. package/dist/super-editor/converter.es.js +1 -1
  7. package/dist/super-editor/src/document-api-adapters/assemble-adapters.d.ts.map +1 -1
  8. package/dist/super-editor/src/document-api-adapters/capabilities-adapter.d.ts.map +1 -1
  9. package/dist/super-editor/src/document-api-adapters/errors.d.ts +1 -1
  10. package/dist/super-editor/src/document-api-adapters/errors.d.ts.map +1 -1
  11. package/dist/super-editor/src/document-api-adapters/helpers/node-address-resolver.d.ts +12 -0
  12. package/dist/super-editor/src/document-api-adapters/helpers/node-address-resolver.d.ts.map +1 -1
  13. package/dist/super-editor/src/document-api-adapters/index.d.ts.map +1 -1
  14. package/dist/super-editor/src/document-api-adapters/plan-engine/blocks-wrappers.d.ts +4 -0
  15. package/dist/super-editor/src/document-api-adapters/plan-engine/blocks-wrappers.d.ts.map +1 -0
  16. package/dist/super-editor/src/document-api-adapters/plan-engine/compiler.d.ts +14 -0
  17. package/dist/super-editor/src/document-api-adapters/plan-engine/compiler.d.ts.map +1 -1
  18. package/dist/super-editor/src/document-api-adapters/plan-engine/create-insertion.d.ts +16 -0
  19. package/dist/super-editor/src/document-api-adapters/plan-engine/create-insertion.d.ts.map +1 -0
  20. package/dist/super-editor/src/document-api-adapters/plan-engine/create-wrappers.d.ts.map +1 -1
  21. package/dist/super-editor/src/document-api-adapters/plan-engine/executor.d.ts.map +1 -1
  22. package/dist/super-editor/src/document-api-adapters/plan-engine/plan-wrappers.d.ts.map +1 -1
  23. package/dist/super-editor/src/document-api-adapters/plan-engine/preview.d.ts.map +1 -1
  24. package/dist/super-editor/src/document-api-adapters/plan-engine/query-match-adapter.d.ts.map +1 -1
  25. package/dist/super-editor/src/document-api-adapters/plan-engine/revision-tracker.d.ts.map +1 -1
  26. package/dist/super-editor/src/document-api-adapters/plan-engine/style-resolver.d.ts.map +1 -1
  27. package/dist/super-editor.cjs +2 -2
  28. package/dist/super-editor.es.js +2 -2
  29. package/dist/superdoc.cjs +3 -3
  30. package/dist/superdoc.es.js +3 -3
  31. package/dist/superdoc.umd.js +459 -137
  32. package/dist/superdoc.umd.js.map +1 -1
  33. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, n as __esmMin, r as __export, t as __commonJSMin } from "./rolldown-runtime-B2q5OVn9.es.js";
2
- import { $ as TextSelection$1, A as findMark, At as Mark$1, B as defaultBlockAt$1, C as docxNumberingHelpers, Ct as TrackDeleteMarkName, D as posToDOMRect, Dt as DOMParser$1, E as isInTable, Et as carbonCopy, F as isMarkActive, Ft as getExtensionConfigField, G as createDocument, H as getNodeType, I as getMarkRange, J as NodeSelection, K as AllSelection, L as isTextSelection, M as isActive, Mt as Slice, N as isNodeActive, Nt as minMax, O as findChildren$1, Ot as DOMSerializer, P as getSchemaTypeNameByName, Pt as callOrGet, Q as SelectionRange, R as findParentNode, S as getResolvedParagraphProperties, St as getUnderlineCssString, T as CommandService, Tt as TrackInsertMarkName, U as cleanSchemaItem, V as getMarkType, W as getSchemaTypeByName, X as PluginKey, Y as Plugin, Z as Selection, _ as ListHelpers, _t as encodeMarksFromRPr, a as _getReferencedTableStyles, at as Transform, b as isList, bt as resolveRunProperties, c as processContent, ct as dropPoint, d as createCellBorders, dt as replaceStep$1, et as AddMarkStep, f as InputRule, ft as generateDocxRandomId, g as unflattenListsInHtml, gt as encodeCSSFromRPr, h as inputRulesPlugin, ht as encodeCSSFromPPr, it as ReplaceStep, j as getMarksFromSelection, jt as Schema$1, k as getActiveFormatting, kt as Fragment$1, l as createDocFromMarkdown, lt as joinPoint, m as htmlHandler, mt as decodeRPrFromMarks, n as kebabCase$1, nt as RemoveMarkStep, o as helpers_exports, ot as canJoin, p as handleClipboardPaste, pt as generateRandomSigned32BitIntStrId, q as EditorState, r as insertNewRelationship, rt as ReplaceAroundStep$1, s as updateDOMAttributes, st as canSplit, t as SuperConverter, tt as Mapping, u as createDocFromHTML, ut as liftTarget, v as changeListLevel, vt as resolveDocxFontFamily, w as generateOrderedListIndex, wt as TrackFormatMarkName, x as calculateResolvedParagraphProperties, xt as resolveTableCellProperties, y as updateNumberingProperties, yt as resolveParagraphProperties, z as findParentNodeClosestToPos } from "./SuperConverter-D0cZ7DBR.es.js";
2
+ import { $ as TextSelection$1, A as findMark, At as Mark$1, B as defaultBlockAt$1, C as docxNumberingHelpers, Ct as TrackDeleteMarkName, D as posToDOMRect, Dt as DOMParser$1, E as isInTable, Et as carbonCopy, F as isMarkActive, Ft as getExtensionConfigField, G as createDocument, H as getNodeType, I as getMarkRange, J as NodeSelection, K as AllSelection, L as isTextSelection, M as isActive, Mt as Slice, N as isNodeActive, Nt as minMax, O as findChildren$1, Ot as DOMSerializer, P as getSchemaTypeNameByName, Pt as callOrGet, Q as SelectionRange, R as findParentNode, S as getResolvedParagraphProperties, St as getUnderlineCssString, T as CommandService, Tt as TrackInsertMarkName, U as cleanSchemaItem, V as getMarkType, W as getSchemaTypeByName, X as PluginKey, Y as Plugin, Z as Selection, _ as ListHelpers, _t as encodeMarksFromRPr, a as _getReferencedTableStyles, at as Transform, b as isList, bt as resolveRunProperties, c as processContent, ct as dropPoint, d as createCellBorders, dt as replaceStep$1, et as AddMarkStep, f as InputRule, ft as generateDocxRandomId, g as unflattenListsInHtml, gt as encodeCSSFromRPr, h as inputRulesPlugin, ht as encodeCSSFromPPr, it as ReplaceStep, j as getMarksFromSelection, jt as Schema$1, k as getActiveFormatting, kt as Fragment$1, l as createDocFromMarkdown, lt as joinPoint, m as htmlHandler, mt as decodeRPrFromMarks, n as kebabCase$1, nt as RemoveMarkStep, o as helpers_exports, ot as canJoin, p as handleClipboardPaste, pt as generateRandomSigned32BitIntStrId, q as EditorState, r as insertNewRelationship, rt as ReplaceAroundStep$1, s as updateDOMAttributes, st as canSplit, t as SuperConverter, tt as Mapping, u as createDocFromHTML, ut as liftTarget, v as changeListLevel, vt as resolveDocxFontFamily, w as generateOrderedListIndex, wt as TrackFormatMarkName, x as calculateResolvedParagraphProperties, xt as resolveTableCellProperties, y as updateNumberingProperties, yt as resolveParagraphProperties, z as findParentNodeClosestToPos } from "./SuperConverter-0JaBYpRa.es.js";
3
3
  import { a as init_dist$2, i as global, n as init_dist, o as Buffer$3, r as process$1, s as init_dist$1 } from "./jszip-ChlR43oI.es.js";
4
4
  import { t as v4_default } from "./uuid-2IzDu5nl.es.js";
5
5
  import { A as resolveOpcTargetPath, E as pixelsToTwips, F as twipsToLines, I as twipsToPixels, P as twipsToInches, _ as halfPointToPoints, c as convertSizeToCSS, k as ptToTwips, p as getArrayBufferFromUrl, t as COMMENT_FILE_BASENAMES, v as inchesToPixels, x as linesToTwips, y as inchesToTwips } from "./constants-DBKi0Amm.es.js";
@@ -260,7 +260,7 @@ var v_click_outside_default = {
260
260
  var DEFAULT_ENDPOINT = "https://ingest.superdoc.dev/v1/collect";
261
261
  function getSuperdocVersion() {
262
262
  try {
263
- return "1.17.0-next.11";
263
+ return "1.17.0-next.13";
264
264
  } catch {
265
265
  return "unknown";
266
266
  }
@@ -20715,7 +20715,7 @@ const canUseDOM = () => {
20715
20715
  return false;
20716
20716
  }
20717
20717
  };
20718
- var summaryVersion = "1.17.0-next.11";
20718
+ var summaryVersion = "1.17.0-next.13";
20719
20719
  var nodeKeys = [
20720
20720
  "group",
20721
20721
  "content",
@@ -21093,6 +21093,13 @@ const BLOCK_NODE_TYPES = [
21093
21093
  "image",
21094
21094
  "sdt"
21095
21095
  ];
21096
+ const DELETABLE_BLOCK_NODE_TYPES = [
21097
+ "paragraph",
21098
+ "heading",
21099
+ "listItem",
21100
+ "table",
21101
+ "sdt"
21102
+ ];
21096
21103
  const INLINE_NODE_TYPES = [
21097
21104
  "run",
21098
21105
  "bookmark",
@@ -21180,6 +21187,9 @@ var T_PLAN_ENGINE = [
21180
21187
  "TARGET_MOVED",
21181
21188
  "PLAN_CONFLICT_OVERLAP",
21182
21189
  "INVALID_STEP_COMBINATION",
21190
+ "REVISION_CHANGED_SINCE_COMPILE",
21191
+ "INVALID_INSERTION_CONTEXT",
21192
+ "DOCUMENT_IDENTITY_CONFLICT",
21183
21193
  "CAPABILITY_UNAVAILABLE"
21184
21194
  ];
21185
21195
  var T_QUERY_MATCH = [
@@ -21281,6 +21291,27 @@ const OPERATION_DEFINITIONS = {
21281
21291
  referenceDocPath: "delete.mdx",
21282
21292
  referenceGroup: "core"
21283
21293
  },
21294
+ "blocks.delete": {
21295
+ memberPath: "blocks.delete",
21296
+ description: "Delete an entire block node (paragraph, heading, list item, table, image, or sdt) deterministically.",
21297
+ requiresDocumentContext: true,
21298
+ metadata: mutationOperation({
21299
+ idempotency: "conditional",
21300
+ supportsDryRun: true,
21301
+ supportsTrackedMode: false,
21302
+ possibleFailureCodes: NONE_FAILURES,
21303
+ throws: [
21304
+ "TARGET_NOT_FOUND",
21305
+ "AMBIGUOUS_TARGET",
21306
+ "CAPABILITY_UNAVAILABLE",
21307
+ "INVALID_TARGET",
21308
+ "INVALID_INPUT",
21309
+ "INTERNAL_ERROR"
21310
+ ]
21311
+ }),
21312
+ referenceDocPath: "blocks/delete.mdx",
21313
+ referenceGroup: "blocks"
21314
+ },
21284
21315
  "format.apply": {
21285
21316
  memberPath: "format.apply",
21286
21317
  description: "Apply explicit inline style changes (bold, italic, underline, strike) to the target range using boolean patch semantics.",
@@ -21855,6 +21886,7 @@ function ref$1(name) {
21855
21886
  }
21856
21887
  var nodeTypeValues = NODE_TYPES;
21857
21888
  var blockNodeTypeValues = BLOCK_NODE_TYPES;
21889
+ var deletableBlockNodeTypeValues = DELETABLE_BLOCK_NODE_TYPES;
21858
21890
  var inlineNodeTypeValues = INLINE_NODE_TYPES;
21859
21891
  objectSchema({
21860
21892
  start: { type: "integer" },
@@ -21891,6 +21923,14 @@ objectSchema({
21891
21923
  "kind",
21892
21924
  "nodeType",
21893
21925
  "nodeId"
21926
+ ]), objectSchema({
21927
+ kind: { const: "block" },
21928
+ nodeType: { enum: [...deletableBlockNodeTypeValues] },
21929
+ nodeId: { type: "string" }
21930
+ }, [
21931
+ "kind",
21932
+ "nodeType",
21933
+ "nodeId"
21894
21934
  ]), objectSchema({
21895
21935
  kind: { const: "block" },
21896
21936
  nodeType: { const: "paragraph" },
@@ -22042,6 +22082,7 @@ ref$1("TargetKind");
22042
22082
  var textAddressSchema = ref$1("TextAddress");
22043
22083
  var textTargetSchema = ref$1("TextTarget");
22044
22084
  var blockNodeAddressSchema = ref$1("BlockNodeAddress");
22085
+ var deletableBlockNodeAddressSchema = ref$1("DeletableBlockNodeAddress");
22045
22086
  var paragraphAddressSchema = ref$1("ParagraphAddress");
22046
22087
  var headingAddressSchema = ref$1("HeadingAddress");
22047
22088
  var listItemAddressSchema = ref$1("ListItemAddress");
@@ -22412,6 +22453,7 @@ discoveryResultSchema(discoveryItemSchema({
22412
22453
  }, ["address", "type"]));
22413
22454
  var capabilityReasonsSchema = arraySchema({ enum: [
22414
22455
  "COMMAND_UNAVAILABLE",
22456
+ "HELPER_UNAVAILABLE",
22415
22457
  "OPERATION_UNAVAILABLE",
22416
22458
  "TRACKED_MODE_UNAVAILABLE",
22417
22459
  "DRY_RUN_UNAVAILABLE",
@@ -22501,7 +22543,13 @@ objectSchema({
22501
22543
  }, ["target", "value"]), textMutationResultSchemaFor("format.color"), textMutationFailureSchemaFor("format.color"), objectSchema({
22502
22544
  target: textAddressSchema,
22503
22545
  alignment: { oneOf: [{ enum: [...ALIGNMENTS] }, { type: "null" }] }
22504
- }, ["target", "alignment"]), textMutationResultSchemaFor("format.align"), textMutationFailureSchemaFor("format.align"), objectSchema({
22546
+ }, ["target", "alignment"]), textMutationResultSchemaFor("format.align"), textMutationFailureSchemaFor("format.align"), objectSchema({ target: deletableBlockNodeAddressSchema }, ["target"]), objectSchema({
22547
+ success: { const: true },
22548
+ deleted: deletableBlockNodeAddressSchema
22549
+ }, ["success", "deleted"]), objectSchema({
22550
+ success: { const: true },
22551
+ deleted: deletableBlockNodeAddressSchema
22552
+ }, ["success", "deleted"]), preApplyFailureResultSchemaFor("blocks.delete"), objectSchema({
22505
22553
  at: { oneOf: [
22506
22554
  objectSchema({ kind: { const: "documentStart" } }, ["kind"]),
22507
22555
  objectSchema({ kind: { const: "documentEnd" } }, ["kind"]),
@@ -22679,6 +22727,11 @@ var GROUP_METADATA = {
22679
22727
  description: "Primary read and write operations.",
22680
22728
  pagePath: "core/index.mdx"
22681
22729
  },
22730
+ blocks: {
22731
+ title: "Blocks",
22732
+ description: "Block-level structural operations.",
22733
+ pagePath: "blocks/index.mdx"
22734
+ },
22682
22735
  capabilities: {
22683
22736
  title: "Capabilities",
22684
22737
  description: "Runtime support discovery for capability-aware branching.",
@@ -22727,6 +22780,7 @@ Object.keys(GROUP_METADATA).map((key$1) => ({
22727
22780
  }));
22728
22781
  const CAPABILITY_REASON_CODES = [
22729
22782
  "COMMAND_UNAVAILABLE",
22783
+ "HELPER_UNAVAILABLE",
22730
22784
  "OPERATION_UNAVAILABLE",
22731
22785
  "TRACKED_MODE_UNAVAILABLE",
22732
22786
  "DRY_RUN_UNAVAILABLE",
@@ -23024,6 +23078,27 @@ function executeCreateHeading(adapter, input, options) {
23024
23078
  validateCreateLocation(normalized.at, "create.heading");
23025
23079
  return adapter.heading(normalized, normalizeMutationOptions(options));
23026
23080
  }
23081
+ var SUPPORTED_DELETE_NODE_TYPES = new Set(DELETABLE_BLOCK_NODE_TYPES);
23082
+ var REJECTED_DELETE_NODE_TYPES = new Set(["tableRow", "tableCell"]);
23083
+ function validateBlocksDeleteInput(input) {
23084
+ if (!input || typeof input !== "object") throw new DocumentApiValidationError("INVALID_INPUT", "blocks.delete requires an input object.", { fields: ["input"] });
23085
+ if (!input.target) throw new DocumentApiValidationError("INVALID_INPUT", "blocks.delete requires a target.", { fields: ["target"] });
23086
+ if (input.target.kind !== "block") throw new DocumentApiValidationError("INVALID_INPUT", "blocks.delete target must have kind \"block\".", { fields: ["target.kind"] });
23087
+ if (!input.target.nodeId || typeof input.target.nodeId !== "string") throw new DocumentApiValidationError("INVALID_INPUT", "blocks.delete target requires a nodeId string.", { fields: ["target.nodeId"] });
23088
+ const { nodeType } = input.target;
23089
+ if (REJECTED_DELETE_NODE_TYPES.has(nodeType)) throw new DocumentApiValidationError("INVALID_TARGET", `blocks.delete does not support "${nodeType}" targets. Table row/column operations are out of scope.`, {
23090
+ fields: ["target.nodeType"],
23091
+ nodeType
23092
+ });
23093
+ if (!SUPPORTED_DELETE_NODE_TYPES.has(nodeType)) throw new DocumentApiValidationError("INVALID_TARGET", `blocks.delete does not support "${nodeType}" targets.`, {
23094
+ fields: ["target.nodeType"],
23095
+ nodeType
23096
+ });
23097
+ }
23098
+ function executeBlocksDelete(adapter, input, options) {
23099
+ validateBlocksDeleteInput(input);
23100
+ return adapter.delete(input, normalizeMutationOptions(options));
23101
+ }
23027
23102
  function executeTrackChangesList(adapter, input) {
23028
23103
  return adapter.list(input);
23029
23104
  }
@@ -23067,6 +23142,7 @@ function buildDispatchTable(api) {
23067
23142
  insert: (input, options) => api.insert(input, options),
23068
23143
  replace: (input, options) => api.replace(input, options),
23069
23144
  delete: (input, options) => api.delete(input, options),
23145
+ "blocks.delete": (input, options) => api.blocks.delete(input, options),
23070
23146
  "format.apply": (input, options) => api.format.apply(input, options),
23071
23147
  "format.fontSize": (input, options) => api.format.fontSize(input, options),
23072
23148
  "format.fontFamily": (input, options) => api.format.fontFamily(input, options),
@@ -23192,6 +23268,9 @@ function createDocumentApi(adapters) {
23192
23268
  return executeTrackChangesDecide(adapters.trackChanges, input, options);
23193
23269
  }
23194
23270
  },
23271
+ blocks: { delete(input, options) {
23272
+ return executeBlocksDelete(adapters.blocks, input, options);
23273
+ } },
23195
23274
  create: {
23196
23275
  paragraph(input, options) {
23197
23276
  return executeCreateParagraph(adapters.create, input, options);
@@ -23276,6 +23355,7 @@ var REQUIRED_COMMANDS = {
23276
23355
  "lists.outdent": ["setTextSelection", "decreaseListIndent"],
23277
23356
  "lists.restart": ["setTextSelection", "restartNumbering"],
23278
23357
  "lists.exit": ["exitListItemAt"],
23358
+ "blocks.delete": ["deleteBlockNodeById"],
23279
23359
  "comments.create": [
23280
23360
  "addComment",
23281
23361
  "setTextSelection",
@@ -23304,6 +23384,12 @@ function hasAllCommands(editor, operationId) {
23304
23384
  if (!required || required.length === 0) return true;
23305
23385
  return required.every((command$1) => hasCommand(editor, command$1));
23306
23386
  }
23387
+ var REQUIRED_HELPERS = { "blocks.delete": (editor) => typeof editor.helpers?.blockNode?.getBlockNodeById === "function" };
23388
+ function hasRequiredHelpers(editor, operationId) {
23389
+ const check = REQUIRED_HELPERS[operationId];
23390
+ if (!check) return true;
23391
+ return check(editor);
23392
+ }
23307
23393
  function hasMarkCapability(editor, markName) {
23308
23394
  return Boolean(editor.schema?.marks?.[markName]);
23309
23395
  }
@@ -23349,7 +23435,7 @@ function pushReason(reasons, reason) {
23349
23435
  function isOperationAvailable(editor, operationId) {
23350
23436
  if (operationId === "format.apply") return MARK_KEYS.some((key$1) => hasMarkCapability(editor, STYLE_MARK_SCHEMA_NAMES[key$1] ?? key$1));
23351
23437
  if (INLINE_FORMAT_OPERATIONS.has(operationId)) return hasAllCommands(editor, operationId) && hasMarkCapability(editor, "textStyle");
23352
- return hasAllCommands(editor, operationId);
23438
+ return hasAllCommands(editor, operationId) && hasRequiredHelpers(editor, operationId);
23353
23439
  }
23354
23440
  function isCommandBackedAvailability(operationId) {
23355
23441
  return !isMarkBackedOperation(operationId) && !INLINE_FORMAT_OPERATIONS.has(operationId);
@@ -23363,7 +23449,10 @@ function buildOperationCapabilities(editor) {
23363
23449
  const dryRun = metadata.supportsDryRun && available;
23364
23450
  const reasons = [];
23365
23451
  if (!available) {
23366
- if (isCommandBackedAvailability(operationId)) pushReason(reasons, "COMMAND_UNAVAILABLE");
23452
+ if (isCommandBackedAvailability(operationId)) {
23453
+ if (!hasAllCommands(editor, operationId)) pushReason(reasons, "COMMAND_UNAVAILABLE");
23454
+ if (!hasRequiredHelpers(editor, operationId)) pushReason(reasons, "HELPER_UNAVAILABLE");
23455
+ }
23367
23456
  pushReason(reasons, "OPERATION_UNAVAILABLE");
23368
23457
  }
23369
23458
  if (metadata.supportsTrackedMode && !tracked) pushReason(reasons, "TRACKED_MODE_UNAVAILABLE");
@@ -23853,13 +23942,21 @@ function buildBlockIndex(editor) {
23853
23942
  });
23854
23943
  return {
23855
23944
  candidates,
23856
- byId
23945
+ byId,
23946
+ ambiguous
23857
23947
  };
23858
23948
  }
23859
23949
  function findBlockById(index, address) {
23860
23950
  if (address.kind !== "block") return void 0;
23861
23951
  return index.byId.get(`${address.nodeType}:${address.nodeId}`);
23862
23952
  }
23953
+ function findBlockByIdStrict(index, address) {
23954
+ const key$1 = `${address.nodeType}:${address.nodeId}`;
23955
+ if (index.ambiguous.has(key$1)) throw new DocumentApiAdapterError("AMBIGUOUS_TARGET", `Multiple blocks share key "${key$1}".`, { target: address });
23956
+ const candidate = index.byId.get(key$1);
23957
+ if (!candidate) throw new DocumentApiAdapterError("TARGET_NOT_FOUND", `Block "${key$1}" was not found.`, { target: address });
23958
+ return candidate;
23959
+ }
23863
23960
  function isTextBlockCandidate(candidate) {
23864
23961
  const node = candidate.node;
23865
23962
  return Boolean(node?.inlineContent || node?.isTextblock);
@@ -23945,9 +24042,11 @@ function trackRevisions(editor) {
23945
24042
  function checkRevision(editor, expectedRevision) {
23946
24043
  if (expectedRevision === void 0) return;
23947
24044
  const current = getRevision(editor);
23948
- if (expectedRevision !== current) throw new PlanError("REVISION_MISMATCH", `REVISION_MISMATCH — expected revision "${expectedRevision}" but document is at "${current}"`, void 0, {
24045
+ if (expectedRevision !== current) throw new PlanError("REVISION_MISMATCH", `REVISION_MISMATCH — expected revision "${expectedRevision}" but document is at "${current}". Re-run query.match to obtain a fresh ref.`, void 0, {
23949
24046
  expectedRevision,
23950
- currentRevision: current
24047
+ currentRevision: current,
24048
+ refStability: "ephemeral",
24049
+ remediation: "Re-run query.match() to obtain a fresh ref valid for the current revision."
23951
24050
  });
23952
24051
  }
23953
24052
  function resolveSegmentPosition(targetOffset, segmentStart, segmentLength, docFrom, docTo) {
@@ -24197,7 +24296,9 @@ function captureRunsInRange(editor, blockPos, from$1, to) {
24197
24296
  return;
24198
24297
  }
24199
24298
  if (node.isLeaf) {
24299
+ const start$1 = offset$1;
24200
24300
  offset$1 += 1;
24301
+ maybePushRun(start$1, offset$1, []);
24201
24302
  return;
24202
24303
  }
24203
24304
  let isFirstChild = true;
@@ -24597,12 +24698,62 @@ function executeBlockSelector(index, query, diagnostics) {
24597
24698
  function isAssertStep(step) {
24598
24699
  return step.op === "assert";
24599
24700
  }
24701
+ function isCreateOp(op) {
24702
+ return op === "create.heading" || op === "create.paragraph";
24703
+ }
24704
+ var VALID_CREATE_POSITIONS = ["before", "after"];
24600
24705
  function isSelectWhere(where) {
24601
24706
  return where.by === "select";
24602
24707
  }
24603
24708
  function isRefWhere(where) {
24604
24709
  return where.by === "ref";
24605
24710
  }
24711
+ function validateCreateStepPosition(step) {
24712
+ const args$1 = step.args;
24713
+ if (args$1.position === void 0 || args$1.position === null) {
24714
+ args$1.position = "after";
24715
+ return;
24716
+ }
24717
+ if (!VALID_CREATE_POSITIONS.includes(args$1.position)) throw planError("INVALID_INPUT", `create step requires args.position to be 'before' or 'after'`, step.id, {
24718
+ receivedPosition: args$1.position,
24719
+ allowedValues: [...VALID_CREATE_POSITIONS],
24720
+ default: "after"
24721
+ });
24722
+ }
24723
+ function resolveCreateAnchorFromTargets(targets, position, stepId) {
24724
+ const target = targets[0];
24725
+ if (!target) throw planError("INVALID_INPUT", "create step has no resolved targets", stepId);
24726
+ if (target.kind === "range") return target.blockId;
24727
+ const segments = target.segments;
24728
+ if (!segments.length) throw planError("INVALID_INPUT", "span target has no segments", stepId);
24729
+ return position === "before" ? segments[0].blockId : segments[segments.length - 1].blockId;
24730
+ }
24731
+ function validateInsertionContext(editor, index, step, stepIndex, anchorBlockId, position) {
24732
+ const candidate = index.candidates.find((c$3) => c$3.nodeId === anchorBlockId);
24733
+ if (!candidate) return;
24734
+ const paragraphType = editor.state.schema?.nodes?.paragraph;
24735
+ if (!paragraphType) return;
24736
+ const resolvedPos = editor.state.doc.resolve(candidate.pos);
24737
+ const parent = resolvedPos.parent;
24738
+ const anchorIndex = resolvedPos.index();
24739
+ const insertionIndex = position === "before" ? anchorIndex : anchorIndex + 1;
24740
+ if (!(typeof parent.canReplaceWith === "function" ? parent.canReplaceWith(insertionIndex, insertionIndex, paragraphType) : parent.type.contentMatch.matchType(paragraphType))) {
24741
+ const allowedChildTypes = [];
24742
+ const match$1 = parent.type.contentMatch;
24743
+ for (const nodeType of Object.values(editor.state.schema.nodes)) if (match$1.matchType(nodeType)) allowedChildTypes.push(nodeType.name);
24744
+ throw planError("INVALID_INSERTION_CONTEXT", `Cannot create ${step.op} inside ${parent.type.name}`, step.id, {
24745
+ stepIndex,
24746
+ stepId: step.id,
24747
+ operation: step.op,
24748
+ anchorBlockId,
24749
+ parentType: parent.type.name,
24750
+ allowedChildTypes,
24751
+ insertionIndex,
24752
+ requestedChildType: "paragraph",
24753
+ requestedSemanticType: step.op === "create.heading" ? "heading" : "paragraph"
24754
+ });
24755
+ }
24756
+ }
24606
24757
  function isV3Ref(payload) {
24607
24758
  return typeof payload === "object" && payload !== null && "v" in payload && payload.v === 3;
24608
24759
  }
@@ -24815,9 +24966,13 @@ function decodeTextRefPayload(encoded, stepId) {
24815
24966
  }
24816
24967
  function resolveV3TextRef(editor, index, step, refData) {
24817
24968
  const currentRevision = getRevision(editor);
24818
- if (refData.rev !== currentRevision) throw planError("REVISION_MISMATCH", `text ref was created at revision "${refData.rev}" but document is at "${currentRevision}"`, step.id, {
24969
+ if (refData.rev !== currentRevision) throw planError("REVISION_MISMATCH", `Text ref is ephemeral and revision-scoped. Re-run query.match to obtain a fresh handle.ref for revision ${currentRevision}.`, step.id, {
24819
24970
  refRevision: refData.rev,
24820
- currentRevision
24971
+ currentRevision,
24972
+ refStability: "ephemeral",
24973
+ refScope: refData.scope,
24974
+ blockId: refData.segments?.[0]?.blockId,
24975
+ remediation: `Re-run query.match() to obtain a fresh ref valid for the current revision.`
24821
24976
  });
24822
24977
  if (!refData.segments?.length) return [];
24823
24978
  const segments = refData.segments.map((s) => ({
@@ -24916,7 +25071,11 @@ function resolveStepTargets(editor, index, step) {
24916
25071
  return t.blockId !== prev.blockId || t.from !== prev.from || t.to !== prev.to;
24917
25072
  });
24918
25073
  if (refWhere) {
24919
- if (targets.length === 0) throw planError("MATCH_NOT_FOUND", `ref "${refWhere.ref}" did not resolve to any targets`, step.id);
25074
+ if (targets.length === 0) throw planError("MATCH_NOT_FOUND", `ref "${refWhere.ref}" did not resolve to any targets`, step.id, {
25075
+ selectorType: "ref",
25076
+ selectorPattern: refWhere.ref,
25077
+ candidateCount: 0
25078
+ });
24920
25079
  if (targets.length > 1) throw planError("AMBIGUOUS_MATCH", `ref "${refWhere.ref}" resolved to ${targets.length} targets`, step.id, { matchCount: targets.length });
24921
25080
  return targets;
24922
25081
  }
@@ -24925,57 +25084,157 @@ function resolveStepTargets(editor, index, step) {
24925
25084
  if (selectWhere.require === "first" && targets.length > 1) targets = [targets[0]];
24926
25085
  return targets;
24927
25086
  }
25087
+ function buildMatchNotFoundDetails(step) {
25088
+ const where = step.where;
25089
+ const select = "select" in where ? where.select : void 0;
25090
+ const within$1 = "within" in where ? where.within : void 0;
25091
+ return {
25092
+ selectorType: select?.type ?? "unknown",
25093
+ selectorPattern: select?.pattern ?? "",
25094
+ selectorMode: select?.mode ?? "contains",
25095
+ searchScope: within$1?.blockId ?? "document",
25096
+ candidateCount: 0
25097
+ };
25098
+ }
24928
25099
  function applyCardinalityCheck(step, targets) {
24929
25100
  const where = step.where;
24930
25101
  if (!("require" in where) || where.require === void 0) return;
24931
25102
  const require$1 = where.require;
24932
25103
  if (require$1 === "first") {
24933
- if (targets.length === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", step.id);
25104
+ if (targets.length === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", step.id, buildMatchNotFoundDetails(step));
24934
25105
  } else if (require$1 === "exactlyOne") {
24935
- if (targets.length === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", step.id);
25106
+ if (targets.length === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", step.id, buildMatchNotFoundDetails(step));
24936
25107
  if (targets.length > 1) throw planError("AMBIGUOUS_MATCH", `selector matched ${targets.length} ranges, expected exactly one`, step.id, { matchCount: targets.length });
24937
25108
  } else if (require$1 === "all") {
24938
- if (targets.length === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", step.id);
24939
- }
24940
- }
24941
- function detectOverlaps(steps) {
24942
- const rangesByBlock = /* @__PURE__ */ new Map();
24943
- for (const compiled of steps) for (const target of compiled.targets) if (target.kind === "range") addRange(rangesByBlock, target.blockId, target.stepId, target.from, target.to);
24944
- else for (const seg of target.segments) addRange(rangesByBlock, seg.blockId, target.stepId, seg.from, seg.to);
24945
- for (const [blockId, ranges] of rangesByBlock) {
24946
- ranges.sort((a, b$1) => a.from - b$1.from);
24947
- for (let i$1 = 1; i$1 < ranges.length; i$1++) {
24948
- const prev = ranges[i$1 - 1];
24949
- const curr = ranges[i$1];
24950
- if (prev.stepId !== curr.stepId && prev.to > curr.from) throw planError("PLAN_CONFLICT_OVERLAP", `steps "${prev.stepId}" and "${curr.stepId}" target overlapping ranges in block "${blockId}"`, curr.stepId, {
25109
+ if (targets.length === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", step.id, buildMatchNotFoundDetails(step));
25110
+ }
25111
+ }
25112
+ const STEP_INTERACTION_MATRIX = new Map([
25113
+ ["text.rewrite::format.apply::same_target", "allow"],
25114
+ ["text.rewrite::text.rewrite::same_target", "reject"],
25115
+ ["text.rewrite::text.delete::overlapping", "reject"],
25116
+ ["text.rewrite::create.*::same_block", "allow"],
25117
+ ["text.rewrite::text.insert::same_target", "reject"],
25118
+ ["format.apply::format.apply::same_target", "allow"],
25119
+ ["format.apply::text.rewrite::same_target", "reject"],
25120
+ ["format.apply::text.delete::overlapping", "reject"],
25121
+ ["format.apply::create.*::same_block", "allow"],
25122
+ ["format.apply::text.insert::same_target", "allow"],
25123
+ ["text.delete::text.rewrite::overlapping", "reject"],
25124
+ ["text.delete::text.delete::overlapping", "reject"],
25125
+ ["text.delete::format.apply::overlapping", "reject"],
25126
+ ["text.delete::create.*::same_block", "allow"],
25127
+ ["text.delete::text.insert::overlapping", "reject"],
25128
+ ["create.*::text.rewrite::same_block", "allow"],
25129
+ ["create.*::format.apply::same_block", "allow"],
25130
+ ["create.*::text.delete::same_block", "allow"],
25131
+ ["create.*::create.*::same_block", "allow"],
25132
+ ["create.*::text.insert::same_block", "allow"],
25133
+ ["text.insert::format.apply::same_target", "allow"],
25134
+ ["text.insert::text.rewrite::same_target", "reject"],
25135
+ ["text.insert::text.delete::overlapping", "reject"],
25136
+ ["text.insert::create.*::same_block", "allow"],
25137
+ ["text.insert::text.insert::same_target", "reject"]
25138
+ ]);
25139
+ const MATRIX_EXEMPT_OPS = new Set(["assert"]);
25140
+ function normalizeOpForMatrix(op) {
25141
+ return op.startsWith("create.") ? "create.*" : op;
25142
+ }
25143
+ function classifyOverlap(stepA, stepB) {
25144
+ const rangesA = extractBlockRanges(stepA);
25145
+ const rangesB = extractBlockRanges(stepB);
25146
+ const opA = normalizeOpForMatrix(stepA.step.op);
25147
+ const opB = normalizeOpForMatrix(stepB.step.op);
25148
+ const isCreateA = opA === "create.*";
25149
+ const isCreateB = opB === "create.*";
25150
+ for (const [blockId, aEntries] of rangesA) {
25151
+ const bEntries = rangesB.get(blockId);
25152
+ if (!bEntries) continue;
25153
+ for (const a of aEntries) for (const b$1 of bEntries) {
25154
+ if (isCreateA || isCreateB) return {
25155
+ overlapClass: "same_block",
24951
25156
  blockId,
24952
- rangeA: {
24953
- from: prev.from,
24954
- to: prev.to
24955
- },
24956
- rangeB: {
24957
- from: curr.from,
24958
- to: curr.to
24959
- }
24960
- });
25157
+ rangeA: a,
25158
+ rangeB: b$1
25159
+ };
25160
+ if (a.to <= b$1.from || b$1.to <= a.from) continue;
25161
+ if (a.from === b$1.from && a.to === b$1.to) return {
25162
+ overlapClass: "same_target",
25163
+ blockId,
25164
+ rangeA: a,
25165
+ rangeB: b$1
25166
+ };
25167
+ return {
25168
+ overlapClass: "overlapping",
25169
+ blockId,
25170
+ rangeA: a,
25171
+ rangeB: b$1
25172
+ };
24961
25173
  }
24962
25174
  }
24963
25175
  }
24964
- function addRange(map$2, blockId, stepId, from$1, to) {
24965
- let blockRanges = map$2.get(blockId);
24966
- if (!blockRanges) {
24967
- blockRanges = [];
24968
- map$2.set(blockId, blockRanges);
25176
+ function extractBlockRanges(compiled) {
25177
+ const result = /* @__PURE__ */ new Map();
25178
+ for (const target of compiled.targets) if (target.kind === "range") pushBlockRange(result, target.blockId, target.from, target.to);
25179
+ else for (const seg of target.segments) pushBlockRange(result, seg.blockId, seg.from, seg.to);
25180
+ return result;
25181
+ }
25182
+ function pushBlockRange(map$2, blockId, from$1, to) {
25183
+ let entries = map$2.get(blockId);
25184
+ if (!entries) {
25185
+ entries = [];
25186
+ map$2.set(blockId, entries);
24969
25187
  }
24970
- blockRanges.push({
24971
- stepId,
25188
+ entries.push({
24972
25189
  from: from$1,
24973
25190
  to
24974
25191
  });
24975
25192
  }
25193
+ function validateStepInteractions(steps) {
25194
+ for (let i$1 = 0; i$1 < steps.length; i$1++) for (let j = i$1 + 1; j < steps.length; j++) {
25195
+ const stepA = steps[i$1];
25196
+ const stepB = steps[j];
25197
+ if (MATRIX_EXEMPT_OPS.has(stepA.step.op) || MATRIX_EXEMPT_OPS.has(stepB.step.op)) continue;
25198
+ const overlap = classifyOverlap(stepA, stepB);
25199
+ if (!overlap) continue;
25200
+ const matrixKey = `${normalizeOpForMatrix(stepA.step.op)}::${normalizeOpForMatrix(stepB.step.op)}::${overlap.overlapClass}`;
25201
+ const verdict = STEP_INTERACTION_MATRIX.get(matrixKey) ?? "reject";
25202
+ if (verdict === "reject") throw planError("PLAN_CONFLICT_OVERLAP", `steps "${stepA.step.id}" and "${stepB.step.id}" target overlapping ranges in block "${overlap.blockId}"`, stepB.step.id, {
25203
+ blockId: overlap.blockId,
25204
+ stepIdA: stepA.step.id,
25205
+ stepIdB: stepB.step.id,
25206
+ opKeyA: stepA.step.op,
25207
+ opKeyB: stepB.step.op,
25208
+ rangeA: overlap.rangeA,
25209
+ rangeB: overlap.rangeB,
25210
+ overlapRegion: {
25211
+ from: Math.max(overlap.rangeA.from, overlap.rangeB.from),
25212
+ to: Math.min(overlap.rangeA.to, overlap.rangeB.to)
25213
+ },
25214
+ matrixVerdict: verdict,
25215
+ matrixKey
25216
+ });
25217
+ }
25218
+ }
25219
+ function assertNoDuplicateBlockIds(index) {
25220
+ const seen = /* @__PURE__ */ new Map();
25221
+ const duplicates = [];
25222
+ for (const candidate of index.candidates) {
25223
+ const count = seen.get(candidate.nodeId) ?? 0;
25224
+ seen.set(candidate.nodeId, count + 1);
25225
+ if (count === 1) duplicates.push(candidate.nodeId);
25226
+ }
25227
+ if (duplicates.length > 0) throw planError("DOCUMENT_IDENTITY_CONFLICT", "Document contains blocks with duplicate identities. This must be resolved before mutations can be applied.", void 0, {
25228
+ duplicateBlockIds: duplicates,
25229
+ blockCount: duplicates.length,
25230
+ remediation: "Re-import the document or call document.repair() to assign unique identities."
25231
+ });
25232
+ }
24976
25233
  function compilePlan(editor, steps) {
24977
25234
  if (steps.length > 200) throw planError("INVALID_INPUT", `plan contains ${steps.length} steps, maximum is 200`);
25235
+ const compiledRevision = getRevision(editor);
24978
25236
  const index = getBlockIndex(editor);
25237
+ assertNoDuplicateBlockIds(index);
24979
25238
  const mutationSteps = [];
24980
25239
  const assertSteps = [];
24981
25240
  const seenIds = /* @__PURE__ */ new Set();
@@ -24985,26 +25244,41 @@ function compilePlan(editor, steps) {
24985
25244
  seenIds.add(step.id);
24986
25245
  }
24987
25246
  let totalTargets = 0;
25247
+ let stepIndex = 0;
24988
25248
  for (const step of steps) {
24989
25249
  if (isAssertStep(step)) {
24990
25250
  assertSteps.push(step);
25251
+ stepIndex++;
24991
25252
  continue;
24992
25253
  }
24993
25254
  if (!hasStepExecutor(step.op)) throw planError("INVALID_INPUT", `unknown step op "${step.op}"`, step.id);
25255
+ if (isCreateOp(step.op)) validateCreateStepPosition(step);
24994
25256
  const targets = resolveStepTargets(editor, index, step);
25257
+ if (isCreateOp(step.op) && targets.length > 0) {
25258
+ const position = step.args.position ?? "after";
25259
+ const anchorBlockId = resolveCreateAnchorFromTargets(targets, position, step.id);
25260
+ validateInsertionContext(editor, index, step, stepIndex, anchorBlockId, position);
25261
+ }
24995
25262
  totalTargets += targets.length;
24996
25263
  mutationSteps.push({
24997
25264
  step,
24998
25265
  targets
24999
25266
  });
25267
+ stepIndex++;
25000
25268
  }
25001
25269
  if (totalTargets > 500) throw planError("INVALID_INPUT", `plan resolved ${totalTargets} total targets, maximum is 500`);
25002
- detectOverlaps(mutationSteps);
25270
+ validateStepInteractions(mutationSteps);
25003
25271
  return {
25004
25272
  mutationSteps,
25005
- assertSteps
25273
+ assertSteps,
25274
+ compiledRevision
25006
25275
  };
25007
25276
  }
25277
+ function resolveBlockInsertionPos(editor, anchorBlockId, position, stepId) {
25278
+ const candidate = getBlockIndex(editor).candidates.find((c$3) => c$3.nodeId === anchorBlockId);
25279
+ if (!candidate) throw planError("TARGET_NOT_FOUND", `block "${anchorBlockId}" not found`, stepId);
25280
+ return position === "before" ? candidate.pos : candidate.end;
25281
+ }
25008
25282
  function applyDirectMutationMeta(tr) {
25009
25283
  tr.setMeta("inputType", "programmatic");
25010
25284
  tr.setMeta("skipTrackChanges", true);
@@ -25049,10 +25323,28 @@ function buildMarksFromSetMarks(editor, setMarks) {
25049
25323
  if (setMarks.strike && schema.marks.strike) marks.push(schema.marks.strike.create());
25050
25324
  return marks;
25051
25325
  }
25052
- function toAbsoluteBlockInsertPos(editor, blockId, offset$1, stepId) {
25053
- const candidate = getBlockIndex(editor).candidates.find((c$3) => c$3.nodeId === blockId);
25054
- if (!candidate) throw planError("TARGET_NOT_FOUND", `block "${blockId}" not found`, stepId);
25055
- return candidate.pos + offset$1;
25326
+ function applyInlineMarkPatches(editor, tr, absFrom, absTo, inline) {
25327
+ const { schema } = editor.state;
25328
+ let changed = false;
25329
+ const markEntries = [
25330
+ [inline.bold, schema.marks.bold],
25331
+ [inline.italic, schema.marks.italic],
25332
+ [inline.underline, schema.marks.underline],
25333
+ [inline.strike, schema.marks.strike]
25334
+ ];
25335
+ for (const [value, markType] of markEntries) {
25336
+ if (value === void 0 || !markType) continue;
25337
+ if (value) tr.addMark(absFrom, absTo, markType.create());
25338
+ else tr.removeMark(absFrom, absTo, markType);
25339
+ changed = true;
25340
+ }
25341
+ return changed;
25342
+ }
25343
+ function resolveCreateAnchorBlockId(target, position, stepId) {
25344
+ if (target.kind === "range") return target.blockId;
25345
+ const segments = target.segments;
25346
+ if (!segments.length) throw planError("INVALID_INPUT", "span target has no segments", stepId);
25347
+ return position === "before" ? segments[0].blockId : segments[segments.length - 1].blockId;
25056
25348
  }
25057
25349
  function executeTextRewrite(editor, tr, target, step, mapping) {
25058
25350
  const absFrom = mapping.map(target.absFrom);
@@ -25086,39 +25378,7 @@ function executeTextDelete(_editor, tr, target, _step, mapping) {
25086
25378
  return { changed: true };
25087
25379
  }
25088
25380
  function executeStyleApply(editor, tr, target, step, mapping) {
25089
- const absFrom = mapping.map(target.absFrom);
25090
- const absTo = mapping.map(target.absTo);
25091
- const { schema } = editor.state;
25092
- let changed = false;
25093
- const markEntries = [
25094
- [
25095
- "bold",
25096
- step.args.inline.bold,
25097
- schema.marks.bold
25098
- ],
25099
- [
25100
- "italic",
25101
- step.args.inline.italic,
25102
- schema.marks.italic
25103
- ],
25104
- [
25105
- "underline",
25106
- step.args.inline.underline,
25107
- schema.marks.underline
25108
- ],
25109
- [
25110
- "strike",
25111
- step.args.inline.strike,
25112
- schema.marks.strike
25113
- ]
25114
- ];
25115
- for (const [, value, markType] of markEntries) {
25116
- if (value === void 0 || !markType) continue;
25117
- if (value) tr.addMark(absFrom, absTo, markType.create());
25118
- else tr.removeMark(absFrom, absTo, markType);
25119
- changed = true;
25120
- }
25121
- return { changed };
25381
+ return { changed: applyInlineMarkPatches(editor, tr, mapping.map(target.absFrom), mapping.map(target.absTo), step.args.inline) };
25122
25382
  }
25123
25383
  function validateMappedSpanContiguity(target, mapping, stepId) {
25124
25384
  let lastMappedEnd = -1;
@@ -25179,41 +25439,9 @@ function executeSpanTextDelete(_editor, tr, target, step, mapping) {
25179
25439
  }
25180
25440
  function executeSpanStyleApply(editor, tr, target, step, mapping) {
25181
25441
  validateMappedSpanContiguity(target, mapping, step.id);
25182
- const { schema } = editor.state;
25183
- let changed = false;
25184
25442
  const firstSeg = target.segments[0];
25185
25443
  const lastSeg = target.segments[target.segments.length - 1];
25186
- const absFrom = mapping.map(firstSeg.absFrom, 1);
25187
- const absTo = mapping.map(lastSeg.absTo, -1);
25188
- const markEntries = [
25189
- [
25190
- "bold",
25191
- step.args.inline.bold,
25192
- schema.marks.bold
25193
- ],
25194
- [
25195
- "italic",
25196
- step.args.inline.italic,
25197
- schema.marks.italic
25198
- ],
25199
- [
25200
- "underline",
25201
- step.args.inline.underline,
25202
- schema.marks.underline
25203
- ],
25204
- [
25205
- "strike",
25206
- step.args.inline.strike,
25207
- schema.marks.strike
25208
- ]
25209
- ];
25210
- for (const [, value, markType] of markEntries) {
25211
- if (value === void 0 || !markType) continue;
25212
- if (value) tr.addMark(absFrom, absTo, markType.create());
25213
- else tr.removeMark(absFrom, absTo, markType);
25214
- changed = true;
25215
- }
25216
- return { changed };
25444
+ return { changed: applyInlineMarkPatches(editor, tr, mapping.map(firstSeg.absFrom, 1), mapping.map(lastSeg.absTo, -1), step.args.inline) };
25217
25445
  }
25218
25446
  function getReplacementText(replacement) {
25219
25447
  if (replacement.blocks !== void 0) return replacement.blocks.map((b$1) => b$1.text).join("\n\n");
@@ -25325,7 +25553,8 @@ function buildAssertIndex(doc$2) {
25325
25553
  });
25326
25554
  return {
25327
25555
  candidates,
25328
- byId
25556
+ byId,
25557
+ ambiguous
25329
25558
  };
25330
25559
  }
25331
25560
  function resolveAssertScope(index, select, within$1) {
@@ -25383,9 +25612,11 @@ function executeAssertStep(_editor, tr, step) {
25383
25612
  }
25384
25613
  function executeCreateStep(editor, tr, step, targets, mapping) {
25385
25614
  const target = targets[0];
25386
- if (!target || target.kind !== "range") throw planError("INVALID_INPUT", `${step.op} step requires exactly one range target`, step.id);
25615
+ if (!target) throw planError("INVALID_INPUT", `${step.op} step requires at least one target`, step.id);
25387
25616
  const args$1 = step.args;
25388
- const pos = mapping.map(toAbsoluteBlockInsertPos(editor, target.blockId, target.from, step.id));
25617
+ const position = args$1.position ?? "after";
25618
+ const anchorPos = resolveBlockInsertionPos(editor, resolveCreateAnchorBlockId(target, position, step.id), position, step.id);
25619
+ const pos = mapping.map(anchorPos);
25389
25620
  const paragraphType = editor.state.schema?.nodes?.paragraph;
25390
25621
  if (!paragraphType) throw planError("INVALID_INPUT", "paragraph node type not in schema", step.id);
25391
25622
  const sdBlockId = args$1.sdBlockId;
@@ -25402,6 +25633,7 @@ function executeCreateStep(editor, tr, step, targets, mapping) {
25402
25633
  const node = paragraphType.createAndFill(attrs, textNode ?? void 0) ?? paragraphType.create(attrs, textNode ? [textNode] : void 0);
25403
25634
  if (!node) throw planError("INVALID_INPUT", `could not create ${step.op} node`, step.id);
25404
25635
  tr.insert(pos, node);
25636
+ assertNoPostInsertDuplicateIds(tr.doc, step.id);
25405
25637
  return {
25406
25638
  stepId: step.id,
25407
25639
  op: step.op,
@@ -25413,6 +25645,27 @@ function executeCreateStep(editor, tr, step, targets, mapping) {
25413
25645
  }
25414
25646
  };
25415
25647
  }
25648
+ function assertNoPostInsertDuplicateIds(doc$2, stepId) {
25649
+ const seen = /* @__PURE__ */ new Set();
25650
+ const duplicateSet = /* @__PURE__ */ new Set();
25651
+ doc$2.descendants((node) => {
25652
+ if (!node.isTextblock) return true;
25653
+ const attrs = node.attrs ?? {};
25654
+ const id = typeof attrs.paraId === "string" && attrs.paraId || typeof attrs.sdBlockId === "string" && attrs.sdBlockId || typeof attrs.nodeId === "string" && attrs.nodeId;
25655
+ if (!id) return true;
25656
+ if (seen.has(id)) duplicateSet.add(id);
25657
+ else seen.add(id);
25658
+ return true;
25659
+ });
25660
+ if (duplicateSet.size > 0) {
25661
+ const duplicates = [...duplicateSet];
25662
+ throw planError("INTERNAL_ERROR", `create step produced duplicate block identities: [${duplicates.join(", ")}]`, stepId, {
25663
+ source: "executor:checkPostInsertIdentityUniqueness",
25664
+ invariant: "post-insert block IDs must be unique",
25665
+ duplicateBlockIds: duplicates
25666
+ });
25667
+ }
25668
+ }
25416
25669
  function runMutationsOnTransaction(editor, tr, compiled, options) {
25417
25670
  const mapping = tr.mapping;
25418
25671
  const stepOutcomes = [];
@@ -25468,6 +25721,13 @@ function executeCompiledPlan(editor, compiled, options = {}) {
25468
25721
  const startTime = performance.now();
25469
25722
  const revisionBefore = getRevision(editor);
25470
25723
  checkRevision(editor, options.expectedRevision);
25724
+ if (compiled.compiledRevision !== revisionBefore) throw planError("REVISION_CHANGED_SINCE_COMPILE", `Document revision changed between compile and execute. Compiled at "${compiled.compiledRevision}", now at "${revisionBefore}".`, void 0, {
25725
+ compiledRevision: compiled.compiledRevision,
25726
+ currentRevision: revisionBefore,
25727
+ stepCount: compiled.mutationSteps.length,
25728
+ failedAtStep: "pre-execution",
25729
+ remediation: "Re-compile the plan against the current document state."
25730
+ });
25471
25731
  const tr = editor.state.tr;
25472
25732
  if ((options.changeMode ?? "direct") === "tracked") applyTrackedMutationMeta(tr);
25473
25733
  else applyDirectMutationMeta(tr);
@@ -25676,7 +25936,8 @@ function executeDomainCommand(editor, handler, options) {
25676
25936
  },
25677
25937
  targets: []
25678
25938
  }],
25679
- assertSteps: []
25939
+ assertSteps: [],
25940
+ compiledRevision: getRevision(editor)
25680
25941
  }, { expectedRevision: options?.expectedRevision });
25681
25942
  }
25682
25943
  function validateWriteRequest(request, resolved) {
@@ -25763,7 +26024,8 @@ function writeWrapper(editor, request, options) {
25763
26024
  step: stepDef,
25764
26025
  targets: [toCompiledTarget(stepId, op, resolved)]
25765
26026
  }],
25766
- assertSteps: []
26027
+ assertSteps: [],
26028
+ compiledRevision: getRevision(editor)
25767
26029
  }, {
25768
26030
  changeMode: mode,
25769
26031
  expectedRevision: options?.expectedRevision
@@ -25829,7 +26091,8 @@ function styleApplyWrapper(editor, input, options) {
25829
26091
  marks: []
25830
26092
  }]
25831
26093
  }],
25832
- assertSteps: []
26094
+ assertSteps: [],
26095
+ compiledRevision: getRevision(editor)
25833
26096
  }, {
25834
26097
  changeMode: mode,
25835
26098
  expectedRevision: options?.expectedRevision
@@ -26595,6 +26858,51 @@ function createCommentsWrapper(editor) {
26595
26858
  list: (query) => listCommentsHandler(editor, query)
26596
26859
  };
26597
26860
  }
26861
+ var SUPPORTED_NODE_TYPES = new Set(DELETABLE_BLOCK_NODE_TYPES);
26862
+ var REJECTED_NODE_TYPES = new Set(["tableRow", "tableCell"]);
26863
+ function validateTargetNodeType(nodeType) {
26864
+ if (REJECTED_NODE_TYPES.has(nodeType)) throw new DocumentApiAdapterError("INVALID_TARGET", `blocks.delete does not support "${nodeType}" targets. Table row/column operations are out of scope.`, { nodeType });
26865
+ if (!SUPPORTED_NODE_TYPES.has(nodeType)) throw new DocumentApiAdapterError("INVALID_TARGET", `blocks.delete does not support "${nodeType}" targets.`, { nodeType });
26866
+ }
26867
+ function resolveSdBlockId(candidate) {
26868
+ const sdBlockId = candidate.node.attrs?.sdBlockId;
26869
+ if (typeof sdBlockId === "string" && sdBlockId.length > 0) return sdBlockId;
26870
+ throw new DocumentApiAdapterError("INTERNAL_ERROR", "Resolved block candidate is missing sdBlockId attribute. This indicates a schema/extension invariant violation.", { attrs: candidate.node.attrs });
26871
+ }
26872
+ function validateCommandLayerUniqueness(editor, sdBlockId) {
26873
+ const getBlockNodeById = editor.helpers?.blockNode?.getBlockNodeById;
26874
+ if (typeof getBlockNodeById !== "function") throw new DocumentApiAdapterError("CAPABILITY_UNAVAILABLE", "blocks.delete requires the blockNode helper to be registered.", { reason: "missing_helper" });
26875
+ const matches = getBlockNodeById(sdBlockId);
26876
+ if (!matches || Array.isArray(matches) && matches.length === 0) throw new DocumentApiAdapterError("TARGET_NOT_FOUND", `Block with sdBlockId "${sdBlockId}" was not found at the command layer.`, { sdBlockId });
26877
+ if (Array.isArray(matches) && matches.length > 1) throw new DocumentApiAdapterError("AMBIGUOUS_TARGET", `Multiple blocks share sdBlockId "${sdBlockId}" at the command layer.`, {
26878
+ sdBlockId,
26879
+ count: matches.length
26880
+ });
26881
+ }
26882
+ function blocksDeleteWrapper(editor, input, options) {
26883
+ rejectTrackedMode("blocks.delete", options);
26884
+ const candidate = findBlockByIdStrict(getBlockIndex(editor), input.target);
26885
+ validateTargetNodeType(candidate.nodeType);
26886
+ const sdBlockId = resolveSdBlockId(candidate);
26887
+ const deleteBlockNodeById = requireEditorCommand(editor.commands?.deleteBlockNodeById, "blocks.delete");
26888
+ validateCommandLayerUniqueness(editor, sdBlockId);
26889
+ if (options?.dryRun) return {
26890
+ success: true,
26891
+ deleted: input.target
26892
+ };
26893
+ if (executeDomainCommand(editor, () => {
26894
+ const didApply = deleteBlockNodeById(sdBlockId);
26895
+ if (didApply) clearIndexCache(editor);
26896
+ return didApply;
26897
+ }, { expectedRevision: options?.expectedRevision }).steps[0]?.effect !== "changed") throw new DocumentApiAdapterError("INTERNAL_ERROR", "blocks.delete command returned false despite passing all pre-apply checks. This is an internal invariant violation.", {
26898
+ sdBlockId,
26899
+ target: input.target
26900
+ });
26901
+ return {
26902
+ success: true,
26903
+ deleted: input.target
26904
+ };
26905
+ }
26598
26906
  var DERIVED_ID_LENGTH = 24;
26599
26907
  function getRawTrackedMarks(editor) {
26600
26908
  try {
@@ -26718,9 +27026,7 @@ function resolveCreateInsertPosition(editor, at, operationLabel) {
26718
27026
  const location$1 = at ?? { kind: "documentEnd" };
26719
27027
  if (location$1.kind === "documentStart") return 0;
26720
27028
  if (location$1.kind === "documentEnd") return editor.state.doc.content.size;
26721
- const target = findBlockById(getBlockIndex(editor), location$1.target);
26722
- if (!target) throw new DocumentApiAdapterError("TARGET_NOT_FOUND", `Create ${operationLabel} target block was not found.`, { target: location$1.target });
26723
- return location$1.kind === "before" ? target.pos : target.end;
27029
+ return resolveBlockInsertionPos(editor, location$1.target.nodeId, location$1.kind);
26724
27030
  }
26725
27031
  function resolveCreatedBlock(editor, nodeType, blockId) {
26726
27032
  const index = getBlockIndex(editor);
@@ -26822,7 +27128,9 @@ function createParagraphWrapper(editor, input, options) {
26822
27128
  try {
26823
27129
  const paragraph = resolveCreatedBlock(editor, "paragraph", paragraphId);
26824
27130
  if (mode === "tracked") trackedChangeRefs = collectTrackInsertRefsInRange(editor, paragraph.pos, paragraph.end);
26825
- } catch {}
27131
+ } catch (e) {
27132
+ if (!(e instanceof DocumentApiAdapterError)) throw e;
27133
+ }
26826
27134
  }
26827
27135
  return didApply;
26828
27136
  }, { expectedRevision: options?.expectedRevision }).steps[0]?.effect !== "changed") return {
@@ -26884,7 +27192,9 @@ function createHeadingWrapper(editor, input, options) {
26884
27192
  try {
26885
27193
  const heading = resolveCreatedBlock(editor, "heading", headingId);
26886
27194
  if (mode === "tracked") trackedChangeRefs = collectTrackInsertRefsInRange(editor, heading.pos, heading.end);
26887
- } catch {}
27195
+ } catch (e) {
27196
+ if (!(e instanceof DocumentApiAdapterError)) throw e;
27197
+ }
26888
27198
  }
26889
27199
  return didApply;
26890
27200
  }, { expectedRevision: options?.expectedRevision }).steps[0]?.effect !== "changed") return {
@@ -28096,14 +28406,15 @@ function trackChangesRejectAllWrapper(editor, _input, options) {
28096
28406
  return { success: true };
28097
28407
  }
28098
28408
  function previewPlan(editor, input) {
28099
- const evaluatedRevision = getRevision(editor);
28100
28409
  checkRevision(editor, input.expectedRevision);
28101
28410
  if (!input.steps?.length) throw planError("INVALID_INPUT", "plan must contain at least one step");
28102
28411
  const failures = [];
28103
28412
  const stepPreviews = [];
28104
28413
  let currentPhase = "compile";
28414
+ let evaluatedRevision = getRevision(editor);
28105
28415
  try {
28106
28416
  const compiled = compilePlan(editor, input.steps);
28417
+ evaluatedRevision = compiled.compiledRevision;
28107
28418
  currentPhase = "execute";
28108
28419
  const tr = editor.state.tr;
28109
28420
  const { stepOutcomes, assertFailures } = runMutationsOnTransaction(editor, tr, compiled, { throwOnAssertFailure: false });
@@ -28606,14 +28917,24 @@ function queryMatchAdapter(editor, input) {
28606
28917
  const userOffset = input.offset ?? 0;
28607
28918
  const paginatedMatches = isTextSelector ? applyPagination(rawMatches, userOffset, input.limit) : rawMatches;
28608
28919
  if (require$1 === "first" || require$1 === "exactlyOne" || require$1 === "all") {
28609
- if (totalMatches === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges");
28920
+ if (totalMatches === 0) throw planError("MATCH_NOT_FOUND", "selector matched zero ranges", void 0, {
28921
+ selectorType: input.select?.type ?? "unknown",
28922
+ selectorPattern: input.select?.pattern ?? "",
28923
+ selectorMode: input.select?.mode ?? "contains",
28924
+ searchScope: (input.within?.kind === "block" ? input.within.nodeId : void 0) ?? "document",
28925
+ candidateCount: 0
28926
+ });
28610
28927
  }
28611
28928
  if (require$1 === "exactlyOne" && totalMatches > 1) throw planError("AMBIGUOUS_MATCH", `selector matched ${totalMatches} ranges, expected exactly one`, void 0, { matchCount: totalMatches });
28612
28929
  const matchItems = paginatedMatches.map((raw, pageIdx) => {
28613
28930
  const id = `m:${userOffset + pageIdx}`;
28614
28931
  if (isTextSelector && raw.textRanges?.length) {
28615
28932
  const blocks = buildMatchBlocks(editor, raw.textRanges, evaluatedRevision, id);
28616
- if (blocks.length === 0) throw planError("INTERNAL_ERROR", `text match produced no blocks for ${id}`);
28933
+ if (blocks.length === 0) throw planError("INTERNAL_ERROR", `text match produced no blocks for ${id}`, void 0, {
28934
+ source: "query-match-adapter:buildMatchEntries",
28935
+ invariant: "text match must have at least one block after zero-width filtering",
28936
+ context: { matchId: id }
28937
+ });
28617
28938
  const snippetResult = buildBlocksSnippet(editor, blocks);
28618
28939
  return {
28619
28940
  id,
@@ -28826,6 +29147,7 @@ function getDocumentApiAdapters(editor) {
28826
29147
  acceptAll: (input, options) => trackChangesAcceptAllWrapper(editor, input, options),
28827
29148
  rejectAll: (input, options) => trackChangesRejectAllWrapper(editor, input, options)
28828
29149
  },
29150
+ blocks: { delete: (input, options) => blocksDeleteWrapper(editor, input, options) },
28829
29151
  create: {
28830
29152
  paragraph: (input, options) => createParagraphWrapper(editor, input, options),
28831
29153
  heading: (input, options) => createHeadingWrapper(editor, input, options)
@@ -30216,7 +30538,7 @@ var Editor = class Editor extends EventEmitter$1 {
30216
30538
  return migrations.length > 0;
30217
30539
  }
30218
30540
  processCollaborationMigrations() {
30219
- console.debug("[checkVersionMigrations] Current editor version", "1.17.0-next.11");
30541
+ console.debug("[checkVersionMigrations] Current editor version", "1.17.0-next.13");
30220
30542
  if (!this.options.ydoc) return;
30221
30543
  let docVersion = this.options.ydoc.getMap("meta").get("version");
30222
30544
  if (!docVersion) docVersion = "initial";