@yorkie-js/react 0.7.7 → 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 +214 -43
- package/dist/yorkie-js-react.es.js.map +1 -1
- package/dist/yorkie-js-react.js +214 -43
- package/dist/yorkie-js-react.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ To get started using Yorkie React SDK, see: https://yorkie.dev/docs/getting-star
|
|
|
8
8
|
|
|
9
9
|
## Contributing
|
|
10
10
|
|
|
11
|
-
See [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
|
|
11
|
+
See [CONTRIBUTING](../../CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
|
|
12
12
|
|
|
13
13
|
## Contributors ✨
|
|
14
14
|
|
|
@@ -10,6 +10,7 @@ import { Presence } from '@yorkie-js/sdk';
|
|
|
10
10
|
import { PropsWithChildren } from 'react';
|
|
11
11
|
import { RevisionSummary } from '@yorkie-js/sdk';
|
|
12
12
|
import { StreamConnectionStatus } from '@yorkie-js/sdk';
|
|
13
|
+
import { SyncMode } from '@yorkie-js/sdk';
|
|
13
14
|
import { Text as Text_2 } from '@yorkie-js/sdk';
|
|
14
15
|
import { Tree } from '@yorkie-js/sdk';
|
|
15
16
|
|
|
@@ -19,12 +20,17 @@ import { Tree } from '@yorkie-js/sdk';
|
|
|
19
20
|
*
|
|
20
21
|
* @example
|
|
21
22
|
* ```tsx
|
|
23
|
+
* import { ChannelProvider, SyncMode } from '@yorkie-js/react';
|
|
24
|
+
*
|
|
22
25
|
* <YorkieProvider apiKey="..." rpcAddr="...">
|
|
23
|
-
* <ChannelProvider channelKey="room-123"
|
|
26
|
+
* <ChannelProvider channelKey="room-123" syncMode={SyncMode.Realtime}>
|
|
24
27
|
* <ChatRoom />
|
|
25
28
|
* </ChannelProvider>
|
|
26
29
|
* </YorkieProvider>
|
|
27
30
|
* ```
|
|
31
|
+
*
|
|
32
|
+
* The boolean `isRealtime` prop is also accepted as a shortcut for
|
|
33
|
+
* the Realtime/Manual choice (`true` → Realtime, `false` → Manual).
|
|
28
34
|
*/
|
|
29
35
|
export declare const ChannelProvider: React.FC<ChannelProviderProps>;
|
|
30
36
|
|
|
@@ -37,10 +43,24 @@ declare type ChannelProviderProps = PropsWithChildren<{
|
|
|
37
43
|
*/
|
|
38
44
|
channelKey: string;
|
|
39
45
|
/**
|
|
40
|
-
* `
|
|
41
|
-
* -
|
|
42
|
-
*
|
|
43
|
-
*
|
|
46
|
+
* `syncMode` selects how the channel keeps presence in sync with the server.
|
|
47
|
+
* - `SyncMode.Realtime` (default): open a watch stream and run the
|
|
48
|
+
* heartbeat. Required to receive broadcast events.
|
|
49
|
+
* - `SyncMode.Polling`: heartbeat-only. No watch stream is opened.
|
|
50
|
+
* Recommended for large channels where broadcast is not needed.
|
|
51
|
+
* - `SyncMode.Manual`: no automatic activity.
|
|
52
|
+
*
|
|
53
|
+
* If `isRealtime` is also set, `syncMode` wins.
|
|
54
|
+
*/
|
|
55
|
+
syncMode?: SyncMode;
|
|
56
|
+
/**
|
|
57
|
+
* `isRealtime` is a convenience prop covering only Realtime/Manual.
|
|
58
|
+
* - `true`: equivalent to `syncMode={SyncMode.Realtime}`.
|
|
59
|
+
* - `false`: equivalent to `syncMode={SyncMode.Manual}`.
|
|
60
|
+
*
|
|
61
|
+
* Use `syncMode` directly when you want `SyncMode.Polling`, which this
|
|
62
|
+
* boolean prop cannot express. When neither prop is set, the channel
|
|
63
|
+
* falls back to `SyncMode.Realtime`.
|
|
44
64
|
*/
|
|
45
65
|
isRealtime?: boolean;
|
|
46
66
|
}>;
|
|
@@ -92,6 +112,8 @@ export { RevisionSummary }
|
|
|
92
112
|
*/
|
|
93
113
|
export declare function shallowEqual<T>(valueA: T, valueB: T): boolean;
|
|
94
114
|
|
|
115
|
+
export { SyncMode }
|
|
116
|
+
|
|
95
117
|
export { Text_2 as Text }
|
|
96
118
|
|
|
97
119
|
export { Tree }
|
|
@@ -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
|
}
|
|
@@ -12532,8 +12534,10 @@ class CRDTTree extends CRDTElement {
|
|
|
12532
12534
|
break;
|
|
12533
12535
|
}
|
|
12534
12536
|
if (next.parent && next.parent === toParent) {
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
+
if (toLeft !== toParent) {
|
|
12538
|
+
collectFromLeft = next;
|
|
12539
|
+
collectFromParent = toParent;
|
|
12540
|
+
}
|
|
12537
12541
|
break;
|
|
12538
12542
|
}
|
|
12539
12543
|
current = next;
|
|
@@ -12596,6 +12600,7 @@ class CRDTTree extends CRDTElement {
|
|
|
12596
12600
|
tokensToBeRemoved,
|
|
12597
12601
|
editedAt
|
|
12598
12602
|
);
|
|
12603
|
+
const mergeLevel = toBeMergedNodes.length;
|
|
12599
12604
|
const pairs = [];
|
|
12600
12605
|
for (const node of nodesToBeRemoved) {
|
|
12601
12606
|
if (node.remove(editedAt)) {
|
|
@@ -12722,7 +12727,15 @@ class CRDTTree extends CRDTElement {
|
|
|
12722
12727
|
}
|
|
12723
12728
|
}
|
|
12724
12729
|
}
|
|
12725
|
-
return [
|
|
12730
|
+
return [
|
|
12731
|
+
changes,
|
|
12732
|
+
pairs,
|
|
12733
|
+
diff,
|
|
12734
|
+
nodesToBeRemoved,
|
|
12735
|
+
fromIdx,
|
|
12736
|
+
mergeLevel,
|
|
12737
|
+
preTombstoned
|
|
12738
|
+
];
|
|
12726
12739
|
}
|
|
12727
12740
|
/**
|
|
12728
12741
|
* `editT` edits the given range with the given value.
|
|
@@ -13163,11 +13176,35 @@ class CRDTTree extends CRDTElement {
|
|
|
13163
13176
|
return [prev, prev.isText ? TokenType.Text : TokenType.End];
|
|
13164
13177
|
}
|
|
13165
13178
|
}
|
|
13166
|
-
function
|
|
13167
|
-
|
|
13179
|
+
function cloneAndDropPreTombstoned(node, preTombstoned) {
|
|
13180
|
+
const clone = node.deepcopy();
|
|
13181
|
+
filterChildren(clone, preTombstoned);
|
|
13182
|
+
traverseAll(clone, (n) => {
|
|
13168
13183
|
n.removedAt = void 0;
|
|
13169
|
-
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;
|
|
13170
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;
|
|
13171
13208
|
}
|
|
13172
13209
|
class TreeEditOperation extends Operation {
|
|
13173
13210
|
fromPos;
|
|
@@ -13239,7 +13276,15 @@ class TreeEditOperation extends Operation {
|
|
|
13239
13276
|
this.toPos = tree.findPos(this.toIdx);
|
|
13240
13277
|
}
|
|
13241
13278
|
}
|
|
13242
|
-
const [
|
|
13279
|
+
const [
|
|
13280
|
+
changes,
|
|
13281
|
+
pairs,
|
|
13282
|
+
diff,
|
|
13283
|
+
removedNodes,
|
|
13284
|
+
preEditFromIdx,
|
|
13285
|
+
mergeLevel,
|
|
13286
|
+
preTombstoned
|
|
13287
|
+
] = tree.edit(
|
|
13243
13288
|
[this.fromPos, this.toPos],
|
|
13244
13289
|
this.contents?.map((content) => content.deepcopy()),
|
|
13245
13290
|
this.splitLevel,
|
|
@@ -13274,7 +13319,13 @@ class TreeEditOperation extends Operation {
|
|
|
13274
13319
|
let reverseOp;
|
|
13275
13320
|
const isPureSplit = this.splitLevel > 0 && !this.contents?.length && removedNodes.length === 0;
|
|
13276
13321
|
if (this.splitLevel === 0) {
|
|
13277
|
-
reverseOp = this.toReverseOperation(
|
|
13322
|
+
reverseOp = this.toReverseOperation(
|
|
13323
|
+
tree,
|
|
13324
|
+
removedNodes,
|
|
13325
|
+
preEditFromIdx,
|
|
13326
|
+
preTombstoned,
|
|
13327
|
+
mergeLevel
|
|
13328
|
+
);
|
|
13278
13329
|
} else if (isPureSplit) {
|
|
13279
13330
|
reverseOp = this.toSplitReverseOperation(tree, preEditFromIdx);
|
|
13280
13331
|
}
|
|
@@ -13312,7 +13363,7 @@ class TreeEditOperation extends Operation {
|
|
|
13312
13363
|
* @param removedNodes - Nodes that were removed by this edit
|
|
13313
13364
|
* @param preEditFromIdx - The from index captured BEFORE the edit
|
|
13314
13365
|
*/
|
|
13315
|
-
toReverseOperation(tree, removedNodes, preEditFromIdx) {
|
|
13366
|
+
toReverseOperation(tree, removedNodes, preEditFromIdx, preTombstoned, mergeLevel) {
|
|
13316
13367
|
if (this.redoSplitLevel !== void 0 && this.redoSplitLevel > 0) {
|
|
13317
13368
|
const splitRedoFromPos = tree.findPos(preEditFromIdx);
|
|
13318
13369
|
const splitRedoOp = TreeEditOperation.create(
|
|
@@ -13331,19 +13382,36 @@ class TreeEditOperation extends Operation {
|
|
|
13331
13382
|
);
|
|
13332
13383
|
return splitRedoOp;
|
|
13333
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
|
+
}
|
|
13334
13404
|
const insertedContentSize = this.contents ? this.contents.reduce((sum, node) => sum + node.paddedSize(), 0) : 0;
|
|
13335
13405
|
const maxNeededIdx = preEditFromIdx + insertedContentSize;
|
|
13336
13406
|
if (maxNeededIdx > tree.getSize()) {
|
|
13337
13407
|
return void 0;
|
|
13338
13408
|
}
|
|
13339
13409
|
const topLevelRemoved = removedNodes.filter(
|
|
13340
|
-
(node) => !node.parent || !removedNodes.includes(node.parent)
|
|
13410
|
+
(node) => !preTombstoned.has(node.id.toIDString()) && (!node.parent || !removedNodes.includes(node.parent))
|
|
13341
13411
|
);
|
|
13342
|
-
const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map(
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
return clone;
|
|
13346
|
-
}) : void 0;
|
|
13412
|
+
const reverseContents = topLevelRemoved.length > 0 ? topLevelRemoved.map(
|
|
13413
|
+
(n) => cloneAndDropPreTombstoned(n, preTombstoned)
|
|
13414
|
+
) : void 0;
|
|
13347
13415
|
const reverseFromPos = tree.findPos(preEditFromIdx);
|
|
13348
13416
|
let reverseToPos;
|
|
13349
13417
|
if (insertedContentSize > 0) {
|
|
@@ -20157,6 +20225,11 @@ class Document {
|
|
|
20157
20225
|
}
|
|
20158
20226
|
]);
|
|
20159
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
|
+
*/
|
|
20160
20233
|
clearHistory() {
|
|
20161
20234
|
this.internalHistory.clearRedo();
|
|
20162
20235
|
this.internalHistory.clearUndo();
|
|
@@ -20616,10 +20689,7 @@ class Document {
|
|
|
20616
20689
|
}
|
|
20617
20690
|
const ops = isUndo ? this.internalHistory.popUndo() : this.internalHistory.popRedo();
|
|
20618
20691
|
if (!ops) {
|
|
20619
|
-
|
|
20620
|
-
Code.ErrRefused,
|
|
20621
|
-
`There is no operation to be ${isUndo ? "undone" : "redone"}`
|
|
20622
|
-
);
|
|
20692
|
+
return;
|
|
20623
20693
|
}
|
|
20624
20694
|
this.ensureClone();
|
|
20625
20695
|
const ctx = ChangeContext.create(
|
|
@@ -20712,6 +20782,8 @@ class Attachment {
|
|
|
20712
20782
|
syncMode;
|
|
20713
20783
|
changeEventReceived;
|
|
20714
20784
|
lastHeartbeatTime;
|
|
20785
|
+
pollInterval;
|
|
20786
|
+
pollIntervalPinned;
|
|
20715
20787
|
reconnectStreamDelay;
|
|
20716
20788
|
cancelled;
|
|
20717
20789
|
watchStream;
|
|
@@ -20719,13 +20791,15 @@ class Attachment {
|
|
|
20719
20791
|
watchAbortController;
|
|
20720
20792
|
syncPromise;
|
|
20721
20793
|
_detaching = false;
|
|
20722
|
-
constructor(reconnectStreamDelay, resource, resourceID, syncMode) {
|
|
20794
|
+
constructor(reconnectStreamDelay, resource, resourceID, syncMode, pollInterval = 0, pollIntervalPinned = false) {
|
|
20723
20795
|
this.reconnectStreamDelay = reconnectStreamDelay;
|
|
20724
20796
|
this.resource = resource;
|
|
20725
20797
|
this.resourceID = resourceID;
|
|
20726
20798
|
this.syncMode = syncMode;
|
|
20727
20799
|
this.changeEventReceived = syncMode !== void 0 ? false : void 0;
|
|
20728
20800
|
this.lastHeartbeatTime = Date.now();
|
|
20801
|
+
this.pollInterval = pollInterval;
|
|
20802
|
+
this.pollIntervalPinned = pollIntervalPinned;
|
|
20729
20803
|
this.cancelled = false;
|
|
20730
20804
|
}
|
|
20731
20805
|
/**
|
|
@@ -20745,6 +20819,9 @@ class Attachment {
|
|
|
20745
20819
|
if (this.syncMode === SyncMode.RealtimePushOnly) {
|
|
20746
20820
|
return this.resource.hasLocalChanges();
|
|
20747
20821
|
}
|
|
20822
|
+
if (this.syncMode === SyncMode.Polling) {
|
|
20823
|
+
return Date.now() - this.lastHeartbeatTime >= this.pollInterval;
|
|
20824
|
+
}
|
|
20748
20825
|
return this.syncMode !== SyncMode.Manual && (this.resource.hasLocalChanges() || (this.changeEventReceived ?? false));
|
|
20749
20826
|
}
|
|
20750
20827
|
/**
|
|
@@ -20758,7 +20835,8 @@ class Attachment {
|
|
|
20758
20835
|
if (this.syncMode === SyncMode.Manual) {
|
|
20759
20836
|
return false;
|
|
20760
20837
|
}
|
|
20761
|
-
|
|
20838
|
+
const interval = this.pollInterval > 0 ? this.pollInterval : heartbeatInterval;
|
|
20839
|
+
return Date.now() - this.lastHeartbeatTime >= interval;
|
|
20762
20840
|
}
|
|
20763
20841
|
/**
|
|
20764
20842
|
* `updateHeartbeatTime` updates the last heartbeat time.
|
|
@@ -20837,6 +20915,16 @@ class Attachment {
|
|
|
20837
20915
|
}
|
|
20838
20916
|
}
|
|
20839
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
|
+
}
|
|
20840
20928
|
/**
|
|
20841
20929
|
* `cancelWatchStream` cancels the watch stream.
|
|
20842
20930
|
*/
|
|
@@ -20871,7 +20959,7 @@ function createAuthInterceptor(apiKey, token) {
|
|
|
20871
20959
|
};
|
|
20872
20960
|
}
|
|
20873
20961
|
const name$1 = "@yorkie-js/sdk";
|
|
20874
|
-
const version$1 = "0.7.
|
|
20962
|
+
const version$1 = "0.7.8";
|
|
20875
20963
|
const pkg$1 = {
|
|
20876
20964
|
name: name$1,
|
|
20877
20965
|
version: version$1
|
|
@@ -21154,8 +21242,10 @@ var SyncMode = /* @__PURE__ */ ((SyncMode2) => {
|
|
|
21154
21242
|
SyncMode2["Realtime"] = "realtime";
|
|
21155
21243
|
SyncMode2["RealtimePushOnly"] = "realtime-pushonly";
|
|
21156
21244
|
SyncMode2["RealtimeSyncOff"] = "realtime-syncoff";
|
|
21245
|
+
SyncMode2["Polling"] = "polling";
|
|
21157
21246
|
return SyncMode2;
|
|
21158
21247
|
})(SyncMode || {});
|
|
21248
|
+
const DefaultPollingIntervalMs = 3e3;
|
|
21159
21249
|
const DefaultClientOptions = {
|
|
21160
21250
|
rpcAddr: "https://api.yorkie.dev",
|
|
21161
21251
|
syncLoopDuration: 50,
|
|
@@ -21351,6 +21441,14 @@ class Client {
|
|
|
21351
21441
|
doc.setActor(this.id);
|
|
21352
21442
|
doc.update((_, p) => p.set(opts.initialPresence || {}));
|
|
21353
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;
|
|
21354
21452
|
return this.enqueueTask(async () => {
|
|
21355
21453
|
try {
|
|
21356
21454
|
const res = await this.rpcClient.attachDocument(
|
|
@@ -21380,10 +21478,12 @@ class Client {
|
|
|
21380
21478
|
this.reconnectStreamDelay,
|
|
21381
21479
|
doc,
|
|
21382
21480
|
res.documentId,
|
|
21383
|
-
syncMode
|
|
21481
|
+
syncMode,
|
|
21482
|
+
pollInterval,
|
|
21483
|
+
pollIntervalPinned
|
|
21384
21484
|
)
|
|
21385
21485
|
);
|
|
21386
|
-
if (syncMode !== "manual") {
|
|
21486
|
+
if (syncMode !== "manual" && syncMode !== "polling") {
|
|
21387
21487
|
await this.runWatchLoop(doc.getKey());
|
|
21388
21488
|
}
|
|
21389
21489
|
logger.info(`[AD] c:"${this.getKey()}" attaches d:"${doc.getKey()}"`);
|
|
@@ -21399,6 +21499,7 @@ class Client {
|
|
|
21399
21499
|
}
|
|
21400
21500
|
});
|
|
21401
21501
|
}
|
|
21502
|
+
doc.clearHistory();
|
|
21402
21503
|
return doc;
|
|
21403
21504
|
} catch (err) {
|
|
21404
21505
|
logger.error(`[AD] c:"${this.getKey()}" err :`, err);
|
|
@@ -21509,12 +21610,23 @@ class Client {
|
|
|
21509
21610
|
channel.setSessionID(res.sessionId);
|
|
21510
21611
|
channel.updateSessionCount(Number(res.sessionCount), 0);
|
|
21511
21612
|
channel.applyStatus(ChannelStatus.Attached);
|
|
21512
|
-
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;
|
|
21513
21623
|
const attachment = new Attachment(
|
|
21514
21624
|
this.reconnectStreamDelay,
|
|
21515
21625
|
channel,
|
|
21516
21626
|
res.sessionId,
|
|
21517
|
-
syncMode
|
|
21627
|
+
syncMode,
|
|
21628
|
+
pollInterval,
|
|
21629
|
+
pollIntervalPinned
|
|
21518
21630
|
);
|
|
21519
21631
|
channel.subscribe("local-broadcast", (event) => {
|
|
21520
21632
|
const { topic, payload, options } = event;
|
|
@@ -21590,9 +21702,17 @@ class Client {
|
|
|
21590
21702
|
return this.enqueueTask(task);
|
|
21591
21703
|
}
|
|
21592
21704
|
/**
|
|
21593
|
-
* `changeSyncMode` changes the synchronization mode of the given
|
|
21705
|
+
* `changeSyncMode` changes the synchronization mode of the given resource.
|
|
21594
21706
|
*/
|
|
21595
|
-
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) {
|
|
21596
21716
|
if (!this.isActive()) {
|
|
21597
21717
|
throw new YorkieError(
|
|
21598
21718
|
Code.ErrClientNotActivated,
|
|
@@ -21610,19 +21730,66 @@ class Client {
|
|
|
21610
21730
|
if (prevSyncMode === syncMode) {
|
|
21611
21731
|
return doc;
|
|
21612
21732
|
}
|
|
21613
|
-
|
|
21614
|
-
if (syncMode === "manual") {
|
|
21733
|
+
if (syncMode === "manual" || syncMode === "polling") {
|
|
21615
21734
|
attachment.cancelWatchStream();
|
|
21616
|
-
return doc;
|
|
21617
21735
|
}
|
|
21736
|
+
attachment.changeSyncMode(syncMode);
|
|
21618
21737
|
if (syncMode === "realtime") {
|
|
21619
21738
|
attachment.changeEventReceived = true;
|
|
21620
21739
|
}
|
|
21621
|
-
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();
|
|
21622
21745
|
await this.runWatchLoop(doc.getKey());
|
|
21623
21746
|
}
|
|
21624
21747
|
return doc;
|
|
21625
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
|
+
}
|
|
21626
21793
|
/**
|
|
21627
21794
|
* `sync` implementation that handles both Document and Channel.
|
|
21628
21795
|
*/
|
|
@@ -22467,6 +22634,7 @@ class Client {
|
|
|
22467
22634
|
return doc;
|
|
22468
22635
|
}
|
|
22469
22636
|
doc.applyChangePack(respPack);
|
|
22637
|
+
attachment.updateHeartbeatTime();
|
|
22470
22638
|
attachment.resource.publish([
|
|
22471
22639
|
{
|
|
22472
22640
|
type: DocEventType.SyncStatusChanged,
|
|
@@ -22792,7 +22960,7 @@ if (typeof globalThis !== "undefined") {
|
|
|
22792
22960
|
};
|
|
22793
22961
|
}
|
|
22794
22962
|
const name = "@yorkie-js/react";
|
|
22795
|
-
const version = "0.7.
|
|
22963
|
+
const version = "0.7.8";
|
|
22796
22964
|
const pkg = {
|
|
22797
22965
|
name,
|
|
22798
22966
|
version
|
|
@@ -23248,7 +23416,7 @@ function createChannelStore(initialState) {
|
|
|
23248
23416
|
const ChannelContext = createContext(
|
|
23249
23417
|
void 0
|
|
23250
23418
|
);
|
|
23251
|
-
function useYorkieChannel(client, clientLoading, clientError, channelKey,
|
|
23419
|
+
function useYorkieChannel(client, clientLoading, clientError, channelKey, syncMode, channelStore) {
|
|
23252
23420
|
const channelRef = useRef(void 0);
|
|
23253
23421
|
const [didMount, setDidMount] = useState(false);
|
|
23254
23422
|
useEffect(() => {
|
|
@@ -23277,7 +23445,7 @@ function useYorkieChannel(client, clientLoading, clientError, channelKey, isReal
|
|
|
23277
23445
|
}));
|
|
23278
23446
|
try {
|
|
23279
23447
|
const newChannel = new Channel2(channelKey);
|
|
23280
|
-
await client.attach(newChannel, {
|
|
23448
|
+
await client.attach(newChannel, { syncMode });
|
|
23281
23449
|
channelRef.current = newChannel;
|
|
23282
23450
|
unsubscribe = newChannel.subscribe(() => {
|
|
23283
23451
|
channelStore.setState((state) => ({
|
|
@@ -23315,12 +23483,13 @@ function useYorkieChannel(client, clientLoading, clientError, channelKey, isReal
|
|
|
23315
23483
|
}
|
|
23316
23484
|
detachChannel();
|
|
23317
23485
|
};
|
|
23318
|
-
}, [client, clientLoading, clientError, channelKey,
|
|
23486
|
+
}, [client, clientLoading, clientError, channelKey, syncMode, didMount]);
|
|
23319
23487
|
}
|
|
23320
23488
|
const ChannelProvider = ({
|
|
23321
23489
|
children,
|
|
23322
23490
|
channelKey,
|
|
23323
|
-
|
|
23491
|
+
syncMode,
|
|
23492
|
+
isRealtime
|
|
23324
23493
|
}) => {
|
|
23325
23494
|
const { client, loading: clientLoading, error: clientError } = useYorkie();
|
|
23326
23495
|
const channelStoreRef = useRef(
|
|
@@ -23335,12 +23504,13 @@ const ChannelProvider = ({
|
|
|
23335
23504
|
});
|
|
23336
23505
|
}
|
|
23337
23506
|
const channelStore = channelStoreRef.current;
|
|
23507
|
+
const resolvedSyncMode = syncMode ?? (isRealtime === false ? SyncMode.Manual : SyncMode.Realtime);
|
|
23338
23508
|
useYorkieChannel(
|
|
23339
23509
|
client,
|
|
23340
23510
|
clientLoading,
|
|
23341
23511
|
clientError,
|
|
23342
23512
|
channelKey,
|
|
23343
|
-
|
|
23513
|
+
resolvedSyncMode,
|
|
23344
23514
|
channelStore
|
|
23345
23515
|
);
|
|
23346
23516
|
return /* @__PURE__ */ jsx(ChannelContext.Provider, { value: channelStore, children });
|
|
@@ -23367,6 +23537,7 @@ export {
|
|
|
23367
23537
|
ChannelProvider,
|
|
23368
23538
|
Counter,
|
|
23369
23539
|
DocumentProvider,
|
|
23540
|
+
SyncMode,
|
|
23370
23541
|
Text,
|
|
23371
23542
|
Tree,
|
|
23372
23543
|
YorkieProvider,
|