@yorkie-js/sdk 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-sdk.d.ts +101 -13
- package/dist/yorkie-js-sdk.es.js +299 -44
- package/dist/yorkie-js-sdk.es.js.map +1 -1
- package/dist/yorkie-js-sdk.js +299 -44
- package/dist/yorkie-js-sdk.js.map +1 -1
- package/package.json +5 -5
package/dist/yorkie-js-sdk.es.js
CHANGED
|
@@ -358,7 +358,7 @@ function isScalarZeroValue(type, value) {
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
const IMPLICIT$3 = 2;
|
|
361
|
-
const unsafeLocal = Symbol.for("reflect unsafe local");
|
|
361
|
+
const unsafeLocal = /* @__PURE__ */ Symbol.for("reflect unsafe local");
|
|
362
362
|
function unsafeOneofCase(target, oneof) {
|
|
363
363
|
const c = target[oneof.localName].case;
|
|
364
364
|
if (c === void 0) {
|
|
@@ -584,7 +584,7 @@ function convertObjectValues(obj, fn) {
|
|
|
584
584
|
}
|
|
585
585
|
return ret;
|
|
586
586
|
}
|
|
587
|
-
const tokenZeroMessageField = Symbol();
|
|
587
|
+
const tokenZeroMessageField = /* @__PURE__ */ Symbol();
|
|
588
588
|
const messagePrototypes = /* @__PURE__ */ new WeakMap();
|
|
589
589
|
function createZeroMessage(desc) {
|
|
590
590
|
let msg;
|
|
@@ -686,7 +686,7 @@ class FieldError extends Error {
|
|
|
686
686
|
function isFieldError(arg) {
|
|
687
687
|
return arg instanceof Error && errorNames.includes(arg.name) && "field" in arg && typeof arg.field == "function";
|
|
688
688
|
}
|
|
689
|
-
const symbol = Symbol.for("@bufbuild/protobuf/text-encoding");
|
|
689
|
+
const symbol = /* @__PURE__ */ Symbol.for("@bufbuild/protobuf/text-encoding");
|
|
690
690
|
function getTextEncoding() {
|
|
691
691
|
if (globalThis[symbol] == void 0) {
|
|
692
692
|
const te = new globalThis.TextEncoder();
|
|
@@ -4054,7 +4054,7 @@ function readScalarField(msg, field, json) {
|
|
|
4054
4054
|
msg.set(field, scalarValue);
|
|
4055
4055
|
}
|
|
4056
4056
|
}
|
|
4057
|
-
const tokenIgnoredUnknownEnum = Symbol();
|
|
4057
|
+
const tokenIgnoredUnknownEnum = /* @__PURE__ */ Symbol();
|
|
4058
4058
|
function readEnum(desc, json, ignoreUnknownFields, nullAsZeroValue) {
|
|
4059
4059
|
if (json === null) {
|
|
4060
4060
|
if (desc.typeName == "google.protobuf.NullValue") {
|
|
@@ -4080,7 +4080,7 @@ function readEnum(desc, json, ignoreUnknownFields, nullAsZeroValue) {
|
|
|
4080
4080
|
}
|
|
4081
4081
|
throw new Error(`cannot decode ${desc} from JSON: ${formatVal(json)}`);
|
|
4082
4082
|
}
|
|
4083
|
-
const tokenNull = Symbol();
|
|
4083
|
+
const tokenNull = /* @__PURE__ */ Symbol();
|
|
4084
4084
|
function scalarFromJson(field, json, nullAsZeroValue) {
|
|
4085
4085
|
if (json === null) {
|
|
4086
4086
|
if (nullAsZeroValue) {
|
|
@@ -5901,6 +5901,8 @@ class YorkieError extends Error {
|
|
|
5901
5901
|
this.message = message;
|
|
5902
5902
|
this.toString = () => `[code=${this.code}]: ${this.message}`;
|
|
5903
5903
|
}
|
|
5904
|
+
code;
|
|
5905
|
+
message;
|
|
5904
5906
|
name = "YorkieError";
|
|
5905
5907
|
stack;
|
|
5906
5908
|
}
|
|
@@ -11130,6 +11132,32 @@ class IndexTreeNode {
|
|
|
11130
11132
|
}
|
|
11131
11133
|
actualRight.push(child);
|
|
11132
11134
|
}
|
|
11135
|
+
if (versionVector) {
|
|
11136
|
+
const movedToLeft = [];
|
|
11137
|
+
const remaining = [];
|
|
11138
|
+
let boundaryReached = false;
|
|
11139
|
+
for (const child of actualRight) {
|
|
11140
|
+
if (!boundaryReached) {
|
|
11141
|
+
if (child.insPrevID !== void 0 && !child.isText) {
|
|
11142
|
+
remaining.push(child);
|
|
11143
|
+
continue;
|
|
11144
|
+
}
|
|
11145
|
+
const actorID = child.id.getCreatedAt().getActorID();
|
|
11146
|
+
const knownLamport = versionVector.get(actorID);
|
|
11147
|
+
if (knownLamport === void 0 || knownLamport < child.id.getCreatedAt().getLamport()) {
|
|
11148
|
+
movedToLeft.push(child);
|
|
11149
|
+
continue;
|
|
11150
|
+
}
|
|
11151
|
+
}
|
|
11152
|
+
boundaryReached = true;
|
|
11153
|
+
remaining.push(child);
|
|
11154
|
+
}
|
|
11155
|
+
if (movedToLeft.length > 0) {
|
|
11156
|
+
left.push(...movedToLeft);
|
|
11157
|
+
actualRight.length = 0;
|
|
11158
|
+
actualRight.push(...remaining);
|
|
11159
|
+
}
|
|
11160
|
+
}
|
|
11133
11161
|
this._children = left;
|
|
11134
11162
|
clone._children = actualRight;
|
|
11135
11163
|
this.visibleSize = this._children.reduce(
|
|
@@ -11906,8 +11934,14 @@ class CRDTTreeNode extends IndexTreeNode {
|
|
|
11906
11934
|
split.insPrevID = this.id;
|
|
11907
11935
|
if (this.insNextID) {
|
|
11908
11936
|
const insNext = tree.findFloorNode(this.insNextID);
|
|
11909
|
-
insNext.insPrevID = split.id;
|
|
11910
11937
|
split.insNextID = this.insNextID;
|
|
11938
|
+
if (insNext) {
|
|
11939
|
+
insNext.insPrevID = split.id;
|
|
11940
|
+
if (!this.isText && insNext.parent && !insNext.isRemoved && insNext.parent !== split.parent && split.allChildren.length === 0) {
|
|
11941
|
+
split.parent.detachChild(split);
|
|
11942
|
+
insNext.parent.insertBefore(split, insNext);
|
|
11943
|
+
}
|
|
11944
|
+
}
|
|
11911
11945
|
}
|
|
11912
11946
|
this.insNextID = split.id;
|
|
11913
11947
|
tree.registerNode(split);
|
|
@@ -12131,7 +12165,7 @@ class CRDTTree extends CRDTElement {
|
|
|
12131
12165
|
* given node, advancing past element-type split siblings that the editing
|
|
12132
12166
|
* client did not know about (not in versionVector).
|
|
12133
12167
|
*/
|
|
12134
|
-
advancePastUnknownSplitSiblings(node, versionVector) {
|
|
12168
|
+
advancePastUnknownSplitSiblings(node, versionVector, relaxParentCheck = false, skipActorID) {
|
|
12135
12169
|
if (!versionVector || !node) {
|
|
12136
12170
|
return node;
|
|
12137
12171
|
}
|
|
@@ -12141,10 +12175,13 @@ class CRDTTree extends CRDTElement {
|
|
|
12141
12175
|
if (!next || next.isText) {
|
|
12142
12176
|
break;
|
|
12143
12177
|
}
|
|
12144
|
-
if (next.parent !== current.parent) {
|
|
12178
|
+
if (!relaxParentCheck && next.parent !== current.parent) {
|
|
12145
12179
|
break;
|
|
12146
12180
|
}
|
|
12147
12181
|
const actorID = next.id.getCreatedAt().getActorID();
|
|
12182
|
+
if (skipActorID !== void 0 && actorID === skipActorID) {
|
|
12183
|
+
break;
|
|
12184
|
+
}
|
|
12148
12185
|
const knownLamport = versionVector.get(actorID);
|
|
12149
12186
|
if (knownLamport !== void 0 && knownLamport >= next.id.getCreatedAt().getLamport()) {
|
|
12150
12187
|
break;
|
|
@@ -12485,6 +12522,25 @@ class CRDTTree extends CRDTElement {
|
|
|
12485
12522
|
addDataSizes(diff, diffTo, diffFrom);
|
|
12486
12523
|
const fromLeft = fromLeftRaw !== fromParent ? this.advancePastUnknownSplitSiblings(fromLeftRaw, versionVector) : fromLeftRaw;
|
|
12487
12524
|
const toLeft = toLeftRaw !== toParent ? this.advancePastUnknownSplitSiblings(toLeftRaw, versionVector) : toLeftRaw;
|
|
12525
|
+
let collectFromParent = fromParent;
|
|
12526
|
+
let collectFromLeft = fromLeft;
|
|
12527
|
+
if (fromLeft !== fromParent && fromParent !== toParent) {
|
|
12528
|
+
let current = fromLeft;
|
|
12529
|
+
while (current.insNextID) {
|
|
12530
|
+
const next = this.findFloorNode(current.insNextID);
|
|
12531
|
+
if (!next || next.isText) {
|
|
12532
|
+
break;
|
|
12533
|
+
}
|
|
12534
|
+
if (next.parent && next.parent === toParent) {
|
|
12535
|
+
if (toLeft !== toParent) {
|
|
12536
|
+
collectFromLeft = next;
|
|
12537
|
+
collectFromParent = toParent;
|
|
12538
|
+
}
|
|
12539
|
+
break;
|
|
12540
|
+
}
|
|
12541
|
+
current = next;
|
|
12542
|
+
}
|
|
12543
|
+
}
|
|
12488
12544
|
const fromIdx = this.toIndex(fromParent, fromLeft);
|
|
12489
12545
|
const fromPath = this.toPath(fromParent, fromLeft);
|
|
12490
12546
|
const nodesToBeRemoved = [];
|
|
@@ -12493,8 +12549,8 @@ class CRDTTree extends CRDTElement {
|
|
|
12493
12549
|
const toBeMergedNodes = [];
|
|
12494
12550
|
const preTombstoned = /* @__PURE__ */ new Set();
|
|
12495
12551
|
this.traverseInPosRange(
|
|
12496
|
-
|
|
12497
|
-
|
|
12552
|
+
collectFromParent,
|
|
12553
|
+
collectFromLeft,
|
|
12498
12554
|
toParent,
|
|
12499
12555
|
toLeft,
|
|
12500
12556
|
([node, tokenType], ended) => {
|
|
@@ -12542,6 +12598,7 @@ class CRDTTree extends CRDTElement {
|
|
|
12542
12598
|
tokensToBeRemoved,
|
|
12543
12599
|
editedAt
|
|
12544
12600
|
);
|
|
12601
|
+
const mergeLevel = toBeMergedNodes.length;
|
|
12545
12602
|
const pairs = [];
|
|
12546
12603
|
for (const node of nodesToBeRemoved) {
|
|
12547
12604
|
if (node.remove(editedAt)) {
|
|
@@ -12598,9 +12655,20 @@ class CRDTTree extends CRDTElement {
|
|
|
12598
12655
|
let parent = fromParent;
|
|
12599
12656
|
let left = fromLeft;
|
|
12600
12657
|
while (splitCount < splitLevel) {
|
|
12658
|
+
if (left !== parent) {
|
|
12659
|
+
left = this.advancePastUnknownSplitSiblings(
|
|
12660
|
+
left,
|
|
12661
|
+
versionVector,
|
|
12662
|
+
true,
|
|
12663
|
+
editedAt.getActorID()
|
|
12664
|
+
);
|
|
12665
|
+
if (left.parent && left.parent !== parent) {
|
|
12666
|
+
parent = left.parent;
|
|
12667
|
+
}
|
|
12668
|
+
}
|
|
12601
12669
|
parent.split(
|
|
12602
12670
|
this,
|
|
12603
|
-
parent.findOffset(left, true) + 1,
|
|
12671
|
+
left !== parent ? parent.findOffset(left, true) + 1 : 0,
|
|
12604
12672
|
issueTimeTicket,
|
|
12605
12673
|
versionVector
|
|
12606
12674
|
);
|
|
@@ -12657,7 +12725,15 @@ class CRDTTree extends CRDTElement {
|
|
|
12657
12725
|
}
|
|
12658
12726
|
}
|
|
12659
12727
|
}
|
|
12660
|
-
return [
|
|
12728
|
+
return [
|
|
12729
|
+
changes,
|
|
12730
|
+
pairs,
|
|
12731
|
+
diff,
|
|
12732
|
+
nodesToBeRemoved,
|
|
12733
|
+
fromIdx,
|
|
12734
|
+
mergeLevel,
|
|
12735
|
+
preTombstoned
|
|
12736
|
+
];
|
|
12661
12737
|
}
|
|
12662
12738
|
/**
|
|
12663
12739
|
* `editT` edits the given range with the given value.
|
|
@@ -13098,11 +13174,35 @@ class CRDTTree extends CRDTElement {
|
|
|
13098
13174
|
return [prev, prev.isText ? TokenType.Text : TokenType.End];
|
|
13099
13175
|
}
|
|
13100
13176
|
}
|
|
13101
|
-
function
|
|
13102
|
-
|
|
13177
|
+
function cloneAndDropPreTombstoned(node, preTombstoned) {
|
|
13178
|
+
const clone = node.deepcopy();
|
|
13179
|
+
filterChildren(clone, preTombstoned);
|
|
13180
|
+
traverseAll(clone, (n) => {
|
|
13103
13181
|
n.removedAt = void 0;
|
|
13104
|
-
n.
|
|
13182
|
+
if (n.isText) {
|
|
13183
|
+
n.visibleSize = n.value.length;
|
|
13184
|
+
n.totalSize = n.value.length;
|
|
13185
|
+
return;
|
|
13186
|
+
}
|
|
13187
|
+
let size = 0;
|
|
13188
|
+
for (const child of n._children) size += child.paddedSize();
|
|
13189
|
+
n.visibleSize = size;
|
|
13190
|
+
n.totalSize = size;
|
|
13105
13191
|
});
|
|
13192
|
+
return clone;
|
|
13193
|
+
}
|
|
13194
|
+
function filterChildren(node, preTombstoned) {
|
|
13195
|
+
const all = node._children;
|
|
13196
|
+
if (!all) return;
|
|
13197
|
+
const kept = [];
|
|
13198
|
+
for (const child of all) {
|
|
13199
|
+
if (preTombstoned.has(child.id.toIDString())) {
|
|
13200
|
+
continue;
|
|
13201
|
+
}
|
|
13202
|
+
filterChildren(child, preTombstoned);
|
|
13203
|
+
kept.push(child);
|
|
13204
|
+
}
|
|
13205
|
+
node._children = kept;
|
|
13106
13206
|
}
|
|
13107
13207
|
class TreeEditOperation extends Operation {
|
|
13108
13208
|
fromPos;
|
|
@@ -13174,7 +13274,15 @@ class TreeEditOperation extends Operation {
|
|
|
13174
13274
|
this.toPos = tree.findPos(this.toIdx);
|
|
13175
13275
|
}
|
|
13176
13276
|
}
|
|
13177
|
-
const [
|
|
13277
|
+
const [
|
|
13278
|
+
changes,
|
|
13279
|
+
pairs,
|
|
13280
|
+
diff,
|
|
13281
|
+
removedNodes,
|
|
13282
|
+
preEditFromIdx,
|
|
13283
|
+
mergeLevel,
|
|
13284
|
+
preTombstoned
|
|
13285
|
+
] = tree.edit(
|
|
13178
13286
|
[this.fromPos, this.toPos],
|
|
13179
13287
|
this.contents?.map((content) => content.deepcopy()),
|
|
13180
13288
|
this.splitLevel,
|
|
@@ -13207,10 +13315,16 @@ class TreeEditOperation extends Operation {
|
|
|
13207
13315
|
);
|
|
13208
13316
|
this.lastToIdx = preEditFromIdx + removedSize;
|
|
13209
13317
|
let reverseOp;
|
|
13210
|
-
const
|
|
13318
|
+
const isPureSplit = this.splitLevel > 0 && !this.contents?.length && removedNodes.length === 0;
|
|
13211
13319
|
if (this.splitLevel === 0) {
|
|
13212
|
-
reverseOp = this.toReverseOperation(
|
|
13213
|
-
|
|
13320
|
+
reverseOp = this.toReverseOperation(
|
|
13321
|
+
tree,
|
|
13322
|
+
removedNodes,
|
|
13323
|
+
preEditFromIdx,
|
|
13324
|
+
preTombstoned,
|
|
13325
|
+
mergeLevel
|
|
13326
|
+
);
|
|
13327
|
+
} else if (isPureSplit) {
|
|
13214
13328
|
reverseOp = this.toSplitReverseOperation(tree, preEditFromIdx);
|
|
13215
13329
|
}
|
|
13216
13330
|
root.acc(diff);
|
|
@@ -13247,7 +13361,7 @@ class TreeEditOperation extends Operation {
|
|
|
13247
13361
|
* @param removedNodes - Nodes that were removed by this edit
|
|
13248
13362
|
* @param preEditFromIdx - The from index captured BEFORE the edit
|
|
13249
13363
|
*/
|
|
13250
|
-
toReverseOperation(tree, removedNodes, preEditFromIdx) {
|
|
13364
|
+
toReverseOperation(tree, removedNodes, preEditFromIdx, preTombstoned, mergeLevel) {
|
|
13251
13365
|
if (this.redoSplitLevel !== void 0 && this.redoSplitLevel > 0) {
|
|
13252
13366
|
const splitRedoFromPos = tree.findPos(preEditFromIdx);
|
|
13253
13367
|
const splitRedoOp = TreeEditOperation.create(
|
|
@@ -13266,19 +13380,36 @@ class TreeEditOperation extends Operation {
|
|
|
13266
13380
|
);
|
|
13267
13381
|
return splitRedoOp;
|
|
13268
13382
|
}
|
|
13383
|
+
if (mergeLevel && mergeLevel > 0) {
|
|
13384
|
+
const splitFromPos = tree.findPos(preEditFromIdx);
|
|
13385
|
+
const splitUndoOp = TreeEditOperation.create(
|
|
13386
|
+
this.getParentCreatedAt(),
|
|
13387
|
+
splitFromPos,
|
|
13388
|
+
splitFromPos,
|
|
13389
|
+
void 0,
|
|
13390
|
+
// no inserted content — split creates boundaries
|
|
13391
|
+
mergeLevel,
|
|
13392
|
+
// splitLevel = number of merged boundaries
|
|
13393
|
+
void 0,
|
|
13394
|
+
// executedAt assigned at undo time
|
|
13395
|
+
true,
|
|
13396
|
+
// isUndoOp
|
|
13397
|
+
preEditFromIdx,
|
|
13398
|
+
preEditFromIdx
|
|
13399
|
+
);
|
|
13400
|
+
return splitUndoOp;
|
|
13401
|
+
}
|
|
13269
13402
|
const insertedContentSize = this.contents ? this.contents.reduce((sum, node) => sum + node.paddedSize(), 0) : 0;
|
|
13270
13403
|
const maxNeededIdx = preEditFromIdx + insertedContentSize;
|
|
13271
13404
|
if (maxNeededIdx > tree.getSize()) {
|
|
13272
13405
|
return void 0;
|
|
13273
13406
|
}
|
|
13274
13407
|
const topLevelRemoved = removedNodes.filter(
|
|
13275
|
-
(node) => !node.parent || !removedNodes.includes(node.parent)
|
|
13408
|
+
(node) => !preTombstoned.has(node.id.toIDString()) && (!node.parent || !removedNodes.includes(node.parent))
|
|
13276
13409
|
);
|
|
13277
|
-
const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map(
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
return clone;
|
|
13281
|
-
}) : void 0;
|
|
13410
|
+
const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map(
|
|
13411
|
+
(n) => cloneAndDropPreTombstoned(n, preTombstoned)
|
|
13412
|
+
) : void 0;
|
|
13282
13413
|
const reverseFromPos = tree.findPos(preEditFromIdx);
|
|
13283
13414
|
let reverseToPos;
|
|
13284
13415
|
if (insertedContentSize > 0) {
|
|
@@ -20097,6 +20228,11 @@ class Document {
|
|
|
20097
20228
|
}
|
|
20098
20229
|
]);
|
|
20099
20230
|
}
|
|
20231
|
+
/**
|
|
20232
|
+
* `clearHistory` flushes both undo and redo stacks. This is used
|
|
20233
|
+
* after applying a snapshot or initialRoot so that setup operations
|
|
20234
|
+
* are not reachable via undo.
|
|
20235
|
+
*/
|
|
20100
20236
|
clearHistory() {
|
|
20101
20237
|
this.internalHistory.clearRedo();
|
|
20102
20238
|
this.internalHistory.clearUndo();
|
|
@@ -20556,10 +20692,7 @@ class Document {
|
|
|
20556
20692
|
}
|
|
20557
20693
|
const ops = isUndo ? this.internalHistory.popUndo() : this.internalHistory.popRedo();
|
|
20558
20694
|
if (!ops) {
|
|
20559
|
-
|
|
20560
|
-
Code.ErrRefused,
|
|
20561
|
-
`There is no operation to be ${isUndo ? "undone" : "redone"}`
|
|
20562
|
-
);
|
|
20695
|
+
return;
|
|
20563
20696
|
}
|
|
20564
20697
|
this.ensureClone();
|
|
20565
20698
|
const ctx = ChangeContext.create(
|
|
@@ -20652,6 +20785,8 @@ class Attachment {
|
|
|
20652
20785
|
syncMode;
|
|
20653
20786
|
changeEventReceived;
|
|
20654
20787
|
lastHeartbeatTime;
|
|
20788
|
+
pollInterval;
|
|
20789
|
+
pollIntervalPinned;
|
|
20655
20790
|
reconnectStreamDelay;
|
|
20656
20791
|
cancelled;
|
|
20657
20792
|
watchStream;
|
|
@@ -20659,13 +20794,15 @@ class Attachment {
|
|
|
20659
20794
|
watchAbortController;
|
|
20660
20795
|
syncPromise;
|
|
20661
20796
|
_detaching = false;
|
|
20662
|
-
constructor(reconnectStreamDelay, resource, resourceID, syncMode) {
|
|
20797
|
+
constructor(reconnectStreamDelay, resource, resourceID, syncMode, pollInterval = 0, pollIntervalPinned = false) {
|
|
20663
20798
|
this.reconnectStreamDelay = reconnectStreamDelay;
|
|
20664
20799
|
this.resource = resource;
|
|
20665
20800
|
this.resourceID = resourceID;
|
|
20666
20801
|
this.syncMode = syncMode;
|
|
20667
20802
|
this.changeEventReceived = syncMode !== void 0 ? false : void 0;
|
|
20668
20803
|
this.lastHeartbeatTime = Date.now();
|
|
20804
|
+
this.pollInterval = pollInterval;
|
|
20805
|
+
this.pollIntervalPinned = pollIntervalPinned;
|
|
20669
20806
|
this.cancelled = false;
|
|
20670
20807
|
}
|
|
20671
20808
|
/**
|
|
@@ -20685,6 +20822,9 @@ class Attachment {
|
|
|
20685
20822
|
if (this.syncMode === SyncMode.RealtimePushOnly) {
|
|
20686
20823
|
return this.resource.hasLocalChanges();
|
|
20687
20824
|
}
|
|
20825
|
+
if (this.syncMode === SyncMode.Polling) {
|
|
20826
|
+
return Date.now() - this.lastHeartbeatTime >= this.pollInterval;
|
|
20827
|
+
}
|
|
20688
20828
|
return this.syncMode !== SyncMode.Manual && (this.resource.hasLocalChanges() || (this.changeEventReceived ?? false));
|
|
20689
20829
|
}
|
|
20690
20830
|
/**
|
|
@@ -20698,7 +20838,8 @@ class Attachment {
|
|
|
20698
20838
|
if (this.syncMode === SyncMode.Manual) {
|
|
20699
20839
|
return false;
|
|
20700
20840
|
}
|
|
20701
|
-
|
|
20841
|
+
const interval = this.pollInterval > 0 ? this.pollInterval : heartbeatInterval;
|
|
20842
|
+
return Date.now() - this.lastHeartbeatTime >= interval;
|
|
20702
20843
|
}
|
|
20703
20844
|
/**
|
|
20704
20845
|
* `updateHeartbeatTime` updates the last heartbeat time.
|
|
@@ -20777,6 +20918,16 @@ class Attachment {
|
|
|
20777
20918
|
}
|
|
20778
20919
|
}
|
|
20779
20920
|
}
|
|
20921
|
+
/**
|
|
20922
|
+
* `resetCancelled` clears the cancelled flag so the watch loop can run again
|
|
20923
|
+
* after a previous cancellation (e.g., after changeSyncMode back to Realtime).
|
|
20924
|
+
* Caller must invoke `runWatchLoop` immediately after to claim the stream slot;
|
|
20925
|
+
* `doLoop`'s `if (this.watchStream)` guard prevents double-stream creation if a
|
|
20926
|
+
* delayed `onDisconnect` callback from the previously-cancelled stream races.
|
|
20927
|
+
*/
|
|
20928
|
+
resetCancelled() {
|
|
20929
|
+
this.cancelled = false;
|
|
20930
|
+
}
|
|
20780
20931
|
/**
|
|
20781
20932
|
* `cancelWatchStream` cancels the watch stream.
|
|
20782
20933
|
*/
|
|
@@ -20811,7 +20962,7 @@ function createAuthInterceptor(apiKey, token) {
|
|
|
20811
20962
|
};
|
|
20812
20963
|
}
|
|
20813
20964
|
const name = "@yorkie-js/sdk";
|
|
20814
|
-
const version = "0.7.
|
|
20965
|
+
const version = "0.7.8";
|
|
20815
20966
|
const pkg = {
|
|
20816
20967
|
name,
|
|
20817
20968
|
version
|
|
@@ -21094,6 +21245,7 @@ var SyncMode = /* @__PURE__ */ ((SyncMode2) => {
|
|
|
21094
21245
|
SyncMode2["Realtime"] = "realtime";
|
|
21095
21246
|
SyncMode2["RealtimePushOnly"] = "realtime-pushonly";
|
|
21096
21247
|
SyncMode2["RealtimeSyncOff"] = "realtime-syncoff";
|
|
21248
|
+
SyncMode2["Polling"] = "polling";
|
|
21097
21249
|
return SyncMode2;
|
|
21098
21250
|
})(SyncMode || {});
|
|
21099
21251
|
var ClientStatus = /* @__PURE__ */ ((ClientStatus2) => {
|
|
@@ -21106,6 +21258,7 @@ var ClientCondition = /* @__PURE__ */ ((ClientCondition2) => {
|
|
|
21106
21258
|
ClientCondition2["WatchLoop"] = "WatchLoop";
|
|
21107
21259
|
return ClientCondition2;
|
|
21108
21260
|
})(ClientCondition || {});
|
|
21261
|
+
const DefaultPollingIntervalMs = 3e3;
|
|
21109
21262
|
const DefaultClientOptions = {
|
|
21110
21263
|
rpcAddr: "https://api.yorkie.dev",
|
|
21111
21264
|
syncLoopDuration: 50,
|
|
@@ -21301,6 +21454,14 @@ class Client {
|
|
|
21301
21454
|
doc.setActor(this.id);
|
|
21302
21455
|
doc.update((_, p) => p.set(opts.initialPresence || {}));
|
|
21303
21456
|
const syncMode = opts.syncMode ?? "realtime";
|
|
21457
|
+
if (opts.documentPollInterval !== void 0 && opts.documentPollInterval <= 0) {
|
|
21458
|
+
throw new YorkieError(
|
|
21459
|
+
Code.ErrInvalidArgument,
|
|
21460
|
+
"documentPollInterval must be greater than 0"
|
|
21461
|
+
);
|
|
21462
|
+
}
|
|
21463
|
+
const pollIntervalPinned = opts.documentPollInterval !== void 0;
|
|
21464
|
+
const pollInterval = pollIntervalPinned ? opts.documentPollInterval : syncMode === "polling" ? DefaultPollingIntervalMs : 0;
|
|
21304
21465
|
return this.enqueueTask(async () => {
|
|
21305
21466
|
try {
|
|
21306
21467
|
const res = await this.rpcClient.attachDocument(
|
|
@@ -21330,10 +21491,12 @@ class Client {
|
|
|
21330
21491
|
this.reconnectStreamDelay,
|
|
21331
21492
|
doc,
|
|
21332
21493
|
res.documentId,
|
|
21333
|
-
syncMode
|
|
21494
|
+
syncMode,
|
|
21495
|
+
pollInterval,
|
|
21496
|
+
pollIntervalPinned
|
|
21334
21497
|
)
|
|
21335
21498
|
);
|
|
21336
|
-
if (syncMode !== "manual") {
|
|
21499
|
+
if (syncMode !== "manual" && syncMode !== "polling") {
|
|
21337
21500
|
await this.runWatchLoop(doc.getKey());
|
|
21338
21501
|
}
|
|
21339
21502
|
logger.info(`[AD] c:"${this.getKey()}" attaches d:"${doc.getKey()}"`);
|
|
@@ -21349,6 +21512,7 @@ class Client {
|
|
|
21349
21512
|
}
|
|
21350
21513
|
});
|
|
21351
21514
|
}
|
|
21515
|
+
doc.clearHistory();
|
|
21352
21516
|
return doc;
|
|
21353
21517
|
} catch (err) {
|
|
21354
21518
|
logger.error(`[AD] c:"${this.getKey()}" err :`, err);
|
|
@@ -21459,12 +21623,23 @@ class Client {
|
|
|
21459
21623
|
channel.setSessionID(res.sessionId);
|
|
21460
21624
|
channel.updateSessionCount(Number(res.sessionCount), 0);
|
|
21461
21625
|
channel.applyStatus(ChannelStatus.Attached);
|
|
21462
|
-
const syncMode = opts.
|
|
21626
|
+
const syncMode = opts.syncMode ?? "realtime";
|
|
21627
|
+
this.assertValidChannelSyncMode(syncMode);
|
|
21628
|
+
if (opts.channelHeartbeatInterval !== void 0 && opts.channelHeartbeatInterval <= 0) {
|
|
21629
|
+
throw new YorkieError(
|
|
21630
|
+
Code.ErrInvalidArgument,
|
|
21631
|
+
"channelHeartbeatInterval must be greater than 0"
|
|
21632
|
+
);
|
|
21633
|
+
}
|
|
21634
|
+
const pollIntervalPinned = opts.channelHeartbeatInterval !== void 0;
|
|
21635
|
+
const pollInterval = pollIntervalPinned ? opts.channelHeartbeatInterval : syncMode === "polling" ? DefaultPollingIntervalMs : this.channelHeartbeatInterval;
|
|
21463
21636
|
const attachment = new Attachment(
|
|
21464
21637
|
this.reconnectStreamDelay,
|
|
21465
21638
|
channel,
|
|
21466
21639
|
res.sessionId,
|
|
21467
|
-
syncMode
|
|
21640
|
+
syncMode,
|
|
21641
|
+
pollInterval,
|
|
21642
|
+
pollIntervalPinned
|
|
21468
21643
|
);
|
|
21469
21644
|
channel.subscribe("local-broadcast", (event) => {
|
|
21470
21645
|
const { topic, payload, options } = event;
|
|
@@ -21540,9 +21715,17 @@ class Client {
|
|
|
21540
21715
|
return this.enqueueTask(task);
|
|
21541
21716
|
}
|
|
21542
21717
|
/**
|
|
21543
|
-
* `changeSyncMode` changes the synchronization mode of the given
|
|
21718
|
+
* `changeSyncMode` changes the synchronization mode of the given resource.
|
|
21544
21719
|
*/
|
|
21545
|
-
async changeSyncMode(
|
|
21720
|
+
async changeSyncMode(resource, syncMode) {
|
|
21721
|
+
return this.enqueueTask(async () => {
|
|
21722
|
+
if (resource instanceof Channel2) {
|
|
21723
|
+
return this.changeChannelSyncMode(resource, syncMode);
|
|
21724
|
+
}
|
|
21725
|
+
return this.changeDocumentSyncMode(resource, syncMode);
|
|
21726
|
+
});
|
|
21727
|
+
}
|
|
21728
|
+
async changeDocumentSyncMode(doc, syncMode) {
|
|
21546
21729
|
if (!this.isActive()) {
|
|
21547
21730
|
throw new YorkieError(
|
|
21548
21731
|
Code.ErrClientNotActivated,
|
|
@@ -21560,19 +21743,66 @@ class Client {
|
|
|
21560
21743
|
if (prevSyncMode === syncMode) {
|
|
21561
21744
|
return doc;
|
|
21562
21745
|
}
|
|
21563
|
-
|
|
21564
|
-
if (syncMode === "manual") {
|
|
21746
|
+
if (syncMode === "manual" || syncMode === "polling") {
|
|
21565
21747
|
attachment.cancelWatchStream();
|
|
21566
|
-
return doc;
|
|
21567
21748
|
}
|
|
21749
|
+
attachment.changeSyncMode(syncMode);
|
|
21568
21750
|
if (syncMode === "realtime") {
|
|
21569
21751
|
attachment.changeEventReceived = true;
|
|
21570
21752
|
}
|
|
21571
|
-
if (
|
|
21753
|
+
if (!attachment.pollIntervalPinned) {
|
|
21754
|
+
attachment.pollInterval = syncMode === "polling" ? DefaultPollingIntervalMs : 0;
|
|
21755
|
+
}
|
|
21756
|
+
if ((prevSyncMode === "manual" || prevSyncMode === "polling") && syncMode !== "manual" && syncMode !== "polling") {
|
|
21757
|
+
attachment.resetCancelled();
|
|
21572
21758
|
await this.runWatchLoop(doc.getKey());
|
|
21573
21759
|
}
|
|
21574
21760
|
return doc;
|
|
21575
21761
|
}
|
|
21762
|
+
/**
|
|
21763
|
+
* `assertValidChannelSyncMode` rejects sync modes that are not valid for
|
|
21764
|
+
* channels. `RealtimePushOnly` and `RealtimeSyncOff` are document-only.
|
|
21765
|
+
*/
|
|
21766
|
+
assertValidChannelSyncMode(syncMode) {
|
|
21767
|
+
if (syncMode !== "manual" && syncMode !== "realtime" && syncMode !== "polling") {
|
|
21768
|
+
throw new YorkieError(
|
|
21769
|
+
Code.ErrInvalidArgument,
|
|
21770
|
+
`invalid channel sync mode: ${syncMode}`
|
|
21771
|
+
);
|
|
21772
|
+
}
|
|
21773
|
+
}
|
|
21774
|
+
async changeChannelSyncMode(channel, syncMode) {
|
|
21775
|
+
if (!this.isActive()) {
|
|
21776
|
+
throw new YorkieError(
|
|
21777
|
+
Code.ErrClientNotActivated,
|
|
21778
|
+
`${this.key} is not active`
|
|
21779
|
+
);
|
|
21780
|
+
}
|
|
21781
|
+
const attachment = this.attachmentMap.get(channel.getKey());
|
|
21782
|
+
if (!attachment) {
|
|
21783
|
+
throw new YorkieError(
|
|
21784
|
+
Code.ErrNotAttached,
|
|
21785
|
+
`${channel.getKey()} is not attached`
|
|
21786
|
+
);
|
|
21787
|
+
}
|
|
21788
|
+
const prevSyncMode = attachment.syncMode;
|
|
21789
|
+
if (prevSyncMode === syncMode) {
|
|
21790
|
+
return channel;
|
|
21791
|
+
}
|
|
21792
|
+
this.assertValidChannelSyncMode(syncMode);
|
|
21793
|
+
if (prevSyncMode === "realtime") {
|
|
21794
|
+
attachment.cancelWatchStream();
|
|
21795
|
+
}
|
|
21796
|
+
attachment.changeSyncMode(syncMode);
|
|
21797
|
+
if (!attachment.pollIntervalPinned) {
|
|
21798
|
+
attachment.pollInterval = syncMode === "polling" ? DefaultPollingIntervalMs : syncMode === "realtime" ? this.channelHeartbeatInterval : 0;
|
|
21799
|
+
}
|
|
21800
|
+
if (syncMode === "realtime") {
|
|
21801
|
+
attachment.resetCancelled();
|
|
21802
|
+
await this.runWatchLoop(channel.getKey());
|
|
21803
|
+
}
|
|
21804
|
+
return channel;
|
|
21805
|
+
}
|
|
21576
21806
|
/**
|
|
21577
21807
|
* `sync` implementation that handles both Document and Channel.
|
|
21578
21808
|
*/
|
|
@@ -22417,6 +22647,7 @@ class Client {
|
|
|
22417
22647
|
return doc;
|
|
22418
22648
|
}
|
|
22419
22649
|
doc.applyChangePack(respPack);
|
|
22650
|
+
attachment.updateHeartbeatTime();
|
|
22420
22651
|
attachment.resource.publish([
|
|
22421
22652
|
{
|
|
22422
22653
|
type: DocEventType.SyncStatusChanged,
|
|
@@ -22527,8 +22758,11 @@ function isBinData(value) {
|
|
|
22527
22758
|
function isCounter(value) {
|
|
22528
22759
|
return typeof value === "object" && value !== null && value.type === "Counter" && typeof value.value === "object";
|
|
22529
22760
|
}
|
|
22761
|
+
function isDedupCounter(value) {
|
|
22762
|
+
return typeof value === "object" && value !== null && value.type === "DedupCounter" && typeof value.value === "object" && typeof value.registers === "string";
|
|
22763
|
+
}
|
|
22530
22764
|
function isObject(value) {
|
|
22531
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && !isText(value) && !isTree(value) && !isInt(value) && !isLong(value) && !isDate(value) && !isBinData(value) && !isCounter(value);
|
|
22765
|
+
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);
|
|
22532
22766
|
}
|
|
22533
22767
|
function parse(yson) {
|
|
22534
22768
|
try {
|
|
@@ -22544,6 +22778,12 @@ function parse(yson) {
|
|
|
22544
22778
|
}
|
|
22545
22779
|
function preprocessYSON(yson) {
|
|
22546
22780
|
let result = yson;
|
|
22781
|
+
result = result.replace(
|
|
22782
|
+
/DedupCounter\(Int\((-?\d+)\),"([^"]+)"\)/g,
|
|
22783
|
+
(_, value, registers) => {
|
|
22784
|
+
return `{"__yson_type":"DedupCounter","__yson_data":{"__yson_type":"Int","__yson_data":${value}},"__yson_registers":"${registers}"}`;
|
|
22785
|
+
}
|
|
22786
|
+
);
|
|
22547
22787
|
result = result.replace(
|
|
22548
22788
|
/Counter\((Int|Long)\((-?\d+)\)\)/g,
|
|
22549
22789
|
(_, type, value) => {
|
|
@@ -22604,6 +22844,20 @@ function postprocessValue(value) {
|
|
|
22604
22844
|
value: value.__yson_data
|
|
22605
22845
|
};
|
|
22606
22846
|
}
|
|
22847
|
+
if (value.__yson_type === "DedupCounter" && typeof value.__yson_data === "object" && typeof value.__yson_registers === "string") {
|
|
22848
|
+
const counterValue = postprocessValue(value.__yson_data);
|
|
22849
|
+
if (typeof counterValue === "object" && counterValue !== null && "type" in counterValue && counterValue.type === "Int") {
|
|
22850
|
+
return {
|
|
22851
|
+
type: "DedupCounter",
|
|
22852
|
+
value: counterValue,
|
|
22853
|
+
registers: value.__yson_registers
|
|
22854
|
+
};
|
|
22855
|
+
}
|
|
22856
|
+
throw new YorkieError(
|
|
22857
|
+
Code.ErrInvalidArgument,
|
|
22858
|
+
"DedupCounter must contain Int"
|
|
22859
|
+
);
|
|
22860
|
+
}
|
|
22607
22861
|
if (value.__yson_type === "Counter" && typeof value.__yson_data === "object") {
|
|
22608
22862
|
const counterValue = postprocessValue(value.__yson_data);
|
|
22609
22863
|
if (typeof counterValue === "object" && counterValue !== null && "type" in counterValue && (counterValue.type === "Int" || counterValue.type === "Long")) {
|
|
@@ -22692,6 +22946,7 @@ const YSON = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
|
|
|
22692
22946
|
isBinData,
|
|
22693
22947
|
isCounter,
|
|
22694
22948
|
isDate,
|
|
22949
|
+
isDedupCounter,
|
|
22695
22950
|
isInt,
|
|
22696
22951
|
isLong,
|
|
22697
22952
|
isObject,
|