@yorkie-js/react 0.6.28 → 0.6.29

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.
@@ -8663,8 +8663,7 @@ class CRDTObject extends CRDTContainer {
8663
8663
  }
8664
8664
  }
8665
8665
  /**
8666
- * eslint-disable-next-line jsdoc/require-jsdoc
8667
- * @internal
8666
+ * `[Symbol.iterator]` returns an iterator for the entries in this object.
8668
8667
  */
8669
8668
  *[Symbol.iterator]() {
8670
8669
  const keySet = /* @__PURE__ */ new Set();
@@ -10456,9 +10455,7 @@ class RGATreeList {
10456
10455
  * `findNextBeforeExecutedAt` returns the node by the given createdAt and
10457
10456
  * executedAt. It passes through nodes created after executedAt from the
10458
10457
  * given node and returns the next node.
10459
- * @param createdAt - created time
10460
- * @param executedAt - executed time
10461
- * @returns next node
10458
+ * @returns the next node of the given createdAt and executedAt
10462
10459
  */
10463
10460
  findNextBeforeExecutedAt(createdAt, executedAt) {
10464
10461
  let node = this.nodeMapByCreatedAt.get(createdAt.toIDString());
@@ -10794,8 +10791,7 @@ class CRDTArray extends CRDTContainer {
10794
10791
  return this.elements.length;
10795
10792
  }
10796
10793
  /**
10797
- * eslint-disable-next-line jsdoc/require-jsdoc
10798
- * @internal
10794
+ * `[Symbol.iterator]` returns an iterator for the elements in this array.
10799
10795
  */
10800
10796
  *[Symbol.iterator]() {
10801
10797
  for (const node of this.elements) {
@@ -11698,8 +11694,6 @@ class CRDTText extends CRDTElement {
11698
11694
  }
11699
11695
  /**
11700
11696
  * `edit` edits the given range with the given value and attributes.
11701
- *
11702
- * @internal
11703
11697
  */
11704
11698
  edit(range, content, editedAt, attributes, versionVector) {
11705
11699
  const crdtTextValue = content ? CRDTTextValue.create(content) : void 0;
@@ -11736,7 +11730,6 @@ class CRDTText extends CRDTElement {
11736
11730
  * @param range - range of RGATreeSplitNode
11737
11731
  * @param attributes - style attributes
11738
11732
  * @param editedAt - edited time
11739
- * @internal
11740
11733
  */
11741
11734
  setStyle(range, attributes, editedAt, versionVector) {
11742
11735
  const diff = { data: 0, meta: 0 };
@@ -11890,8 +11883,6 @@ class CRDTText extends CRDTElement {
11890
11883
  }
11891
11884
  /**
11892
11885
  * `getRGATreeSplit` returns rgaTreeSplit.
11893
- *
11894
- * @internal
11895
11886
  */
11896
11887
  getRGATreeSplit() {
11897
11888
  return this.rgaTreeSplit;
@@ -14004,8 +13995,6 @@ class CRDTTree extends CRDTElement {
14004
13995
  }
14005
13996
  /**
14006
13997
  * `toJSForTest` returns value with meta data for testing.
14007
- *
14008
- * @internal
14009
13998
  */
14010
13999
  toJSForTest() {
14011
14000
  return {
@@ -14016,8 +14005,6 @@ class CRDTTree extends CRDTElement {
14016
14005
  }
14017
14006
  /**
14018
14007
  * `toJSInfoForTest` returns detailed TreeNode information for use in Devtools.
14019
- *
14020
- * @internal
14021
14008
  */
14022
14009
  toJSInfoForTest() {
14023
14010
  const rootNode = this.indexTree.getRoot();
@@ -17571,11 +17558,11 @@ function uuid() {
17571
17558
  class Attachment {
17572
17559
  constructor(reconnectStreamDelay, doc, docID, syncMode, unsubscribeBroadcastEvent) {
17573
17560
  // TODO(hackerwins): Consider to changing the modifiers of the following properties to private.
17574
- __publicField(this, "reconnectStreamDelay");
17575
17561
  __publicField(this, "doc");
17576
17562
  __publicField(this, "docID");
17577
17563
  __publicField(this, "syncMode");
17578
17564
  __publicField(this, "remoteChangeEventReceived");
17565
+ __publicField(this, "reconnectStreamDelay");
17579
17566
  __publicField(this, "cancelled");
17580
17567
  __publicField(this, "watchStream");
17581
17568
  __publicField(this, "watchLoopTimerID");
@@ -17649,156 +17636,6 @@ class Attachment {
17649
17636
  this.watchLoopTimerID = void 0;
17650
17637
  }
17651
17638
  }
17652
- function validateYorkieRuleset(data, ruleset) {
17653
- const errors = [];
17654
- for (const rule of ruleset) {
17655
- const value = getValueByPath(data, rule.path);
17656
- const result = validateValue(value, rule);
17657
- if (!result.valid) {
17658
- for (const error of result.errors || []) {
17659
- errors.push(error);
17660
- }
17661
- }
17662
- }
17663
- return {
17664
- valid: errors.length === 0,
17665
- errors
17666
- };
17667
- }
17668
- function getValueByPath(obj, path) {
17669
- if (!path.startsWith("$")) {
17670
- throw new Error(`Path must start with $, got ${path}`);
17671
- }
17672
- const keys = path.split(".");
17673
- let current = obj;
17674
- for (let i = 1; i < keys.length; i++) {
17675
- const key = keys[i];
17676
- if (!(current instanceof CRDTObject)) {
17677
- return void 0;
17678
- }
17679
- current = current.get(key);
17680
- }
17681
- return current;
17682
- }
17683
- function validateValue(value, rule) {
17684
- switch (rule.type) {
17685
- case "string":
17686
- case "boolean":
17687
- case "integer":
17688
- case "double":
17689
- case "long":
17690
- case "date":
17691
- case "bytes":
17692
- case "null":
17693
- return validatePrimitiveValue(value, rule);
17694
- case "object":
17695
- if (!(value instanceof CRDTObject)) {
17696
- return {
17697
- valid: false,
17698
- errors: [
17699
- {
17700
- path: rule.path,
17701
- message: `expected object at path ${rule.path}`
17702
- }
17703
- ]
17704
- };
17705
- }
17706
- break;
17707
- case "array":
17708
- if (!(value instanceof CRDTArray)) {
17709
- return {
17710
- valid: false,
17711
- errors: [
17712
- {
17713
- path: rule.path,
17714
- message: `expected array at path ${rule.path}`
17715
- }
17716
- ]
17717
- };
17718
- }
17719
- break;
17720
- case "yorkie.Text":
17721
- if (!(value instanceof CRDTText)) {
17722
- return {
17723
- valid: false,
17724
- errors: [
17725
- {
17726
- path: rule.path,
17727
- message: `expected yorkie.Text at path ${rule.path}`
17728
- }
17729
- ]
17730
- };
17731
- }
17732
- break;
17733
- case "yorkie.Tree":
17734
- if (!(value instanceof CRDTTree)) {
17735
- return {
17736
- valid: false,
17737
- errors: [
17738
- {
17739
- path: rule.path,
17740
- message: `expected yorkie.Tree at path ${rule.path}`
17741
- }
17742
- ]
17743
- };
17744
- }
17745
- break;
17746
- case "yorkie.Counter":
17747
- if (!(value instanceof CRDTCounter)) {
17748
- return {
17749
- valid: false,
17750
- errors: [
17751
- {
17752
- path: rule.path,
17753
- message: `expected yorkie.Counter at path ${rule.path}`
17754
- }
17755
- ]
17756
- };
17757
- }
17758
- break;
17759
- default:
17760
- throw new Error(`Unknown rule type: ${rule.type}`);
17761
- }
17762
- return {
17763
- valid: true
17764
- };
17765
- }
17766
- function getPrimitiveType(type) {
17767
- switch (type) {
17768
- case "null":
17769
- return PrimitiveType.Null;
17770
- case "boolean":
17771
- return PrimitiveType.Boolean;
17772
- case "integer":
17773
- return PrimitiveType.Integer;
17774
- case "long":
17775
- return PrimitiveType.Long;
17776
- case "double":
17777
- return PrimitiveType.Double;
17778
- case "string":
17779
- return PrimitiveType.String;
17780
- case "bytes":
17781
- return PrimitiveType.Bytes;
17782
- case "date":
17783
- return PrimitiveType.Date;
17784
- default:
17785
- throw new Error(`Unknown primitive type: ${type}`);
17786
- }
17787
- }
17788
- function validatePrimitiveValue(value, rule) {
17789
- if (value instanceof Primitive && value.getType() === getPrimitiveType(rule.type)) {
17790
- return { valid: true };
17791
- }
17792
- return {
17793
- valid: false,
17794
- errors: [
17795
- {
17796
- path: rule.path,
17797
- message: `expected ${rule.type} at path ${rule.path}`
17798
- }
17799
- ]
17800
- };
17801
- }
17802
17639
  const Noop = () => {
17803
17640
  };
17804
17641
  class ObserverProxy {
@@ -18073,332 +17910,66 @@ class ChangeContext {
18073
17910
  this.root.acc(diff);
18074
17911
  }
18075
17912
  }
18076
- class CRDTRoot {
18077
- constructor(rootObject) {
18078
- /**
18079
- * `rootObject` is the root object of the document.
18080
- */
18081
- __publicField(this, "rootObject");
18082
- /**
18083
- * `elementPairMapByCreatedAt` is a hash table that maps the creation time of
18084
- * an element to the element itself and its parent.
18085
- */
18086
- __publicField(this, "elementPairMapByCreatedAt");
18087
- /**
18088
- * `gcElementSetByCreatedAt` is a hash set that contains the creation
18089
- * time of the removed element. It is used to find the removed element when
18090
- * executing garbage collection.
18091
- */
18092
- __publicField(this, "gcElementSetByCreatedAt");
18093
- /**
18094
- * `gcPairMap` is a hash table that maps the IDString of GCChild to the
18095
- * element itself and its parent.
18096
- */
18097
- __publicField(this, "gcPairMap");
18098
- /**
18099
- * `docSize` is a structure that represents the size of the document.
18100
- */
18101
- __publicField(this, "docSize");
18102
- this.rootObject = rootObject;
18103
- this.elementPairMapByCreatedAt = /* @__PURE__ */ new Map();
18104
- this.gcElementSetByCreatedAt = /* @__PURE__ */ new Set();
18105
- this.gcPairMap = /* @__PURE__ */ new Map();
18106
- this.docSize = { live: { data: 0, meta: 0 }, gc: { data: 0, meta: 0 } };
18107
- this.registerElement(rootObject, void 0);
18108
- rootObject.getDescendants((elem) => {
18109
- if (elem.getRemovedAt()) {
18110
- this.registerRemovedElement(elem);
18111
- }
18112
- if (elem instanceof CRDTText || elem instanceof CRDTTree) {
18113
- for (const pair of elem.getGCPairs()) {
18114
- this.registerGCPair(pair);
17913
+ function createJSONObject(context, target) {
17914
+ const objectProxy = new ObjectProxy(context);
17915
+ return new Proxy(target, objectProxy.getHandlers());
17916
+ }
17917
+ class ObjectProxy {
17918
+ constructor(context) {
17919
+ __publicField(this, "context");
17920
+ __publicField(this, "handlers");
17921
+ this.context = context;
17922
+ this.handlers = {
17923
+ set: (target, key, value) => {
17924
+ if (logger.isEnabled(LogLevel.Trivial)) {
17925
+ logger.trivial(`obj[${key}]=${JSON.stringify(value)}`);
17926
+ }
17927
+ ObjectProxy.setInternal(context, target, key, value);
17928
+ return true;
17929
+ },
17930
+ get: (target, keyOrMethod) => {
17931
+ if (logger.isEnabled(LogLevel.Trivial)) {
17932
+ logger.trivial(`obj[${keyOrMethod}]`);
17933
+ }
17934
+ if (keyOrMethod === "getID") {
17935
+ return () => {
17936
+ return target.getCreatedAt();
17937
+ };
17938
+ } else if (keyOrMethod === "toJSON" || keyOrMethod === "toString") {
17939
+ return () => {
17940
+ return target.toJSON();
17941
+ };
17942
+ } else if (keyOrMethod === "toJS") {
17943
+ return () => {
17944
+ return target.toJS();
17945
+ };
17946
+ } else if (keyOrMethod === "toJSForTest") {
17947
+ return () => {
17948
+ return target.toJSForTest();
17949
+ };
17950
+ }
17951
+ return toJSONElement(context, target.get(keyOrMethod));
17952
+ },
17953
+ ownKeys: (target) => {
17954
+ return target.getKeys();
17955
+ },
17956
+ getOwnPropertyDescriptor: () => {
17957
+ return {
17958
+ enumerable: true,
17959
+ configurable: true
17960
+ };
17961
+ },
17962
+ deleteProperty: (target, key) => {
17963
+ if (logger.isEnabled(LogLevel.Trivial)) {
17964
+ logger.trivial(`obj[${key}]`);
18115
17965
  }
17966
+ ObjectProxy.deleteInternal(context, target, key);
17967
+ return true;
18116
17968
  }
18117
- return false;
18118
- });
17969
+ };
18119
17970
  }
18120
17971
  /**
18121
- * `create` creates a new instance of Root.
18122
- */
18123
- static create() {
18124
- return new CRDTRoot(CRDTObject.create(InitialTimeTicket));
18125
- }
18126
- /**
18127
- * `findByCreatedAt` returns the element of given creation time.
18128
- */
18129
- findByCreatedAt(createdAt) {
18130
- const pair = this.elementPairMapByCreatedAt.get(createdAt.toIDString());
18131
- if (!pair) {
18132
- return;
18133
- }
18134
- return pair.element;
18135
- }
18136
- /**
18137
- * `findElementPairByCreatedAt` returns the element and parent pair
18138
- * of given creation time.
18139
- */
18140
- findElementPairByCreatedAt(createdAt) {
18141
- return this.elementPairMapByCreatedAt.get(createdAt.toIDString());
18142
- }
18143
- /**
18144
- * `createSubPaths` creates an array of the sub paths for the given element.
18145
- */
18146
- createSubPaths(createdAt) {
18147
- let pair = this.elementPairMapByCreatedAt.get(createdAt.toIDString());
18148
- if (!pair) {
18149
- return [];
18150
- }
18151
- const subPaths = [];
18152
- while (pair.parent) {
18153
- const createdAt2 = pair.element.getCreatedAt();
18154
- const subPath = pair.parent.subPathOf(createdAt2);
18155
- if (subPath === void 0) {
18156
- throw new YorkieError(
18157
- Code.ErrInvalidArgument,
18158
- `cant find the given element: ${createdAt2.toIDString()}`
18159
- );
18160
- }
18161
- subPaths.unshift(subPath);
18162
- pair = this.elementPairMapByCreatedAt.get(
18163
- pair.parent.getCreatedAt().toIDString()
18164
- );
18165
- }
18166
- subPaths.unshift("$");
18167
- return subPaths;
18168
- }
18169
- /**
18170
- * `createPath` creates path of the given element.
18171
- */
18172
- createPath(createdAt) {
18173
- return this.createSubPaths(createdAt).join(".");
18174
- }
18175
- /**
18176
- * `registerElement` registers the given element and its descendants to hash table.
18177
- */
18178
- registerElement(element, parent) {
18179
- this.elementPairMapByCreatedAt.set(element.getCreatedAt().toIDString(), {
18180
- parent,
18181
- element
18182
- });
18183
- addDataSizes(this.docSize.live, element.getDataSize());
18184
- if (element instanceof CRDTContainer) {
18185
- element.getDescendants((elem, par) => {
18186
- this.elementPairMapByCreatedAt.set(elem.getCreatedAt().toIDString(), {
18187
- parent: par,
18188
- element: elem
18189
- });
18190
- addDataSizes(this.docSize.live, elem.getDataSize());
18191
- return false;
18192
- });
18193
- }
18194
- }
18195
- /**
18196
- * `deregisterElement` deregister the given element and its descendants from hash table.
18197
- */
18198
- deregisterElement(element) {
18199
- let count = 0;
18200
- const deregisterElementInternal = (elem) => {
18201
- const createdAt = elem.getCreatedAt().toIDString();
18202
- subDataSize(this.docSize.gc, elem.getDataSize());
18203
- this.elementPairMapByCreatedAt.delete(createdAt);
18204
- this.gcElementSetByCreatedAt.delete(createdAt);
18205
- count++;
18206
- };
18207
- deregisterElementInternal(element);
18208
- if (element instanceof CRDTContainer) {
18209
- element.getDescendants((e) => {
18210
- deregisterElementInternal(e);
18211
- return false;
18212
- });
18213
- }
18214
- return count;
18215
- }
18216
- /**
18217
- * `registerRemovedElement` registers the given element to the hash set.
18218
- */
18219
- registerRemovedElement(element) {
18220
- addDataSizes(this.docSize.gc, element.getDataSize());
18221
- subDataSize(this.docSize.live, element.getDataSize());
18222
- this.docSize.live.meta += TimeTicketSize;
18223
- this.gcElementSetByCreatedAt.add(element.getCreatedAt().toIDString());
18224
- }
18225
- /**
18226
- * `registerGCPair` registers the given pair to hash table.
18227
- */
18228
- registerGCPair(pair) {
18229
- const prev = this.gcPairMap.get(pair.child.toIDString());
18230
- if (prev) {
18231
- this.gcPairMap.delete(pair.child.toIDString());
18232
- return;
18233
- }
18234
- this.gcPairMap.set(pair.child.toIDString(), pair);
18235
- const size = this.gcPairMap.get(pair.child.toIDString()).child.getDataSize();
18236
- addDataSizes(this.docSize.gc, size);
18237
- subDataSize(this.docSize.live, size);
18238
- if (!(pair.child instanceof RHTNode)) {
18239
- this.docSize.live.meta += TimeTicketSize;
18240
- }
18241
- }
18242
- /**
18243
- * `getElementMapSize` returns the size of element map.
18244
- */
18245
- getElementMapSize() {
18246
- return this.elementPairMapByCreatedAt.size;
18247
- }
18248
- /**
18249
- * `getGarbageElementSetSize()` returns the size of removed element set.
18250
- */
18251
- getGarbageElementSetSize() {
18252
- const seen = /* @__PURE__ */ new Set();
18253
- for (const createdAt of this.gcElementSetByCreatedAt) {
18254
- seen.add(createdAt);
18255
- const pair = this.elementPairMapByCreatedAt.get(createdAt);
18256
- if (pair.element instanceof CRDTContainer) {
18257
- pair.element.getDescendants((el) => {
18258
- seen.add(el.getCreatedAt().toIDString());
18259
- return false;
18260
- });
18261
- }
18262
- }
18263
- return seen.size;
18264
- }
18265
- /**
18266
- * `getObject` returns root object.
18267
- */
18268
- getObject() {
18269
- return this.rootObject;
18270
- }
18271
- /**
18272
- * `getGarbageLen` returns length of nodes which can be garbage collected.
18273
- */
18274
- getGarbageLen() {
18275
- return this.getGarbageElementSetSize() + this.gcPairMap.size;
18276
- }
18277
- /**
18278
- * `getDocSize` returns the size of the document.
18279
- */
18280
- getDocSize() {
18281
- return this.docSize;
18282
- }
18283
- /**
18284
- * `deepcopy` copies itself deeply.
18285
- */
18286
- deepcopy() {
18287
- return new CRDTRoot(this.rootObject.deepcopy());
18288
- }
18289
- /**
18290
- * `garbageCollect` purges elements that were removed before the given time.
18291
- */
18292
- garbageCollect(minSyncedVersionVector) {
18293
- let count = 0;
18294
- for (const createdAt of this.gcElementSetByCreatedAt) {
18295
- const pair = this.elementPairMapByCreatedAt.get(createdAt);
18296
- const removedAt = pair.element.getRemovedAt();
18297
- if (removedAt && (minSyncedVersionVector == null ? void 0 : minSyncedVersionVector.afterOrEqual(removedAt))) {
18298
- pair.parent.purge(pair.element);
18299
- count += this.deregisterElement(pair.element);
18300
- }
18301
- }
18302
- for (const [, pair] of this.gcPairMap) {
18303
- const removedAt = pair.child.getRemovedAt();
18304
- if (removedAt && (minSyncedVersionVector == null ? void 0 : minSyncedVersionVector.afterOrEqual(removedAt))) {
18305
- pair.parent.purge(pair.child);
18306
- this.gcPairMap.delete(pair.child.toIDString());
18307
- count += 1;
18308
- }
18309
- }
18310
- return count;
18311
- }
18312
- /**
18313
- * `toJSON` returns the JSON encoding of this root object.
18314
- */
18315
- toJSON() {
18316
- return this.rootObject.toJSON();
18317
- }
18318
- /**
18319
- * `toSortedJSON` returns the sorted JSON encoding of this root object.
18320
- */
18321
- toSortedJSON() {
18322
- return this.rootObject.toSortedJSON();
18323
- }
18324
- /**
18325
- * `getStats` returns the current statistics of the root object.
18326
- * This includes counts of various types of elements and structural information.
18327
- */
18328
- getStats() {
18329
- return {
18330
- elements: this.getElementMapSize(),
18331
- gcPairs: this.gcPairMap.size,
18332
- gcElements: this.getGarbageElementSetSize()
18333
- };
18334
- }
18335
- /**
18336
- * `acc` accumulates the given DataSize to Live.
18337
- */
18338
- acc(diff) {
18339
- addDataSizes(this.docSize.live, diff);
18340
- }
18341
- }
18342
- function createJSONObject(context, target) {
18343
- const objectProxy = new ObjectProxy(context);
18344
- return new Proxy(target, objectProxy.getHandlers());
18345
- }
18346
- class ObjectProxy {
18347
- constructor(context) {
18348
- __publicField(this, "context");
18349
- __publicField(this, "handlers");
18350
- this.context = context;
18351
- this.handlers = {
18352
- set: (target, key, value) => {
18353
- if (logger.isEnabled(LogLevel.Trivial)) {
18354
- logger.trivial(`obj[${key}]=${JSON.stringify(value)}`);
18355
- }
18356
- ObjectProxy.setInternal(context, target, key, value);
18357
- return true;
18358
- },
18359
- get: (target, keyOrMethod) => {
18360
- if (logger.isEnabled(LogLevel.Trivial)) {
18361
- logger.trivial(`obj[${keyOrMethod}]`);
18362
- }
18363
- if (keyOrMethod === "getID") {
18364
- return () => {
18365
- return target.getCreatedAt();
18366
- };
18367
- } else if (keyOrMethod === "toJSON" || keyOrMethod === "toString") {
18368
- return () => {
18369
- return target.toJSON();
18370
- };
18371
- } else if (keyOrMethod === "toJS") {
18372
- return () => {
18373
- return target.toJS();
18374
- };
18375
- } else if (keyOrMethod === "toJSForTest") {
18376
- return () => {
18377
- return target.toJSForTest();
18378
- };
18379
- }
18380
- return toJSONElement(context, target.get(keyOrMethod));
18381
- },
18382
- ownKeys: (target) => {
18383
- return target.getKeys();
18384
- },
18385
- getOwnPropertyDescriptor: () => {
18386
- return {
18387
- enumerable: true,
18388
- configurable: true
18389
- };
18390
- },
18391
- deleteProperty: (target, key) => {
18392
- if (logger.isEnabled(LogLevel.Trivial)) {
18393
- logger.trivial(`obj[${key}]`);
18394
- }
18395
- ObjectProxy.deleteInternal(context, target, key);
18396
- return true;
18397
- }
18398
- };
18399
- }
18400
- /**
18401
- * `setInternal` sets a new Object for the given key
17972
+ * `setInternal` sets a new Object for the given key
18402
17973
  */
18403
17974
  static setInternal(context, target, key, value) {
18404
17975
  if (key.includes(".")) {
@@ -19043,7 +18614,6 @@ class Text {
19043
18614
  }
19044
18615
  /**
19045
18616
  * `initialize` initialize this text with context and internal text.
19046
- * @internal
19047
18617
  */
19048
18618
  initialize(context, text) {
19049
18619
  this.context = context;
@@ -19253,7 +18823,6 @@ class Text {
19253
18823
  }
19254
18824
  /**
19255
18825
  * `toJSForTest` returns value with meta data for testing.
19256
- * @internal
19257
18826
  */
19258
18827
  toJSForTest() {
19259
18828
  if (!this.context || !this.text) {
@@ -19289,7 +18858,6 @@ class Counter {
19289
18858
  }
19290
18859
  /**
19291
18860
  * `initialize` initialize this text with context and internal text.
19292
- * @internal
19293
18861
  */
19294
18862
  initialize(context, counter) {
19295
18863
  this.valueType = counter.getValueType();
@@ -19305,7 +18873,6 @@ class Counter {
19305
18873
  }
19306
18874
  /**
19307
18875
  * `getValue` returns the value of this counter;
19308
- * @internal
19309
18876
  */
19310
18877
  getValue() {
19311
18878
  return this.value;
@@ -19341,7 +18908,6 @@ class Counter {
19341
18908
  }
19342
18909
  /**
19343
18910
  * `toJSForTest` returns value with meta data for testing.
19344
- * @internal
19345
18911
  */
19346
18912
  toJSForTest() {
19347
18913
  if (!this.context || !this.counter) {
@@ -19540,7 +19106,6 @@ class Tree {
19540
19106
  }
19541
19107
  /**
19542
19108
  * `initialize` initialize this tree with context and internal tree.
19543
- * @internal
19544
19109
  */
19545
19110
  initialize(context, tree) {
19546
19111
  this.context = context;
@@ -19552,593 +19117,857 @@ class Tree {
19552
19117
  getID() {
19553
19118
  return this.tree.getID();
19554
19119
  }
19555
- /**
19556
- * `buildRoot` builds the root of this tree with the given initial root
19557
- * which set by the user.
19558
- */
19559
- buildRoot(context) {
19560
- if (!this.initialRoot) {
19561
- return CRDTTreeNode.create(
19562
- CRDTTreeNodeID.of(context.issueTimeTicket(), 0),
19563
- DefaultRootType
19120
+ /**
19121
+ * `buildRoot` builds the root of this tree with the given initial root
19122
+ * which set by the user.
19123
+ */
19124
+ buildRoot(context) {
19125
+ if (!this.initialRoot) {
19126
+ return CRDTTreeNode.create(
19127
+ CRDTTreeNodeID.of(context.issueTimeTicket(), 0),
19128
+ DefaultRootType
19129
+ );
19130
+ }
19131
+ const root = CRDTTreeNode.create(
19132
+ CRDTTreeNodeID.of(context.issueTimeTicket(), 0),
19133
+ this.initialRoot.type
19134
+ );
19135
+ for (const child of this.initialRoot.children) {
19136
+ buildDescendants(child, root, context);
19137
+ }
19138
+ return root;
19139
+ }
19140
+ /**
19141
+ * `getSize` returns the size of this tree.
19142
+ */
19143
+ getSize() {
19144
+ if (!this.context || !this.tree) {
19145
+ throw new YorkieError(
19146
+ Code.ErrNotInitialized,
19147
+ "Tree is not initialized yet"
19148
+ );
19149
+ }
19150
+ return this.tree.getSize();
19151
+ }
19152
+ /**
19153
+ * `getNodeSize` returns the node size of this tree.
19154
+ */
19155
+ getNodeSize() {
19156
+ if (!this.context || !this.tree) {
19157
+ throw new YorkieError(
19158
+ Code.ErrNotInitialized,
19159
+ "Tree is not initialized yet"
19160
+ );
19161
+ }
19162
+ return this.tree.getNodeSize();
19163
+ }
19164
+ /**
19165
+ * `getIndexTree` returns the index tree of this tree.
19166
+ */
19167
+ getIndexTree() {
19168
+ if (!this.context || !this.tree) {
19169
+ throw new YorkieError(
19170
+ Code.ErrNotInitialized,
19171
+ "Tree is not initialized yet"
19172
+ );
19173
+ }
19174
+ return this.tree.getIndexTree();
19175
+ }
19176
+ /**
19177
+ * `splitByPath` splits the tree by the given path.
19178
+ */
19179
+ splitByPath(path) {
19180
+ if (!this.context || !this.tree) {
19181
+ throw new YorkieError(
19182
+ Code.ErrNotInitialized,
19183
+ "Tree is not initialized yet"
19184
+ );
19185
+ }
19186
+ if (!path.length) {
19187
+ throw new YorkieError(
19188
+ Code.ErrInvalidArgument,
19189
+ "path should not be empty"
19190
+ );
19191
+ }
19192
+ const treePos = this.tree.pathToTreePos(path);
19193
+ const commands = separateSplit(treePos, path);
19194
+ for (const command of commands) {
19195
+ const { fromPath, toPath, content } = command;
19196
+ const fromPos = this.tree.pathToPos(fromPath);
19197
+ const toPos = this.tree.pathToPos(toPath);
19198
+ this.editInternal(fromPos, toPos, content ? [content] : [], 0);
19199
+ }
19200
+ }
19201
+ /**
19202
+ * `mergeByPath` merges the tree by the given path.
19203
+ */
19204
+ mergeByPath(path) {
19205
+ if (!this.context || !this.tree) {
19206
+ throw new YorkieError(
19207
+ Code.ErrNotInitialized,
19208
+ "Tree is not initialized yet"
19209
+ );
19210
+ }
19211
+ if (!path.length) {
19212
+ throw new YorkieError(
19213
+ Code.ErrInvalidArgument,
19214
+ "path should not be empty"
19215
+ );
19216
+ }
19217
+ const treePos = this.tree.pathToTreePos(path);
19218
+ if (treePos.node.isText) {
19219
+ throw new YorkieError(
19220
+ Code.ErrInvalidArgument,
19221
+ "text node cannot be merged"
19222
+ );
19223
+ }
19224
+ const commands = separateMerge(treePos, path);
19225
+ for (const command of commands) {
19226
+ const { fromPath, toPath, content } = command;
19227
+ const fromPos = this.tree.pathToPos(fromPath);
19228
+ const toPos = this.tree.pathToPos(toPath);
19229
+ this.editInternal(fromPos, toPos, content ?? [], 0);
19230
+ }
19231
+ }
19232
+ /**
19233
+ * `styleByPath` sets the attributes to the elements of the given path.
19234
+ */
19235
+ styleByPath(path, attributes) {
19236
+ if (!this.context || !this.tree) {
19237
+ throw new YorkieError(
19238
+ Code.ErrNotInitialized,
19239
+ "Tree is not initialized yet"
19240
+ );
19241
+ }
19242
+ if (!path.length) {
19243
+ throw new YorkieError(
19244
+ Code.ErrInvalidArgument,
19245
+ "path should not be empty"
19246
+ );
19247
+ }
19248
+ const [fromPos, toPos] = this.tree.pathToPosRange(path);
19249
+ const ticket = this.context.issueTimeTicket();
19250
+ const attrs = attributes ? stringifyObjectValues(attributes) : void 0;
19251
+ const [pairs, , diff] = this.tree.style([fromPos, toPos], attrs, ticket);
19252
+ this.context.acc(diff);
19253
+ for (const pair of pairs) {
19254
+ this.context.registerGCPair(pair);
19255
+ }
19256
+ this.context.push(
19257
+ TreeStyleOperation.create(
19258
+ this.tree.getCreatedAt(),
19259
+ fromPos,
19260
+ toPos,
19261
+ attrs ? new Map(Object.entries(attrs)) : /* @__PURE__ */ new Map(),
19262
+ ticket
19263
+ )
19264
+ );
19265
+ }
19266
+ /**
19267
+ * `style` sets the attributes to the elements of the given range.
19268
+ */
19269
+ style(fromIdx, toIdx, attributes) {
19270
+ if (!this.context || !this.tree) {
19271
+ throw new YorkieError(
19272
+ Code.ErrNotInitialized,
19273
+ "Tree is not initialized yet"
19274
+ );
19275
+ }
19276
+ if (fromIdx > toIdx) {
19277
+ throw new YorkieError(
19278
+ Code.ErrInvalidArgument,
19279
+ "from should be less than or equal to to"
19280
+ );
19281
+ }
19282
+ const fromPos = this.tree.findPos(fromIdx);
19283
+ const toPos = this.tree.findPos(toIdx);
19284
+ const ticket = this.context.issueTimeTicket();
19285
+ const attrs = attributes ? stringifyObjectValues(attributes) : void 0;
19286
+ const [pairs, , diff] = this.tree.style([fromPos, toPos], attrs, ticket);
19287
+ this.context.acc(diff);
19288
+ for (const pair of pairs) {
19289
+ this.context.registerGCPair(pair);
19290
+ }
19291
+ this.context.push(
19292
+ TreeStyleOperation.create(
19293
+ this.tree.getCreatedAt(),
19294
+ fromPos,
19295
+ toPos,
19296
+ attrs ? new Map(Object.entries(attrs)) : /* @__PURE__ */ new Map(),
19297
+ ticket
19298
+ )
19299
+ );
19300
+ }
19301
+ /**
19302
+ * `removeStyle` removes the attributes to the elements of the given range.
19303
+ */
19304
+ removeStyle(fromIdx, toIdx, attributesToRemove) {
19305
+ if (!this.context || !this.tree) {
19306
+ throw new YorkieError(
19307
+ Code.ErrNotInitialized,
19308
+ "Tree is not initialized yet"
19309
+ );
19310
+ }
19311
+ if (fromIdx > toIdx) {
19312
+ throw new YorkieError(
19313
+ Code.ErrInvalidArgument,
19314
+ "from should be less than or equal to to"
19315
+ );
19316
+ }
19317
+ const fromPos = this.tree.findPos(fromIdx);
19318
+ const toPos = this.tree.findPos(toIdx);
19319
+ const ticket = this.context.issueTimeTicket();
19320
+ const [pairs, , diff] = this.tree.removeStyle(
19321
+ [fromPos, toPos],
19322
+ attributesToRemove,
19323
+ ticket
19324
+ );
19325
+ this.context.acc(diff);
19326
+ for (const pair of pairs) {
19327
+ this.context.registerGCPair(pair);
19328
+ }
19329
+ this.context.push(
19330
+ TreeStyleOperation.createTreeRemoveStyleOperation(
19331
+ this.tree.getCreatedAt(),
19332
+ fromPos,
19333
+ toPos,
19334
+ attributesToRemove,
19335
+ ticket
19336
+ )
19337
+ );
19338
+ }
19339
+ editInternal(fromPos, toPos, contents, splitLevel = 0) {
19340
+ var _a2;
19341
+ if (contents.length !== 0 && contents[0]) {
19342
+ validateTreeNodes(contents);
19343
+ if (contents[0].type !== DefaultTextType) {
19344
+ for (const content of contents) {
19345
+ const { children = [] } = content;
19346
+ validateTreeNodes(children);
19347
+ }
19348
+ }
19349
+ }
19350
+ const ticket = this.context.getLastTimeTicket();
19351
+ let crdtNodes = new Array();
19352
+ if (((_a2 = contents[0]) == null ? void 0 : _a2.type) === DefaultTextType) {
19353
+ let compVal = "";
19354
+ for (const content of contents) {
19355
+ const { value } = content;
19356
+ compVal += value;
19357
+ }
19358
+ crdtNodes.push(
19359
+ CRDTTreeNode.create(
19360
+ CRDTTreeNodeID.of(this.context.issueTimeTicket(), 0),
19361
+ DefaultTextType,
19362
+ compVal
19363
+ )
19564
19364
  );
19365
+ } else {
19366
+ crdtNodes = contents.map((content) => content && createCRDTTreeNode(this.context, content)).filter((a) => a);
19565
19367
  }
19566
- const root = CRDTTreeNode.create(
19567
- CRDTTreeNodeID.of(context.issueTimeTicket(), 0),
19568
- this.initialRoot.type
19368
+ const [, pairs, diff] = this.tree.edit(
19369
+ [fromPos, toPos],
19370
+ crdtNodes.length ? crdtNodes.map((crdtNode) => crdtNode == null ? void 0 : crdtNode.deepcopy()) : void 0,
19371
+ splitLevel,
19372
+ ticket,
19373
+ () => this.context.issueTimeTicket()
19569
19374
  );
19570
- for (const child of this.initialRoot.children) {
19571
- buildDescendants(child, root, context);
19375
+ this.context.acc(diff);
19376
+ for (const pair of pairs) {
19377
+ this.context.registerGCPair(pair);
19572
19378
  }
19573
- return root;
19379
+ this.context.push(
19380
+ TreeEditOperation.create(
19381
+ this.tree.getCreatedAt(),
19382
+ fromPos,
19383
+ toPos,
19384
+ crdtNodes.length ? crdtNodes : void 0,
19385
+ splitLevel,
19386
+ ticket
19387
+ )
19388
+ );
19389
+ return true;
19574
19390
  }
19575
19391
  /**
19576
- * `getSize` returns the size of this tree.
19392
+ * `editByPath` edits this tree with the given node and path.
19577
19393
  */
19578
- getSize() {
19394
+ editByPath(fromPath, toPath, content, splitLevel = 0) {
19579
19395
  if (!this.context || !this.tree) {
19580
19396
  throw new YorkieError(
19581
19397
  Code.ErrNotInitialized,
19582
19398
  "Tree is not initialized yet"
19583
19399
  );
19584
19400
  }
19585
- return this.tree.getSize();
19401
+ if (fromPath.length !== toPath.length) {
19402
+ throw new YorkieError(
19403
+ Code.ErrInvalidArgument,
19404
+ "path length should be equal"
19405
+ );
19406
+ }
19407
+ if (!fromPath.length || !toPath.length) {
19408
+ throw new YorkieError(
19409
+ Code.ErrInvalidArgument,
19410
+ "path should not be empty"
19411
+ );
19412
+ }
19413
+ const fromPos = this.tree.pathToPos(fromPath);
19414
+ const toPos = this.tree.pathToPos(toPath);
19415
+ return this.editInternal(
19416
+ fromPos,
19417
+ toPos,
19418
+ content ? [content] : [],
19419
+ splitLevel
19420
+ );
19586
19421
  }
19587
19422
  /**
19588
- * `getNodeSize` returns the node size of this tree.
19423
+ * `editBulkByPath` edits this tree with the given node and path.
19589
19424
  */
19590
- getNodeSize() {
19425
+ editBulkByPath(fromPath, toPath, contents, splitLevel = 0) {
19591
19426
  if (!this.context || !this.tree) {
19592
19427
  throw new YorkieError(
19593
19428
  Code.ErrNotInitialized,
19594
19429
  "Tree is not initialized yet"
19595
19430
  );
19596
19431
  }
19597
- return this.tree.getNodeSize();
19432
+ if (fromPath.length !== toPath.length) {
19433
+ throw new YorkieError(
19434
+ Code.ErrInvalidArgument,
19435
+ "path length should be equal"
19436
+ );
19437
+ }
19438
+ if (!fromPath.length || !toPath.length) {
19439
+ throw new YorkieError(
19440
+ Code.ErrInvalidArgument,
19441
+ "path should not be empty"
19442
+ );
19443
+ }
19444
+ const fromPos = this.tree.pathToPos(fromPath);
19445
+ const toPos = this.tree.pathToPos(toPath);
19446
+ return this.editInternal(fromPos, toPos, contents, splitLevel);
19598
19447
  }
19599
19448
  /**
19600
- * `getIndexTree` returns the index tree of this tree.
19449
+ * `edit` edits this tree with the given nodes.
19601
19450
  */
19602
- getIndexTree() {
19451
+ edit(fromIdx, toIdx, content, splitLevel = 0) {
19603
19452
  if (!this.context || !this.tree) {
19604
19453
  throw new YorkieError(
19605
19454
  Code.ErrNotInitialized,
19606
19455
  "Tree is not initialized yet"
19607
19456
  );
19608
19457
  }
19609
- return this.tree.getIndexTree();
19458
+ if (fromIdx > toIdx) {
19459
+ throw new YorkieError(
19460
+ Code.ErrInvalidArgument,
19461
+ "from should be less than or equal to to"
19462
+ );
19463
+ }
19464
+ const fromPos = this.tree.findPos(fromIdx);
19465
+ const toPos = this.tree.findPos(toIdx);
19466
+ return this.editInternal(
19467
+ fromPos,
19468
+ toPos,
19469
+ content ? [content] : [],
19470
+ splitLevel
19471
+ );
19610
19472
  }
19611
19473
  /**
19612
- * `splitByPath` splits the tree by the given path.
19474
+ * `editBulk` edits this tree with the given nodes.
19613
19475
  */
19614
- splitByPath(path) {
19476
+ editBulk(fromIdx, toIdx, contents, splitLevel = 0) {
19615
19477
  if (!this.context || !this.tree) {
19616
19478
  throw new YorkieError(
19617
19479
  Code.ErrNotInitialized,
19618
19480
  "Tree is not initialized yet"
19619
19481
  );
19620
19482
  }
19621
- if (!path.length) {
19483
+ if (fromIdx > toIdx) {
19622
19484
  throw new YorkieError(
19623
19485
  Code.ErrInvalidArgument,
19624
- "path should not be empty"
19486
+ "from should be less than or equal to to"
19625
19487
  );
19626
19488
  }
19627
- const treePos = this.tree.pathToTreePos(path);
19628
- const commands = separateSplit(treePos, path);
19629
- for (const command of commands) {
19630
- const { fromPath, toPath, content } = command;
19631
- const fromPos = this.tree.pathToPos(fromPath);
19632
- const toPos = this.tree.pathToPos(toPath);
19633
- this.editInternal(fromPos, toPos, content ? [content] : [], 0);
19634
- }
19489
+ const fromPos = this.tree.findPos(fromIdx);
19490
+ const toPos = this.tree.findPos(toIdx);
19491
+ return this.editInternal(fromPos, toPos, contents, splitLevel);
19635
19492
  }
19636
19493
  /**
19637
- * `mergeByPath` merges the tree by the given path.
19494
+ * `toXML` returns the XML string of this tree.
19638
19495
  */
19639
- mergeByPath(path) {
19496
+ toXML() {
19640
19497
  if (!this.context || !this.tree) {
19641
19498
  throw new YorkieError(
19642
19499
  Code.ErrNotInitialized,
19643
19500
  "Tree is not initialized yet"
19644
19501
  );
19645
19502
  }
19646
- if (!path.length) {
19503
+ return this.tree.toXML();
19504
+ }
19505
+ /**
19506
+ * `toJSON` returns the JSON string of this tree.
19507
+ */
19508
+ toJSON() {
19509
+ if (!this.context || !this.tree) {
19647
19510
  throw new YorkieError(
19648
- Code.ErrInvalidArgument,
19649
- "path should not be empty"
19511
+ Code.ErrNotInitialized,
19512
+ "Tree is not initialized yet"
19650
19513
  );
19651
19514
  }
19652
- const treePos = this.tree.pathToTreePos(path);
19653
- if (treePos.node.isText) {
19515
+ return this.tree.toJSON();
19516
+ }
19517
+ /**
19518
+ * `toJSForTest` returns value with meta data for testing.
19519
+ */
19520
+ toJSForTest() {
19521
+ if (!this.context || !this.tree) {
19654
19522
  throw new YorkieError(
19655
- Code.ErrInvalidArgument,
19656
- "text node cannot be merged"
19523
+ Code.ErrNotInitialized,
19524
+ "Tree is not initialized yet"
19657
19525
  );
19658
19526
  }
19659
- const commands = separateMerge(treePos, path);
19660
- for (const command of commands) {
19661
- const { fromPath, toPath, content } = command;
19662
- const fromPos = this.tree.pathToPos(fromPath);
19663
- const toPos = this.tree.pathToPos(toPath);
19664
- this.editInternal(fromPos, toPos, content ?? [], 0);
19665
- }
19527
+ return this.tree.toJSForTest();
19666
19528
  }
19667
19529
  /**
19668
- * `styleByPath` sets the attributes to the elements of the given path.
19530
+ * `toJSInfoForTest` returns detailed TreeNode information for use in Devtools.
19669
19531
  */
19670
- styleByPath(path, attributes) {
19532
+ toJSInfoForTest() {
19671
19533
  if (!this.context || !this.tree) {
19672
19534
  throw new YorkieError(
19673
19535
  Code.ErrNotInitialized,
19674
19536
  "Tree is not initialized yet"
19675
19537
  );
19676
19538
  }
19677
- if (!path.length) {
19539
+ return this.tree.toJSInfoForTest();
19540
+ }
19541
+ /**
19542
+ * `getRootTreeNode` returns TreeNode of this tree.
19543
+ */
19544
+ getRootTreeNode() {
19545
+ if (!this.context || !this.tree) {
19678
19546
  throw new YorkieError(
19679
- Code.ErrInvalidArgument,
19680
- "path should not be empty"
19547
+ Code.ErrNotInitialized,
19548
+ "Tree is not initialized yet"
19681
19549
  );
19682
19550
  }
19683
- const [fromPos, toPos] = this.tree.pathToPosRange(path);
19684
- const ticket = this.context.issueTimeTicket();
19685
- const attrs = attributes ? stringifyObjectValues(attributes) : void 0;
19686
- const [pairs, , diff] = this.tree.style([fromPos, toPos], attrs, ticket);
19687
- this.context.acc(diff);
19688
- for (const pair of pairs) {
19689
- this.context.registerGCPair(pair);
19690
- }
19691
- this.context.push(
19692
- TreeStyleOperation.create(
19693
- this.tree.getCreatedAt(),
19694
- fromPos,
19695
- toPos,
19696
- attrs ? new Map(Object.entries(attrs)) : /* @__PURE__ */ new Map(),
19697
- ticket
19698
- )
19699
- );
19551
+ return this.tree.getRootTreeNode();
19700
19552
  }
19701
19553
  /**
19702
- * `style` sets the attributes to the elements of the given range.
19554
+ * `indexToPath` returns the path of the given index.
19703
19555
  */
19704
- style(fromIdx, toIdx, attributes) {
19556
+ indexToPath(index) {
19705
19557
  if (!this.context || !this.tree) {
19706
19558
  throw new YorkieError(
19707
19559
  Code.ErrNotInitialized,
19708
19560
  "Tree is not initialized yet"
19709
19561
  );
19710
19562
  }
19711
- if (fromIdx > toIdx) {
19563
+ return this.tree.indexToPath(index);
19564
+ }
19565
+ /**
19566
+ * `pathToIndex` returns the index of given path.
19567
+ */
19568
+ pathToIndex(path) {
19569
+ if (!this.context || !this.tree) {
19712
19570
  throw new YorkieError(
19713
- Code.ErrInvalidArgument,
19714
- "from should be less than or equal to to"
19715
- );
19716
- }
19717
- const fromPos = this.tree.findPos(fromIdx);
19718
- const toPos = this.tree.findPos(toIdx);
19719
- const ticket = this.context.issueTimeTicket();
19720
- const attrs = attributes ? stringifyObjectValues(attributes) : void 0;
19721
- const [pairs, , diff] = this.tree.style([fromPos, toPos], attrs, ticket);
19722
- this.context.acc(diff);
19723
- for (const pair of pairs) {
19724
- this.context.registerGCPair(pair);
19725
- }
19726
- this.context.push(
19727
- TreeStyleOperation.create(
19728
- this.tree.getCreatedAt(),
19729
- fromPos,
19730
- toPos,
19731
- attrs ? new Map(Object.entries(attrs)) : /* @__PURE__ */ new Map(),
19732
- ticket
19733
- )
19734
- );
19571
+ Code.ErrNotInitialized,
19572
+ "Tree is not initialized yet"
19573
+ );
19574
+ }
19575
+ return this.tree.pathToIndex(path);
19735
19576
  }
19736
19577
  /**
19737
- * `removeStyle` removes the attributes to the elements of the given range.
19578
+ * `pathRangeToPosRange` converts the path range into the position range.
19738
19579
  */
19739
- removeStyle(fromIdx, toIdx, attributesToRemove) {
19580
+ pathRangeToPosRange(range) {
19740
19581
  if (!this.context || !this.tree) {
19741
19582
  throw new YorkieError(
19742
19583
  Code.ErrNotInitialized,
19743
19584
  "Tree is not initialized yet"
19744
19585
  );
19745
19586
  }
19746
- if (fromIdx > toIdx) {
19587
+ const indexRange = [
19588
+ this.tree.pathToIndex(range[0]),
19589
+ this.tree.pathToIndex(range[1])
19590
+ ];
19591
+ const posRange = this.tree.indexRangeToPosRange(indexRange);
19592
+ return [posRange[0].toStruct(), posRange[1].toStruct()];
19593
+ }
19594
+ /**
19595
+ * `indexRangeToPosRange` converts the index range into the position range.
19596
+ */
19597
+ indexRangeToPosRange(range) {
19598
+ if (!this.context || !this.tree) {
19747
19599
  throw new YorkieError(
19748
- Code.ErrInvalidArgument,
19749
- "from should be less than or equal to to"
19600
+ Code.ErrNotInitialized,
19601
+ "Tree is not initialized yet"
19750
19602
  );
19751
19603
  }
19752
- const fromPos = this.tree.findPos(fromIdx);
19753
- const toPos = this.tree.findPos(toIdx);
19754
- const ticket = this.context.issueTimeTicket();
19755
- const [pairs, , diff] = this.tree.removeStyle(
19756
- [fromPos, toPos],
19757
- attributesToRemove,
19758
- ticket
19759
- );
19760
- this.context.acc(diff);
19761
- for (const pair of pairs) {
19762
- this.context.registerGCPair(pair);
19763
- }
19764
- this.context.push(
19765
- TreeStyleOperation.createTreeRemoveStyleOperation(
19766
- this.tree.getCreatedAt(),
19767
- fromPos,
19768
- toPos,
19769
- attributesToRemove,
19770
- ticket
19771
- )
19772
- );
19604
+ return this.tree.indexRangeToPosStructRange(range);
19773
19605
  }
19774
- editInternal(fromPos, toPos, contents, splitLevel = 0) {
19775
- var _a2;
19776
- if (contents.length !== 0 && contents[0]) {
19777
- validateTreeNodes(contents);
19778
- if (contents[0].type !== DefaultTextType) {
19779
- for (const content of contents) {
19780
- const { children = [] } = content;
19781
- validateTreeNodes(children);
19782
- }
19783
- }
19784
- }
19785
- const ticket = this.context.getLastTimeTicket();
19786
- let crdtNodes = new Array();
19787
- if (((_a2 = contents[0]) == null ? void 0 : _a2.type) === DefaultTextType) {
19788
- let compVal = "";
19789
- for (const content of contents) {
19790
- const { value } = content;
19791
- compVal += value;
19792
- }
19793
- crdtNodes.push(
19794
- CRDTTreeNode.create(
19795
- CRDTTreeNodeID.of(this.context.issueTimeTicket(), 0),
19796
- DefaultTextType,
19797
- compVal
19798
- )
19606
+ /**
19607
+ * `posRangeToIndexRange` converts the position range into the index range.
19608
+ */
19609
+ posRangeToIndexRange(range) {
19610
+ if (!this.context || !this.tree) {
19611
+ throw new YorkieError(
19612
+ Code.ErrNotInitialized,
19613
+ "Tree is not initialized yet"
19799
19614
  );
19800
- } else {
19801
- crdtNodes = contents.map((content) => content && createCRDTTreeNode(this.context, content)).filter((a) => a);
19802
- }
19803
- const [, pairs, diff] = this.tree.edit(
19804
- [fromPos, toPos],
19805
- crdtNodes.length ? crdtNodes.map((crdtNode) => crdtNode == null ? void 0 : crdtNode.deepcopy()) : void 0,
19806
- splitLevel,
19807
- ticket,
19808
- () => this.context.issueTimeTicket()
19809
- );
19810
- this.context.acc(diff);
19811
- for (const pair of pairs) {
19812
- this.context.registerGCPair(pair);
19813
19615
  }
19814
- this.context.push(
19815
- TreeEditOperation.create(
19816
- this.tree.getCreatedAt(),
19817
- fromPos,
19818
- toPos,
19819
- crdtNodes.length ? crdtNodes : void 0,
19820
- splitLevel,
19821
- ticket
19822
- )
19823
- );
19824
- return true;
19616
+ const posRange = [
19617
+ CRDTTreePos.fromStruct(range[0]),
19618
+ CRDTTreePos.fromStruct(range[1])
19619
+ ];
19620
+ return this.tree.posRangeToIndexRange(posRange);
19825
19621
  }
19826
19622
  /**
19827
- * `editByPath` edits this tree with the given node and path.
19623
+ * `posRangeToPathRange` converts the position range into the path range.
19828
19624
  */
19829
- editByPath(fromPath, toPath, content, splitLevel = 0) {
19625
+ posRangeToPathRange(range) {
19830
19626
  if (!this.context || !this.tree) {
19831
19627
  throw new YorkieError(
19832
19628
  Code.ErrNotInitialized,
19833
19629
  "Tree is not initialized yet"
19834
19630
  );
19835
19631
  }
19836
- if (fromPath.length !== toPath.length) {
19837
- throw new YorkieError(
19838
- Code.ErrInvalidArgument,
19839
- "path length should be equal"
19632
+ const posRange = [
19633
+ CRDTTreePos.fromStruct(range[0]),
19634
+ CRDTTreePos.fromStruct(range[1])
19635
+ ];
19636
+ return this.tree.posRangeToPathRange(posRange);
19637
+ }
19638
+ }
19639
+ function createJSON(context, target) {
19640
+ return createJSONObject(context, target);
19641
+ }
19642
+ function toWrappedElement(context, elem) {
19643
+ if (!elem) {
19644
+ return;
19645
+ } else if (elem instanceof Primitive) {
19646
+ return elem;
19647
+ } else if (elem instanceof CRDTObject) {
19648
+ return createJSONObject(context, elem);
19649
+ } else if (elem instanceof CRDTArray) {
19650
+ return createJSONArray(context, elem);
19651
+ } else if (elem instanceof CRDTText) {
19652
+ return new Text(context, elem);
19653
+ } else if (elem instanceof CRDTCounter) {
19654
+ const counter = new Counter(CounterType.Int, 0);
19655
+ counter.initialize(context, elem);
19656
+ return counter;
19657
+ } else if (elem instanceof CRDTTree) {
19658
+ const tree = new Tree();
19659
+ tree.initialize(context, elem);
19660
+ return tree;
19661
+ }
19662
+ throw new TypeError(`Unsupported type of element: ${typeof elem}`);
19663
+ }
19664
+ function toJSONElement(context, elem) {
19665
+ const wrappedElement = toWrappedElement(context, elem);
19666
+ if (wrappedElement instanceof Primitive) {
19667
+ return wrappedElement.getValue();
19668
+ }
19669
+ return wrappedElement;
19670
+ }
19671
+ function buildCRDTElement(context, value, createdAt) {
19672
+ let element;
19673
+ if (Primitive.isSupport(value)) {
19674
+ element = Primitive.of(value, createdAt);
19675
+ } else if (Array.isArray(value)) {
19676
+ element = CRDTArray.create(
19677
+ createdAt,
19678
+ ArrayProxy.buildArrayElements(context, value)
19679
+ );
19680
+ } else if (typeof value === "object") {
19681
+ if (value instanceof Text) {
19682
+ element = CRDTText.create(RGATreeSplit.create(), createdAt);
19683
+ value.initialize(context, element);
19684
+ } else if (value instanceof Counter) {
19685
+ element = CRDTCounter.create(
19686
+ value.getValueType(),
19687
+ value.getValue(),
19688
+ createdAt
19840
19689
  );
19841
- }
19842
- if (!fromPath.length || !toPath.length) {
19843
- throw new YorkieError(
19844
- Code.ErrInvalidArgument,
19845
- "path should not be empty"
19690
+ value.initialize(context, element);
19691
+ } else if (value instanceof Tree) {
19692
+ element = CRDTTree.create(value.buildRoot(context), createdAt);
19693
+ value.initialize(context, element);
19694
+ } else {
19695
+ element = CRDTObject.create(
19696
+ createdAt,
19697
+ ObjectProxy.buildObjectMembers(context, value)
19846
19698
  );
19847
19699
  }
19848
- const fromPos = this.tree.pathToPos(fromPath);
19849
- const toPos = this.tree.pathToPos(toPath);
19850
- return this.editInternal(
19851
- fromPos,
19852
- toPos,
19853
- content ? [content] : [],
19854
- splitLevel
19855
- );
19700
+ } else {
19701
+ throw new TypeError(`Unsupported type of value: ${typeof value}`);
19702
+ }
19703
+ return element;
19704
+ }
19705
+ class CRDTRoot {
19706
+ constructor(rootObject) {
19707
+ /**
19708
+ * `rootObject` is the root object of the document.
19709
+ */
19710
+ __publicField(this, "rootObject");
19711
+ /**
19712
+ * `elementPairMapByCreatedAt` is a hash table that maps the creation time of
19713
+ * an element to the element itself and its parent.
19714
+ */
19715
+ __publicField(this, "elementPairMapByCreatedAt");
19716
+ /**
19717
+ * `gcElementSetByCreatedAt` is a hash set that contains the creation
19718
+ * time of the removed element. It is used to find the removed element when
19719
+ * executing garbage collection.
19720
+ */
19721
+ __publicField(this, "gcElementSetByCreatedAt");
19722
+ /**
19723
+ * `gcPairMap` is a hash table that maps the IDString of GCChild to the
19724
+ * element itself and its parent.
19725
+ */
19726
+ __publicField(this, "gcPairMap");
19727
+ /**
19728
+ * `docSize` is a structure that represents the size of the document.
19729
+ */
19730
+ __publicField(this, "docSize");
19731
+ this.rootObject = rootObject;
19732
+ this.elementPairMapByCreatedAt = /* @__PURE__ */ new Map();
19733
+ this.gcElementSetByCreatedAt = /* @__PURE__ */ new Set();
19734
+ this.gcPairMap = /* @__PURE__ */ new Map();
19735
+ this.docSize = { live: { data: 0, meta: 0 }, gc: { data: 0, meta: 0 } };
19736
+ this.registerElement(rootObject, void 0);
19737
+ rootObject.getDescendants((elem) => {
19738
+ if (elem.getRemovedAt()) {
19739
+ this.registerRemovedElement(elem);
19740
+ }
19741
+ if (elem instanceof CRDTText || elem instanceof CRDTTree) {
19742
+ for (const pair of elem.getGCPairs()) {
19743
+ this.registerGCPair(pair);
19744
+ }
19745
+ }
19746
+ return false;
19747
+ });
19748
+ }
19749
+ /**
19750
+ * `create` creates a new instance of Root.
19751
+ */
19752
+ static create() {
19753
+ return new CRDTRoot(CRDTObject.create(InitialTimeTicket));
19754
+ }
19755
+ /**
19756
+ * `findByCreatedAt` returns the element of given creation time.
19757
+ */
19758
+ findByCreatedAt(createdAt) {
19759
+ const pair = this.elementPairMapByCreatedAt.get(createdAt.toIDString());
19760
+ if (!pair) {
19761
+ return;
19762
+ }
19763
+ return pair.element;
19856
19764
  }
19857
19765
  /**
19858
- * `editBulkByPath` edits this tree with the given node and path.
19766
+ * `findElementPairByCreatedAt` returns the element and parent pair
19767
+ * of given creation time.
19859
19768
  */
19860
- editBulkByPath(fromPath, toPath, contents, splitLevel = 0) {
19861
- if (!this.context || !this.tree) {
19862
- throw new YorkieError(
19863
- Code.ErrNotInitialized,
19864
- "Tree is not initialized yet"
19865
- );
19866
- }
19867
- if (fromPath.length !== toPath.length) {
19868
- throw new YorkieError(
19869
- Code.ErrInvalidArgument,
19870
- "path length should be equal"
19871
- );
19769
+ findElementPairByCreatedAt(createdAt) {
19770
+ return this.elementPairMapByCreatedAt.get(createdAt.toIDString());
19771
+ }
19772
+ /**
19773
+ * `createSubPaths` creates an array of the sub paths for the given element.
19774
+ */
19775
+ createSubPaths(createdAt) {
19776
+ let pair = this.elementPairMapByCreatedAt.get(createdAt.toIDString());
19777
+ if (!pair) {
19778
+ return [];
19872
19779
  }
19873
- if (!fromPath.length || !toPath.length) {
19874
- throw new YorkieError(
19875
- Code.ErrInvalidArgument,
19876
- "path should not be empty"
19780
+ const subPaths = [];
19781
+ while (pair.parent) {
19782
+ const createdAt2 = pair.element.getCreatedAt();
19783
+ const subPath = pair.parent.subPathOf(createdAt2);
19784
+ if (subPath === void 0) {
19785
+ throw new YorkieError(
19786
+ Code.ErrInvalidArgument,
19787
+ `cant find the given element: ${createdAt2.toIDString()}`
19788
+ );
19789
+ }
19790
+ subPaths.unshift(subPath);
19791
+ pair = this.elementPairMapByCreatedAt.get(
19792
+ pair.parent.getCreatedAt().toIDString()
19877
19793
  );
19878
19794
  }
19879
- const fromPos = this.tree.pathToPos(fromPath);
19880
- const toPos = this.tree.pathToPos(toPath);
19881
- return this.editInternal(fromPos, toPos, contents, splitLevel);
19795
+ subPaths.unshift("$");
19796
+ return subPaths;
19882
19797
  }
19883
19798
  /**
19884
- * `edit` edits this tree with the given nodes.
19799
+ * `createPath` creates path of the given element.
19885
19800
  */
19886
- edit(fromIdx, toIdx, content, splitLevel = 0) {
19887
- if (!this.context || !this.tree) {
19888
- throw new YorkieError(
19889
- Code.ErrNotInitialized,
19890
- "Tree is not initialized yet"
19891
- );
19801
+ createPath(createdAt) {
19802
+ return this.createSubPaths(createdAt).join(".");
19803
+ }
19804
+ /**
19805
+ * `registerElement` registers the given element and its descendants to hash table.
19806
+ */
19807
+ registerElement(element, parent) {
19808
+ this.elementPairMapByCreatedAt.set(element.getCreatedAt().toIDString(), {
19809
+ parent,
19810
+ element
19811
+ });
19812
+ addDataSizes(this.docSize.live, element.getDataSize());
19813
+ if (element instanceof CRDTContainer) {
19814
+ element.getDescendants((elem, par) => {
19815
+ this.elementPairMapByCreatedAt.set(elem.getCreatedAt().toIDString(), {
19816
+ parent: par,
19817
+ element: elem
19818
+ });
19819
+ addDataSizes(this.docSize.live, elem.getDataSize());
19820
+ return false;
19821
+ });
19892
19822
  }
19893
- if (fromIdx > toIdx) {
19894
- throw new YorkieError(
19895
- Code.ErrInvalidArgument,
19896
- "from should be less than or equal to to"
19897
- );
19823
+ }
19824
+ /**
19825
+ * `deregisterElement` deregister the given element and its descendants from hash table.
19826
+ */
19827
+ deregisterElement(element) {
19828
+ let count = 0;
19829
+ const deregisterElementInternal = (elem) => {
19830
+ const createdAt = elem.getCreatedAt().toIDString();
19831
+ subDataSize(this.docSize.gc, elem.getDataSize());
19832
+ this.elementPairMapByCreatedAt.delete(createdAt);
19833
+ this.gcElementSetByCreatedAt.delete(createdAt);
19834
+ count++;
19835
+ };
19836
+ deregisterElementInternal(element);
19837
+ if (element instanceof CRDTContainer) {
19838
+ element.getDescendants((e) => {
19839
+ deregisterElementInternal(e);
19840
+ return false;
19841
+ });
19898
19842
  }
19899
- const fromPos = this.tree.findPos(fromIdx);
19900
- const toPos = this.tree.findPos(toIdx);
19901
- return this.editInternal(
19902
- fromPos,
19903
- toPos,
19904
- content ? [content] : [],
19905
- splitLevel
19906
- );
19843
+ return count;
19907
19844
  }
19908
19845
  /**
19909
- * `editBulk` edits this tree with the given nodes.
19846
+ * `registerRemovedElement` registers the given element to the hash set.
19910
19847
  */
19911
- editBulk(fromIdx, toIdx, contents, splitLevel = 0) {
19912
- if (!this.context || !this.tree) {
19913
- throw new YorkieError(
19914
- Code.ErrNotInitialized,
19915
- "Tree is not initialized yet"
19916
- );
19848
+ registerRemovedElement(element) {
19849
+ addDataSizes(this.docSize.gc, element.getDataSize());
19850
+ subDataSize(this.docSize.live, element.getDataSize());
19851
+ this.docSize.live.meta += TimeTicketSize;
19852
+ this.gcElementSetByCreatedAt.add(element.getCreatedAt().toIDString());
19853
+ }
19854
+ /**
19855
+ * `registerGCPair` registers the given pair to hash table.
19856
+ */
19857
+ registerGCPair(pair) {
19858
+ const prev = this.gcPairMap.get(pair.child.toIDString());
19859
+ if (prev) {
19860
+ this.gcPairMap.delete(pair.child.toIDString());
19861
+ return;
19917
19862
  }
19918
- if (fromIdx > toIdx) {
19919
- throw new YorkieError(
19920
- Code.ErrInvalidArgument,
19921
- "from should be less than or equal to to"
19922
- );
19863
+ this.gcPairMap.set(pair.child.toIDString(), pair);
19864
+ const size = this.gcPairMap.get(pair.child.toIDString()).child.getDataSize();
19865
+ addDataSizes(this.docSize.gc, size);
19866
+ subDataSize(this.docSize.live, size);
19867
+ if (!(pair.child instanceof RHTNode)) {
19868
+ this.docSize.live.meta += TimeTicketSize;
19923
19869
  }
19924
- const fromPos = this.tree.findPos(fromIdx);
19925
- const toPos = this.tree.findPos(toIdx);
19926
- return this.editInternal(fromPos, toPos, contents, splitLevel);
19927
19870
  }
19928
19871
  /**
19929
- * `toXML` returns the XML string of this tree.
19872
+ * `getElementMapSize` returns the size of element map.
19930
19873
  */
19931
- toXML() {
19932
- if (!this.context || !this.tree) {
19933
- throw new YorkieError(
19934
- Code.ErrNotInitialized,
19935
- "Tree is not initialized yet"
19936
- );
19937
- }
19938
- return this.tree.toXML();
19874
+ getElementMapSize() {
19875
+ return this.elementPairMapByCreatedAt.size;
19939
19876
  }
19940
19877
  /**
19941
- * `toJSON` returns the JSON string of this tree.
19878
+ * `getGarbageElementSetSize()` returns the size of removed element set.
19942
19879
  */
19943
- toJSON() {
19944
- if (!this.context || !this.tree) {
19945
- throw new YorkieError(
19946
- Code.ErrNotInitialized,
19947
- "Tree is not initialized yet"
19948
- );
19880
+ getGarbageElementSetSize() {
19881
+ const seen = /* @__PURE__ */ new Set();
19882
+ for (const createdAt of this.gcElementSetByCreatedAt) {
19883
+ seen.add(createdAt);
19884
+ const pair = this.elementPairMapByCreatedAt.get(createdAt);
19885
+ if (pair.element instanceof CRDTContainer) {
19886
+ pair.element.getDescendants((el) => {
19887
+ seen.add(el.getCreatedAt().toIDString());
19888
+ return false;
19889
+ });
19890
+ }
19949
19891
  }
19950
- return this.tree.toJSON();
19892
+ return seen.size;
19951
19893
  }
19952
19894
  /**
19953
- * `toJSForTest` returns value with meta data for testing.
19954
- * @internal
19895
+ * `getObject` returns root object.
19955
19896
  */
19956
- toJSForTest() {
19957
- if (!this.context || !this.tree) {
19958
- throw new YorkieError(
19959
- Code.ErrNotInitialized,
19960
- "Tree is not initialized yet"
19961
- );
19962
- }
19963
- return this.tree.toJSForTest();
19897
+ getObject() {
19898
+ return this.rootObject;
19964
19899
  }
19965
19900
  /**
19966
- * `toJSInfoForTest` returns detailed TreeNode information for use in Devtools.
19967
- *
19968
- * @internal
19901
+ * `getGarbageLen` returns length of nodes which can be garbage collected.
19969
19902
  */
19970
- toJSInfoForTest() {
19971
- if (!this.context || !this.tree) {
19972
- throw new YorkieError(
19973
- Code.ErrNotInitialized,
19974
- "Tree is not initialized yet"
19975
- );
19976
- }
19977
- return this.tree.toJSInfoForTest();
19903
+ getGarbageLen() {
19904
+ return this.getGarbageElementSetSize() + this.gcPairMap.size;
19978
19905
  }
19979
19906
  /**
19980
- * `getRootTreeNode` returns TreeNode of this tree.
19907
+ * `getDocSize` returns the size of the document.
19981
19908
  */
19982
- getRootTreeNode() {
19983
- if (!this.context || !this.tree) {
19984
- throw new YorkieError(
19985
- Code.ErrNotInitialized,
19986
- "Tree is not initialized yet"
19987
- );
19988
- }
19989
- return this.tree.getRootTreeNode();
19909
+ getDocSize() {
19910
+ return this.docSize;
19990
19911
  }
19991
19912
  /**
19992
- * `indexToPath` returns the path of the given index.
19913
+ * `deepcopy` copies itself deeply.
19993
19914
  */
19994
- indexToPath(index) {
19995
- if (!this.context || !this.tree) {
19996
- throw new YorkieError(
19997
- Code.ErrNotInitialized,
19998
- "Tree is not initialized yet"
19999
- );
20000
- }
20001
- return this.tree.indexToPath(index);
19915
+ deepcopy() {
19916
+ return new CRDTRoot(this.rootObject.deepcopy());
20002
19917
  }
20003
19918
  /**
20004
- * `pathToIndex` returns the index of given path.
19919
+ * `garbageCollect` purges elements that were removed before the given time.
20005
19920
  */
20006
- pathToIndex(path) {
20007
- if (!this.context || !this.tree) {
20008
- throw new YorkieError(
20009
- Code.ErrNotInitialized,
20010
- "Tree is not initialized yet"
20011
- );
19921
+ garbageCollect(minSyncedVersionVector) {
19922
+ let count = 0;
19923
+ for (const createdAt of this.gcElementSetByCreatedAt) {
19924
+ const pair = this.elementPairMapByCreatedAt.get(createdAt);
19925
+ const removedAt = pair.element.getRemovedAt();
19926
+ if (removedAt && (minSyncedVersionVector == null ? void 0 : minSyncedVersionVector.afterOrEqual(removedAt))) {
19927
+ pair.parent.purge(pair.element);
19928
+ count += this.deregisterElement(pair.element);
19929
+ }
19930
+ }
19931
+ for (const [, pair] of this.gcPairMap) {
19932
+ const removedAt = pair.child.getRemovedAt();
19933
+ if (removedAt && (minSyncedVersionVector == null ? void 0 : minSyncedVersionVector.afterOrEqual(removedAt))) {
19934
+ pair.parent.purge(pair.child);
19935
+ subDataSize(this.docSize.gc, pair.child.getDataSize());
19936
+ this.gcPairMap.delete(pair.child.toIDString());
19937
+ count += 1;
19938
+ }
20012
19939
  }
20013
- return this.tree.pathToIndex(path);
19940
+ return count;
20014
19941
  }
20015
19942
  /**
20016
- * `pathRangeToPosRange` converts the path range into the position range.
19943
+ * `toJSON` returns the JSON encoding of this root object.
20017
19944
  */
20018
- pathRangeToPosRange(range) {
20019
- if (!this.context || !this.tree) {
20020
- throw new YorkieError(
20021
- Code.ErrNotInitialized,
20022
- "Tree is not initialized yet"
20023
- );
20024
- }
20025
- const indexRange = [
20026
- this.tree.pathToIndex(range[0]),
20027
- this.tree.pathToIndex(range[1])
20028
- ];
20029
- const posRange = this.tree.indexRangeToPosRange(indexRange);
20030
- return [posRange[0].toStruct(), posRange[1].toStruct()];
19945
+ toJSON() {
19946
+ return this.rootObject.toJSON();
20031
19947
  }
20032
19948
  /**
20033
- * `indexRangeToPosRange` converts the index range into the position range.
19949
+ * `toSortedJSON` returns the sorted JSON encoding of this root object.
20034
19950
  */
20035
- indexRangeToPosRange(range) {
20036
- if (!this.context || !this.tree) {
20037
- throw new YorkieError(
20038
- Code.ErrNotInitialized,
20039
- "Tree is not initialized yet"
20040
- );
20041
- }
20042
- return this.tree.indexRangeToPosStructRange(range);
19951
+ toSortedJSON() {
19952
+ return this.rootObject.toSortedJSON();
20043
19953
  }
20044
19954
  /**
20045
- * `posRangeToIndexRange` converts the position range into the index range.
19955
+ * `getStats` returns the current statistics of the root object.
19956
+ * This includes counts of various types of elements and structural information.
20046
19957
  */
20047
- posRangeToIndexRange(range) {
20048
- if (!this.context || !this.tree) {
20049
- throw new YorkieError(
20050
- Code.ErrNotInitialized,
20051
- "Tree is not initialized yet"
20052
- );
20053
- }
20054
- const posRange = [
20055
- CRDTTreePos.fromStruct(range[0]),
20056
- CRDTTreePos.fromStruct(range[1])
20057
- ];
20058
- return this.tree.posRangeToIndexRange(posRange);
19958
+ getStats() {
19959
+ return {
19960
+ elements: this.getElementMapSize(),
19961
+ gcPairs: this.gcPairMap.size,
19962
+ gcElements: this.getGarbageElementSetSize()
19963
+ };
20059
19964
  }
20060
19965
  /**
20061
- * `posRangeToPathRange` converts the position range into the path range.
19966
+ * `acc` accumulates the given DataSize to Live.
20062
19967
  */
20063
- posRangeToPathRange(range) {
20064
- if (!this.context || !this.tree) {
20065
- throw new YorkieError(
20066
- Code.ErrNotInitialized,
20067
- "Tree is not initialized yet"
20068
- );
20069
- }
20070
- const posRange = [
20071
- CRDTTreePos.fromStruct(range[0]),
20072
- CRDTTreePos.fromStruct(range[1])
20073
- ];
20074
- return this.tree.posRangeToPathRange(posRange);
20075
- }
20076
- }
20077
- function createJSON(context, target) {
20078
- return createJSONObject(context, target);
20079
- }
20080
- function toWrappedElement(context, elem) {
20081
- if (!elem) {
20082
- return;
20083
- } else if (elem instanceof Primitive) {
20084
- return elem;
20085
- } else if (elem instanceof CRDTObject) {
20086
- return createJSONObject(context, elem);
20087
- } else if (elem instanceof CRDTArray) {
20088
- return createJSONArray(context, elem);
20089
- } else if (elem instanceof CRDTText) {
20090
- return new Text(context, elem);
20091
- } else if (elem instanceof CRDTCounter) {
20092
- const counter = new Counter(CounterType.Int, 0);
20093
- counter.initialize(context, elem);
20094
- return counter;
20095
- } else if (elem instanceof CRDTTree) {
20096
- const tree = new Tree();
20097
- tree.initialize(context, elem);
20098
- return tree;
20099
- }
20100
- throw new TypeError(`Unsupported type of element: ${typeof elem}`);
20101
- }
20102
- function toJSONElement(context, elem) {
20103
- const wrappedElement = toWrappedElement(context, elem);
20104
- if (wrappedElement instanceof Primitive) {
20105
- return wrappedElement.getValue();
20106
- }
20107
- return wrappedElement;
20108
- }
20109
- function buildCRDTElement(context, value, createdAt) {
20110
- let element;
20111
- if (Primitive.isSupport(value)) {
20112
- element = Primitive.of(value, createdAt);
20113
- } else if (Array.isArray(value)) {
20114
- element = CRDTArray.create(
20115
- createdAt,
20116
- ArrayProxy.buildArrayElements(context, value)
20117
- );
20118
- } else if (typeof value === "object") {
20119
- if (value instanceof Text) {
20120
- element = CRDTText.create(RGATreeSplit.create(), createdAt);
20121
- value.initialize(context, element);
20122
- } else if (value instanceof Counter) {
20123
- element = CRDTCounter.create(
20124
- value.getValueType(),
20125
- value.getValue(),
20126
- createdAt
20127
- );
20128
- value.initialize(context, element);
20129
- } else if (value instanceof Tree) {
20130
- element = CRDTTree.create(value.buildRoot(context), createdAt);
20131
- value.initialize(context, element);
20132
- } else {
20133
- element = CRDTObject.create(
20134
- createdAt,
20135
- ObjectProxy.buildObjectMembers(context, value)
20136
- );
20137
- }
20138
- } else {
20139
- throw new TypeError(`Unsupported type of value: ${typeof value}`);
19968
+ acc(diff) {
19969
+ addDataSizes(this.docSize.live, diff);
20140
19970
  }
20141
- return element;
20142
19971
  }
20143
19972
  class Presence {
20144
19973
  constructor(changeContext, presence) {
@@ -20168,7 +19997,6 @@ class Presence {
20168
19997
  }
20169
19998
  /**
20170
19999
  * `clear` clears the presence.
20171
- * @internal
20172
20000
  */
20173
20001
  clear() {
20174
20002
  this.presence = {};
@@ -20270,6 +20098,156 @@ class History {
20270
20098
  replace(this.redoStack);
20271
20099
  }
20272
20100
  }
20101
+ function validateYorkieRuleset(data, ruleset) {
20102
+ const errors = [];
20103
+ for (const rule of ruleset) {
20104
+ const value = getValueByPath(data, rule.path);
20105
+ const result = validateValue(value, rule);
20106
+ if (!result.valid) {
20107
+ for (const error of result.errors || []) {
20108
+ errors.push(error);
20109
+ }
20110
+ }
20111
+ }
20112
+ return {
20113
+ valid: errors.length === 0,
20114
+ errors
20115
+ };
20116
+ }
20117
+ function getValueByPath(obj, path) {
20118
+ if (!path.startsWith("$")) {
20119
+ throw new Error(`Path must start with $, got ${path}`);
20120
+ }
20121
+ const keys = path.split(".");
20122
+ let current = obj;
20123
+ for (let i = 1; i < keys.length; i++) {
20124
+ const key = keys[i];
20125
+ if (!(current instanceof CRDTObject)) {
20126
+ return void 0;
20127
+ }
20128
+ current = current.get(key);
20129
+ }
20130
+ return current;
20131
+ }
20132
+ function validateValue(value, rule) {
20133
+ switch (rule.type) {
20134
+ case "string":
20135
+ case "boolean":
20136
+ case "integer":
20137
+ case "double":
20138
+ case "long":
20139
+ case "date":
20140
+ case "bytes":
20141
+ case "null":
20142
+ return validatePrimitiveValue(value, rule);
20143
+ case "object":
20144
+ if (!(value instanceof CRDTObject)) {
20145
+ return {
20146
+ valid: false,
20147
+ errors: [
20148
+ {
20149
+ path: rule.path,
20150
+ message: `expected object at path ${rule.path}`
20151
+ }
20152
+ ]
20153
+ };
20154
+ }
20155
+ break;
20156
+ case "array":
20157
+ if (!(value instanceof CRDTArray)) {
20158
+ return {
20159
+ valid: false,
20160
+ errors: [
20161
+ {
20162
+ path: rule.path,
20163
+ message: `expected array at path ${rule.path}`
20164
+ }
20165
+ ]
20166
+ };
20167
+ }
20168
+ break;
20169
+ case "yorkie.Text":
20170
+ if (!(value instanceof CRDTText)) {
20171
+ return {
20172
+ valid: false,
20173
+ errors: [
20174
+ {
20175
+ path: rule.path,
20176
+ message: `expected yorkie.Text at path ${rule.path}`
20177
+ }
20178
+ ]
20179
+ };
20180
+ }
20181
+ break;
20182
+ case "yorkie.Tree":
20183
+ if (!(value instanceof CRDTTree)) {
20184
+ return {
20185
+ valid: false,
20186
+ errors: [
20187
+ {
20188
+ path: rule.path,
20189
+ message: `expected yorkie.Tree at path ${rule.path}`
20190
+ }
20191
+ ]
20192
+ };
20193
+ }
20194
+ break;
20195
+ case "yorkie.Counter":
20196
+ if (!(value instanceof CRDTCounter)) {
20197
+ return {
20198
+ valid: false,
20199
+ errors: [
20200
+ {
20201
+ path: rule.path,
20202
+ message: `expected yorkie.Counter at path ${rule.path}`
20203
+ }
20204
+ ]
20205
+ };
20206
+ }
20207
+ break;
20208
+ default:
20209
+ throw new Error(`Unknown rule type: ${rule.type}`);
20210
+ }
20211
+ return {
20212
+ valid: true
20213
+ };
20214
+ }
20215
+ function getPrimitiveType(type) {
20216
+ switch (type) {
20217
+ case "null":
20218
+ return PrimitiveType.Null;
20219
+ case "boolean":
20220
+ return PrimitiveType.Boolean;
20221
+ case "integer":
20222
+ return PrimitiveType.Integer;
20223
+ case "long":
20224
+ return PrimitiveType.Long;
20225
+ case "double":
20226
+ return PrimitiveType.Double;
20227
+ case "string":
20228
+ return PrimitiveType.String;
20229
+ case "bytes":
20230
+ return PrimitiveType.Bytes;
20231
+ case "date":
20232
+ return PrimitiveType.Date;
20233
+ default:
20234
+ throw new Error(`Unknown primitive type: ${type}`);
20235
+ }
20236
+ }
20237
+ function validatePrimitiveValue(value, rule) {
20238
+ if (value instanceof Primitive && value.getType() === getPrimitiveType(rule.type)) {
20239
+ return { valid: true };
20240
+ }
20241
+ return {
20242
+ valid: false,
20243
+ errors: [
20244
+ {
20245
+ path: rule.path,
20246
+ message: `expected ${rule.type} at path ${rule.path}`
20247
+ }
20248
+ ]
20249
+ };
20250
+ }
20273
20251
  const EventSourceDevPanel = "yorkie-devtools-panel";
20274
20252
  const EventSourceSDK = "yorkie-devtools-sdk";
20275
20253
  function isDocEventForReplay(event) {
@@ -20405,57 +20383,44 @@ class Document {
20405
20383
  __publicField(this, "key");
20406
20384
  __publicField(this, "status");
20407
20385
  __publicField(this, "opts");
20386
+ __publicField(this, "maxSizeLimit");
20387
+ __publicField(this, "schemaRules");
20408
20388
  __publicField(this, "changeID");
20409
20389
  __publicField(this, "checkpoint");
20410
20390
  __publicField(this, "localChanges");
20411
- __publicField(this, "maxSizeLimit");
20412
- __publicField(this, "schemaRules");
20413
20391
  __publicField(this, "root");
20392
+ __publicField(this, "presences");
20414
20393
  __publicField(this, "clone");
20394
+ __publicField(this, "internalHistory");
20395
+ __publicField(this, "isUpdating");
20396
+ __publicField(this, "onlineClients");
20415
20397
  __publicField(this, "eventStream");
20416
20398
  __publicField(this, "eventStreamObserver");
20417
- /**
20418
- * `onlineClients` is a set of client IDs that are currently online.
20419
- */
20420
- __publicField(this, "onlineClients");
20421
- /**
20422
- * `presences` is a map of client IDs to their presence information.
20423
- */
20424
- __publicField(this, "presences");
20425
20399
  /**
20426
20400
  * `history` is exposed to the user to manage undo/redo operations.
20427
20401
  */
20428
20402
  __publicField(this, "history");
20429
- /**
20430
- * `internalHistory` is used to manage undo/redo operations internally.
20431
- */
20432
- __publicField(this, "internalHistory");
20433
- /**
20434
- * `isUpdating` is whether the document is updating by updater or not. It is
20435
- * used to prevent the updater from calling undo/redo.
20436
- */
20437
- __publicField(this, "isUpdating");
20438
- this.opts = opts || {};
20439
20403
  this.key = key;
20440
20404
  this.status = "detached";
20441
- this.root = CRDTRoot.create();
20405
+ this.opts = opts || {};
20406
+ this.maxSizeLimit = 0;
20407
+ this.schemaRules = [];
20442
20408
  this.changeID = InitialChangeID;
20443
20409
  this.checkpoint = InitialCheckpoint;
20444
20410
  this.localChanges = [];
20445
- this.maxSizeLimit = 0;
20446
- this.schemaRules = [];
20447
- this.eventStream = createObservable((observer) => {
20448
- this.eventStreamObserver = observer;
20449
- });
20450
- this.onlineClients = /* @__PURE__ */ new Set();
20411
+ this.root = CRDTRoot.create();
20451
20412
  this.presences = /* @__PURE__ */ new Map();
20452
- this.isUpdating = false;
20413
+ this.onlineClients = /* @__PURE__ */ new Set();
20453
20414
  this.internalHistory = new History();
20415
+ this.isUpdating = false;
20416
+ this.eventStream = createObservable(
20417
+ (observer) => this.eventStreamObserver = observer
20418
+ );
20454
20419
  this.history = {
20455
- canUndo: this.canUndo.bind(this),
20456
- canRedo: this.canRedo.bind(this),
20457
- undo: this.undo.bind(this),
20458
- redo: this.redo.bind(this)
20420
+ canUndo: () => this.internalHistory.hasUndo() && !this.isUpdating,
20421
+ canRedo: () => this.internalHistory.hasRedo() && !this.isUpdating,
20422
+ undo: () => this.executeUndoRedo(true),
20423
+ redo: () => this.executeUndoRedo(false)
20459
20424
  };
20460
20425
  setupDevtools(this);
20461
20426
  }
@@ -20469,7 +20434,7 @@ class Document {
20469
20434
  }
20470
20435
  this.ensureClone();
20471
20436
  const actorID = this.changeID.getActorID();
20472
- const context = ChangeContext.create(
20437
+ const ctx = ChangeContext.create(
20473
20438
  this.changeID,
20474
20439
  this.clone.root,
20475
20440
  this.clone.presences.get(actorID) || {},
@@ -20477,29 +20442,23 @@ class Document {
20477
20442
  );
20478
20443
  try {
20479
20444
  const proxy = createJSON(
20480
- context,
20445
+ ctx,
20481
20446
  this.clone.root.getObject()
20482
20447
  );
20483
20448
  if (!this.presences.has(actorID)) {
20484
20449
  this.clone.presences.set(actorID, {});
20485
20450
  }
20486
20451
  this.isUpdating = true;
20487
- updater(
20488
- proxy,
20489
- new Presence(context, this.clone.presences.get(actorID))
20490
- );
20452
+ updater(proxy, new Presence(ctx, this.clone.presences.get(actorID)));
20491
20453
  } catch (err) {
20492
20454
  this.clone = void 0;
20493
20455
  throw err;
20494
20456
  } finally {
20495
20457
  this.isUpdating = false;
20496
20458
  }
20497
- const schemaRules = this.getSchemaRules();
20498
- if (!context.isPresenceOnlyChange() && schemaRules.length > 0) {
20499
- const result = validateYorkieRuleset(
20500
- (_a2 = this.clone) == null ? void 0 : _a2.root.getObject(),
20501
- schemaRules
20502
- );
20459
+ const rules = this.getSchemaRules();
20460
+ if (!ctx.isPresenceOnlyChange() && rules.length) {
20461
+ const result = validateYorkieRuleset((_a2 = this.clone) == null ? void 0 : _a2.root.getObject(), rules);
20503
20462
  if (!result.valid) {
20504
20463
  this.clone = void 0;
20505
20464
  throw new YorkieError(
@@ -20509,18 +20468,18 @@ class Document {
20509
20468
  }
20510
20469
  }
20511
20470
  const size = totalDocSize((_c2 = this.clone) == null ? void 0 : _c2.root.getDocSize());
20512
- if (!context.isPresenceOnlyChange() && this.maxSizeLimit > 0 && this.maxSizeLimit < size) {
20471
+ if (!ctx.isPresenceOnlyChange() && this.maxSizeLimit > 0 && this.maxSizeLimit < size) {
20513
20472
  this.clone = void 0;
20514
20473
  throw new YorkieError(
20515
20474
  Code.ErrDocumentSizeExceedsLimit,
20516
20475
  `document size exceeded: ${size} > ${this.maxSizeLimit}`
20517
20476
  );
20518
20477
  }
20519
- if (context.hasChange()) {
20478
+ if (ctx.hasChange()) {
20520
20479
  if (logger.isEnabled(LogLevel.Trivial)) {
20521
20480
  logger.trivial(`trying to update a local change: ${this.toJSON()}`);
20522
20481
  }
20523
- const change = context.toChange();
20482
+ const change = ctx.toChange();
20524
20483
  const { opInfos, reverseOps } = change.execute(
20525
20484
  this.root,
20526
20485
  this.presences,
@@ -20534,7 +20493,7 @@ class Document {
20534
20493
  );
20535
20494
  }
20536
20495
  }
20537
- const reversePresence = context.getReversePresence();
20496
+ const reversePresence = ctx.getReversePresence();
20538
20497
  if (reversePresence) {
20539
20498
  reverseOps.push({
20540
20499
  type: "presence",
@@ -20542,15 +20501,15 @@ class Document {
20542
20501
  });
20543
20502
  }
20544
20503
  this.localChanges.push(change);
20545
- if (reverseOps.length > 0) {
20504
+ if (reverseOps.length) {
20546
20505
  this.internalHistory.pushUndo(reverseOps);
20547
20506
  }
20548
- if (opInfos.length > 0) {
20507
+ if (opInfos.length) {
20549
20508
  this.internalHistory.clearRedo();
20550
20509
  }
20551
- this.changeID = context.getNextID();
20510
+ this.changeID = ctx.getNextID();
20552
20511
  const event = [];
20553
- if (opInfos.length > 0) {
20512
+ if (opInfos.length) {
20554
20513
  event.push({
20555
20514
  type: "local-change",
20556
20515
  source: OpSource.Local,
@@ -20772,32 +20731,9 @@ class Document {
20772
20731
  * `publish` triggers an event in this document, which can be received by
20773
20732
  * callback functions from document.subscribe().
20774
20733
  */
20775
- publish(event) {
20734
+ publish(events) {
20776
20735
  if (this.eventStreamObserver) {
20777
- this.eventStreamObserver.next(event);
20778
- }
20779
- }
20780
- isSameElementOrChildOf(elem, parent) {
20781
- if (parent === elem) {
20782
- return true;
20783
- }
20784
- const nodePath = elem.split(".");
20785
- const targetPath = parent.split(".");
20786
- return targetPath.every((path, index) => path === nodePath[index]);
20787
- }
20788
- /**
20789
- * `removePushedLocalChanges` removes local changes that have been applied to
20790
- * the server from the local changes.
20791
- *
20792
- * @param clientSeq - client sequence number to remove local changes before it
20793
- */
20794
- removePushedLocalChanges(clientSeq) {
20795
- while (this.localChanges.length) {
20796
- const change = this.localChanges[0];
20797
- if (change.getID().getClientSeq() > clientSeq) {
20798
- break;
20799
- }
20800
- this.localChanges.shift();
20736
+ this.eventStreamObserver.next(events);
20801
20737
  }
20802
20738
  }
20803
20739
  /**
@@ -20805,13 +20741,9 @@ class Document {
20805
20741
  * 1. Remove local changes applied to server.
20806
20742
  * 2. Update the checkpoint.
20807
20743
  * 3. Do Garbage collection.
20808
- *
20809
- * @param pack - change pack
20810
- * @internal
20811
20744
  */
20812
20745
  applyChangePack(pack) {
20813
- const hasSnapshot = pack.hasSnapshot();
20814
- if (hasSnapshot) {
20746
+ if (pack.hasSnapshot()) {
20815
20747
  this.applySnapshot(
20816
20748
  pack.getCheckpoint().getServerSeq(),
20817
20749
  pack.getVersionVector(),
@@ -20823,7 +20755,7 @@ class Document {
20823
20755
  this.removePushedLocalChanges(pack.getCheckpoint().getClientSeq());
20824
20756
  }
20825
20757
  this.checkpoint = this.checkpoint.forward(pack.getCheckpoint());
20826
- if (!hasSnapshot) {
20758
+ if (!pack.hasSnapshot()) {
20827
20759
  this.garbageCollect(pack.getVersionVector());
20828
20760
  }
20829
20761
  if (pack.getIsRemoved()) {
@@ -20838,16 +20770,12 @@ class Document {
20838
20770
  }
20839
20771
  /**
20840
20772
  * `getCheckpoint` returns the checkpoint of this document.
20841
- *
20842
- * @internal
20843
20773
  */
20844
20774
  getCheckpoint() {
20845
20775
  return this.checkpoint;
20846
20776
  }
20847
20777
  /**
20848
20778
  * `getChangeID` returns the change id of this document.
20849
- *
20850
- * @internal
20851
20779
  */
20852
20780
  getChangeID() {
20853
20781
  return this.changeID;
@@ -20860,8 +20788,6 @@ class Document {
20860
20788
  }
20861
20789
  /**
20862
20790
  * `ensureClone` make a clone of root.
20863
- *
20864
- * @internal
20865
20791
  */
20866
20792
  ensureClone() {
20867
20793
  if (this.clone) {
@@ -20875,8 +20801,6 @@ class Document {
20875
20801
  /**
20876
20802
  * `createChangePack` create change pack of the local changes to send to the
20877
20803
  * remote server.
20878
- *
20879
- * @internal
20880
20804
  */
20881
20805
  createChangePack() {
20882
20806
  const changes = Array.from(this.localChanges);
@@ -20892,8 +20816,6 @@ class Document {
20892
20816
  /**
20893
20817
  * `setActor` sets actor into this document. This is also applied in the local
20894
20818
  * changes the document has.
20895
- *
20896
- * @internal
20897
20819
  */
20898
20820
  setActor(actorID) {
20899
20821
  for (const change of this.localChanges) {
@@ -20927,8 +20849,6 @@ class Document {
20927
20849
  }
20928
20850
  /**
20929
20851
  * `getCloneRoot` returns clone object.
20930
- *
20931
- * @internal
20932
20852
  */
20933
20853
  getCloneRoot() {
20934
20854
  if (!this.clone) {
@@ -20941,12 +20861,12 @@ class Document {
20941
20861
  */
20942
20862
  getRoot() {
20943
20863
  this.ensureClone();
20944
- const context = ChangeContext.create(
20864
+ const ctx = ChangeContext.create(
20945
20865
  this.changeID.next(),
20946
20866
  this.clone.root,
20947
20867
  this.clone.presences.get(this.changeID.getActorID()) || {}
20948
20868
  );
20949
- return createJSON(context, this.clone.root.getObject());
20869
+ return createJSON(ctx, this.clone.root.getObject());
20950
20870
  }
20951
20871
  /**
20952
20872
  * `getDocSize` returns the size of this document.
@@ -20980,8 +20900,6 @@ class Document {
20980
20900
  }
20981
20901
  /**
20982
20902
  * `garbageCollect` purges elements that were removed before the given time.
20983
- *
20984
- * @internal
20985
20903
  */
20986
20904
  garbageCollect(minSyncedVersionVector) {
20987
20905
  if (this.opts.disableGC) {
@@ -20994,16 +20912,12 @@ class Document {
20994
20912
  }
20995
20913
  /**
20996
20914
  * `getRootObject` returns root object.
20997
- *
20998
- * @internal
20999
20915
  */
21000
20916
  getRootObject() {
21001
20917
  return this.root.getObject();
21002
20918
  }
21003
20919
  /**
21004
20920
  * `getGarbageLen` returns the length of elements should be purged.
21005
- *
21006
- * @internal
21007
20921
  */
21008
20922
  getGarbageLen() {
21009
20923
  return this.root.getGarbageLen();
@@ -21098,13 +21012,13 @@ class Document {
21098
21012
  applyChange(change, source) {
21099
21013
  this.ensureClone();
21100
21014
  change.execute(this.clone.root, this.clone.presences, source);
21101
- const event = [];
21015
+ const events = [];
21102
21016
  const actorID = change.getID().getActorID();
21103
21017
  if (change.hasPresenceChange() && this.onlineClients.has(actorID)) {
21104
21018
  const presenceChange = change.getPresenceChange();
21105
21019
  switch (presenceChange.type) {
21106
21020
  case PresenceChangeType.Put:
21107
- event.push(
21021
+ events.push(
21108
21022
  this.presences.has(actorID) ? {
21109
21023
  type: "presence-changed",
21110
21024
  source,
@@ -21123,7 +21037,7 @@ class Document {
21123
21037
  );
21124
21038
  break;
21125
21039
  case PresenceChangeType.Clear:
21126
- event.push({
21040
+ events.push({
21127
21041
  type: "unwatched",
21128
21042
  source: OpSource.Remote,
21129
21043
  value: {
@@ -21137,9 +21051,9 @@ class Document {
21137
21051
  }
21138
21052
  const { opInfos } = change.execute(this.root, this.presences, source);
21139
21053
  this.changeID = this.changeID.syncClocks(change.getID());
21140
- if (opInfos.length > 0) {
21054
+ if (opInfos.length) {
21141
21055
  const rawChange = this.isEnableDevtools() ? change.toStruct() : void 0;
21142
- event.push(
21056
+ events.push(
21143
21057
  source === OpSource.Remote ? {
21144
21058
  type: "remote-change",
21145
21059
  source,
@@ -21165,8 +21079,8 @@ class Document {
21165
21079
  }
21166
21080
  );
21167
21081
  }
21168
- if (event.length > 0) {
21169
- this.publish(event);
21082
+ if (events.length) {
21083
+ this.publish(events);
21170
21084
  }
21171
21085
  }
21172
21086
  /**
@@ -21194,14 +21108,14 @@ class Document {
21194
21108
  }
21195
21109
  if (resp.body.case === "event") {
21196
21110
  const { type, publisher } = resp.body.value;
21197
- const event = [];
21111
+ const events = [];
21198
21112
  if (type === DocEventType$1.DOCUMENT_WATCHED) {
21199
21113
  if (this.onlineClients.has(publisher) && this.hasPresence(publisher)) {
21200
21114
  return;
21201
21115
  }
21202
21116
  this.addOnlineClient(publisher);
21203
21117
  if (this.hasPresence(publisher)) {
21204
- event.push({
21118
+ events.push({
21205
21119
  type: "watched",
21206
21120
  source: OpSource.Remote,
21207
21121
  value: {
@@ -21214,7 +21128,7 @@ class Document {
21214
21128
  const presence = this.getPresence(publisher);
21215
21129
  this.removeOnlineClient(publisher);
21216
21130
  if (presence) {
21217
- event.push({
21131
+ events.push({
21218
21132
  type: "unwatched",
21219
21133
  source: OpSource.Remote,
21220
21134
  value: { clientID: publisher, presence }
@@ -21224,7 +21138,7 @@ class Document {
21224
21138
  if (resp.body.value.body) {
21225
21139
  const { topic, payload } = resp.body.value.body;
21226
21140
  const decoder = new TextDecoder();
21227
- event.push({
21141
+ events.push({
21228
21142
  type: "broadcast",
21229
21143
  value: {
21230
21144
  clientID: publisher,
@@ -21234,8 +21148,8 @@ class Document {
21234
21148
  });
21235
21149
  }
21236
21150
  }
21237
- if (event.length > 0) {
21238
- this.publish(event);
21151
+ if (events.length) {
21152
+ this.publish(events);
21239
21153
  }
21240
21154
  }
21241
21155
  }
@@ -21256,62 +21170,51 @@ class Document {
21256
21170
  ]);
21257
21171
  }
21258
21172
  /**
21259
- * `applyDocEventForReplay` applies the given event into this document.
21173
+ * `applyDocEventsForReplay` applies the given events into this document.
21260
21174
  */
21261
- applyDocEventForReplay(event) {
21262
- if (event.type === "status-changed") {
21263
- this.applyStatus(event.value.status);
21264
- if (event.value.status === "attached") {
21265
- this.setActor(event.value.actorID);
21175
+ applyDocEventsForReplay(events) {
21176
+ for (const event of events) {
21177
+ if (event.type === "status-changed") {
21178
+ this.applyStatus(event.value.status);
21179
+ if (event.value.status === "attached") {
21180
+ this.setActor(event.value.actorID);
21181
+ }
21182
+ continue;
21266
21183
  }
21267
- return;
21268
- }
21269
- if (event.type === "snapshot") {
21270
- const { snapshot, serverSeq, snapshotVector } = event.value;
21271
- if (!snapshot) return;
21272
- this.applySnapshot(
21273
- BigInt(serverSeq),
21274
- converter.hexToVersionVector(snapshotVector),
21275
- converter.hexToBytes(snapshot)
21276
- );
21277
- return;
21278
- }
21279
- if (event.type === "local-change" || event.type === "remote-change") {
21280
- if (!event.rawChange) return;
21281
- const change = Change.fromStruct(event.rawChange);
21282
- this.applyChange(change, event.source);
21283
- }
21284
- if (event.type === "initialized") {
21285
- const onlineClients = /* @__PURE__ */ new Set();
21286
- for (const { clientID, presence } of event.value) {
21287
- onlineClients.add(clientID);
21184
+ if (event.type === "snapshot") {
21185
+ const { snapshot, serverSeq, snapshotVector } = event.value;
21186
+ if (!snapshot) continue;
21187
+ this.applySnapshot(
21188
+ BigInt(serverSeq),
21189
+ converter.hexToVersionVector(snapshotVector),
21190
+ converter.hexToBytes(snapshot)
21191
+ );
21192
+ continue;
21193
+ }
21194
+ if (event.type === "local-change" || event.type === "remote-change") {
21195
+ if (!event.rawChange) continue;
21196
+ const change = Change.fromStruct(event.rawChange);
21197
+ this.applyChange(change, event.source);
21198
+ }
21199
+ if (event.type === "initialized") {
21200
+ const onlineClients = /* @__PURE__ */ new Set();
21201
+ for (const { clientID, presence } of event.value) {
21202
+ onlineClients.add(clientID);
21203
+ this.presences.set(clientID, presence);
21204
+ }
21205
+ this.setOnlineClients(onlineClients);
21206
+ } else if (event.type === "watched") {
21207
+ const { clientID, presence } = event.value;
21208
+ this.addOnlineClient(clientID);
21209
+ this.presences.set(clientID, presence);
21210
+ } else if (event.type === "unwatched") {
21211
+ const { clientID } = event.value;
21212
+ this.removeOnlineClient(clientID);
21213
+ this.presences.delete(clientID);
21214
+ } else if (event.type === "presence-changed") {
21215
+ const { clientID, presence } = event.value;
21288
21216
  this.presences.set(clientID, presence);
21289
21217
  }
21290
- this.setOnlineClients(onlineClients);
21291
- return;
21292
- }
21293
- if (event.type === "watched") {
21294
- const { clientID, presence } = event.value;
21295
- this.addOnlineClient(clientID);
21296
- this.presences.set(clientID, presence);
21297
- return;
21298
- }
21299
- if (event.type === "unwatched") {
21300
- const { clientID } = event.value;
21301
- this.removeOnlineClient(clientID);
21302
- this.presences.delete(clientID);
21303
- }
21304
- if (event.type === "presence-changed") {
21305
- const { clientID, presence } = event.value;
21306
- this.presences.set(clientID, presence);
21307
- }
21308
- }
21309
- /**
21310
- * `applyDocEventsForReplay` applies the given events into this document.
21311
- */
21312
- applyDocEventsForReplay(event) {
21313
- for (const docEvent of event) {
21314
- this.applyDocEventForReplay(docEvent);
21315
21218
  }
21316
21219
  }
21317
21220
  /**
@@ -21324,51 +21227,41 @@ class Document {
21324
21227
  `path must start with "$"`
21325
21228
  );
21326
21229
  }
21327
- const pathArr = path.split(".");
21328
- pathArr.shift();
21230
+ const paths = path.split(".");
21231
+ paths.shift();
21329
21232
  let value = this.getRoot();
21330
- for (const key of pathArr) {
21233
+ for (const key of paths) {
21331
21234
  value = value[key];
21332
- if (value === void 0) return void 0;
21235
+ if (!value) return void 0;
21333
21236
  }
21334
21237
  return value;
21335
21238
  }
21336
21239
  /**
21337
21240
  * `setOnlineClients` sets the given online client set.
21338
- *
21339
- * @internal
21340
21241
  */
21341
21242
  setOnlineClients(onlineClients) {
21342
21243
  this.onlineClients = onlineClients;
21343
21244
  }
21344
21245
  /**
21345
21246
  * `resetOnlineClients` resets the online client set.
21346
- *
21347
- * @internal
21348
21247
  */
21349
21248
  resetOnlineClients() {
21350
21249
  this.onlineClients = /* @__PURE__ */ new Set();
21351
21250
  }
21352
21251
  /**
21353
21252
  * `addOnlineClient` adds the given clientID into the online client set.
21354
- *
21355
- * @internal
21356
21253
  */
21357
21254
  addOnlineClient(clientID) {
21358
21255
  this.onlineClients.add(clientID);
21359
21256
  }
21360
21257
  /**
21361
21258
  * `removeOnlineClient` removes the clientID from the online client set.
21362
- *
21363
- * @internal
21364
21259
  */
21365
21260
  removeOnlineClient(clientID) {
21366
21261
  this.onlineClients.delete(clientID);
21367
21262
  }
21368
21263
  /**
21369
21264
  * `hasPresence` returns whether the given clientID has a presence or not.
21370
- *
21371
- * @internal
21372
21265
  */
21373
21266
  hasPresence(clientID) {
21374
21267
  return this.presences.has(clientID);
@@ -21432,8 +21325,6 @@ class Document {
21432
21325
  /**
21433
21326
  * `getPresenceForTest` returns the presence of the given clientID
21434
21327
  * regardless of whether the client is online or not.
21435
- *
21436
- * @internal
21437
21328
  */
21438
21329
  getPresenceForTest(clientID) {
21439
21330
  const p = this.presences.get(clientID);
@@ -21441,8 +21332,6 @@ class Document {
21441
21332
  }
21442
21333
  /**
21443
21334
  * `getSelfForTest` returns the client that has attached this document.
21444
- *
21445
- * @internal
21446
21335
  */
21447
21336
  getSelfForTest() {
21448
21337
  return {
@@ -21452,193 +21341,135 @@ class Document {
21452
21341
  }
21453
21342
  /**
21454
21343
  * `getOthersForTest` returns all the other clients in online, sorted by clientID.
21455
- *
21456
- * @internal
21457
21344
  */
21458
21345
  getOthersForTest() {
21459
21346
  const myClientID = this.getChangeID().getActorID();
21460
21347
  return this.getPresences().filter((a) => a.clientID !== myClientID).sort((a, b) => a.clientID > b.clientID ? 1 : -1);
21461
21348
  }
21462
21349
  /**
21463
- * `canUndo` returns whether there are any operations to undo.
21350
+ * `getUndoStackForTest` returns the undo stack for test.
21464
21351
  */
21465
- canUndo() {
21466
- return this.internalHistory.hasUndo() && !this.isUpdating;
21352
+ getUndoStackForTest() {
21353
+ return this.internalHistory.getUndoStackForTest();
21467
21354
  }
21468
21355
  /**
21469
- * `canRedo` returns whether there are any operations to redo.
21356
+ * `getRedoStackForTest` returns the redo stack for test.
21470
21357
  */
21471
- canRedo() {
21472
- return this.internalHistory.hasRedo() && !this.isUpdating;
21358
+ getRedoStackForTest() {
21359
+ return this.internalHistory.getRedoStackForTest();
21473
21360
  }
21474
21361
  /**
21475
- * `undo` undoes the last operation executed by the current client.
21476
- * It does not impact operations made by other clients.
21362
+ * `broadcast` the payload to the given topic.
21477
21363
  */
21478
- undo() {
21479
- if (this.isUpdating) {
21480
- throw new YorkieError(
21481
- Code.ErrRefused,
21482
- "Undo is not allowed during an update"
21483
- );
21484
- }
21485
- const undoOps = this.internalHistory.popUndo();
21486
- if (undoOps === void 0) {
21487
- throw new YorkieError(
21488
- Code.ErrRefused,
21489
- "There is no operation to be undone"
21490
- );
21491
- }
21492
- this.ensureClone();
21493
- const context = ChangeContext.create(
21494
- this.changeID,
21495
- this.clone.root,
21496
- this.clone.presences.get(this.changeID.getActorID()) || {}
21497
- );
21498
- for (const undoOp of undoOps) {
21499
- if (!(undoOp instanceof Operation)) {
21500
- const presence = new Presence(
21501
- context,
21502
- deepcopy(this.clone.presences.get(this.changeID.getActorID()))
21503
- );
21504
- presence.set(undoOp.value, { addToHistory: true });
21505
- continue;
21506
- }
21507
- const ticket = context.issueTimeTicket();
21508
- undoOp.setExecutedAt(ticket);
21509
- if (undoOp instanceof ArraySetOperation) {
21510
- const prev = undoOp.getCreatedAt();
21511
- undoOp.getValue().setCreatedAt(ticket);
21512
- this.internalHistory.reconcileCreatedAt(prev, ticket);
21513
- } else if (undoOp instanceof AddOperation) {
21514
- const prev = undoOp.getValue().getCreatedAt();
21515
- undoOp.getValue().setCreatedAt(ticket);
21516
- this.internalHistory.reconcileCreatedAt(prev, ticket);
21364
+ broadcast(topic, payload, options) {
21365
+ this.publish([
21366
+ {
21367
+ type: "local-broadcast",
21368
+ value: { topic, payload },
21369
+ options
21517
21370
  }
21518
- context.push(undoOp);
21519
- }
21520
- const change = context.toChange();
21521
- change.execute(this.clone.root, this.clone.presences, OpSource.UndoRedo);
21522
- const { opInfos, reverseOps } = change.execute(
21523
- this.root,
21524
- this.presences,
21525
- OpSource.UndoRedo
21526
- );
21527
- const reversePresence = context.getReversePresence();
21528
- if (reversePresence) {
21529
- reverseOps.push({
21530
- type: "presence",
21531
- value: reversePresence
21532
- });
21533
- }
21534
- if (reverseOps.length > 0) {
21535
- this.internalHistory.pushRedo(reverseOps);
21536
- }
21537
- if (!change.hasPresenceChange() && opInfos.length === 0) {
21538
- return;
21539
- }
21540
- this.localChanges.push(change);
21541
- this.changeID = context.getNextID();
21542
- const actorID = this.changeID.getActorID();
21543
- const event = [];
21544
- if (opInfos.length > 0) {
21545
- event.push({
21546
- type: "local-change",
21547
- source: OpSource.UndoRedo,
21548
- value: {
21549
- message: change.getMessage() || "",
21550
- operations: opInfos,
21551
- actor: actorID,
21552
- clientSeq: change.getID().getClientSeq(),
21553
- serverSeq: change.getID().getServerSeq()
21554
- },
21555
- rawChange: this.isEnableDevtools() ? change.toStruct() : void 0
21556
- });
21371
+ ]);
21372
+ }
21373
+ /**
21374
+ * `getVersionVector` returns the version vector of document
21375
+ */
21376
+ getVersionVector() {
21377
+ return this.changeID.getVersionVector();
21378
+ }
21379
+ isSameElementOrChildOf(elem, parent) {
21380
+ if (parent === elem) {
21381
+ return true;
21557
21382
  }
21558
- if (change.hasPresenceChange()) {
21559
- event.push({
21560
- type: "presence-changed",
21561
- source: OpSource.UndoRedo,
21562
- value: {
21563
- clientID: actorID,
21564
- presence: this.getPresence(actorID)
21565
- }
21566
- });
21383
+ const nodePath = elem.split(".");
21384
+ return parent.split(".").every((path, index) => path === nodePath[index]);
21385
+ }
21386
+ /**
21387
+ * `removePushedLocalChanges` removes local changes that have been applied to
21388
+ * the server from the local changes.
21389
+ *
21390
+ * @param clientSeq - client sequence number to remove local changes before it
21391
+ */
21392
+ removePushedLocalChanges(clientSeq) {
21393
+ while (this.localChanges.length) {
21394
+ const change = this.localChanges[0];
21395
+ if (change.getID().getClientSeq() > clientSeq) {
21396
+ break;
21397
+ }
21398
+ this.localChanges.shift();
21567
21399
  }
21568
- this.publish(event);
21569
21400
  }
21570
21401
  /**
21571
- * `redo` redoes the last operation executed by the current client.
21572
- * It does not impact operations made by other clients.
21402
+ * `executeUndoRedo` executes undo or redo operation with shared logic.
21573
21403
  */
21574
- redo() {
21404
+ executeUndoRedo(isUndo) {
21575
21405
  if (this.isUpdating) {
21576
21406
  throw new YorkieError(
21577
21407
  Code.ErrRefused,
21578
- "Redo is not allowed during an update"
21408
+ `${isUndo ? "Undo" : "Redo"} is not allowed during an update`
21579
21409
  );
21580
21410
  }
21581
- const redoOps = this.internalHistory.popRedo();
21582
- if (redoOps === void 0) {
21411
+ const ops = isUndo ? this.internalHistory.popUndo() : this.internalHistory.popRedo();
21412
+ if (!ops) {
21583
21413
  throw new YorkieError(
21584
21414
  Code.ErrRefused,
21585
- "There is no operation to be redone"
21415
+ `There is no operation to be ${isUndo ? "undone" : "redone"}`
21586
21416
  );
21587
21417
  }
21588
21418
  this.ensureClone();
21589
- const context = ChangeContext.create(
21419
+ const ctx = ChangeContext.create(
21590
21420
  this.changeID,
21591
21421
  this.clone.root,
21592
21422
  this.clone.presences.get(this.changeID.getActorID()) || {}
21593
21423
  );
21594
- for (const redoOp of redoOps) {
21595
- if (!(redoOp instanceof Operation)) {
21424
+ for (const op of ops) {
21425
+ if (!(op instanceof Operation)) {
21596
21426
  const presence = new Presence(
21597
- context,
21427
+ ctx,
21598
21428
  deepcopy(this.clone.presences.get(this.changeID.getActorID()))
21599
21429
  );
21600
- presence.set(redoOp.value, { addToHistory: true });
21430
+ presence.set(op.value, { addToHistory: true });
21601
21431
  continue;
21602
21432
  }
21603
- const ticket = context.issueTimeTicket();
21604
- redoOp.setExecutedAt(ticket);
21605
- if (redoOp instanceof ArraySetOperation) {
21606
- const prev = redoOp.getCreatedAt();
21607
- redoOp.getValue().setCreatedAt(ticket);
21433
+ const ticket = ctx.issueTimeTicket();
21434
+ op.setExecutedAt(ticket);
21435
+ if (op instanceof ArraySetOperation) {
21436
+ const prev = op.getCreatedAt();
21437
+ op.getValue().setCreatedAt(ticket);
21608
21438
  this.internalHistory.reconcileCreatedAt(prev, ticket);
21609
- } else if (redoOp instanceof AddOperation) {
21610
- const prev = redoOp.getValue().getCreatedAt();
21611
- redoOp.getValue().setCreatedAt(ticket);
21439
+ } else if (op instanceof AddOperation) {
21440
+ const prev = op.getValue().getCreatedAt();
21441
+ op.getValue().setCreatedAt(ticket);
21612
21442
  this.internalHistory.reconcileCreatedAt(prev, ticket);
21613
21443
  }
21614
- context.push(redoOp);
21444
+ ctx.push(op);
21615
21445
  }
21616
- const change = context.toChange();
21446
+ const change = ctx.toChange();
21617
21447
  change.execute(this.clone.root, this.clone.presences, OpSource.UndoRedo);
21618
21448
  const { opInfos, reverseOps } = change.execute(
21619
21449
  this.root,
21620
21450
  this.presences,
21621
21451
  OpSource.UndoRedo
21622
21452
  );
21623
- const reversePresence = context.getReversePresence();
21624
- if (reversePresence) {
21625
- reverseOps.push({
21626
- type: "presence",
21627
- value: reversePresence
21628
- });
21453
+ const reverse = ctx.getReversePresence();
21454
+ if (reverse) {
21455
+ reverseOps.push({ type: "presence", value: reverse });
21629
21456
  }
21630
- if (reverseOps.length > 0) {
21631
- this.internalHistory.pushUndo(reverseOps);
21457
+ if (reverseOps.length) {
21458
+ if (isUndo) {
21459
+ this.internalHistory.pushRedo(reverseOps);
21460
+ } else {
21461
+ this.internalHistory.pushUndo(reverseOps);
21462
+ }
21632
21463
  }
21633
- if (!change.hasPresenceChange() && opInfos.length === 0) {
21464
+ if (!change.hasPresenceChange() && !opInfos.length) {
21634
21465
  return;
21635
21466
  }
21636
21467
  this.localChanges.push(change);
21637
- this.changeID = context.getNextID();
21468
+ this.changeID = ctx.getNextID();
21638
21469
  const actorID = this.changeID.getActorID();
21639
- const event = [];
21640
- if (opInfos.length > 0) {
21641
- event.push({
21470
+ const events = [];
21471
+ if (opInfos.length) {
21472
+ events.push({
21642
21473
  type: "local-change",
21643
21474
  source: OpSource.UndoRedo,
21644
21475
  value: {
@@ -21652,7 +21483,7 @@ class Document {
21652
21483
  });
21653
21484
  }
21654
21485
  if (change.hasPresenceChange()) {
21655
- event.push({
21486
+ events.push({
21656
21487
  type: "presence-changed",
21657
21488
  source: OpSource.UndoRedo,
21658
21489
  value: {
@@ -21661,36 +21492,7 @@ class Document {
21661
21492
  }
21662
21493
  });
21663
21494
  }
21664
- this.publish(event);
21665
- }
21666
- /**
21667
- * `getUndoStackForTest` returns the undo stack for test.
21668
- */
21669
- getUndoStackForTest() {
21670
- return this.internalHistory.getUndoStackForTest();
21671
- }
21672
- /**
21673
- * `getRedoStackForTest` returns the redo stack for test.
21674
- */
21675
- getRedoStackForTest() {
21676
- return this.internalHistory.getRedoStackForTest();
21677
- }
21678
- /**
21679
- * `broadcast` the payload to the given topic.
21680
- */
21681
- broadcast(topic, payload, options) {
21682
- const broadcastEvent = {
21683
- type: "local-broadcast",
21684
- value: { topic, payload },
21685
- options
21686
- };
21687
- this.publish([broadcastEvent]);
21688
- }
21689
- /**
21690
- * `getVersionVector` returns the version vector of document
21691
- */
21692
- getVersionVector() {
21693
- return this.changeID.getVersionVector();
21495
+ this.publish(events);
21694
21496
  }
21695
21497
  }
21696
21498
  function createAuthInterceptor(apiKey, token) {
@@ -21713,7 +21515,7 @@ function createAuthInterceptor(apiKey, token) {
21713
21515
  };
21714
21516
  }
21715
21517
  const name$1 = "@yorkie-js/sdk";
21716
- const version$1 = "0.6.28";
21518
+ const version$1 = "0.6.29";
21717
21519
  const pkg$1 = {
21718
21520
  name: name$1,
21719
21521
  version: version$1
@@ -21811,11 +21613,10 @@ class Client {
21811
21613
  createMetricInterceptor(opts == null ? void 0 : opts.userAgent)
21812
21614
  ],
21813
21615
  fetch: (input, init) => {
21814
- const newInit = {
21616
+ return fetch(input, {
21815
21617
  ...init,
21816
21618
  keepalive: this.keepalive
21817
- };
21818
- return fetch(input, newInit);
21619
+ });
21819
21620
  }
21820
21621
  })
21821
21622
  );
@@ -22609,7 +22410,7 @@ if (typeof globalThis !== "undefined") {
22609
22410
  };
22610
22411
  }
22611
22412
  const name = "@yorkie-js/react";
22612
- const version = "0.6.28";
22413
+ const version = "0.6.29";
22613
22414
  const pkg = {
22614
22415
  name,
22615
22416
  version