@yorkie-js/react 0.7.6 → 0.7.8
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.
- package/README.md +1 -1
- package/dist/yorkie-js-react.d.ts +27 -5
- package/dist/yorkie-js-react.es.js +308 -50
- package/dist/yorkie-js-react.es.js.map +1 -1
- package/dist/yorkie-js-react.js +308 -50
- package/dist/yorkie-js-react.js.map +1 -1
- package/package.json +3 -3
|
@@ -360,7 +360,7 @@ function isScalarZeroValue(type, value) {
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
const IMPLICIT$3 = 2;
|
|
363
|
-
const unsafeLocal = Symbol.for("reflect unsafe local");
|
|
363
|
+
const unsafeLocal = /* @__PURE__ */ Symbol.for("reflect unsafe local");
|
|
364
364
|
function unsafeOneofCase(target, oneof) {
|
|
365
365
|
const c = target[oneof.localName].case;
|
|
366
366
|
if (c === void 0) {
|
|
@@ -586,7 +586,7 @@ function convertObjectValues(obj, fn) {
|
|
|
586
586
|
}
|
|
587
587
|
return ret;
|
|
588
588
|
}
|
|
589
|
-
const tokenZeroMessageField = Symbol();
|
|
589
|
+
const tokenZeroMessageField = /* @__PURE__ */ Symbol();
|
|
590
590
|
const messagePrototypes = /* @__PURE__ */ new WeakMap();
|
|
591
591
|
function createZeroMessage(desc) {
|
|
592
592
|
let msg;
|
|
@@ -688,7 +688,7 @@ class FieldError extends Error {
|
|
|
688
688
|
function isFieldError(arg) {
|
|
689
689
|
return arg instanceof Error && errorNames.includes(arg.name) && "field" in arg && typeof arg.field == "function";
|
|
690
690
|
}
|
|
691
|
-
const symbol = Symbol.for("@bufbuild/protobuf/text-encoding");
|
|
691
|
+
const symbol = /* @__PURE__ */ Symbol.for("@bufbuild/protobuf/text-encoding");
|
|
692
692
|
function getTextEncoding() {
|
|
693
693
|
if (globalThis[symbol] == void 0) {
|
|
694
694
|
const te = new globalThis.TextEncoder();
|
|
@@ -4056,7 +4056,7 @@ function readScalarField(msg, field, json) {
|
|
|
4056
4056
|
msg.set(field, scalarValue);
|
|
4057
4057
|
}
|
|
4058
4058
|
}
|
|
4059
|
-
const tokenIgnoredUnknownEnum = Symbol();
|
|
4059
|
+
const tokenIgnoredUnknownEnum = /* @__PURE__ */ Symbol();
|
|
4060
4060
|
function readEnum(desc, json, ignoreUnknownFields, nullAsZeroValue) {
|
|
4061
4061
|
if (json === null) {
|
|
4062
4062
|
if (desc.typeName == "google.protobuf.NullValue") {
|
|
@@ -4082,7 +4082,7 @@ function readEnum(desc, json, ignoreUnknownFields, nullAsZeroValue) {
|
|
|
4082
4082
|
}
|
|
4083
4083
|
throw new Error(`cannot decode ${desc} from JSON: ${formatVal(json)}`);
|
|
4084
4084
|
}
|
|
4085
|
-
const tokenNull = Symbol();
|
|
4085
|
+
const tokenNull = /* @__PURE__ */ Symbol();
|
|
4086
4086
|
function scalarFromJson(field, json, nullAsZeroValue) {
|
|
4087
4087
|
if (json === null) {
|
|
4088
4088
|
if (nullAsZeroValue) {
|
|
@@ -5903,6 +5903,8 @@ class YorkieError extends Error {
|
|
|
5903
5903
|
this.message = message;
|
|
5904
5904
|
this.toString = () => `[code=${this.code}]: ${this.message}`;
|
|
5905
5905
|
}
|
|
5906
|
+
code;
|
|
5907
|
+
message;
|
|
5906
5908
|
name = "YorkieError";
|
|
5907
5909
|
stack;
|
|
5908
5910
|
}
|
|
@@ -11132,6 +11134,32 @@ class IndexTreeNode {
|
|
|
11132
11134
|
}
|
|
11133
11135
|
actualRight.push(child);
|
|
11134
11136
|
}
|
|
11137
|
+
if (versionVector) {
|
|
11138
|
+
const movedToLeft = [];
|
|
11139
|
+
const remaining = [];
|
|
11140
|
+
let boundaryReached = false;
|
|
11141
|
+
for (const child of actualRight) {
|
|
11142
|
+
if (!boundaryReached) {
|
|
11143
|
+
if (child.insPrevID !== void 0 && !child.isText) {
|
|
11144
|
+
remaining.push(child);
|
|
11145
|
+
continue;
|
|
11146
|
+
}
|
|
11147
|
+
const actorID = child.id.getCreatedAt().getActorID();
|
|
11148
|
+
const knownLamport = versionVector.get(actorID);
|
|
11149
|
+
if (knownLamport === void 0 || knownLamport < child.id.getCreatedAt().getLamport()) {
|
|
11150
|
+
movedToLeft.push(child);
|
|
11151
|
+
continue;
|
|
11152
|
+
}
|
|
11153
|
+
}
|
|
11154
|
+
boundaryReached = true;
|
|
11155
|
+
remaining.push(child);
|
|
11156
|
+
}
|
|
11157
|
+
if (movedToLeft.length > 0) {
|
|
11158
|
+
left.push(...movedToLeft);
|
|
11159
|
+
actualRight.length = 0;
|
|
11160
|
+
actualRight.push(...remaining);
|
|
11161
|
+
}
|
|
11162
|
+
}
|
|
11135
11163
|
this._children = left;
|
|
11136
11164
|
clone._children = actualRight;
|
|
11137
11165
|
this.visibleSize = this._children.reduce(
|
|
@@ -11908,8 +11936,14 @@ class CRDTTreeNode extends IndexTreeNode {
|
|
|
11908
11936
|
split.insPrevID = this.id;
|
|
11909
11937
|
if (this.insNextID) {
|
|
11910
11938
|
const insNext = tree.findFloorNode(this.insNextID);
|
|
11911
|
-
insNext.insPrevID = split.id;
|
|
11912
11939
|
split.insNextID = this.insNextID;
|
|
11940
|
+
if (insNext) {
|
|
11941
|
+
insNext.insPrevID = split.id;
|
|
11942
|
+
if (!this.isText && insNext.parent && !insNext.isRemoved && insNext.parent !== split.parent && split.allChildren.length === 0) {
|
|
11943
|
+
split.parent.detachChild(split);
|
|
11944
|
+
insNext.parent.insertBefore(split, insNext);
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11913
11947
|
}
|
|
11914
11948
|
this.insNextID = split.id;
|
|
11915
11949
|
tree.registerNode(split);
|
|
@@ -12133,7 +12167,7 @@ class CRDTTree extends CRDTElement {
|
|
|
12133
12167
|
* given node, advancing past element-type split siblings that the editing
|
|
12134
12168
|
* client did not know about (not in versionVector).
|
|
12135
12169
|
*/
|
|
12136
|
-
advancePastUnknownSplitSiblings(node, versionVector) {
|
|
12170
|
+
advancePastUnknownSplitSiblings(node, versionVector, relaxParentCheck = false, skipActorID) {
|
|
12137
12171
|
if (!versionVector || !node) {
|
|
12138
12172
|
return node;
|
|
12139
12173
|
}
|
|
@@ -12143,10 +12177,13 @@ class CRDTTree extends CRDTElement {
|
|
|
12143
12177
|
if (!next || next.isText) {
|
|
12144
12178
|
break;
|
|
12145
12179
|
}
|
|
12146
|
-
if (next.parent !== current.parent) {
|
|
12180
|
+
if (!relaxParentCheck && next.parent !== current.parent) {
|
|
12147
12181
|
break;
|
|
12148
12182
|
}
|
|
12149
12183
|
const actorID = next.id.getCreatedAt().getActorID();
|
|
12184
|
+
if (skipActorID !== void 0 && actorID === skipActorID) {
|
|
12185
|
+
break;
|
|
12186
|
+
}
|
|
12150
12187
|
const knownLamport = versionVector.get(actorID);
|
|
12151
12188
|
if (knownLamport !== void 0 && knownLamport >= next.id.getCreatedAt().getLamport()) {
|
|
12152
12189
|
break;
|
|
@@ -12487,6 +12524,25 @@ class CRDTTree extends CRDTElement {
|
|
|
12487
12524
|
addDataSizes(diff, diffTo, diffFrom);
|
|
12488
12525
|
const fromLeft = fromLeftRaw !== fromParent ? this.advancePastUnknownSplitSiblings(fromLeftRaw, versionVector) : fromLeftRaw;
|
|
12489
12526
|
const toLeft = toLeftRaw !== toParent ? this.advancePastUnknownSplitSiblings(toLeftRaw, versionVector) : toLeftRaw;
|
|
12527
|
+
let collectFromParent = fromParent;
|
|
12528
|
+
let collectFromLeft = fromLeft;
|
|
12529
|
+
if (fromLeft !== fromParent && fromParent !== toParent) {
|
|
12530
|
+
let current = fromLeft;
|
|
12531
|
+
while (current.insNextID) {
|
|
12532
|
+
const next = this.findFloorNode(current.insNextID);
|
|
12533
|
+
if (!next || next.isText) {
|
|
12534
|
+
break;
|
|
12535
|
+
}
|
|
12536
|
+
if (next.parent && next.parent === toParent) {
|
|
12537
|
+
if (toLeft !== toParent) {
|
|
12538
|
+
collectFromLeft = next;
|
|
12539
|
+
collectFromParent = toParent;
|
|
12540
|
+
}
|
|
12541
|
+
break;
|
|
12542
|
+
}
|
|
12543
|
+
current = next;
|
|
12544
|
+
}
|
|
12545
|
+
}
|
|
12490
12546
|
const fromIdx = this.toIndex(fromParent, fromLeft);
|
|
12491
12547
|
const fromPath = this.toPath(fromParent, fromLeft);
|
|
12492
12548
|
const nodesToBeRemoved = [];
|
|
@@ -12495,8 +12551,8 @@ class CRDTTree extends CRDTElement {
|
|
|
12495
12551
|
const toBeMergedNodes = [];
|
|
12496
12552
|
const preTombstoned = /* @__PURE__ */ new Set();
|
|
12497
12553
|
this.traverseInPosRange(
|
|
12498
|
-
|
|
12499
|
-
|
|
12554
|
+
collectFromParent,
|
|
12555
|
+
collectFromLeft,
|
|
12500
12556
|
toParent,
|
|
12501
12557
|
toLeft,
|
|
12502
12558
|
([node, tokenType], ended) => {
|
|
@@ -12544,6 +12600,7 @@ class CRDTTree extends CRDTElement {
|
|
|
12544
12600
|
tokensToBeRemoved,
|
|
12545
12601
|
editedAt
|
|
12546
12602
|
);
|
|
12603
|
+
const mergeLevel = toBeMergedNodes.length;
|
|
12547
12604
|
const pairs = [];
|
|
12548
12605
|
for (const node of nodesToBeRemoved) {
|
|
12549
12606
|
if (node.remove(editedAt)) {
|
|
@@ -12600,9 +12657,20 @@ class CRDTTree extends CRDTElement {
|
|
|
12600
12657
|
let parent = fromParent;
|
|
12601
12658
|
let left = fromLeft;
|
|
12602
12659
|
while (splitCount < splitLevel) {
|
|
12660
|
+
if (left !== parent) {
|
|
12661
|
+
left = this.advancePastUnknownSplitSiblings(
|
|
12662
|
+
left,
|
|
12663
|
+
versionVector,
|
|
12664
|
+
true,
|
|
12665
|
+
editedAt.getActorID()
|
|
12666
|
+
);
|
|
12667
|
+
if (left.parent && left.parent !== parent) {
|
|
12668
|
+
parent = left.parent;
|
|
12669
|
+
}
|
|
12670
|
+
}
|
|
12603
12671
|
parent.split(
|
|
12604
12672
|
this,
|
|
12605
|
-
parent.findOffset(left, true) + 1,
|
|
12673
|
+
left !== parent ? parent.findOffset(left, true) + 1 : 0,
|
|
12606
12674
|
issueTimeTicket,
|
|
12607
12675
|
versionVector
|
|
12608
12676
|
);
|
|
@@ -12659,7 +12727,15 @@ class CRDTTree extends CRDTElement {
|
|
|
12659
12727
|
}
|
|
12660
12728
|
}
|
|
12661
12729
|
}
|
|
12662
|
-
return [
|
|
12730
|
+
return [
|
|
12731
|
+
changes,
|
|
12732
|
+
pairs,
|
|
12733
|
+
diff,
|
|
12734
|
+
nodesToBeRemoved,
|
|
12735
|
+
fromIdx,
|
|
12736
|
+
mergeLevel,
|
|
12737
|
+
preTombstoned
|
|
12738
|
+
];
|
|
12663
12739
|
}
|
|
12664
12740
|
/**
|
|
12665
12741
|
* `editT` edits the given range with the given value.
|
|
@@ -13100,11 +13176,35 @@ class CRDTTree extends CRDTElement {
|
|
|
13100
13176
|
return [prev, prev.isText ? TokenType.Text : TokenType.End];
|
|
13101
13177
|
}
|
|
13102
13178
|
}
|
|
13103
|
-
function
|
|
13104
|
-
|
|
13179
|
+
function cloneAndDropPreTombstoned(node, preTombstoned) {
|
|
13180
|
+
const clone = node.deepcopy();
|
|
13181
|
+
filterChildren(clone, preTombstoned);
|
|
13182
|
+
traverseAll(clone, (n) => {
|
|
13105
13183
|
n.removedAt = void 0;
|
|
13106
|
-
n.
|
|
13184
|
+
if (n.isText) {
|
|
13185
|
+
n.visibleSize = n.value.length;
|
|
13186
|
+
n.totalSize = n.value.length;
|
|
13187
|
+
return;
|
|
13188
|
+
}
|
|
13189
|
+
let size = 0;
|
|
13190
|
+
for (const child of n._children) size += child.paddedSize();
|
|
13191
|
+
n.visibleSize = size;
|
|
13192
|
+
n.totalSize = size;
|
|
13107
13193
|
});
|
|
13194
|
+
return clone;
|
|
13195
|
+
}
|
|
13196
|
+
function filterChildren(node, preTombstoned) {
|
|
13197
|
+
const all = node._children;
|
|
13198
|
+
if (!all) return;
|
|
13199
|
+
const kept = [];
|
|
13200
|
+
for (const child of all) {
|
|
13201
|
+
if (preTombstoned.has(child.id.toIDString())) {
|
|
13202
|
+
continue;
|
|
13203
|
+
}
|
|
13204
|
+
filterChildren(child, preTombstoned);
|
|
13205
|
+
kept.push(child);
|
|
13206
|
+
}
|
|
13207
|
+
node._children = kept;
|
|
13108
13208
|
}
|
|
13109
13209
|
class TreeEditOperation extends Operation {
|
|
13110
13210
|
fromPos;
|
|
@@ -13176,7 +13276,15 @@ class TreeEditOperation extends Operation {
|
|
|
13176
13276
|
this.toPos = tree.findPos(this.toIdx);
|
|
13177
13277
|
}
|
|
13178
13278
|
}
|
|
13179
|
-
const [
|
|
13279
|
+
const [
|
|
13280
|
+
changes,
|
|
13281
|
+
pairs,
|
|
13282
|
+
diff,
|
|
13283
|
+
removedNodes,
|
|
13284
|
+
preEditFromIdx,
|
|
13285
|
+
mergeLevel,
|
|
13286
|
+
preTombstoned
|
|
13287
|
+
] = tree.edit(
|
|
13180
13288
|
[this.fromPos, this.toPos],
|
|
13181
13289
|
this.contents?.map((content) => content.deepcopy()),
|
|
13182
13290
|
this.splitLevel,
|
|
@@ -13209,10 +13317,16 @@ class TreeEditOperation extends Operation {
|
|
|
13209
13317
|
);
|
|
13210
13318
|
this.lastToIdx = preEditFromIdx + removedSize;
|
|
13211
13319
|
let reverseOp;
|
|
13212
|
-
const
|
|
13320
|
+
const isPureSplit = this.splitLevel > 0 && !this.contents?.length && removedNodes.length === 0;
|
|
13213
13321
|
if (this.splitLevel === 0) {
|
|
13214
|
-
reverseOp = this.toReverseOperation(
|
|
13215
|
-
|
|
13322
|
+
reverseOp = this.toReverseOperation(
|
|
13323
|
+
tree,
|
|
13324
|
+
removedNodes,
|
|
13325
|
+
preEditFromIdx,
|
|
13326
|
+
preTombstoned,
|
|
13327
|
+
mergeLevel
|
|
13328
|
+
);
|
|
13329
|
+
} else if (isPureSplit) {
|
|
13216
13330
|
reverseOp = this.toSplitReverseOperation(tree, preEditFromIdx);
|
|
13217
13331
|
}
|
|
13218
13332
|
root.acc(diff);
|
|
@@ -13249,7 +13363,7 @@ class TreeEditOperation extends Operation {
|
|
|
13249
13363
|
* @param removedNodes - Nodes that were removed by this edit
|
|
13250
13364
|
* @param preEditFromIdx - The from index captured BEFORE the edit
|
|
13251
13365
|
*/
|
|
13252
|
-
toReverseOperation(tree, removedNodes, preEditFromIdx) {
|
|
13366
|
+
toReverseOperation(tree, removedNodes, preEditFromIdx, preTombstoned, mergeLevel) {
|
|
13253
13367
|
if (this.redoSplitLevel !== void 0 && this.redoSplitLevel > 0) {
|
|
13254
13368
|
const splitRedoFromPos = tree.findPos(preEditFromIdx);
|
|
13255
13369
|
const splitRedoOp = TreeEditOperation.create(
|
|
@@ -13268,19 +13382,36 @@ class TreeEditOperation extends Operation {
|
|
|
13268
13382
|
);
|
|
13269
13383
|
return splitRedoOp;
|
|
13270
13384
|
}
|
|
13385
|
+
if (mergeLevel && mergeLevel > 0) {
|
|
13386
|
+
const splitFromPos = tree.findPos(preEditFromIdx);
|
|
13387
|
+
const splitUndoOp = TreeEditOperation.create(
|
|
13388
|
+
this.getParentCreatedAt(),
|
|
13389
|
+
splitFromPos,
|
|
13390
|
+
splitFromPos,
|
|
13391
|
+
void 0,
|
|
13392
|
+
// no inserted content — split creates boundaries
|
|
13393
|
+
mergeLevel,
|
|
13394
|
+
// splitLevel = number of merged boundaries
|
|
13395
|
+
void 0,
|
|
13396
|
+
// executedAt assigned at undo time
|
|
13397
|
+
true,
|
|
13398
|
+
// isUndoOp
|
|
13399
|
+
preEditFromIdx,
|
|
13400
|
+
preEditFromIdx
|
|
13401
|
+
);
|
|
13402
|
+
return splitUndoOp;
|
|
13403
|
+
}
|
|
13271
13404
|
const insertedContentSize = this.contents ? this.contents.reduce((sum, node) => sum + node.paddedSize(), 0) : 0;
|
|
13272
13405
|
const maxNeededIdx = preEditFromIdx + insertedContentSize;
|
|
13273
13406
|
if (maxNeededIdx > tree.getSize()) {
|
|
13274
13407
|
return void 0;
|
|
13275
13408
|
}
|
|
13276
13409
|
const topLevelRemoved = removedNodes.filter(
|
|
13277
|
-
(node) => !node.parent || !removedNodes.includes(node.parent)
|
|
13410
|
+
(node) => !preTombstoned.has(node.id.toIDString()) && (!node.parent || !removedNodes.includes(node.parent))
|
|
13278
13411
|
);
|
|
13279
|
-
const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map(
|
|
13280
|
-
|
|
13281
|
-
|
|
13282
|
-
return clone;
|
|
13283
|
-
}) : void 0;
|
|
13412
|
+
const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map(
|
|
13413
|
+
(n) => cloneAndDropPreTombstoned(n, preTombstoned)
|
|
13414
|
+
) : void 0;
|
|
13284
13415
|
const reverseFromPos = tree.findPos(preEditFromIdx);
|
|
13285
13416
|
let reverseToPos;
|
|
13286
13417
|
if (insertedContentSize > 0) {
|
|
@@ -20094,6 +20225,11 @@ class Document {
|
|
|
20094
20225
|
}
|
|
20095
20226
|
]);
|
|
20096
20227
|
}
|
|
20228
|
+
/**
|
|
20229
|
+
* `clearHistory` flushes both undo and redo stacks. This is used
|
|
20230
|
+
* after applying a snapshot or initialRoot so that setup operations
|
|
20231
|
+
* are not reachable via undo.
|
|
20232
|
+
*/
|
|
20097
20233
|
clearHistory() {
|
|
20098
20234
|
this.internalHistory.clearRedo();
|
|
20099
20235
|
this.internalHistory.clearUndo();
|
|
@@ -20553,10 +20689,7 @@ class Document {
|
|
|
20553
20689
|
}
|
|
20554
20690
|
const ops = isUndo ? this.internalHistory.popUndo() : this.internalHistory.popRedo();
|
|
20555
20691
|
if (!ops) {
|
|
20556
|
-
|
|
20557
|
-
Code.ErrRefused,
|
|
20558
|
-
`There is no operation to be ${isUndo ? "undone" : "redone"}`
|
|
20559
|
-
);
|
|
20692
|
+
return;
|
|
20560
20693
|
}
|
|
20561
20694
|
this.ensureClone();
|
|
20562
20695
|
const ctx = ChangeContext.create(
|
|
@@ -20649,6 +20782,8 @@ class Attachment {
|
|
|
20649
20782
|
syncMode;
|
|
20650
20783
|
changeEventReceived;
|
|
20651
20784
|
lastHeartbeatTime;
|
|
20785
|
+
pollInterval;
|
|
20786
|
+
pollIntervalPinned;
|
|
20652
20787
|
reconnectStreamDelay;
|
|
20653
20788
|
cancelled;
|
|
20654
20789
|
watchStream;
|
|
@@ -20656,13 +20791,15 @@ class Attachment {
|
|
|
20656
20791
|
watchAbortController;
|
|
20657
20792
|
syncPromise;
|
|
20658
20793
|
_detaching = false;
|
|
20659
|
-
constructor(reconnectStreamDelay, resource, resourceID, syncMode) {
|
|
20794
|
+
constructor(reconnectStreamDelay, resource, resourceID, syncMode, pollInterval = 0, pollIntervalPinned = false) {
|
|
20660
20795
|
this.reconnectStreamDelay = reconnectStreamDelay;
|
|
20661
20796
|
this.resource = resource;
|
|
20662
20797
|
this.resourceID = resourceID;
|
|
20663
20798
|
this.syncMode = syncMode;
|
|
20664
20799
|
this.changeEventReceived = syncMode !== void 0 ? false : void 0;
|
|
20665
20800
|
this.lastHeartbeatTime = Date.now();
|
|
20801
|
+
this.pollInterval = pollInterval;
|
|
20802
|
+
this.pollIntervalPinned = pollIntervalPinned;
|
|
20666
20803
|
this.cancelled = false;
|
|
20667
20804
|
}
|
|
20668
20805
|
/**
|
|
@@ -20682,6 +20819,9 @@ class Attachment {
|
|
|
20682
20819
|
if (this.syncMode === SyncMode.RealtimePushOnly) {
|
|
20683
20820
|
return this.resource.hasLocalChanges();
|
|
20684
20821
|
}
|
|
20822
|
+
if (this.syncMode === SyncMode.Polling) {
|
|
20823
|
+
return Date.now() - this.lastHeartbeatTime >= this.pollInterval;
|
|
20824
|
+
}
|
|
20685
20825
|
return this.syncMode !== SyncMode.Manual && (this.resource.hasLocalChanges() || (this.changeEventReceived ?? false));
|
|
20686
20826
|
}
|
|
20687
20827
|
/**
|
|
@@ -20695,7 +20835,8 @@ class Attachment {
|
|
|
20695
20835
|
if (this.syncMode === SyncMode.Manual) {
|
|
20696
20836
|
return false;
|
|
20697
20837
|
}
|
|
20698
|
-
|
|
20838
|
+
const interval = this.pollInterval > 0 ? this.pollInterval : heartbeatInterval;
|
|
20839
|
+
return Date.now() - this.lastHeartbeatTime >= interval;
|
|
20699
20840
|
}
|
|
20700
20841
|
/**
|
|
20701
20842
|
* `updateHeartbeatTime` updates the last heartbeat time.
|
|
@@ -20774,6 +20915,16 @@ class Attachment {
|
|
|
20774
20915
|
}
|
|
20775
20916
|
}
|
|
20776
20917
|
}
|
|
20918
|
+
/**
|
|
20919
|
+
* `resetCancelled` clears the cancelled flag so the watch loop can run again
|
|
20920
|
+
* after a previous cancellation (e.g., after changeSyncMode back to Realtime).
|
|
20921
|
+
* Caller must invoke `runWatchLoop` immediately after to claim the stream slot;
|
|
20922
|
+
* `doLoop`'s `if (this.watchStream)` guard prevents double-stream creation if a
|
|
20923
|
+
* delayed `onDisconnect` callback from the previously-cancelled stream races.
|
|
20924
|
+
*/
|
|
20925
|
+
resetCancelled() {
|
|
20926
|
+
this.cancelled = false;
|
|
20927
|
+
}
|
|
20777
20928
|
/**
|
|
20778
20929
|
* `cancelWatchStream` cancels the watch stream.
|
|
20779
20930
|
*/
|
|
@@ -20808,7 +20959,7 @@ function createAuthInterceptor(apiKey, token) {
|
|
|
20808
20959
|
};
|
|
20809
20960
|
}
|
|
20810
20961
|
const name$1 = "@yorkie-js/sdk";
|
|
20811
|
-
const version$1 = "0.7.
|
|
20962
|
+
const version$1 = "0.7.8";
|
|
20812
20963
|
const pkg$1 = {
|
|
20813
20964
|
name: name$1,
|
|
20814
20965
|
version: version$1
|
|
@@ -21091,8 +21242,10 @@ var SyncMode = /* @__PURE__ */ ((SyncMode2) => {
|
|
|
21091
21242
|
SyncMode2["Realtime"] = "realtime";
|
|
21092
21243
|
SyncMode2["RealtimePushOnly"] = "realtime-pushonly";
|
|
21093
21244
|
SyncMode2["RealtimeSyncOff"] = "realtime-syncoff";
|
|
21245
|
+
SyncMode2["Polling"] = "polling";
|
|
21094
21246
|
return SyncMode2;
|
|
21095
21247
|
})(SyncMode || {});
|
|
21248
|
+
const DefaultPollingIntervalMs = 3e3;
|
|
21096
21249
|
const DefaultClientOptions = {
|
|
21097
21250
|
rpcAddr: "https://api.yorkie.dev",
|
|
21098
21251
|
syncLoopDuration: 50,
|
|
@@ -21288,6 +21441,14 @@ class Client {
|
|
|
21288
21441
|
doc.setActor(this.id);
|
|
21289
21442
|
doc.update((_, p) => p.set(opts.initialPresence || {}));
|
|
21290
21443
|
const syncMode = opts.syncMode ?? "realtime";
|
|
21444
|
+
if (opts.documentPollInterval !== void 0 && opts.documentPollInterval <= 0) {
|
|
21445
|
+
throw new YorkieError(
|
|
21446
|
+
Code.ErrInvalidArgument,
|
|
21447
|
+
"documentPollInterval must be greater than 0"
|
|
21448
|
+
);
|
|
21449
|
+
}
|
|
21450
|
+
const pollIntervalPinned = opts.documentPollInterval !== void 0;
|
|
21451
|
+
const pollInterval = pollIntervalPinned ? opts.documentPollInterval : syncMode === "polling" ? DefaultPollingIntervalMs : 0;
|
|
21291
21452
|
return this.enqueueTask(async () => {
|
|
21292
21453
|
try {
|
|
21293
21454
|
const res = await this.rpcClient.attachDocument(
|
|
@@ -21317,10 +21478,12 @@ class Client {
|
|
|
21317
21478
|
this.reconnectStreamDelay,
|
|
21318
21479
|
doc,
|
|
21319
21480
|
res.documentId,
|
|
21320
|
-
syncMode
|
|
21481
|
+
syncMode,
|
|
21482
|
+
pollInterval,
|
|
21483
|
+
pollIntervalPinned
|
|
21321
21484
|
)
|
|
21322
21485
|
);
|
|
21323
|
-
if (syncMode !== "manual") {
|
|
21486
|
+
if (syncMode !== "manual" && syncMode !== "polling") {
|
|
21324
21487
|
await this.runWatchLoop(doc.getKey());
|
|
21325
21488
|
}
|
|
21326
21489
|
logger.info(`[AD] c:"${this.getKey()}" attaches d:"${doc.getKey()}"`);
|
|
@@ -21336,6 +21499,7 @@ class Client {
|
|
|
21336
21499
|
}
|
|
21337
21500
|
});
|
|
21338
21501
|
}
|
|
21502
|
+
doc.clearHistory();
|
|
21339
21503
|
return doc;
|
|
21340
21504
|
} catch (err) {
|
|
21341
21505
|
logger.error(`[AD] c:"${this.getKey()}" err :`, err);
|
|
@@ -21446,12 +21610,23 @@ class Client {
|
|
|
21446
21610
|
channel.setSessionID(res.sessionId);
|
|
21447
21611
|
channel.updateSessionCount(Number(res.sessionCount), 0);
|
|
21448
21612
|
channel.applyStatus(ChannelStatus.Attached);
|
|
21449
|
-
const syncMode = opts.
|
|
21613
|
+
const syncMode = opts.syncMode ?? "realtime";
|
|
21614
|
+
this.assertValidChannelSyncMode(syncMode);
|
|
21615
|
+
if (opts.channelHeartbeatInterval !== void 0 && opts.channelHeartbeatInterval <= 0) {
|
|
21616
|
+
throw new YorkieError(
|
|
21617
|
+
Code.ErrInvalidArgument,
|
|
21618
|
+
"channelHeartbeatInterval must be greater than 0"
|
|
21619
|
+
);
|
|
21620
|
+
}
|
|
21621
|
+
const pollIntervalPinned = opts.channelHeartbeatInterval !== void 0;
|
|
21622
|
+
const pollInterval = pollIntervalPinned ? opts.channelHeartbeatInterval : syncMode === "polling" ? DefaultPollingIntervalMs : this.channelHeartbeatInterval;
|
|
21450
21623
|
const attachment = new Attachment(
|
|
21451
21624
|
this.reconnectStreamDelay,
|
|
21452
21625
|
channel,
|
|
21453
21626
|
res.sessionId,
|
|
21454
|
-
syncMode
|
|
21627
|
+
syncMode,
|
|
21628
|
+
pollInterval,
|
|
21629
|
+
pollIntervalPinned
|
|
21455
21630
|
);
|
|
21456
21631
|
channel.subscribe("local-broadcast", (event) => {
|
|
21457
21632
|
const { topic, payload, options } = event;
|
|
@@ -21527,9 +21702,17 @@ class Client {
|
|
|
21527
21702
|
return this.enqueueTask(task);
|
|
21528
21703
|
}
|
|
21529
21704
|
/**
|
|
21530
|
-
* `changeSyncMode` changes the synchronization mode of the given
|
|
21705
|
+
* `changeSyncMode` changes the synchronization mode of the given resource.
|
|
21531
21706
|
*/
|
|
21532
|
-
async changeSyncMode(
|
|
21707
|
+
async changeSyncMode(resource, syncMode) {
|
|
21708
|
+
return this.enqueueTask(async () => {
|
|
21709
|
+
if (resource instanceof Channel2) {
|
|
21710
|
+
return this.changeChannelSyncMode(resource, syncMode);
|
|
21711
|
+
}
|
|
21712
|
+
return this.changeDocumentSyncMode(resource, syncMode);
|
|
21713
|
+
});
|
|
21714
|
+
}
|
|
21715
|
+
async changeDocumentSyncMode(doc, syncMode) {
|
|
21533
21716
|
if (!this.isActive()) {
|
|
21534
21717
|
throw new YorkieError(
|
|
21535
21718
|
Code.ErrClientNotActivated,
|
|
@@ -21547,19 +21730,66 @@ class Client {
|
|
|
21547
21730
|
if (prevSyncMode === syncMode) {
|
|
21548
21731
|
return doc;
|
|
21549
21732
|
}
|
|
21550
|
-
|
|
21551
|
-
if (syncMode === "manual") {
|
|
21733
|
+
if (syncMode === "manual" || syncMode === "polling") {
|
|
21552
21734
|
attachment.cancelWatchStream();
|
|
21553
|
-
return doc;
|
|
21554
21735
|
}
|
|
21736
|
+
attachment.changeSyncMode(syncMode);
|
|
21555
21737
|
if (syncMode === "realtime") {
|
|
21556
21738
|
attachment.changeEventReceived = true;
|
|
21557
21739
|
}
|
|
21558
|
-
if (
|
|
21740
|
+
if (!attachment.pollIntervalPinned) {
|
|
21741
|
+
attachment.pollInterval = syncMode === "polling" ? DefaultPollingIntervalMs : 0;
|
|
21742
|
+
}
|
|
21743
|
+
if ((prevSyncMode === "manual" || prevSyncMode === "polling") && syncMode !== "manual" && syncMode !== "polling") {
|
|
21744
|
+
attachment.resetCancelled();
|
|
21559
21745
|
await this.runWatchLoop(doc.getKey());
|
|
21560
21746
|
}
|
|
21561
21747
|
return doc;
|
|
21562
21748
|
}
|
|
21749
|
+
/**
|
|
21750
|
+
* `assertValidChannelSyncMode` rejects sync modes that are not valid for
|
|
21751
|
+
* channels. `RealtimePushOnly` and `RealtimeSyncOff` are document-only.
|
|
21752
|
+
*/
|
|
21753
|
+
assertValidChannelSyncMode(syncMode) {
|
|
21754
|
+
if (syncMode !== "manual" && syncMode !== "realtime" && syncMode !== "polling") {
|
|
21755
|
+
throw new YorkieError(
|
|
21756
|
+
Code.ErrInvalidArgument,
|
|
21757
|
+
`invalid channel sync mode: ${syncMode}`
|
|
21758
|
+
);
|
|
21759
|
+
}
|
|
21760
|
+
}
|
|
21761
|
+
async changeChannelSyncMode(channel, syncMode) {
|
|
21762
|
+
if (!this.isActive()) {
|
|
21763
|
+
throw new YorkieError(
|
|
21764
|
+
Code.ErrClientNotActivated,
|
|
21765
|
+
`${this.key} is not active`
|
|
21766
|
+
);
|
|
21767
|
+
}
|
|
21768
|
+
const attachment = this.attachmentMap.get(channel.getKey());
|
|
21769
|
+
if (!attachment) {
|
|
21770
|
+
throw new YorkieError(
|
|
21771
|
+
Code.ErrNotAttached,
|
|
21772
|
+
`${channel.getKey()} is not attached`
|
|
21773
|
+
);
|
|
21774
|
+
}
|
|
21775
|
+
const prevSyncMode = attachment.syncMode;
|
|
21776
|
+
if (prevSyncMode === syncMode) {
|
|
21777
|
+
return channel;
|
|
21778
|
+
}
|
|
21779
|
+
this.assertValidChannelSyncMode(syncMode);
|
|
21780
|
+
if (prevSyncMode === "realtime") {
|
|
21781
|
+
attachment.cancelWatchStream();
|
|
21782
|
+
}
|
|
21783
|
+
attachment.changeSyncMode(syncMode);
|
|
21784
|
+
if (!attachment.pollIntervalPinned) {
|
|
21785
|
+
attachment.pollInterval = syncMode === "polling" ? DefaultPollingIntervalMs : syncMode === "realtime" ? this.channelHeartbeatInterval : 0;
|
|
21786
|
+
}
|
|
21787
|
+
if (syncMode === "realtime") {
|
|
21788
|
+
attachment.resetCancelled();
|
|
21789
|
+
await this.runWatchLoop(channel.getKey());
|
|
21790
|
+
}
|
|
21791
|
+
return channel;
|
|
21792
|
+
}
|
|
21563
21793
|
/**
|
|
21564
21794
|
* `sync` implementation that handles both Document and Channel.
|
|
21565
21795
|
*/
|
|
@@ -22404,6 +22634,7 @@ class Client {
|
|
|
22404
22634
|
return doc;
|
|
22405
22635
|
}
|
|
22406
22636
|
doc.applyChangePack(respPack);
|
|
22637
|
+
attachment.updateHeartbeatTime();
|
|
22407
22638
|
attachment.resource.publish([
|
|
22408
22639
|
{
|
|
22409
22640
|
type: DocEventType.SyncStatusChanged,
|
|
@@ -22514,8 +22745,11 @@ function isBinData(value) {
|
|
|
22514
22745
|
function isCounter(value) {
|
|
22515
22746
|
return typeof value === "object" && value !== null && value.type === "Counter" && typeof value.value === "object";
|
|
22516
22747
|
}
|
|
22748
|
+
function isDedupCounter(value) {
|
|
22749
|
+
return typeof value === "object" && value !== null && value.type === "DedupCounter" && typeof value.value === "object" && typeof value.registers === "string";
|
|
22750
|
+
}
|
|
22517
22751
|
function isObject(value) {
|
|
22518
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && !isText(value) && !isTree(value) && !isInt(value) && !isLong(value) && !isDate(value) && !isBinData(value) && !isCounter(value);
|
|
22752
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !isText(value) && !isTree(value) && !isInt(value) && !isLong(value) && !isDate(value) && !isBinData(value) && !isCounter(value) && !isDedupCounter(value);
|
|
22519
22753
|
}
|
|
22520
22754
|
function parse(yson) {
|
|
22521
22755
|
try {
|
|
@@ -22531,6 +22765,12 @@ function parse(yson) {
|
|
|
22531
22765
|
}
|
|
22532
22766
|
function preprocessYSON(yson) {
|
|
22533
22767
|
let result = yson;
|
|
22768
|
+
result = result.replace(
|
|
22769
|
+
/DedupCounter\(Int\((-?\d+)\),"([^"]+)"\)/g,
|
|
22770
|
+
(_, value, registers) => {
|
|
22771
|
+
return `{"__yson_type":"DedupCounter","__yson_data":{"__yson_type":"Int","__yson_data":${value}},"__yson_registers":"${registers}"}`;
|
|
22772
|
+
}
|
|
22773
|
+
);
|
|
22534
22774
|
result = result.replace(
|
|
22535
22775
|
/Counter\((Int|Long)\((-?\d+)\)\)/g,
|
|
22536
22776
|
(_, type, value) => {
|
|
@@ -22591,6 +22831,20 @@ function postprocessValue(value) {
|
|
|
22591
22831
|
value: value.__yson_data
|
|
22592
22832
|
};
|
|
22593
22833
|
}
|
|
22834
|
+
if (value.__yson_type === "DedupCounter" && typeof value.__yson_data === "object" && typeof value.__yson_registers === "string") {
|
|
22835
|
+
const counterValue = postprocessValue(value.__yson_data);
|
|
22836
|
+
if (typeof counterValue === "object" && counterValue !== null && "type" in counterValue && counterValue.type === "Int") {
|
|
22837
|
+
return {
|
|
22838
|
+
type: "DedupCounter",
|
|
22839
|
+
value: counterValue,
|
|
22840
|
+
registers: value.__yson_registers
|
|
22841
|
+
};
|
|
22842
|
+
}
|
|
22843
|
+
throw new YorkieError(
|
|
22844
|
+
Code.ErrInvalidArgument,
|
|
22845
|
+
"DedupCounter must contain Int"
|
|
22846
|
+
);
|
|
22847
|
+
}
|
|
22594
22848
|
if (value.__yson_type === "Counter" && typeof value.__yson_data === "object") {
|
|
22595
22849
|
const counterValue = postprocessValue(value.__yson_data);
|
|
22596
22850
|
if (typeof counterValue === "object" && counterValue !== null && "type" in counterValue && (counterValue.type === "Int" || counterValue.type === "Long")) {
|
|
@@ -22679,6 +22933,7 @@ const YSON = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
|
|
|
22679
22933
|
isBinData,
|
|
22680
22934
|
isCounter,
|
|
22681
22935
|
isDate,
|
|
22936
|
+
isDedupCounter,
|
|
22682
22937
|
isInt,
|
|
22683
22938
|
isLong,
|
|
22684
22939
|
isObject,
|
|
@@ -22705,7 +22960,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
22705
22960
|
};
|
|
22706
22961
|
}
|
|
22707
22962
|
const name = "@yorkie-js/react";
|
|
22708
|
-
const version = "0.7.
|
|
22963
|
+
const version = "0.7.8";
|
|
22709
22964
|
const pkg = {
|
|
22710
22965
|
name,
|
|
22711
22966
|
version
|
|
@@ -23161,7 +23416,7 @@ function createChannelStore(initialState) {
|
|
|
23161
23416
|
const ChannelContext = createContext(
|
|
23162
23417
|
void 0
|
|
23163
23418
|
);
|
|
23164
|
-
function useYorkieChannel(client, clientLoading, clientError, channelKey,
|
|
23419
|
+
function useYorkieChannel(client, clientLoading, clientError, channelKey, syncMode, channelStore) {
|
|
23165
23420
|
const channelRef = useRef(void 0);
|
|
23166
23421
|
const [didMount, setDidMount] = useState(false);
|
|
23167
23422
|
useEffect(() => {
|
|
@@ -23190,7 +23445,7 @@ function useYorkieChannel(client, clientLoading, clientError, channelKey, isReal
|
|
|
23190
23445
|
}));
|
|
23191
23446
|
try {
|
|
23192
23447
|
const newChannel = new Channel2(channelKey);
|
|
23193
|
-
await client.attach(newChannel, {
|
|
23448
|
+
await client.attach(newChannel, { syncMode });
|
|
23194
23449
|
channelRef.current = newChannel;
|
|
23195
23450
|
unsubscribe = newChannel.subscribe(() => {
|
|
23196
23451
|
channelStore.setState((state) => ({
|
|
@@ -23228,12 +23483,13 @@ function useYorkieChannel(client, clientLoading, clientError, channelKey, isReal
|
|
|
23228
23483
|
}
|
|
23229
23484
|
detachChannel();
|
|
23230
23485
|
};
|
|
23231
|
-
}, [client, clientLoading, clientError, channelKey,
|
|
23486
|
+
}, [client, clientLoading, clientError, channelKey, syncMode, didMount]);
|
|
23232
23487
|
}
|
|
23233
23488
|
const ChannelProvider = ({
|
|
23234
23489
|
children,
|
|
23235
23490
|
channelKey,
|
|
23236
|
-
|
|
23491
|
+
syncMode,
|
|
23492
|
+
isRealtime
|
|
23237
23493
|
}) => {
|
|
23238
23494
|
const { client, loading: clientLoading, error: clientError } = useYorkie();
|
|
23239
23495
|
const channelStoreRef = useRef(
|
|
@@ -23248,12 +23504,13 @@ const ChannelProvider = ({
|
|
|
23248
23504
|
});
|
|
23249
23505
|
}
|
|
23250
23506
|
const channelStore = channelStoreRef.current;
|
|
23507
|
+
const resolvedSyncMode = syncMode ?? (isRealtime === false ? SyncMode.Manual : SyncMode.Realtime);
|
|
23251
23508
|
useYorkieChannel(
|
|
23252
23509
|
client,
|
|
23253
23510
|
clientLoading,
|
|
23254
23511
|
clientError,
|
|
23255
23512
|
channelKey,
|
|
23256
|
-
|
|
23513
|
+
resolvedSyncMode,
|
|
23257
23514
|
channelStore
|
|
23258
23515
|
);
|
|
23259
23516
|
return /* @__PURE__ */ jsx(ChannelContext.Provider, { value: channelStore, children });
|
|
@@ -23280,6 +23537,7 @@ export {
|
|
|
23280
23537
|
ChannelProvider,
|
|
23281
23538
|
Counter,
|
|
23282
23539
|
DocumentProvider,
|
|
23540
|
+
SyncMode,
|
|
23283
23541
|
Text,
|
|
23284
23542
|
Tree,
|
|
23285
23543
|
YorkieProvider,
|