@yorkie-js/sdk 0.6.49 → 0.7.0

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.
@@ -1920,12 +1920,12 @@ declare class CRDTTree extends CRDTElement implements GCParent {
1920
1920
  * `edit` edits the tree with the given range and content.
1921
1921
  * If the content is undefined, the range will be removed.
1922
1922
  */
1923
- edit(range: [CRDTTreePos, CRDTTreePos], contents: Array<CRDTTreeNode> | undefined, splitLevel: number, editedAt: TimeTicket, issueTimeTicket: (() => TimeTicket) | undefined, versionVector?: VersionVector): [Array<TreeChange>, Array<GCPair>, DataSize];
1923
+ edit(range: [CRDTTreePos, CRDTTreePos], contents: Array<CRDTTreeNode> | undefined, splitLevel: number, editedAt: TimeTicket, issueTimeTicket: (() => TimeTicket) | undefined, versionVector?: VersionVector): [Array<TreeChange>, Array<GCPair>, DataSize, Array<CRDTTreeNode>, number];
1924
1924
  /**
1925
1925
  * `editT` edits the given range with the given value.
1926
1926
  * This method uses indexes instead of a pair of TreePos for testing.
1927
1927
  */
1928
- editT(range: [number, number], contents: Array<CRDTTreeNode> | undefined, splitLevel: number, editedAt: TimeTicket, issueTimeTicket: () => TimeTicket): [Array<TreeChange>, Array<GCPair>, DataSize];
1928
+ editT(range: [number, number], contents: Array<CRDTTreeNode> | undefined, splitLevel: number, editedAt: TimeTicket, issueTimeTicket: () => TimeTicket): [Array<TreeChange>, Array<GCPair>, DataSize, Array<CRDTTreeNode>, number];
1929
1929
  /**
1930
1930
  * `move` move the given source range to the given target range.
1931
1931
  */
@@ -12495,7 +12495,7 @@ class CRDTTree extends CRDTElement {
12495
12495
  }
12496
12496
  }
12497
12497
  }
12498
- return [changes, pairs, diff];
12498
+ return [changes, pairs, diff, nodesToBeRemoved, fromIdx];
12499
12499
  }
12500
12500
  /**
12501
12501
  * `editT` edits the given range with the given value.
@@ -12933,29 +12933,46 @@ class CRDTTree extends CRDTElement {
12933
12933
  return [prev, prev.isText ? TokenType.Text : TokenType.End];
12934
12934
  }
12935
12935
  }
12936
+ function clearRemovedAt(node) {
12937
+ traverseAll(node, (n) => {
12938
+ n.removedAt = void 0;
12939
+ n.visibleSize = n.totalSize;
12940
+ });
12941
+ }
12936
12942
  class TreeEditOperation extends Operation {
12937
12943
  fromPos;
12938
12944
  toPos;
12939
12945
  contents;
12940
12946
  splitLevel;
12941
- constructor(parentCreatedAt, fromPos, toPos, contents, splitLevel, executedAt) {
12947
+ isUndoOp;
12948
+ fromIdx;
12949
+ toIdx;
12950
+ lastFromIdx;
12951
+ lastToIdx;
12952
+ constructor(parentCreatedAt, fromPos, toPos, contents, splitLevel, executedAt, isUndoOp, fromIdx, toIdx) {
12942
12953
  super(parentCreatedAt, executedAt);
12943
12954
  this.fromPos = fromPos;
12944
12955
  this.toPos = toPos;
12945
12956
  this.contents = contents;
12946
12957
  this.splitLevel = splitLevel;
12958
+ this.isUndoOp = isUndoOp;
12959
+ this.fromIdx = fromIdx;
12960
+ this.toIdx = toIdx;
12947
12961
  }
12948
12962
  /**
12949
12963
  * `create` creates a new instance of EditOperation.
12950
12964
  */
12951
- static create(parentCreatedAt, fromPos, toPos, contents, splitLevel, executedAt) {
12965
+ static create(parentCreatedAt, fromPos, toPos, contents, splitLevel, executedAt, isUndoOp, fromIdx, toIdx) {
12952
12966
  return new TreeEditOperation(
12953
12967
  parentCreatedAt,
12954
12968
  fromPos,
12955
12969
  toPos,
12956
12970
  contents,
12957
12971
  splitLevel,
12958
- executedAt
12972
+ executedAt,
12973
+ isUndoOp,
12974
+ fromIdx,
12975
+ toIdx
12959
12976
  );
12960
12977
  }
12961
12978
  /**
@@ -12977,7 +12994,15 @@ class TreeEditOperation extends Operation {
12977
12994
  }
12978
12995
  const editedAt = this.getExecutedAt();
12979
12996
  const tree = parentObject;
12980
- const [changes, pairs, diff] = tree.edit(
12997
+ if (this.isUndoOp && this.fromIdx !== void 0 && this.toIdx !== void 0) {
12998
+ this.fromPos = tree.findPos(this.fromIdx);
12999
+ if (this.fromIdx === this.toIdx) {
13000
+ this.toPos = this.fromPos;
13001
+ } else {
13002
+ this.toPos = tree.findPos(this.toIdx);
13003
+ }
13004
+ }
13005
+ const [changes, pairs, diff, removedNodes, preEditFromIdx] = tree.edit(
12981
13006
  [this.fromPos, this.toPos],
12982
13007
  this.contents?.map((content) => content.deepcopy()),
12983
13008
  this.splitLevel,
@@ -13003,6 +13028,13 @@ class TreeEditOperation extends Operation {
13003
13028
  })(),
13004
13029
  versionVector
13005
13030
  );
13031
+ this.lastFromIdx = preEditFromIdx;
13032
+ const removedSize = removedNodes.reduce(
13033
+ (sum, node) => sum + node.paddedSize(),
13034
+ 0
13035
+ );
13036
+ this.lastToIdx = preEditFromIdx + removedSize;
13037
+ const reverseOp = this.splitLevel === 0 ? this.toReverseOperation(tree, removedNodes, preEditFromIdx) : void 0;
13006
13038
  root.acc(diff);
13007
13039
  for (const pair of pairs) {
13008
13040
  root.registerGCPair(pair);
@@ -13021,9 +13053,131 @@ class TreeEditOperation extends Operation {
13021
13053
  toPath
13022
13054
  };
13023
13055
  }
13024
- )
13056
+ ),
13057
+ reverseOp
13025
13058
  };
13026
13059
  }
13060
+ /**
13061
+ * `toReverseOperation` creates the reverse operation for undo.
13062
+ *
13063
+ * The reverse op stores both CRDTTreePos (for initial use) and integer
13064
+ * indices (for reconciliation adjustment when remote edits arrive).
13065
+ * At undo execution time, the integer indices take precedence and are
13066
+ * converted to CRDTTreePos via tree.findPos().
13067
+ *
13068
+ * @param tree - The CRDTTree after the edit has been applied
13069
+ * @param removedNodes - Nodes that were removed by this edit
13070
+ * @param preEditFromIdx - The from index captured BEFORE the edit
13071
+ */
13072
+ toReverseOperation(tree, removedNodes, preEditFromIdx) {
13073
+ const insertedContentSize = this.contents ? this.contents.reduce((sum, node) => sum + node.paddedSize(), 0) : 0;
13074
+ const maxNeededIdx = preEditFromIdx + insertedContentSize;
13075
+ if (maxNeededIdx > tree.getSize()) {
13076
+ return void 0;
13077
+ }
13078
+ const topLevelRemoved = removedNodes.filter(
13079
+ (node) => !node.parent || !removedNodes.includes(node.parent)
13080
+ );
13081
+ const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map((n) => {
13082
+ const clone = n.deepcopy();
13083
+ clearRemovedAt(clone);
13084
+ return clone;
13085
+ }) : void 0;
13086
+ const reverseFromPos = tree.findPos(preEditFromIdx);
13087
+ let reverseToPos;
13088
+ if (insertedContentSize > 0) {
13089
+ reverseToPos = tree.findPos(preEditFromIdx + insertedContentSize);
13090
+ } else {
13091
+ reverseToPos = reverseFromPos;
13092
+ }
13093
+ const reverseFromIdx = preEditFromIdx;
13094
+ const reverseToIdx = preEditFromIdx + insertedContentSize;
13095
+ return TreeEditOperation.create(
13096
+ this.getParentCreatedAt(),
13097
+ reverseFromPos,
13098
+ reverseToPos,
13099
+ reverseContents,
13100
+ 0,
13101
+ // splitLevel always 0
13102
+ void 0,
13103
+ // executedAt set during undo
13104
+ true,
13105
+ // isUndoOp
13106
+ reverseFromIdx,
13107
+ reverseToIdx
13108
+ );
13109
+ }
13110
+ /**
13111
+ * `normalizePos` returns the visible-index range of this operation.
13112
+ * For undo ops, returns the stored (possibly reconciled) indices.
13113
+ * For forward ops, returns the pre-edit indices captured during execute().
13114
+ */
13115
+ normalizePos() {
13116
+ if (this.isUndoOp && this.fromIdx !== void 0 && this.toIdx !== void 0) {
13117
+ return [this.fromIdx, this.toIdx];
13118
+ }
13119
+ if (this.lastFromIdx !== void 0 && this.lastToIdx !== void 0) {
13120
+ return [this.lastFromIdx, this.lastToIdx];
13121
+ }
13122
+ return [0, 0];
13123
+ }
13124
+ /**
13125
+ * `reconcileOperation` adjusts this undo operation's integer indices
13126
+ * when a remote edit modifies the same tree. Uses the same 6-case
13127
+ * overlap logic as EditOperation.reconcileOperation for Text.
13128
+ */
13129
+ reconcileOperation(remoteFrom, remoteTo, contentLen) {
13130
+ if (!this.isUndoOp) {
13131
+ return;
13132
+ }
13133
+ if (this.fromIdx === void 0 || this.toIdx === void 0) {
13134
+ return;
13135
+ }
13136
+ if (remoteFrom > remoteTo) {
13137
+ return;
13138
+ }
13139
+ const remoteRangeLen = remoteTo - remoteFrom;
13140
+ const localFrom = this.fromIdx;
13141
+ const localTo = this.toIdx;
13142
+ const apply = (na, nb) => {
13143
+ this.fromIdx = Math.max(0, na);
13144
+ this.toIdx = Math.max(0, nb);
13145
+ };
13146
+ if (remoteTo <= localFrom) {
13147
+ apply(
13148
+ localFrom - remoteRangeLen + contentLen,
13149
+ localTo - remoteRangeLen + contentLen
13150
+ );
13151
+ return;
13152
+ }
13153
+ if (localTo <= remoteFrom) {
13154
+ return;
13155
+ }
13156
+ if (remoteFrom <= localFrom && localTo <= remoteTo && remoteFrom !== remoteTo) {
13157
+ apply(remoteFrom, remoteFrom);
13158
+ return;
13159
+ }
13160
+ if (localFrom <= remoteFrom && remoteTo <= localTo && localFrom !== localTo) {
13161
+ apply(localFrom, localTo - remoteRangeLen + contentLen);
13162
+ return;
13163
+ }
13164
+ if (remoteFrom < localFrom && localFrom < remoteTo && remoteTo < localTo) {
13165
+ apply(remoteFrom, remoteFrom + (localTo - remoteTo));
13166
+ return;
13167
+ }
13168
+ if (localFrom < remoteFrom && remoteFrom < localTo && localTo < remoteTo) {
13169
+ apply(localFrom, remoteFrom);
13170
+ return;
13171
+ }
13172
+ }
13173
+ /**
13174
+ * `getContentSize` returns the total visible size of this operation's
13175
+ * content (for reconciliation).
13176
+ */
13177
+ getContentSize() {
13178
+ if (!this.contents) return 0;
13179
+ return this.contents.reduce((sum, node) => sum + node.paddedSize(), 0);
13180
+ }
13027
13181
  /**
13028
13182
  * `getEffectedCreatedAt` returns the creation time of the effected element.
13029
13183
  */
@@ -17951,6 +18105,24 @@ class History {
17951
18105
  replace(this.undoStack);
17952
18106
  replace(this.redoStack);
17953
18107
  }
18108
+ /**
18109
+ * `reconcileTreeEdit` reconciles the tree edit operation.
18110
+ * Scan both undo/redo stacks and adjust tree edit operations
18111
+ * when a remote edit modifies the same tree.
18112
+ */
18113
+ reconcileTreeEdit(parentCreatedAt, rangeFrom, rangeTo, contentSize) {
18114
+ const replace = (stack) => {
18115
+ for (const ops of stack) {
18116
+ for (const op of ops) {
18117
+ if (op instanceof TreeEditOperation && op.getParentCreatedAt().compare(parentCreatedAt) === 0) {
18118
+ op.reconcileOperation(rangeFrom, rangeTo, contentSize);
18119
+ }
18120
+ }
18121
+ }
18122
+ };
18123
+ replace(this.undoStack);
18124
+ replace(this.redoStack);
18125
+ }
17954
18126
  }
17955
18127
  function validateYorkieRuleset(data, ruleset) {
17956
18128
  const errors = [];
@@ -18909,6 +19081,15 @@ class Document {
18909
19081
  op.getContent()?.length ?? 0
18910
19082
  );
18911
19083
  }
19084
+ if (op instanceof TreeEditOperation) {
19085
+ const [from, to] = op.normalizePos();
19086
+ this.internalHistory.reconcileTreeEdit(
19087
+ op.getParentCreatedAt(),
19088
+ from,
19089
+ to,
19090
+ op.getContentSize()
19091
+ );
19092
+ }
18912
19093
  }
18913
19094
  this.changeID = this.changeID.syncClocks(change.getID());
18914
19095
  if (opInfos.length) {
@@ -19454,7 +19635,7 @@ function createAuthInterceptor(apiKey, token) {
19454
19635
  };
19455
19636
  }
19456
19637
  const name = "@yorkie-js/sdk";
19457
- const version = "0.6.49";
19638
+ const version = "0.7.0";
19458
19639
  const pkg = {
19459
19640
  name,
19460
19641
  version