@drghaliasri/butex 6.0.0 → 6.1.1

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.
@@ -107,14 +107,6 @@ function normalizeDocument2Meta(value) {
107
107
  abstract: typeof raw.abstract === "string" ? raw.abstract : ""
108
108
  };
109
109
  }
110
- function cloneDocument2Meta(meta) {
111
- return {
112
- title: meta.title,
113
- authors: meta.authors,
114
- date: { ...meta.date },
115
- abstract: meta.abstract
116
- };
117
- }
118
110
 
119
111
  // src/document2/citations.ts
120
112
  var DEFAULT_REFERENCE_FIELD_SEPARATOR = "\u060C";
@@ -596,6 +588,76 @@ function refTokenLatex(keys, refCommand) {
596
588
  return `${command}{${keys.join(",")}}`;
597
589
  }
598
590
 
591
+ // src/document2/inlineIdentity.ts
592
+ function createDocument2IdentityState() {
593
+ return {
594
+ blockIds: /* @__PURE__ */ new Set(),
595
+ itemIds: /* @__PURE__ */ new Set(),
596
+ fieldIds: /* @__PURE__ */ new Set(),
597
+ tokenIds: /* @__PURE__ */ new Set()
598
+ };
599
+ }
600
+ function importedOrGeneratedId(value, prefix, used) {
601
+ const imported = typeof value === "string" ? value.trim() : "";
602
+ if (imported.length > 0 && !used.has(imported)) {
603
+ used.add(imported);
604
+ return imported;
605
+ }
606
+ let generated = document2Id(prefix);
607
+ while (used.has(generated)) {
608
+ generated = document2Id(prefix);
609
+ }
610
+ used.add(generated);
611
+ return generated;
612
+ }
613
+ function inlineTokenSource(token) {
614
+ if (token.kind === "text") {
615
+ return token.text;
616
+ }
617
+ if (token.kind === "math") {
618
+ return token.source;
619
+ }
620
+ if (token.kind === "cite") {
621
+ return citeTokenLatex(token.keys);
622
+ }
623
+ return refTokenLatex(token.keys, token.refCommand);
624
+ }
625
+ function inlineIdsFromField(field) {
626
+ let offset = 0;
627
+ const tokens = field.tokens.map((token) => {
628
+ const start = offset;
629
+ offset += inlineTokenSource(token).length;
630
+ return { id: token.id, kind: token.kind, start, end: offset };
631
+ });
632
+ return { field_id: field.id, tokens };
633
+ }
634
+ function isAlignedSidecar(value, field) {
635
+ if (typeof value !== "object" || value === null || !Array.isArray(value.tokens)) {
636
+ return false;
637
+ }
638
+ const tokens = value.tokens;
639
+ const expected = inlineIdsFromField(field).tokens;
640
+ if (tokens.length !== expected.length) {
641
+ return false;
642
+ }
643
+ return tokens.every((token, index) => {
644
+ const expectedToken = expected[index];
645
+ return typeof token === "object" && token !== null && expectedToken !== void 0 && token.kind === expectedToken.kind && token.start === expectedToken.start && token.end === expectedToken.end;
646
+ });
647
+ }
648
+ function applyImportedInlineIds(field, sidecar, state) {
649
+ field.id = importedOrGeneratedId(
650
+ typeof sidecar === "object" && sidecar !== null ? sidecar.field_id : void 0,
651
+ "field",
652
+ state.fieldIds
653
+ );
654
+ const aligned = isAlignedSidecar(sidecar, field);
655
+ field.tokens.forEach((token, index) => {
656
+ token.id = importedOrGeneratedId(aligned ? sidecar.tokens[index]?.id : void 0, token.kind, state.tokenIds);
657
+ });
658
+ return field;
659
+ }
660
+
599
661
  // src/document2/inlineScanner.ts
600
662
  var MATH_ENVIRONMENTS = /* @__PURE__ */ new Set([
601
663
  "equation",
@@ -674,17 +736,13 @@ function citeSpan(value, start) {
674
736
  function refSpan(value, start) {
675
737
  const eqrefPrefix = "\\eqref{";
676
738
  const refPrefix = "\\ref{";
677
- let refCommand = "ref";
678
- let prefix = refPrefix;
679
- if (value.startsWith(eqrefPrefix, start) && !isEscaped(value, start)) {
680
- refCommand = "eqref";
681
- prefix = eqrefPrefix;
682
- } else if (value.startsWith(refPrefix, start) && !isEscaped(value, start)) {
683
- refCommand = "ref";
684
- prefix = refPrefix;
685
- } else {
739
+ const isEqref = value.startsWith(eqrefPrefix, start) && !isEscaped(value, start);
740
+ const isRef = value.startsWith(refPrefix, start) && !isEscaped(value, start);
741
+ if (!isEqref && !isRef) {
686
742
  return null;
687
743
  }
744
+ const refCommand = isEqref ? "eqref" : "ref";
745
+ const prefix = isEqref ? eqrefPrefix : refPrefix;
688
746
  const openBrace = start + prefix.length - 1;
689
747
  if (value[openBrace] !== "{") {
690
748
  return null;
@@ -743,7 +801,14 @@ function mathSpanAt(value, i) {
743
801
  return null;
744
802
  }
745
803
  function detectMathSpans2(value) {
746
- return detectInlineSpans2(value).filter((span) => span.kind === "math").map(({ kind: _kind, ...span }) => span);
804
+ return detectInlineSpans2(value).filter((span) => span.kind === "math").map((span) => ({
805
+ start: span.start,
806
+ end: span.end,
807
+ source: span.source,
808
+ opening: span.opening,
809
+ closing: span.closing,
810
+ display: span.display
811
+ }));
747
812
  }
748
813
  function detectInlineSpans2(value) {
749
814
  const spans = [];
@@ -797,17 +862,13 @@ function asBlockJson(value) {
797
862
  return value;
798
863
  }
799
864
  function blockIdFromJson(json, state) {
800
- const imported = typeof json.id === "string" ? json.id.trim() : "";
801
- if (imported.length > 0 && !state.used.has(imported)) {
802
- state.used.add(imported);
803
- return imported;
804
- }
805
- let generated = document2Id("block");
806
- while (state.used.has(generated)) {
807
- generated = document2Id("block");
865
+ return importedOrGeneratedId(json.id, "block", state.blockIds);
866
+ }
867
+ function metadataFromJson(value) {
868
+ if (!isObject2(value) || value.source !== "agent" && value.source !== "user") {
869
+ return {};
808
870
  }
809
- state.used.add(generated);
810
- return generated;
871
+ return { metadata: { source: value.source } };
811
872
  }
812
873
  function pushDiagnostic(diagnostics, options, path, message) {
813
874
  if (options.strict) {
@@ -990,44 +1051,54 @@ function pushFormattedTextTokens(tokens, text, sourceStart, formats) {
990
1051
  function closingForList(command) {
991
1052
  return command === "\\begin{itemize}" ? "\\end{itemize}" : "\\end{enumerate}";
992
1053
  }
993
- function parseTextBlock(json, options, path, diagnostics, blockIds) {
1054
+ function parseTextBlock(json, options, path, diagnostics, identities) {
994
1055
  const value = requireString(json.value, `${json.command} requires string value`);
995
1056
  return {
996
- id: blockIdFromJson(json, blockIds),
1057
+ id: blockIdFromJson(json, identities),
997
1058
  kind: "textBlock",
998
1059
  command: json.command,
999
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.command === "\\paragraph" ? json.formats ?? [] : []),
1060
+ field: applyImportedInlineIds(
1061
+ createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.command === "\\paragraph" ? json.formats ?? [] : []),
1062
+ json.inline_ids,
1063
+ identities
1064
+ ),
1065
+ ...metadataFromJson(json.metadata),
1000
1066
  ...json.centered === true ? { centered: true } : {}
1001
1067
  };
1002
1068
  }
1003
- function parseListItem(json, options, path, diagnostics, blockIds) {
1069
+ function parseListItem(json, options, path, diagnostics, identities) {
1004
1070
  const value = requireString(json.value, "Document list item requires string value");
1005
1071
  const blocksJson = Array.isArray(json.blocks) ? json.blocks : [];
1006
1072
  return {
1007
- id: document2Id("item"),
1008
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.formats ?? []),
1073
+ id: importedOrGeneratedId(json.id, "item", identities.itemIds),
1074
+ field: applyImportedInlineIds(
1075
+ createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.formats ?? []),
1076
+ json.inline_ids,
1077
+ identities
1078
+ ),
1009
1079
  blocks: blocksJson.map(
1010
- (block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics, blockIds)
1080
+ (block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics, identities)
1011
1081
  )
1012
1082
  };
1013
1083
  }
1014
- function parseListBlock(json, options, path, diagnostics, blockIds) {
1084
+ function parseListBlock(json, options, path, diagnostics, identities) {
1015
1085
  if (!Array.isArray(json.items)) {
1016
1086
  throw new Error(`${json.command} requires items array`);
1017
1087
  }
1018
1088
  const command = json.command;
1019
1089
  const closing = closingForList(command);
1020
1090
  return {
1021
- id: blockIdFromJson(json, blockIds),
1091
+ id: blockIdFromJson(json, identities),
1022
1092
  kind: "list",
1023
1093
  command,
1024
1094
  closing,
1025
1095
  items: json.items.map(
1026
- (item, index) => parseListItem(item, options, `${path}.items[${String(index)}]`, diagnostics, blockIds)
1027
- )
1096
+ (item, index) => parseListItem(item, options, `${path}.items[${String(index)}]`, diagnostics, identities)
1097
+ ),
1098
+ ...metadataFromJson(json.metadata)
1028
1099
  };
1029
1100
  }
1030
- function parseTableBlock(json, options, path, diagnostics, blockIds) {
1101
+ function parseTableBlock(json, options, path, diagnostics, identities) {
1031
1102
  if (!Array.isArray(json.rows)) {
1032
1103
  throw new Error("\\begin{tabular} requires rows array");
1033
1104
  }
@@ -1043,75 +1114,84 @@ function parseTableBlock(json, options, path, diagnostics, blockIds) {
1043
1114
  const cellMathObjects = mathObjects.slice(mathObjectIndex, mathObjectIndex + spanCount);
1044
1115
  mathObjectIndex += spanCount;
1045
1116
  const cellFormats = json.cell_formats?.[rowIndex]?.[columnIndex] ?? [];
1046
- return createInlineField2(value, cellMathObjects, options, `${path}.rows[${String(rowIndex)}][${String(columnIndex)}]`, diagnostics, cellFormats);
1117
+ return applyImportedInlineIds(
1118
+ createInlineField2(value, cellMathObjects, options, `${path}.rows[${String(rowIndex)}][${String(columnIndex)}]`, diagnostics, cellFormats),
1119
+ json.cell_inline_ids?.[rowIndex]?.[columnIndex],
1120
+ identities
1121
+ );
1047
1122
  });
1048
1123
  });
1049
1124
  if (mathObjects.length > 0 && mathObjects.length !== mathObjectIndex) {
1050
1125
  pushDiagnostic(diagnostics, options, path, `math_objects count mismatch: detected ${String(mathObjectIndex)}, got ${String(mathObjects.length)}`);
1051
1126
  }
1052
1127
  return {
1053
- id: blockIdFromJson(json, blockIds),
1128
+ id: blockIdFromJson(json, identities),
1054
1129
  kind: "table",
1055
1130
  command: "\\begin{tabular}",
1056
1131
  closing: "\\end{tabular}",
1057
1132
  columns: typeof json.columns === "string" ? json.columns : "",
1058
1133
  rows,
1059
- ...parseFloatMetaFromJson(json)
1134
+ ...parseFloatMetaFromJson(json),
1135
+ ...metadataFromJson(json.metadata)
1060
1136
  };
1061
1137
  }
1062
- function parseImageBlock(json, blockIds) {
1138
+ function parseImageBlock(json, identities) {
1063
1139
  const assetId = typeof json.asset_id === "string" && json.asset_id.length > 0 ? json.asset_id : void 0;
1064
1140
  const value = assetId !== void 0 ? typeof json.value === "string" ? json.value : "" : requireString(json.value, "\\includegraphics requires string value");
1065
1141
  return {
1066
- id: blockIdFromJson(json, blockIds),
1142
+ id: blockIdFromJson(json, identities),
1067
1143
  kind: "image",
1068
1144
  command: "\\includegraphics",
1069
1145
  value,
1070
1146
  ...assetId !== void 0 ? { assetId } : {},
1071
1147
  options: isRecordOfStrings(json.options) ? json.options : {},
1072
- ...parseFloatMetaFromJson(json)
1148
+ ...parseFloatMetaFromJson(json),
1149
+ ...metadataFromJson(json.metadata)
1073
1150
  };
1074
1151
  }
1075
- function parseRawBlock(json, blockIds) {
1152
+ function parseRawBlock(json, identities) {
1076
1153
  return {
1077
- id: blockIdFromJson(json, blockIds),
1154
+ id: blockIdFromJson(json, identities),
1078
1155
  kind: "raw",
1079
1156
  command: "\\raw",
1080
- value: typeof json.value === "string" ? json.value : ""
1157
+ value: typeof json.value === "string" ? json.value : "",
1158
+ ...metadataFromJson(json.metadata)
1081
1159
  };
1082
1160
  }
1083
- function parseBibliographyBlock(json, blockIds) {
1161
+ function parseBibliographyBlock(json, identities) {
1084
1162
  return {
1085
- id: blockIdFromJson(json, blockIds),
1163
+ id: blockIdFromJson(json, identities),
1086
1164
  kind: "bibliography",
1087
1165
  command: "\\begin{thebibliography}",
1088
- closing: "\\end{thebibliography}"
1166
+ closing: "\\end{thebibliography}",
1167
+ ...metadataFromJson(json.metadata)
1089
1168
  };
1090
1169
  }
1091
- function parseBlock(json, options, path, diagnostics, blockIds) {
1170
+ function parseBlock(json, options, path, diagnostics, identities) {
1092
1171
  if (TEXT_COMMANDS.has(json.command)) {
1093
- return parseTextBlock(json, options, path, diagnostics, blockIds);
1172
+ return parseTextBlock(json, options, path, diagnostics, identities);
1094
1173
  }
1095
1174
  if (LIST_COMMANDS.has(json.command)) {
1096
- return parseListBlock(json, options, path, diagnostics, blockIds);
1175
+ return parseListBlock(json, options, path, diagnostics, identities);
1097
1176
  }
1098
1177
  if (json.command === "\\begin{tabular}") {
1099
- return parseTableBlock(json, options, path, diagnostics, blockIds);
1178
+ return parseTableBlock(json, options, path, diagnostics, identities);
1100
1179
  }
1101
1180
  if (json.command === "\\includegraphics") {
1102
- return parseImageBlock(json, blockIds);
1181
+ return parseImageBlock(json, identities);
1103
1182
  }
1104
1183
  if (json.command === "\\begin{thebibliography}" || json.command === "\\bibliography") {
1105
- return parseBibliographyBlock(json, blockIds);
1184
+ return parseBibliographyBlock(json, identities);
1106
1185
  }
1107
1186
  if (json.command === "\\raw") {
1108
- return parseRawBlock(json, blockIds);
1187
+ return parseRawBlock(json, identities);
1109
1188
  }
1110
1189
  return {
1111
- id: blockIdFromJson(json, blockIds),
1190
+ id: blockIdFromJson(json, identities),
1112
1191
  kind: "raw",
1113
1192
  command: "\\raw",
1114
- value: typeof json.value === "string" ? json.value : json.command
1193
+ value: typeof json.value === "string" ? json.value : json.command,
1194
+ ...metadataFromJson(json.metadata)
1115
1195
  };
1116
1196
  }
1117
1197
  function fromDocumentJson2(json, options = {}) {
@@ -1122,13 +1202,13 @@ function fromDocumentJson2(json, options = {}) {
1122
1202
  throw new Error("DocumentObject requires blocks array");
1123
1203
  }
1124
1204
  const diagnostics = [];
1125
- const blockIds = { used: /* @__PURE__ */ new Set() };
1205
+ const identities = createDocument2IdentityState();
1126
1206
  return {
1127
1207
  nodeType: "DocumentObject",
1128
1208
  meta: normalizeDocument2Meta(json.meta),
1129
1209
  references: parseReferences(json.references),
1130
1210
  blocks: json.blocks.map(
1131
- (block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics, blockIds)
1211
+ (block, index) => parseBlock(asBlockJson(block), options, `$.blocks[${String(index)}]`, diagnostics, identities)
1132
1212
  ),
1133
1213
  diagnostics
1134
1214
  };
@@ -2446,97 +2526,762 @@ var CHAR_FONT_EXPORT_TEX = Object.fromEntries(
2446
2526
  NON_DEFAULT_CHARACTER_FONT_SPECS.map((font) => [font.id, font.macro])
2447
2527
  );
2448
2528
 
2449
- // src/document2/commands.ts
2450
- function cloneToken(token) {
2451
- return token.kind === "text" ? { ...token } : { ...token };
2529
+ // src/document2/jsonCommandTypes.ts
2530
+ var Document2CommandError = class extends Error {
2531
+ code;
2532
+ constructor(code, message) {
2533
+ super(message);
2534
+ this.name = "Document2CommandError";
2535
+ this.code = code;
2536
+ }
2537
+ };
2538
+
2539
+ // src/document2/jsonCommandHelpers.ts
2540
+ function isObject3(value) {
2541
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2452
2542
  }
2453
- function cloneField(field) {
2454
- return { id: field.id, tokens: field.tokens.map(cloneToken) };
2543
+ function requireString2(value, field, allowEmpty = true) {
2544
+ if (typeof value !== "string" || !allowEmpty && value.trim().length === 0) {
2545
+ throw new Document2CommandError("invalid_command", `${field} must be a${allowEmpty ? "" : " non-empty"} string`);
2546
+ }
2547
+ return value;
2455
2548
  }
2456
- function cloneBlock(block) {
2457
- if (block.kind === "textBlock") {
2458
- return { ...block, field: cloneField(block.field) };
2549
+ function optionalString(value, field) {
2550
+ if (!(field in value)) {
2551
+ return void 0;
2459
2552
  }
2460
- if (block.kind === "list") {
2553
+ if (typeof value[field] !== "string") {
2554
+ throw new Document2CommandError("invalid_command", `${field} must be a string when provided`);
2555
+ }
2556
+ return value[field];
2557
+ }
2558
+ function requireBoolean(value, field) {
2559
+ if (typeof value !== "boolean") {
2560
+ throw new Document2CommandError("invalid_command", `${field} must be a boolean`);
2561
+ }
2562
+ return value;
2563
+ }
2564
+ function requireIndex(value, field) {
2565
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
2566
+ throw new Document2CommandError("invalid_command", `${field} must be a non-negative integer`);
2567
+ }
2568
+ return value;
2569
+ }
2570
+ function parseMetadata(value) {
2571
+ if (value === void 0) {
2572
+ return void 0;
2573
+ }
2574
+ if (!isObject3(value)) {
2575
+ throw new Document2CommandError("invalid_command", "metadata must be an object");
2576
+ }
2577
+ if (value.source === void 0) {
2578
+ return void 0;
2579
+ }
2580
+ if (value.source !== "agent" && value.source !== "user") {
2581
+ throw new Document2CommandError("invalid_command", "metadata.source must be agent or user");
2582
+ }
2583
+ return { source: value.source };
2584
+ }
2585
+ function parseBlockAnchor(value) {
2586
+ if (!isObject3(value)) {
2587
+ throw new Document2CommandError("invalid_anchor", "anchor must be an object");
2588
+ }
2589
+ const before = typeof value.before_block_id === "string" ? value.before_block_id.trim() : "";
2590
+ const after = typeof value.after_block_id === "string" ? value.after_block_id.trim() : "";
2591
+ const choices = Number(before.length > 0) + Number(after.length > 0) + Number(value.end === true);
2592
+ if (choices !== 1) {
2593
+ throw new Document2CommandError("invalid_anchor", "anchor requires exactly one block position");
2594
+ }
2595
+ if (before) {
2596
+ return { before_block_id: before };
2597
+ }
2598
+ if (after) {
2599
+ return { after_block_id: after };
2600
+ }
2601
+ return { end: true };
2602
+ }
2603
+ function parseListItemAnchor(value) {
2604
+ if (!isObject3(value)) {
2605
+ throw new Document2CommandError("invalid_anchor", "anchor must be an object");
2606
+ }
2607
+ const before = typeof value.before_item_id === "string" ? value.before_item_id.trim() : "";
2608
+ const after = typeof value.after_item_id === "string" ? value.after_item_id.trim() : "";
2609
+ const choices = Number(before.length > 0) + Number(after.length > 0) + Number(value.end === true);
2610
+ if (choices !== 1) {
2611
+ throw new Document2CommandError("invalid_anchor", "anchor requires exactly one list-item position");
2612
+ }
2613
+ if (before) {
2614
+ return { before_item_id: before };
2615
+ }
2616
+ if (after) {
2617
+ return { after_item_id: after };
2618
+ }
2619
+ return { end: true };
2620
+ }
2621
+ function blockInsertionIndex(document2, anchor) {
2622
+ if ("end" in anchor) {
2623
+ return document2.blocks.length;
2624
+ }
2625
+ const id = "before_block_id" in anchor ? anchor.before_block_id : anchor.after_block_id;
2626
+ const index = document2.blocks.findIndex((block) => block.id === id);
2627
+ if (index < 0) {
2628
+ throw new Document2CommandError("anchor_not_found", "Anchor block was not found");
2629
+ }
2630
+ return "before_block_id" in anchor ? index : index + 1;
2631
+ }
2632
+ function itemInsertionIndex(list, anchor) {
2633
+ if ("end" in anchor) {
2634
+ return list.items.length;
2635
+ }
2636
+ const id = "before_item_id" in anchor ? anchor.before_item_id : anchor.after_item_id;
2637
+ const index = list.items.findIndex((item) => item.id === id);
2638
+ if (index < 0) {
2639
+ throw new Document2CommandError("anchor_not_found", "Anchor list item was not found");
2640
+ }
2641
+ return "before_item_id" in anchor ? index : index + 1;
2642
+ }
2643
+ function findField(blocks, fieldId) {
2644
+ for (const block of blocks) {
2645
+ if (block.kind === "textBlock" && block.field.id === fieldId) {
2646
+ return block.field;
2647
+ }
2648
+ if (block.kind === "list") {
2649
+ for (const item of block.items) {
2650
+ if (item.field.id === fieldId) {
2651
+ return item.field;
2652
+ }
2653
+ const nested = findField(item.blocks, fieldId);
2654
+ if (nested) {
2655
+ return nested;
2656
+ }
2657
+ }
2658
+ }
2659
+ if (block.kind === "table") {
2660
+ for (const row of block.rows) {
2661
+ const cell = row.find((field) => field.id === fieldId);
2662
+ if (cell) {
2663
+ return cell;
2664
+ }
2665
+ }
2666
+ }
2667
+ }
2668
+ return null;
2669
+ }
2670
+ function findBlock(blocks, blockId) {
2671
+ for (const block of blocks) {
2672
+ if (block.id === blockId) {
2673
+ return block;
2674
+ }
2675
+ if (block.kind === "list") {
2676
+ for (const item of block.items) {
2677
+ const nested = findBlock(item.blocks, blockId);
2678
+ if (nested) {
2679
+ return nested;
2680
+ }
2681
+ }
2682
+ }
2683
+ }
2684
+ return null;
2685
+ }
2686
+ function collectIdentitySets(blocks, sets) {
2687
+ for (const block of blocks) {
2688
+ sets.blocks.add(block.id);
2689
+ if (block.kind === "textBlock") {
2690
+ sets.fields.add(block.field.id);
2691
+ block.field.tokens.forEach((token) => sets.tokens.add(token.id));
2692
+ } else if (block.kind === "list") {
2693
+ for (const item of block.items) {
2694
+ sets.items.add(item.id);
2695
+ sets.fields.add(item.field.id);
2696
+ item.field.tokens.forEach((token) => sets.tokens.add(token.id));
2697
+ collectIdentitySets(item.blocks, sets);
2698
+ }
2699
+ } else if (block.kind === "table") {
2700
+ for (const row of block.rows) {
2701
+ for (const field of row) {
2702
+ sets.fields.add(field.id);
2703
+ field.tokens.forEach((token) => sets.tokens.add(token.id));
2704
+ }
2705
+ }
2706
+ }
2707
+ }
2708
+ }
2709
+ function documentIdentitySets(document2) {
2710
+ const sets = {
2711
+ blocks: /* @__PURE__ */ new Set(),
2712
+ items: /* @__PURE__ */ new Set(),
2713
+ fields: /* @__PURE__ */ new Set(),
2714
+ tokens: /* @__PURE__ */ new Set()
2715
+ };
2716
+ collectIdentitySets(document2.blocks, sets);
2717
+ return sets;
2718
+ }
2719
+ function unusedGeneratedId(prefix, used) {
2720
+ let id = document2Id(prefix);
2721
+ while (used.has(id)) {
2722
+ id = document2Id(prefix);
2723
+ }
2724
+ return id;
2725
+ }
2726
+ function newBlockId(document2) {
2727
+ return unusedGeneratedId("block", documentIdentitySets(document2).blocks);
2728
+ }
2729
+ function newItemId(document2) {
2730
+ return unusedGeneratedId("item", documentIdentitySets(document2).items);
2731
+ }
2732
+ function newTokenId(document2, prefix) {
2733
+ return unusedGeneratedId(prefix, documentIdentitySets(document2).tokens);
2734
+ }
2735
+ function createCommandInlineField(document2, text) {
2736
+ const field = createInlineField2(text);
2737
+ const used = documentIdentitySets(document2);
2738
+ field.id = unusedGeneratedId("field", used.fields);
2739
+ field.tokens.forEach((token) => {
2740
+ token.id = unusedGeneratedId(token.kind, used.tokens);
2741
+ used.tokens.add(token.id);
2742
+ });
2743
+ return field;
2744
+ }
2745
+ function requireList(document2, listId) {
2746
+ const block = findBlock(document2.blocks, listId);
2747
+ if (!block) {
2748
+ throw new Document2CommandError("list_not_found", "Document list was not found");
2749
+ }
2750
+ if (block.kind !== "list") {
2751
+ throw new Document2CommandError("block_kind_mismatch", "Target block is not a list");
2752
+ }
2753
+ return block;
2754
+ }
2755
+ function requireTable(document2, tableId) {
2756
+ const block = findBlock(document2.blocks, tableId);
2757
+ if (!block) {
2758
+ throw new Document2CommandError("table_not_found", "Document table was not found");
2759
+ }
2760
+ if (block.kind !== "table") {
2761
+ throw new Document2CommandError("block_kind_mismatch", "Target block is not a table");
2762
+ }
2763
+ return block;
2764
+ }
2765
+ function fieldHasStructuredContent(field) {
2766
+ return field.tokens.some((token) => token.kind !== "text" || token.style !== void 0);
2767
+ }
2768
+ function replacePlainField(document2, field, text) {
2769
+ if (fieldHasStructuredContent(field)) {
2770
+ throw new Document2CommandError(
2771
+ "unsupported_inline_content",
2772
+ "Whole-field replacement is not supported for formatted or structured inline content"
2773
+ );
2774
+ }
2775
+ const next = createCommandInlineField(document2, text);
2776
+ field.tokens = next.tokens;
2777
+ }
2778
+ function textStylesEqual2(a, b) {
2779
+ return Boolean(a?.bold) === Boolean(b?.bold) && Boolean(a?.italic) === Boolean(b?.italic) && Boolean(a?.underline) === Boolean(b?.underline);
2780
+ }
2781
+ function compactTextTokens2(tokens, emptyTokenId = document2Id("text")) {
2782
+ const compacted = [];
2783
+ for (const token of tokens) {
2784
+ const previous = compacted[compacted.length - 1];
2785
+ if (previous?.kind === "text" && token.kind === "text" && textStylesEqual2(previous.style, token.style)) {
2786
+ previous.text += token.text;
2787
+ } else {
2788
+ compacted.push(token);
2789
+ }
2790
+ }
2791
+ return compacted.length > 0 ? compacted : [{ id: emptyTokenId, kind: "text", text: "" }];
2792
+ }
2793
+
2794
+ // src/document2/inlineJsonCommands.ts
2795
+ function parseInlineAnchor(value) {
2796
+ if (!isObject3(value)) {
2797
+ throw new Document2CommandError("invalid_anchor", "anchor must be an object");
2798
+ }
2799
+ const before = typeof value.before_token_id === "string" ? value.before_token_id.trim() : "";
2800
+ const after = typeof value.after_token_id === "string" ? value.after_token_id.trim() : "";
2801
+ const choices = Number(before.length > 0) + Number(after.length > 0) + Number(value.start === true) + Number(value.end === true);
2802
+ if (choices !== 1) {
2803
+ throw new Document2CommandError("invalid_anchor", "anchor requires exactly one inline-token position");
2804
+ }
2805
+ if (before) {
2806
+ return { before_token_id: before };
2807
+ }
2808
+ if (after) {
2809
+ return { after_token_id: after };
2810
+ }
2811
+ return value.start === true ? { start: true } : { end: true };
2812
+ }
2813
+ function parseStyle(value) {
2814
+ if (value === void 0) {
2815
+ return void 0;
2816
+ }
2817
+ if (!isObject3(value)) {
2818
+ throw new Document2CommandError("invalid_inline_token", "text token style must be an object");
2819
+ }
2820
+ const style = {};
2821
+ for (const key of ["bold", "italic", "underline"]) {
2822
+ if (value[key] !== void 0 && value[key] !== true) {
2823
+ throw new Document2CommandError("invalid_inline_token", `text token style.${key} must be true when provided`);
2824
+ }
2825
+ if (value[key] === true) {
2826
+ style[key] = true;
2827
+ }
2828
+ }
2829
+ return style.bold || style.italic || style.underline ? style : void 0;
2830
+ }
2831
+ function normalizedKeys(value) {
2832
+ if (!Array.isArray(value)) {
2833
+ throw new Document2CommandError("invalid_inline_token", "citation/reference keys must be an array");
2834
+ }
2835
+ const keys = [];
2836
+ for (const entry of value) {
2837
+ if (typeof entry !== "string") {
2838
+ throw new Document2CommandError("invalid_inline_token", "citation/reference keys must be strings");
2839
+ }
2840
+ const key = entry.trim();
2841
+ if (key.length > 0 && !keys.includes(key)) {
2842
+ keys.push(key);
2843
+ }
2844
+ }
2845
+ if (keys.length === 0) {
2846
+ throw new Document2CommandError("invalid_inline_token", "citation/reference requires at least one non-empty key");
2847
+ }
2848
+ return keys;
2849
+ }
2850
+ function parseMathObject(value, source) {
2851
+ if (!isObject3(value) || value.node_type !== "MathObject") {
2852
+ throw new Document2CommandError("math_object_mismatch", "math_object must be a structured MathObject");
2853
+ }
2854
+ const spans = detectInlineSpans2(source);
2855
+ const span = spans[0];
2856
+ if (spans.length !== 1 || span?.kind !== "math" || span.start !== 0 || span.end !== source.length) {
2857
+ throw new Document2CommandError("math_object_mismatch", "math source must contain exactly one complete delimited span");
2858
+ }
2859
+ if (value.math_mode !== span.opening || value.closing !== span.closing) {
2860
+ throw new Document2CommandError("math_object_mismatch", "math_object delimiters do not match math source");
2861
+ }
2862
+ try {
2863
+ fromMathObjectJson(value, value.source_side === "arabic" ? "arabic" : "english");
2864
+ } catch {
2865
+ throw new Document2CommandError("math_object_mismatch", "math_object must contain a valid structured equation");
2866
+ }
2867
+ return value;
2868
+ }
2869
+ function parseToken(value) {
2870
+ if (!isObject3(value) || typeof value.kind !== "string") {
2871
+ throw new Document2CommandError("invalid_inline_token", "token requires a kind");
2872
+ }
2873
+ if (value.kind === "text") {
2874
+ const text = requireString2(value.text, "token.text");
2875
+ if (detectInlineSpans2(text).length > 0) {
2876
+ throw new Document2CommandError("invalid_inline_token", "text token contains structured inline syntax");
2877
+ }
2878
+ const style = parseStyle(value.style);
2879
+ return { kind: "text", text, ...style ? { style } : {} };
2880
+ }
2881
+ if (value.kind === "cite") {
2882
+ return { kind: "cite", keys: normalizedKeys(value.keys) };
2883
+ }
2884
+ if (value.kind === "ref") {
2885
+ if (value.ref_command !== "ref" && value.ref_command !== "eqref") {
2886
+ throw new Document2CommandError("invalid_inline_token", "ref_command must be ref or eqref");
2887
+ }
2888
+ return { kind: "ref", keys: normalizedKeys(value.keys), ref_command: value.ref_command };
2889
+ }
2890
+ if (value.kind === "math") {
2891
+ const source = requireString2(value.source, "token.source", false);
2892
+ return { kind: "math", source, math_object: parseMathObject(value.math_object, source) };
2893
+ }
2894
+ throw new Document2CommandError("invalid_inline_token", "Unsupported inline token kind");
2895
+ }
2896
+ function parseInlineCommand(value) {
2897
+ if (value.op === "insert_inline_token") {
2461
2898
  return {
2462
- ...block,
2463
- items: block.items.map((item) => ({
2464
- ...item,
2465
- field: cloneField(item.field),
2466
- blocks: item.blocks.map(cloneBlock)
2467
- }))
2899
+ op: value.op,
2900
+ field_id: requireString2(value.field_id, "field_id", false).trim(),
2901
+ token: parseToken(value.token),
2902
+ anchor: parseInlineAnchor(value.anchor)
2468
2903
  };
2469
2904
  }
2470
- if (block.kind === "table") {
2471
- return { ...block, rows: block.rows.map((row) => row.map(cloneField)) };
2905
+ if (value.op === "replace_inline_token") {
2906
+ return {
2907
+ op: value.op,
2908
+ field_id: requireString2(value.field_id, "field_id", false).trim(),
2909
+ token_id: requireString2(value.token_id, "token_id", false).trim(),
2910
+ token: parseToken(value.token)
2911
+ };
2472
2912
  }
2473
- if (block.kind === "image") {
2474
- return { ...block, options: { ...block.options } };
2913
+ if (value.op === "remove_inline_token") {
2914
+ return {
2915
+ op: value.op,
2916
+ field_id: requireString2(value.field_id, "field_id", false).trim(),
2917
+ token_id: requireString2(value.token_id, "token_id", false).trim()
2918
+ };
2475
2919
  }
2476
- return { ...block };
2920
+ throw new Document2CommandError("invalid_command", "Unsupported inline command");
2477
2921
  }
2478
- function cloneDocument(document2) {
2922
+ function liveToken(input, id) {
2923
+ if (input.kind === "text") {
2924
+ return { id, kind: "text", text: input.text, ...input.style ? { style: { ...input.style } } : {} };
2925
+ }
2926
+ if (input.kind === "cite") {
2927
+ return { id, kind: "cite", keys: [...input.keys] };
2928
+ }
2929
+ if (input.kind === "ref") {
2930
+ return { id, kind: "ref", keys: [...input.keys], refCommand: input.ref_command };
2931
+ }
2932
+ const mathObject = input.math_object;
2933
+ const spans = detectInlineSpans2(input.source);
2934
+ const span = spans[0];
2935
+ if (!span || span.kind !== "math") {
2936
+ throw new Document2CommandError("math_object_mismatch", "math source is not a complete delimited span");
2937
+ }
2479
2938
  return {
2480
- nodeType: "DocumentObject",
2481
- meta: cloneDocument2Meta(document2.meta ?? emptyDocument2Meta()),
2482
- references: document2.references.map((reference) => ({ ...reference })),
2483
- blocks: document2.blocks.map(cloneBlock),
2484
- diagnostics: document2.diagnostics.map((diagnostic) => ({ ...diagnostic }))
2939
+ id,
2940
+ kind: "math",
2941
+ display: span.display,
2942
+ opening: span.opening,
2943
+ closing: span.closing,
2944
+ source: input.source,
2945
+ sourceSide: mathObject.source_side === "arabic" ? "arabic" : "english",
2946
+ math: fromMathObjectJson(mathObject, mathObject.source_side === "arabic" ? "arabic" : "english"),
2947
+ editable: true,
2948
+ sourceOwner: mathObject.source_owner === "editor" ? "editor" : "imported-structured",
2949
+ ...mathObject.label_enabled !== void 0 ? { labelEnabled: mathObject.label_enabled } : {},
2950
+ ...mathObject.label !== void 0 ? { label: mathObject.label } : {}
2485
2951
  };
2486
2952
  }
2487
- function insertBlockAfter(blocks, afterBlockId2, block) {
2488
- if (!afterBlockId2) {
2489
- blocks.push(block);
2953
+ function insertionIndex(tokens, anchor) {
2954
+ if ("start" in anchor) {
2955
+ return 0;
2956
+ }
2957
+ if ("end" in anchor) {
2958
+ return tokens.length;
2959
+ }
2960
+ const id = "before_token_id" in anchor ? anchor.before_token_id : anchor.after_token_id;
2961
+ const index = tokens.findIndex((token) => token.id === id);
2962
+ if (index < 0) {
2963
+ throw new Document2CommandError("token_not_found", "Anchor token was not found");
2964
+ }
2965
+ return "before_token_id" in anchor ? index : index + 1;
2966
+ }
2967
+ function applyInlineCommand(document2, command) {
2968
+ const field = findField(document2.blocks, command.field_id);
2969
+ if (!field) {
2970
+ throw new Document2CommandError("field_not_found", "Inline field was not found");
2971
+ }
2972
+ if (command.op === "insert_inline_token") {
2973
+ const index2 = insertionIndex(field.tokens, command.anchor);
2974
+ field.tokens.splice(index2, 0, liveToken(command.token, newTokenId(document2, command.token.kind)));
2490
2975
  return;
2491
2976
  }
2492
- const index = blocks.findIndex((entry) => entry.id === afterBlockId2);
2977
+ const index = field.tokens.findIndex((token) => token.id === command.token_id);
2493
2978
  if (index < 0) {
2494
- blocks.push(block);
2979
+ throw new Document2CommandError("token_not_found", "Inline token was not found");
2980
+ }
2981
+ if (command.op === "replace_inline_token") {
2982
+ field.tokens[index] = liveToken(command.token, command.token_id);
2495
2983
  return;
2496
2984
  }
2497
- blocks.splice(index + 1, 0, block);
2985
+ field.tokens = compactTextTokens2(
2986
+ [...field.tokens.slice(0, index), ...field.tokens.slice(index + 1)],
2987
+ newTokenId(document2, "text")
2988
+ );
2989
+ }
2990
+
2991
+ // src/document2/listJsonCommands.ts
2992
+ function stringArray(value, field) {
2993
+ if (!Array.isArray(value) || value.length === 0 || value.some((entry) => typeof entry !== "string")) {
2994
+ throw new Document2CommandError("invalid_command", `${field} must be a non-empty string array`);
2995
+ }
2996
+ return [...value];
2997
+ }
2998
+ function parseListCommand(value) {
2999
+ if (value.op === "insert_list") {
3000
+ const metadata = parseMetadata(value.metadata);
3001
+ return {
3002
+ op: value.op,
3003
+ ordered: requireBoolean(value.ordered, "ordered"),
3004
+ items: stringArray(value.items, "items"),
3005
+ anchor: parseBlockAnchor(value.anchor),
3006
+ ...metadata ? { metadata } : {}
3007
+ };
3008
+ }
3009
+ if (value.op === "insert_list_item") {
3010
+ return {
3011
+ op: value.op,
3012
+ list_id: requireString2(value.list_id, "list_id", false).trim(),
3013
+ text: requireString2(value.text, "text"),
3014
+ anchor: parseListItemAnchor(value.anchor)
3015
+ };
3016
+ }
3017
+ if (value.op === "replace_list_item") {
3018
+ return {
3019
+ op: value.op,
3020
+ list_id: requireString2(value.list_id, "list_id", false).trim(),
3021
+ item_id: requireString2(value.item_id, "item_id", false).trim(),
3022
+ text: requireString2(value.text, "text")
3023
+ };
3024
+ }
3025
+ if (value.op === "remove_list_item") {
3026
+ return {
3027
+ op: value.op,
3028
+ list_id: requireString2(value.list_id, "list_id", false).trim(),
3029
+ item_id: requireString2(value.item_id, "item_id", false).trim()
3030
+ };
3031
+ }
3032
+ if (value.op === "move_list_item") {
3033
+ return {
3034
+ op: value.op,
3035
+ list_id: requireString2(value.list_id, "list_id", false).trim(),
3036
+ item_id: requireString2(value.item_id, "item_id", false).trim(),
3037
+ anchor: parseListItemAnchor(value.anchor)
3038
+ };
3039
+ }
3040
+ throw new Document2CommandError("invalid_command", "Unsupported list command");
2498
3041
  }
2499
- function insertDocument2BlockAfter(document2, afterBlockId2, block) {
2500
- const next = cloneDocument(document2);
2501
- insertBlockAfter(next.blocks, afterBlockId2, block);
2502
- return next;
3042
+ function newItem(document2, text) {
3043
+ return { id: newItemId(document2), field: createCommandInlineField(document2, text), blocks: [] };
2503
3044
  }
2504
- function addDocument2TextBlock(document2, command = "\\paragraph", afterBlockId2) {
3045
+ function insertList(document2, command) {
3046
+ const ordered = command.ordered;
2505
3047
  const block = {
2506
- id: document2Id("block"),
2507
- kind: "textBlock",
2508
- command,
2509
- field: createInlineField2(""),
2510
- ...command === "\\paragraph" ? { centered: false } : {}
3048
+ id: newBlockId(document2),
3049
+ kind: "list",
3050
+ command: ordered ? "\\begin{enumerate}" : "\\begin{itemize}",
3051
+ closing: ordered ? "\\end{enumerate}" : "\\end{itemize}",
3052
+ items: command.items.map((text) => newItem(document2, text)),
3053
+ ...command.metadata ? { metadata: { ...command.metadata } } : {}
2511
3054
  };
2512
- return insertDocument2BlockAfter(document2, afterBlockId2, block);
3055
+ document2.blocks.splice(blockInsertionIndex(document2, command.anchor), 0, block);
2513
3056
  }
2514
- function removeDocument2BlockById(document2, blockId) {
2515
- const next = cloneDocument(document2);
2516
- next.blocks = next.blocks.filter((block) => block.id !== blockId);
2517
- return next;
3057
+ function applyListCommand(document2, command) {
3058
+ if (command.op === "insert_list") {
3059
+ insertList(document2, command);
3060
+ return;
3061
+ }
3062
+ const list = requireList(document2, command.list_id);
3063
+ if (command.op === "insert_list_item") {
3064
+ list.items.splice(itemInsertionIndex(list, command.anchor), 0, newItem(document2, command.text));
3065
+ return;
3066
+ }
3067
+ const itemIndex = list.items.findIndex((item2) => item2.id === command.item_id);
3068
+ if (itemIndex < 0) {
3069
+ throw new Document2CommandError("item_not_found", "Document list item was not found");
3070
+ }
3071
+ if (command.op === "replace_list_item") {
3072
+ replacePlainField(document2, list.items[itemIndex].field, command.text);
3073
+ return;
3074
+ }
3075
+ if (command.op === "remove_list_item") {
3076
+ if (list.items.length === 1) {
3077
+ throw new Document2CommandError("minimum_structure", "A list must retain at least one item");
3078
+ }
3079
+ list.items.splice(itemIndex, 1);
3080
+ return;
3081
+ }
3082
+ const anchorId = "before_item_id" in command.anchor ? command.anchor.before_item_id : "after_item_id" in command.anchor ? command.anchor.after_item_id : null;
3083
+ if (anchorId === command.item_id) {
3084
+ throw new Document2CommandError("invalid_anchor", "A list item cannot be moved relative to itself");
3085
+ }
3086
+ const [item] = list.items.splice(itemIndex, 1);
3087
+ if (!item) {
3088
+ throw new Document2CommandError("item_not_found", "Document list item was not found");
3089
+ }
3090
+ list.items.splice(itemInsertionIndex(list, command.anchor), 0, item);
3091
+ }
3092
+
3093
+ // src/document2/tableJsonCommands.ts
3094
+ function stringArray2(value, field) {
3095
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
3096
+ throw new Document2CommandError("invalid_command", `${field} must be a string array`);
3097
+ }
3098
+ return [...value];
2518
3099
  }
2519
- function normalizeImageInput(srcOrAsset) {
2520
- if (typeof srcOrAsset === "string") {
2521
- return srcOrAsset.length > 0 ? { value: srcOrAsset, assetId: srcOrAsset } : { value: "" };
3100
+ function rectangularRows(value) {
3101
+ if (!Array.isArray(value) || value.length === 0) {
3102
+ throw new Document2CommandError("invalid_table_shape", "rows must be a non-empty array");
2522
3103
  }
2523
- if (srcOrAsset && srcOrAsset.assetId.length > 0) {
2524
- return { value: srcOrAsset.value ?? srcOrAsset.assetId, assetId: srcOrAsset.assetId };
3104
+ const rows = value.map((row, index) => stringArray2(row, `rows[${String(index)}]`));
3105
+ const columnCount = rows[0]?.length ?? 0;
3106
+ if (columnCount === 0 || rows.some((row) => row.length !== columnCount)) {
3107
+ throw new Document2CommandError("invalid_table_shape", "Table rows must form a non-empty rectangle");
2525
3108
  }
2526
- return { value: "" };
3109
+ return rows;
2527
3110
  }
2528
- function addDocument2ImageBlock(document2, srcOrAsset, afterBlockId2) {
2529
- const image = normalizeImageInput(srcOrAsset);
3111
+ function columns(value) {
3112
+ const result = requireString2(value, "columns", false);
3113
+ if (result.trim().length === 0) {
3114
+ throw new Document2CommandError("invalid_table_shape", "columns must be a non-empty LaTeX specification");
3115
+ }
3116
+ return result;
3117
+ }
3118
+ function parseTableCommand(value) {
3119
+ if (value.op === "insert_table") {
3120
+ const caption = optionalString(value, "caption");
3121
+ const label = optionalString(value, "label");
3122
+ const metadata = parseMetadata(value.metadata);
3123
+ return {
3124
+ op: value.op,
3125
+ rows: rectangularRows(value.rows),
3126
+ columns: columns(value.columns),
3127
+ ...caption !== void 0 ? { caption } : {},
3128
+ ...label !== void 0 ? { label } : {},
3129
+ anchor: parseBlockAnchor(value.anchor),
3130
+ ...metadata ? { metadata } : {}
3131
+ };
3132
+ }
3133
+ if (value.op === "replace_table_cell") {
3134
+ return {
3135
+ op: value.op,
3136
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3137
+ row_index: requireIndex(value.row_index, "row_index"),
3138
+ column_index: requireIndex(value.column_index, "column_index"),
3139
+ text: requireString2(value.text, "text")
3140
+ };
3141
+ }
3142
+ if (value.op === "insert_table_row") {
3143
+ return {
3144
+ op: value.op,
3145
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3146
+ index: requireIndex(value.index, "index"),
3147
+ values: stringArray2(value.values, "values")
3148
+ };
3149
+ }
3150
+ if (value.op === "remove_table_row") {
3151
+ return {
3152
+ op: value.op,
3153
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3154
+ index: requireIndex(value.index, "index")
3155
+ };
3156
+ }
3157
+ if (value.op === "move_table_row") {
3158
+ return {
3159
+ op: value.op,
3160
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3161
+ from_index: requireIndex(value.from_index, "from_index"),
3162
+ to_index: requireIndex(value.to_index, "to_index")
3163
+ };
3164
+ }
3165
+ if (value.op === "insert_table_column") {
3166
+ return {
3167
+ op: value.op,
3168
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3169
+ index: requireIndex(value.index, "index"),
3170
+ values: stringArray2(value.values, "values"),
3171
+ columns: columns(value.columns)
3172
+ };
3173
+ }
3174
+ if (value.op === "remove_table_column") {
3175
+ return {
3176
+ op: value.op,
3177
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3178
+ index: requireIndex(value.index, "index"),
3179
+ columns: columns(value.columns)
3180
+ };
3181
+ }
3182
+ if (value.op === "move_table_column") {
3183
+ return {
3184
+ op: value.op,
3185
+ table_id: requireString2(value.table_id, "table_id", false).trim(),
3186
+ from_index: requireIndex(value.from_index, "from_index"),
3187
+ to_index: requireIndex(value.to_index, "to_index"),
3188
+ columns: columns(value.columns)
3189
+ };
3190
+ }
3191
+ throw new Document2CommandError("invalid_command", "Unsupported table command");
3192
+ }
3193
+ function insertTable(document2, command) {
2530
3194
  const block = {
2531
- id: document2Id("block"),
2532
- kind: "image",
2533
- command: "\\includegraphics",
2534
- value: image.value,
2535
- ...image.assetId !== void 0 ? { assetId: image.assetId } : {},
2536
- options: { width: "0.8\\columnwidth" },
2537
- ...defaultFloatMeta()
3195
+ id: newBlockId(document2),
3196
+ kind: "table",
3197
+ command: "\\begin{tabular}",
3198
+ closing: "\\end{tabular}",
3199
+ columns: command.columns,
3200
+ rows: command.rows.map((row) => row.map((cell) => createCommandInlineField(document2, cell))),
3201
+ ...defaultFloatMeta(),
3202
+ ...command.caption !== void 0 ? { caption: command.caption, captionEnabled: command.caption.trim().length > 0 } : {},
3203
+ ...command.label !== void 0 ? { label: command.label, labelEnabled: command.label.trim().length > 0 } : {},
3204
+ ...command.metadata ? { metadata: { ...command.metadata } } : {}
2538
3205
  };
2539
- return insertDocument2BlockAfter(document2, afterBlockId2, block);
3206
+ document2.blocks.splice(blockInsertionIndex(document2, command.anchor), 0, block);
3207
+ }
3208
+ function checkCell(table, rowIndex, columnIndex) {
3209
+ if (rowIndex >= table.rows.length || columnIndex >= (table.rows[rowIndex]?.length ?? 0)) {
3210
+ throw new Document2CommandError("index_out_of_range", "Table cell index is out of range");
3211
+ }
3212
+ }
3213
+ function checkExistingIndex(index, length, field) {
3214
+ if (index >= length) {
3215
+ throw new Document2CommandError("index_out_of_range", `${field} is out of range`);
3216
+ }
3217
+ }
3218
+ function applyTableCommand(document2, command) {
3219
+ if (command.op === "insert_table") {
3220
+ insertTable(document2, command);
3221
+ return;
3222
+ }
3223
+ const table = requireTable(document2, command.table_id);
3224
+ const columnCount = table.rows[0]?.length ?? 0;
3225
+ if (table.rows.length === 0 || columnCount === 0 || table.rows.some((row) => row.length !== columnCount)) {
3226
+ throw new Document2CommandError("invalid_table_shape", "Target table must be a non-empty rectangle");
3227
+ }
3228
+ if (command.op === "replace_table_cell") {
3229
+ checkCell(table, command.row_index, command.column_index);
3230
+ replacePlainField(document2, table.rows[command.row_index][command.column_index], command.text);
3231
+ return;
3232
+ }
3233
+ if (command.op === "insert_table_row") {
3234
+ if (command.index > table.rows.length) {
3235
+ throw new Document2CommandError("index_out_of_range", "Table row insertion index is out of range");
3236
+ }
3237
+ if (command.values.length !== columnCount) {
3238
+ throw new Document2CommandError("invalid_table_shape", "Inserted row must match the current column count");
3239
+ }
3240
+ table.rows.splice(command.index, 0, command.values.map((value) => createCommandInlineField(document2, value)));
3241
+ return;
3242
+ }
3243
+ if (command.op === "remove_table_row") {
3244
+ checkExistingIndex(command.index, table.rows.length, "Table row index");
3245
+ if (table.rows.length === 1) {
3246
+ throw new Document2CommandError("minimum_structure", "A table must retain at least one row");
3247
+ }
3248
+ table.rows.splice(command.index, 1);
3249
+ return;
3250
+ }
3251
+ if (command.op === "move_table_row") {
3252
+ checkExistingIndex(command.from_index, table.rows.length, "Table source row index");
3253
+ checkExistingIndex(command.to_index, table.rows.length, "Table target row index");
3254
+ const [row] = table.rows.splice(command.from_index, 1);
3255
+ table.rows.splice(command.to_index, 0, row);
3256
+ return;
3257
+ }
3258
+ if (command.op === "insert_table_column") {
3259
+ if (command.index > columnCount) {
3260
+ throw new Document2CommandError("index_out_of_range", "Table column insertion index is out of range");
3261
+ }
3262
+ if (command.values.length !== table.rows.length) {
3263
+ throw new Document2CommandError("invalid_table_shape", "Inserted column requires one value per row");
3264
+ }
3265
+ table.rows.forEach((row, index) => row.splice(command.index, 0, createCommandInlineField(document2, command.values[index])));
3266
+ table.columns = command.columns;
3267
+ return;
3268
+ }
3269
+ if (command.op === "remove_table_column") {
3270
+ checkExistingIndex(command.index, columnCount, "Table column index");
3271
+ if (columnCount === 1) {
3272
+ throw new Document2CommandError("minimum_structure", "A table must retain at least one column");
3273
+ }
3274
+ table.rows.forEach((row) => row.splice(command.index, 1));
3275
+ table.columns = command.columns;
3276
+ return;
3277
+ }
3278
+ checkExistingIndex(command.from_index, columnCount, "Table source column index");
3279
+ checkExistingIndex(command.to_index, columnCount, "Table target column index");
3280
+ table.rows.forEach((row) => {
3281
+ const [cell] = row.splice(command.from_index, 1);
3282
+ row.splice(command.to_index, 0, cell);
3283
+ });
3284
+ table.columns = command.columns;
2540
3285
  }
2541
3286
 
2542
3287
  // src/document2/exportJson.ts
@@ -2591,12 +3336,13 @@ function serializeField(field) {
2591
3336
  ...token.label !== void 0 ? { label: token.label } : {}
2592
3337
  });
2593
3338
  }
2594
- return { value, formats, mathObjects, hasPersistedMath };
3339
+ return { value, formats, mathObjects, hasPersistedMath, inlineIds: inlineIdsFromField(field) };
2595
3340
  }
2596
3341
  function fieldJson(field) {
2597
3342
  const serialized = serializeField(field);
2598
3343
  return {
2599
3344
  value: serialized.value,
3345
+ inline_ids: serialized.inlineIds,
2600
3346
  ...serialized.formats.length > 0 ? { formats: serialized.formats } : {},
2601
3347
  ...serialized.hasPersistedMath ? { math_objects: serialized.mathObjects } : {}
2602
3348
  };
@@ -2604,7 +3350,9 @@ function fieldJson(field) {
2604
3350
  function listItemJson(item) {
2605
3351
  const field = fieldJson(item.field);
2606
3352
  return {
3353
+ id: item.id,
2607
3354
  value: field.value ?? "",
3355
+ inline_ids: field.inline_ids,
2608
3356
  ...field.formats ? { formats: field.formats } : {},
2609
3357
  ...field.math_objects ? { math_objects: field.math_objects } : {},
2610
3358
  ...item.blocks.length > 0 ? { blocks: item.blocks.map(blockJson) } : {}
@@ -2616,6 +3364,7 @@ function blockJson(block) {
2616
3364
  id: block.id,
2617
3365
  command: block.command,
2618
3366
  ...fieldJson(block.field),
3367
+ ...block.metadata ? { metadata: { ...block.metadata } } : {},
2619
3368
  ...block.command === "\\paragraph" ? { centered: block.centered === true } : {}
2620
3369
  };
2621
3370
  }
@@ -2624,7 +3373,8 @@ function blockJson(block) {
2624
3373
  id: block.id,
2625
3374
  command: block.command,
2626
3375
  closing: block.closing,
2627
- items: block.items.map(listItemJson)
3376
+ items: block.items.map(listItemJson),
3377
+ ...block.metadata ? { metadata: { ...block.metadata } } : {}
2628
3378
  };
2629
3379
  }
2630
3380
  if (block.kind === "table") {
@@ -2636,13 +3386,15 @@ function blockJson(block) {
2636
3386
  closing: block.closing,
2637
3387
  columns: block.columns,
2638
3388
  rows: fields.map((row) => row.map((field) => field.value)),
3389
+ cell_inline_ids: fields.map((row) => row.map((field) => field.inlineIds)),
2639
3390
  ...fields.some((row) => row.some((field) => field.formats.length > 0)) ? { cell_formats: fields.map((row) => row.map((field) => field.formats)) } : {},
2640
3391
  ...hasPersistedMath ? { math_objects: fields.flatMap((row) => row.flatMap((field) => field.mathObjects)) } : {},
2641
3392
  centered: block.centered,
2642
3393
  caption_enabled: block.captionEnabled,
2643
3394
  caption: block.caption,
2644
3395
  label_enabled: block.labelEnabled,
2645
- label: block.label
3396
+ label: block.label,
3397
+ ...block.metadata ? { metadata: { ...block.metadata } } : {}
2646
3398
  };
2647
3399
  }
2648
3400
  if (block.kind === "image") {
@@ -2656,13 +3408,24 @@ function blockJson(block) {
2656
3408
  caption_enabled: block.captionEnabled,
2657
3409
  caption: block.caption,
2658
3410
  label_enabled: block.labelEnabled,
2659
- label: block.label
3411
+ label: block.label,
3412
+ ...block.metadata ? { metadata: { ...block.metadata } } : {}
2660
3413
  };
2661
3414
  }
2662
3415
  if (block.kind === "bibliography") {
2663
- return { id: block.id, command: block.command, closing: block.closing };
3416
+ return {
3417
+ id: block.id,
3418
+ command: block.command,
3419
+ closing: block.closing,
3420
+ ...block.metadata ? { metadata: { ...block.metadata } } : {}
3421
+ };
2664
3422
  }
2665
- return { id: block.id, command: block.command, value: block.value };
3423
+ return {
3424
+ id: block.id,
3425
+ command: block.command,
3426
+ value: block.value,
3427
+ ...block.metadata ? { metadata: { ...block.metadata } } : {}
3428
+ };
2666
3429
  }
2667
3430
  function referenceJson(reference) {
2668
3431
  return {
@@ -2693,49 +3456,14 @@ function toDocumentJson2(document2) {
2693
3456
  }
2694
3457
 
2695
3458
  // src/document2/jsonCommands.ts
2696
- var Document2CommandError = class extends Error {
2697
- code;
2698
- constructor(code, message) {
2699
- super(message);
2700
- this.name = "Document2CommandError";
2701
- this.code = code;
2702
- }
2703
- };
2704
- function isObject3(value) {
2705
- return typeof value === "object" && value !== null && !Array.isArray(value);
3459
+ function isInlineCommand(command) {
3460
+ return command.op === "insert_inline_token" || command.op === "replace_inline_token" || command.op === "remove_inline_token";
2706
3461
  }
2707
- function requireString2(value, field, allowEmpty = true) {
2708
- if (typeof value !== "string" || !allowEmpty && value.trim().length === 0) {
2709
- throw new Document2CommandError("invalid_command", `${field} must be a${allowEmpty ? "" : " non-empty"} string`);
2710
- }
2711
- return value;
3462
+ function isListCommand(command) {
3463
+ return command.op === "insert_list" || command.op === "insert_list_item" || command.op === "replace_list_item" || command.op === "remove_list_item" || command.op === "move_list_item";
2712
3464
  }
2713
- function parseAnchor(value) {
2714
- if (!isObject3(value)) {
2715
- throw new Document2CommandError("invalid_command", "anchor must be an object");
2716
- }
2717
- const hasEnd = value.end === true;
2718
- const afterBlockId2 = typeof value.after_block_id === "string" ? value.after_block_id.trim() : "";
2719
- const hasAfterBlock = afterBlockId2.length > 0;
2720
- if (hasEnd && hasAfterBlock) {
2721
- throw new Document2CommandError("invalid_command", "anchor cannot contain both end and after_block_id");
2722
- }
2723
- if (hasEnd) {
2724
- return { end: true };
2725
- }
2726
- if (hasAfterBlock) {
2727
- return { after_block_id: afterBlockId2 };
2728
- }
2729
- throw new Document2CommandError("invalid_command", "anchor requires end: true or a non-empty after_block_id");
2730
- }
2731
- function optionalString(value, field) {
2732
- if (!(field in value)) {
2733
- return void 0;
2734
- }
2735
- if (typeof value[field] !== "string") {
2736
- throw new Document2CommandError("invalid_command", `${field} must be a string when provided`);
2737
- }
2738
- return value[field];
3465
+ function isTableCommand(command) {
3466
+ return command.op === "insert_table" || command.op === "replace_table_cell" || command.op === "insert_table_row" || command.op === "remove_table_row" || command.op === "move_table_row" || command.op === "insert_table_column" || command.op === "remove_table_column" || command.op === "move_table_column";
2739
3467
  }
2740
3468
  function parseTextBlockKind(value) {
2741
3469
  if (value === "section" || value === "subsection" || value === "subsubsection" || value === "paragraph") {
@@ -2743,16 +3471,15 @@ function parseTextBlockKind(value) {
2743
3471
  }
2744
3472
  throw new Document2CommandError("invalid_command", "kind must be section, subsection, subsubsection, or paragraph");
2745
3473
  }
2746
- function parseCommand2(value) {
2747
- if (!isObject3(value) || typeof value.op !== "string") {
2748
- throw new Document2CommandError("invalid_command", "command requires an op");
2749
- }
3474
+ function parseBasicCommand(value) {
2750
3475
  if (value.op === "insert_text_block") {
3476
+ const metadata = parseMetadata(value.metadata);
2751
3477
  return {
2752
3478
  op: value.op,
2753
3479
  kind: parseTextBlockKind(value.kind),
2754
3480
  text: requireString2(value.text, "text"),
2755
- anchor: parseAnchor(value.anchor)
3481
+ anchor: parseBlockAnchor(value.anchor),
3482
+ ...metadata ? { metadata } : {}
2756
3483
  };
2757
3484
  }
2758
3485
  if (value.op === "replace_text_block") {
@@ -2763,140 +3490,118 @@ function parseCommand2(value) {
2763
3490
  };
2764
3491
  }
2765
3492
  if (value.op === "remove_block") {
2766
- return {
2767
- op: value.op,
2768
- block_id: requireString2(value.block_id, "block_id", false).trim()
2769
- };
3493
+ return { op: value.op, block_id: requireString2(value.block_id, "block_id", false).trim() };
2770
3494
  }
2771
3495
  if (value.op === "insert_figure") {
2772
3496
  const figureValue = optionalString(value, "value");
2773
3497
  const caption = optionalString(value, "caption");
2774
3498
  const label = optionalString(value, "label");
3499
+ const metadata = parseMetadata(value.metadata);
2775
3500
  return {
2776
3501
  op: value.op,
2777
3502
  asset_id: requireString2(value.asset_id, "asset_id", false).trim(),
2778
3503
  ...figureValue !== void 0 ? { value: figureValue } : {},
2779
3504
  ...caption !== void 0 ? { caption } : {},
2780
3505
  ...label !== void 0 ? { label } : {},
2781
- anchor: parseAnchor(value.anchor)
3506
+ anchor: parseBlockAnchor(value.anchor),
3507
+ ...metadata ? { metadata } : {}
2782
3508
  };
2783
3509
  }
2784
- throw new Document2CommandError("invalid_command", `Unsupported document command: ${value.op}`);
3510
+ throw new Document2CommandError("invalid_command", "Unsupported block command");
3511
+ }
3512
+ function parseCommand2(value) {
3513
+ if (!isObject3(value) || typeof value.op !== "string") {
3514
+ throw new Document2CommandError("invalid_command", "command requires an op");
3515
+ }
3516
+ if (value.op === "insert_inline_token" || value.op === "replace_inline_token" || value.op === "remove_inline_token") {
3517
+ return parseInlineCommand(value);
3518
+ }
3519
+ if (value.op === "insert_list" || value.op === "insert_list_item" || value.op === "replace_list_item" || value.op === "remove_list_item" || value.op === "move_list_item") {
3520
+ return parseListCommand(value);
3521
+ }
3522
+ if (value.op === "insert_table" || value.op === "replace_table_cell" || value.op === "insert_table_row" || value.op === "remove_table_row" || value.op === "move_table_row" || value.op === "insert_table_column" || value.op === "remove_table_column" || value.op === "move_table_column") {
3523
+ return parseTableCommand(value);
3524
+ }
3525
+ if (value.op === "insert_text_block" || value.op === "replace_text_block" || value.op === "remove_block" || value.op === "insert_figure") {
3526
+ return parseBasicCommand(value);
3527
+ }
3528
+ throw new Document2CommandError("invalid_command", "Unsupported document command");
2785
3529
  }
2786
3530
  function parseDocument(json) {
2787
3531
  try {
2788
3532
  return fromDocumentJson2(json);
2789
- } catch (error) {
2790
- const message = error instanceof Error ? error.message : "Invalid DocumentObject JSON";
2791
- throw new Document2CommandError("invalid_document", message);
3533
+ } catch {
3534
+ throw new Document2CommandError("invalid_document", "Document JSON is invalid");
2792
3535
  }
2793
3536
  }
2794
- function commandForKind(kind) {
3537
+ function textCommand(kind) {
2795
3538
  if (kind === "section") {
2796
3539
  return "\\section";
2797
3540
  }
2798
3541
  if (kind === "subsection") {
2799
3542
  return "\\subsection";
2800
3543
  }
2801
- if (kind === "subsubsection") {
2802
- return "\\subsubsection";
2803
- }
2804
- return "\\paragraph";
3544
+ return kind === "subsubsection" ? "\\subsubsection" : "\\paragraph";
2805
3545
  }
2806
- function insertionIndex(document2, anchor) {
2807
- if ("end" in anchor) {
2808
- return document2.blocks.length;
2809
- }
2810
- const index = document2.blocks.findIndex((block) => block.id === anchor.after_block_id);
2811
- if (index < 0) {
2812
- throw new Document2CommandError("anchor_not_found", `Anchor block was not found: ${anchor.after_block_id}`);
2813
- }
2814
- return index + 1;
2815
- }
2816
- function afterBlockId(document2, index) {
2817
- return index > 0 ? document2.blocks[index - 1]?.id ?? null : null;
2818
- }
2819
- function insertedBlock(document2, index) {
2820
- const block = document2.blocks[index];
2821
- if (!block) {
2822
- throw new Document2CommandError("invalid_command", "Document command did not insert a block");
2823
- }
2824
- return block;
2825
- }
2826
- function insertTextBlock(document2, command) {
2827
- const index = insertionIndex(document2, command.anchor);
2828
- const next = addDocument2TextBlock(document2, commandForKind(command.kind), afterBlockId(document2, index));
2829
- const block = insertedBlock(next, index);
2830
- if (block.kind !== "textBlock") {
2831
- throw new Document2CommandError("block_kind_mismatch", "Inserted block is not a text block");
2832
- }
2833
- block.field = createInlineField2(command.text);
2834
- return next;
2835
- }
2836
- function replaceTextBlock(document2, command) {
2837
- const index = document2.blocks.findIndex((block2) => block2.id === command.block_id);
2838
- if (index < 0) {
2839
- throw new Document2CommandError("block_not_found", `Document block was not found: ${command.block_id}`);
2840
- }
2841
- const block = document2.blocks[index];
2842
- if (!block || block.kind !== "textBlock") {
2843
- throw new Document2CommandError("block_kind_mismatch", `Document block is not a text block: ${command.block_id}`);
2844
- }
2845
- if (block.field.tokens.some((token) => token.kind !== "text" || token.style !== void 0)) {
2846
- throw new Document2CommandError(
2847
- "unsupported_inline_content",
2848
- "Whole-block replacement is not supported for formatted or structured inline content"
2849
- );
2850
- }
2851
- const next = parseDocument(toDocumentJson2(document2));
2852
- const nextBlock = next.blocks[index];
2853
- if (!nextBlock || nextBlock.kind !== "textBlock") {
2854
- throw new Document2CommandError("block_kind_mismatch", `Document block is not a text block: ${command.block_id}`);
2855
- }
2856
- nextBlock.field = createInlineField2(command.text);
2857
- return next;
2858
- }
2859
- function removeBlock(document2, command) {
2860
- if (!document2.blocks.some((block) => block.id === command.block_id)) {
2861
- throw new Document2CommandError("block_not_found", `Document block was not found: ${command.block_id}`);
2862
- }
2863
- return removeDocument2BlockById(document2, command.block_id);
2864
- }
2865
- function insertFigure(document2, command) {
2866
- const index = insertionIndex(document2, command.anchor);
2867
- const next = addDocument2ImageBlock(
2868
- document2,
2869
- { assetId: command.asset_id, ...command.value !== void 0 ? { value: command.value } : {} },
2870
- afterBlockId(document2, index)
2871
- );
2872
- const block = insertedBlock(next, index);
2873
- if (block.kind !== "image") {
2874
- throw new Document2CommandError("block_kind_mismatch", "Inserted block is not a figure");
3546
+ function applyBasicCommand(document2, command) {
3547
+ if (command.op === "insert_text_block") {
3548
+ const block2 = {
3549
+ id: newBlockId(document2),
3550
+ kind: "textBlock",
3551
+ command: textCommand(command.kind),
3552
+ field: createCommandInlineField(document2, command.text),
3553
+ ...command.kind === "paragraph" ? { centered: false } : {},
3554
+ ...command.metadata ? { metadata: { ...command.metadata } } : {}
3555
+ };
3556
+ document2.blocks.splice(blockInsertionIndex(document2, command.anchor), 0, block2);
3557
+ return;
2875
3558
  }
2876
- if (command.caption !== void 0) {
2877
- block.caption = command.caption;
2878
- block.captionEnabled = command.caption.trim().length > 0;
3559
+ if (command.op === "replace_text_block") {
3560
+ const block2 = document2.blocks.find((entry) => entry.id === command.block_id);
3561
+ if (!block2) {
3562
+ throw new Document2CommandError("block_not_found", "Document block was not found");
3563
+ }
3564
+ if (block2.kind !== "textBlock") {
3565
+ throw new Document2CommandError("block_kind_mismatch", "Document block is not a text block");
3566
+ }
3567
+ replacePlainField(document2, block2.field, command.text);
3568
+ return;
2879
3569
  }
2880
- if (command.label !== void 0) {
2881
- block.label = command.label;
2882
- block.labelEnabled = command.label.trim().length > 0;
3570
+ if (command.op === "remove_block") {
3571
+ const index = document2.blocks.findIndex((block2) => block2.id === command.block_id);
3572
+ if (index < 0) {
3573
+ throw new Document2CommandError("block_not_found", "Document block was not found");
3574
+ }
3575
+ document2.blocks.splice(index, 1);
3576
+ return;
2883
3577
  }
2884
- return next;
3578
+ const block = {
3579
+ id: newBlockId(document2),
3580
+ kind: "image",
3581
+ command: "\\includegraphics",
3582
+ value: command.value ?? command.asset_id,
3583
+ assetId: command.asset_id,
3584
+ options: { width: "0.8\\columnwidth" },
3585
+ ...defaultFloatMeta(),
3586
+ ...command.caption !== void 0 ? { caption: command.caption, captionEnabled: command.caption.trim().length > 0 } : {},
3587
+ ...command.label !== void 0 ? { label: command.label, labelEnabled: command.label.trim().length > 0 } : {},
3588
+ ...command.metadata ? { metadata: { ...command.metadata } } : {}
3589
+ };
3590
+ document2.blocks.splice(blockInsertionIndex(document2, command.anchor), 0, block);
2885
3591
  }
2886
3592
  function applyDocument2Command(json, commandInput) {
2887
3593
  const document2 = parseDocument(json);
2888
3594
  const command = parseCommand2(commandInput);
2889
- let next;
2890
- if (command.op === "insert_text_block") {
2891
- next = insertTextBlock(document2, command);
2892
- } else if (command.op === "replace_text_block") {
2893
- next = replaceTextBlock(document2, command);
2894
- } else if (command.op === "remove_block") {
2895
- next = removeBlock(document2, command);
3595
+ if (isInlineCommand(command)) {
3596
+ applyInlineCommand(document2, command);
3597
+ } else if (isListCommand(command)) {
3598
+ applyListCommand(document2, command);
3599
+ } else if (isTableCommand(command)) {
3600
+ applyTableCommand(document2, command);
2896
3601
  } else {
2897
- next = insertFigure(document2, command);
3602
+ applyBasicCommand(document2, command);
2898
3603
  }
2899
- return toDocumentJson2(next);
3604
+ return toDocumentJson2(document2);
2900
3605
  }
2901
3606
 
2902
3607
  // src/document2/outline.ts
@@ -3041,9 +3746,8 @@ function asDocument2Command(value) {
3041
3746
  function normalizeDocument(document2) {
3042
3747
  try {
3043
3748
  return toDocumentJson2(fromDocumentJson2(document2));
3044
- } catch (error) {
3045
- const message = error instanceof Error ? error.message : "Invalid DocumentObject JSON";
3046
- throw new Document2CommandError("invalid_document", message);
3749
+ } catch {
3750
+ throw new Document2CommandError("invalid_document", "Document JSON is invalid");
3047
3751
  }
3048
3752
  }
3049
3753
  function executeDocument2WorkerRequest(input) {