@liveblocks/core 3.20.0 → 3.21.0-exp10
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/dist/index.cjs +1388 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +354 -32
- package/dist/index.d.ts +354 -32
- package/dist/index.js +1318 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/version.ts
|
|
8
8
|
var PKG_NAME = "@liveblocks/core";
|
|
9
|
-
var PKG_VERSION = "3.
|
|
9
|
+
var PKG_VERSION = "3.21.0-exp10";
|
|
10
10
|
var PKG_FORMAT = "cjs";
|
|
11
11
|
|
|
12
12
|
// src/dupe-detection.ts
|
|
@@ -3847,6 +3847,7 @@ var ManagedSocket = class {
|
|
|
3847
3847
|
|
|
3848
3848
|
// src/internal.ts
|
|
3849
3849
|
var kInternal = /* @__PURE__ */ Symbol();
|
|
3850
|
+
var kStorageUpdateSource = /* @__PURE__ */ Symbol();
|
|
3850
3851
|
|
|
3851
3852
|
// src/lib/IncrementalJsonParser.ts
|
|
3852
3853
|
var EMPTY_OBJECT = Object.freeze({});
|
|
@@ -5767,13 +5768,15 @@ var OpCode = Object.freeze({
|
|
|
5767
5768
|
DELETE_CRDT: 5,
|
|
5768
5769
|
DELETE_OBJECT_KEY: 6,
|
|
5769
5770
|
CREATE_MAP: 7,
|
|
5770
|
-
CREATE_REGISTER: 8
|
|
5771
|
+
CREATE_REGISTER: 8,
|
|
5772
|
+
CREATE_TEXT: 9,
|
|
5773
|
+
UPDATE_TEXT: 10
|
|
5771
5774
|
});
|
|
5772
5775
|
function isIgnoredOp(op) {
|
|
5773
5776
|
return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
|
|
5774
5777
|
}
|
|
5775
5778
|
function isCreateOp(op) {
|
|
5776
|
-
return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
|
|
5779
|
+
return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_TEXT;
|
|
5777
5780
|
}
|
|
5778
5781
|
|
|
5779
5782
|
// src/protocol/StorageNode.ts
|
|
@@ -5781,7 +5784,8 @@ var CrdtType = Object.freeze({
|
|
|
5781
5784
|
OBJECT: 0,
|
|
5782
5785
|
LIST: 1,
|
|
5783
5786
|
MAP: 2,
|
|
5784
|
-
REGISTER: 3
|
|
5787
|
+
REGISTER: 3,
|
|
5788
|
+
TEXT: 4
|
|
5785
5789
|
});
|
|
5786
5790
|
function isRootStorageNode(node) {
|
|
5787
5791
|
return node[0] === "root";
|
|
@@ -5798,6 +5802,9 @@ function isMapStorageNode(node) {
|
|
|
5798
5802
|
function isRegisterStorageNode(node) {
|
|
5799
5803
|
return node[1].type === CrdtType.REGISTER;
|
|
5800
5804
|
}
|
|
5805
|
+
function isTextStorageNode(node) {
|
|
5806
|
+
return node[1].type === CrdtType.TEXT;
|
|
5807
|
+
}
|
|
5801
5808
|
function isCompactRootNode(node) {
|
|
5802
5809
|
return node[0] === "root";
|
|
5803
5810
|
}
|
|
@@ -5820,6 +5827,9 @@ function* compactNodesToNodeStream(compactNodes) {
|
|
|
5820
5827
|
case CrdtType.REGISTER:
|
|
5821
5828
|
yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
|
|
5822
5829
|
break;
|
|
5830
|
+
case CrdtType.TEXT:
|
|
5831
|
+
yield [cnode[0], { type: CrdtType.TEXT, parentId: cnode[2], parentKey: cnode[3], data: cnode[4], version: cnode[5] }];
|
|
5832
|
+
break;
|
|
5823
5833
|
default:
|
|
5824
5834
|
}
|
|
5825
5835
|
}
|
|
@@ -5848,6 +5858,17 @@ function* nodeStreamToCompactNodes(nodes) {
|
|
|
5848
5858
|
const id = node[0];
|
|
5849
5859
|
const crdt = node[1];
|
|
5850
5860
|
yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
|
|
5861
|
+
} else if (isTextStorageNode(node)) {
|
|
5862
|
+
const id = node[0];
|
|
5863
|
+
const crdt = node[1];
|
|
5864
|
+
yield [
|
|
5865
|
+
id,
|
|
5866
|
+
CrdtType.TEXT,
|
|
5867
|
+
crdt.parentId,
|
|
5868
|
+
crdt.parentKey,
|
|
5869
|
+
crdt.data,
|
|
5870
|
+
crdt.version
|
|
5871
|
+
];
|
|
5851
5872
|
} else {
|
|
5852
5873
|
}
|
|
5853
5874
|
}
|
|
@@ -6050,6 +6071,10 @@ ${parentKey}`;
|
|
|
6050
6071
|
get size() {
|
|
6051
6072
|
return this.#byOpId.size;
|
|
6052
6073
|
}
|
|
6074
|
+
/** The still-unacknowledged op with the given opId, if any. */
|
|
6075
|
+
get(opId) {
|
|
6076
|
+
return this.#byOpId.get(opId);
|
|
6077
|
+
}
|
|
6053
6078
|
/**
|
|
6054
6079
|
* Mark the given Op as still unacknowledged.
|
|
6055
6080
|
*/
|
|
@@ -6150,8 +6175,8 @@ function createManagedPool(roomId, options) {
|
|
|
6150
6175
|
deleteNode: (id) => void nodes.delete(id),
|
|
6151
6176
|
generateId: () => `${getCurrentConnectionId()}:${clock++}`,
|
|
6152
6177
|
generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
|
|
6153
|
-
dispatch(ops, reverse, storageUpdates) {
|
|
6154
|
-
_optionalChain([onDispatch, 'optionalCall', _126 => _126(ops, reverse, storageUpdates)]);
|
|
6178
|
+
dispatch(ops, reverse, storageUpdates, options2) {
|
|
6179
|
+
_optionalChain([onDispatch, 'optionalCall', _126 => _126(ops, reverse, storageUpdates, options2)]);
|
|
6155
6180
|
},
|
|
6156
6181
|
assertStorageIsWritable: () => {
|
|
6157
6182
|
if (!isStorageWritable()) {
|
|
@@ -8710,6 +8735,1117 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8710
8735
|
}
|
|
8711
8736
|
}, _class2.__initStatic(), _class2);
|
|
8712
8737
|
|
|
8738
|
+
// src/crdts/liveTextOps.ts
|
|
8739
|
+
function attributesEqual(left, right) {
|
|
8740
|
+
if (left === right) {
|
|
8741
|
+
return true;
|
|
8742
|
+
}
|
|
8743
|
+
if (left === void 0 || right === void 0) {
|
|
8744
|
+
return false;
|
|
8745
|
+
}
|
|
8746
|
+
const leftKeys = Object.keys(left);
|
|
8747
|
+
const rightKeys = Object.keys(right);
|
|
8748
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
8749
|
+
return false;
|
|
8750
|
+
}
|
|
8751
|
+
for (const key of leftKeys) {
|
|
8752
|
+
if (left[key] !== right[key]) {
|
|
8753
|
+
return false;
|
|
8754
|
+
}
|
|
8755
|
+
}
|
|
8756
|
+
return true;
|
|
8757
|
+
}
|
|
8758
|
+
function cloneAttributes(attributes) {
|
|
8759
|
+
return attributes === void 0 ? void 0 : freeze({ ...attributes });
|
|
8760
|
+
}
|
|
8761
|
+
function normalizeSegments(segments) {
|
|
8762
|
+
const normalized = [];
|
|
8763
|
+
for (const segment of segments) {
|
|
8764
|
+
if (segment.text.length === 0) {
|
|
8765
|
+
continue;
|
|
8766
|
+
}
|
|
8767
|
+
const last = normalized.at(-1);
|
|
8768
|
+
const attributes = cloneAttributes(segment.attributes);
|
|
8769
|
+
if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
|
|
8770
|
+
last.text += segment.text;
|
|
8771
|
+
} else {
|
|
8772
|
+
normalized.push({ text: segment.text, attributes });
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8775
|
+
return normalized;
|
|
8776
|
+
}
|
|
8777
|
+
function dataToSegments(data) {
|
|
8778
|
+
return normalizeSegments(
|
|
8779
|
+
data.map(([text, attributes]) => ({
|
|
8780
|
+
text,
|
|
8781
|
+
attributes
|
|
8782
|
+
}))
|
|
8783
|
+
);
|
|
8784
|
+
}
|
|
8785
|
+
function segmentsToData(segments) {
|
|
8786
|
+
return segments.map(
|
|
8787
|
+
(segment) => segment.attributes === void 0 ? [segment.text] : [segment.text, { ...segment.attributes }]
|
|
8788
|
+
);
|
|
8789
|
+
}
|
|
8790
|
+
function textLength(segments) {
|
|
8791
|
+
return segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
8792
|
+
}
|
|
8793
|
+
function splitSegmentsAt(segments, index) {
|
|
8794
|
+
const result = [];
|
|
8795
|
+
let offset = 0;
|
|
8796
|
+
for (const segment of segments) {
|
|
8797
|
+
const end = offset + segment.text.length;
|
|
8798
|
+
if (index > offset && index < end) {
|
|
8799
|
+
const before2 = segment.text.slice(0, index - offset);
|
|
8800
|
+
const after2 = segment.text.slice(index - offset);
|
|
8801
|
+
result.push({ text: before2, attributes: segment.attributes });
|
|
8802
|
+
result.push({ text: after2, attributes: segment.attributes });
|
|
8803
|
+
} else {
|
|
8804
|
+
result.push({ text: segment.text, attributes: segment.attributes });
|
|
8805
|
+
}
|
|
8806
|
+
offset = end;
|
|
8807
|
+
}
|
|
8808
|
+
return result;
|
|
8809
|
+
}
|
|
8810
|
+
function clipRange(index, length, contentLength) {
|
|
8811
|
+
const clippedIndex = Math.max(0, Math.min(index, contentLength));
|
|
8812
|
+
const clippedEnd = Math.max(
|
|
8813
|
+
clippedIndex,
|
|
8814
|
+
Math.min(index + length, contentLength)
|
|
8815
|
+
);
|
|
8816
|
+
return { index: clippedIndex, length: clippedEnd - clippedIndex };
|
|
8817
|
+
}
|
|
8818
|
+
function applyInsert(segments, index, text, attributes) {
|
|
8819
|
+
if (text.length === 0) {
|
|
8820
|
+
return normalizeSegments(segments);
|
|
8821
|
+
}
|
|
8822
|
+
const split = splitSegmentsAt(segments, index);
|
|
8823
|
+
const result = [];
|
|
8824
|
+
let offset = 0;
|
|
8825
|
+
let inserted = false;
|
|
8826
|
+
for (const segment of split) {
|
|
8827
|
+
if (!inserted && offset === index) {
|
|
8828
|
+
result.push({ text, attributes });
|
|
8829
|
+
inserted = true;
|
|
8830
|
+
}
|
|
8831
|
+
result.push(segment);
|
|
8832
|
+
offset += segment.text.length;
|
|
8833
|
+
}
|
|
8834
|
+
if (!inserted) {
|
|
8835
|
+
result.push({ text, attributes });
|
|
8836
|
+
}
|
|
8837
|
+
return normalizeSegments(result);
|
|
8838
|
+
}
|
|
8839
|
+
function extractDeletedSegments(segments, index, length) {
|
|
8840
|
+
const split = splitSegmentsAt(
|
|
8841
|
+
splitSegmentsAt(segments, index),
|
|
8842
|
+
index + length
|
|
8843
|
+
);
|
|
8844
|
+
const deleted = [];
|
|
8845
|
+
let offset = 0;
|
|
8846
|
+
for (const segment of split) {
|
|
8847
|
+
const end = offset + segment.text.length;
|
|
8848
|
+
if (offset >= index && end <= index + length) {
|
|
8849
|
+
deleted.push({
|
|
8850
|
+
text: segment.text,
|
|
8851
|
+
attributes: segment.attributes
|
|
8852
|
+
});
|
|
8853
|
+
}
|
|
8854
|
+
offset = end;
|
|
8855
|
+
}
|
|
8856
|
+
return normalizeSegments(deleted);
|
|
8857
|
+
}
|
|
8858
|
+
function applyDelete(segments, index, length) {
|
|
8859
|
+
const deletedSegments = extractDeletedSegments(segments, index, length);
|
|
8860
|
+
const split = splitSegmentsAt(
|
|
8861
|
+
splitSegmentsAt(segments, index),
|
|
8862
|
+
index + length
|
|
8863
|
+
);
|
|
8864
|
+
const result = [];
|
|
8865
|
+
let offset = 0;
|
|
8866
|
+
let deletedText = "";
|
|
8867
|
+
for (const segment of split) {
|
|
8868
|
+
const end = offset + segment.text.length;
|
|
8869
|
+
if (offset >= index && end <= index + length) {
|
|
8870
|
+
deletedText += segment.text;
|
|
8871
|
+
} else {
|
|
8872
|
+
result.push(segment);
|
|
8873
|
+
}
|
|
8874
|
+
offset = end;
|
|
8875
|
+
}
|
|
8876
|
+
return {
|
|
8877
|
+
segments: normalizeSegments(result),
|
|
8878
|
+
deletedText,
|
|
8879
|
+
deletedSegments
|
|
8880
|
+
};
|
|
8881
|
+
}
|
|
8882
|
+
function applyFormat(segments, index, length, attributes) {
|
|
8883
|
+
const split = splitSegmentsAt(
|
|
8884
|
+
splitSegmentsAt(segments, index),
|
|
8885
|
+
index + length
|
|
8886
|
+
);
|
|
8887
|
+
const result = [];
|
|
8888
|
+
let offset = 0;
|
|
8889
|
+
for (const segment of split) {
|
|
8890
|
+
const end = offset + segment.text.length;
|
|
8891
|
+
if (offset >= index && end <= index + length) {
|
|
8892
|
+
const nextAttributes = {
|
|
8893
|
+
..._nullishCoalesce(segment.attributes, () => ( {}))
|
|
8894
|
+
};
|
|
8895
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
8896
|
+
if (value === null) {
|
|
8897
|
+
delete nextAttributes[key];
|
|
8898
|
+
} else {
|
|
8899
|
+
nextAttributes[key] = value;
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
8902
|
+
result.push({
|
|
8903
|
+
text: segment.text,
|
|
8904
|
+
attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
|
|
8905
|
+
});
|
|
8906
|
+
} else {
|
|
8907
|
+
result.push(segment);
|
|
8908
|
+
}
|
|
8909
|
+
offset = end;
|
|
8910
|
+
}
|
|
8911
|
+
return normalizeSegments(result);
|
|
8912
|
+
}
|
|
8913
|
+
function formatReverseOperations(segments, index, length, patch) {
|
|
8914
|
+
const split = splitSegmentsAt(
|
|
8915
|
+
splitSegmentsAt(segments, index),
|
|
8916
|
+
index + length
|
|
8917
|
+
);
|
|
8918
|
+
const result = [];
|
|
8919
|
+
let offset = 0;
|
|
8920
|
+
for (const segment of split) {
|
|
8921
|
+
const end = offset + segment.text.length;
|
|
8922
|
+
if (offset >= index && end <= index + length) {
|
|
8923
|
+
const attributes = {};
|
|
8924
|
+
for (const key of Object.keys(patch)) {
|
|
8925
|
+
attributes[key] = _nullishCoalesce(_optionalChain([segment, 'access', _214 => _214.attributes, 'optionalAccess', _215 => _215[key]]), () => ( null));
|
|
8926
|
+
}
|
|
8927
|
+
result.push({
|
|
8928
|
+
type: "format",
|
|
8929
|
+
index: offset,
|
|
8930
|
+
length: segment.text.length,
|
|
8931
|
+
attributes
|
|
8932
|
+
});
|
|
8933
|
+
}
|
|
8934
|
+
offset = end;
|
|
8935
|
+
}
|
|
8936
|
+
return result;
|
|
8937
|
+
}
|
|
8938
|
+
function mapIndexThroughOperation(index, op) {
|
|
8939
|
+
if (op.type === "insert") {
|
|
8940
|
+
return op.index <= index ? index + op.text.length : index;
|
|
8941
|
+
} else if (op.type === "delete") {
|
|
8942
|
+
if (op.index >= index) {
|
|
8943
|
+
return index;
|
|
8944
|
+
}
|
|
8945
|
+
return Math.max(op.index, index - op.length);
|
|
8946
|
+
} else {
|
|
8947
|
+
return index;
|
|
8948
|
+
}
|
|
8949
|
+
}
|
|
8950
|
+
function mapTextIndexThroughOperations(index, ops) {
|
|
8951
|
+
let mapped = index;
|
|
8952
|
+
for (const op of ops) {
|
|
8953
|
+
mapped = mapIndexThroughOperation(mapped, op);
|
|
8954
|
+
}
|
|
8955
|
+
return mapped;
|
|
8956
|
+
}
|
|
8957
|
+
function inverseMapIndexThroughOperation(index, op) {
|
|
8958
|
+
if (op.type === "insert") {
|
|
8959
|
+
if (index <= op.index) {
|
|
8960
|
+
return index;
|
|
8961
|
+
}
|
|
8962
|
+
return Math.max(op.index, index - op.text.length);
|
|
8963
|
+
} else if (op.type === "delete") {
|
|
8964
|
+
return op.index <= index ? index + op.length : index;
|
|
8965
|
+
} else {
|
|
8966
|
+
return index;
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
function inverseMapTextIndexThroughOperations(index, ops) {
|
|
8970
|
+
let mapped = index;
|
|
8971
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
8972
|
+
mapped = inverseMapIndexThroughOperation(mapped, ops[i]);
|
|
8973
|
+
}
|
|
8974
|
+
return mapped;
|
|
8975
|
+
}
|
|
8976
|
+
function oppositeOrder(order) {
|
|
8977
|
+
return order === "before" ? "after" : "before";
|
|
8978
|
+
}
|
|
8979
|
+
function mapIndexOverDelete(index, deleteIndex, deleteLength) {
|
|
8980
|
+
if (deleteIndex >= index) {
|
|
8981
|
+
return index;
|
|
8982
|
+
}
|
|
8983
|
+
return Math.max(deleteIndex, index - deleteLength);
|
|
8984
|
+
}
|
|
8985
|
+
function transformInsert(op, over, order) {
|
|
8986
|
+
if (over.type === "insert") {
|
|
8987
|
+
const shifts = over.index < op.index || over.index === op.index && order === "after";
|
|
8988
|
+
return [shifts ? { ...op, index: op.index + over.text.length } : { ...op }];
|
|
8989
|
+
} else if (over.type === "delete") {
|
|
8990
|
+
return [
|
|
8991
|
+
{ ...op, index: mapIndexOverDelete(op.index, over.index, over.length) }
|
|
8992
|
+
];
|
|
8993
|
+
} else {
|
|
8994
|
+
return [{ ...op }];
|
|
8995
|
+
}
|
|
8996
|
+
}
|
|
8997
|
+
function transformDelete(op, over) {
|
|
8998
|
+
const start = op.index;
|
|
8999
|
+
const end = op.index + op.length;
|
|
9000
|
+
if (over.type === "insert") {
|
|
9001
|
+
const at = over.index;
|
|
9002
|
+
const len = over.text.length;
|
|
9003
|
+
if (at <= start) {
|
|
9004
|
+
return [{ ...op, index: start + len }];
|
|
9005
|
+
}
|
|
9006
|
+
if (at >= end) {
|
|
9007
|
+
return [{ ...op }];
|
|
9008
|
+
}
|
|
9009
|
+
return [
|
|
9010
|
+
{ type: "delete", index: start, length: at - start },
|
|
9011
|
+
{ type: "delete", index: start + len, length: end - at }
|
|
9012
|
+
];
|
|
9013
|
+
} else if (over.type === "delete") {
|
|
9014
|
+
const newStart = mapIndexOverDelete(start, over.index, over.length);
|
|
9015
|
+
const newEnd = mapIndexOverDelete(end, over.index, over.length);
|
|
9016
|
+
return newEnd - newStart > 0 ? [{ type: "delete", index: newStart, length: newEnd - newStart }] : [];
|
|
9017
|
+
} else {
|
|
9018
|
+
return [{ ...op }];
|
|
9019
|
+
}
|
|
9020
|
+
}
|
|
9021
|
+
function transformFormat(op, over, order) {
|
|
9022
|
+
const start = op.index;
|
|
9023
|
+
const end = op.index + op.length;
|
|
9024
|
+
if (over.type === "insert") {
|
|
9025
|
+
const at = over.index;
|
|
9026
|
+
const len = over.text.length;
|
|
9027
|
+
if (at <= start) {
|
|
9028
|
+
return [{ ...op, index: start + len }];
|
|
9029
|
+
}
|
|
9030
|
+
if (at >= end) {
|
|
9031
|
+
return [{ ...op }];
|
|
9032
|
+
}
|
|
9033
|
+
return [
|
|
9034
|
+
{
|
|
9035
|
+
type: "format",
|
|
9036
|
+
index: start,
|
|
9037
|
+
length: at - start,
|
|
9038
|
+
attributes: op.attributes
|
|
9039
|
+
},
|
|
9040
|
+
{
|
|
9041
|
+
type: "format",
|
|
9042
|
+
index: at + len,
|
|
9043
|
+
length: end - at,
|
|
9044
|
+
attributes: op.attributes
|
|
9045
|
+
}
|
|
9046
|
+
];
|
|
9047
|
+
} else if (over.type === "delete") {
|
|
9048
|
+
const newStart = mapIndexOverDelete(start, over.index, over.length);
|
|
9049
|
+
const newEnd = mapIndexOverDelete(end, over.index, over.length);
|
|
9050
|
+
return newEnd - newStart > 0 ? [
|
|
9051
|
+
{
|
|
9052
|
+
type: "format",
|
|
9053
|
+
index: newStart,
|
|
9054
|
+
length: newEnd - newStart,
|
|
9055
|
+
attributes: op.attributes
|
|
9056
|
+
}
|
|
9057
|
+
] : [];
|
|
9058
|
+
} else {
|
|
9059
|
+
if (order === "after") {
|
|
9060
|
+
return [{ ...op }];
|
|
9061
|
+
}
|
|
9062
|
+
const overlapStart = Math.max(start, over.index);
|
|
9063
|
+
const overlapEnd = Math.min(end, over.index + over.length);
|
|
9064
|
+
if (overlapStart >= overlapEnd) {
|
|
9065
|
+
return [{ ...op }];
|
|
9066
|
+
}
|
|
9067
|
+
const hasConflict = Object.keys(op.attributes).some(
|
|
9068
|
+
(key) => key in over.attributes
|
|
9069
|
+
);
|
|
9070
|
+
if (!hasConflict) {
|
|
9071
|
+
return [{ ...op }];
|
|
9072
|
+
}
|
|
9073
|
+
const reduced = {};
|
|
9074
|
+
for (const [key, value] of Object.entries(op.attributes)) {
|
|
9075
|
+
if (!(key in over.attributes)) {
|
|
9076
|
+
reduced[key] = value;
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
const pieces = [];
|
|
9080
|
+
if (start < overlapStart) {
|
|
9081
|
+
pieces.push({
|
|
9082
|
+
type: "format",
|
|
9083
|
+
index: start,
|
|
9084
|
+
length: overlapStart - start,
|
|
9085
|
+
attributes: op.attributes
|
|
9086
|
+
});
|
|
9087
|
+
}
|
|
9088
|
+
if (Object.keys(reduced).length > 0) {
|
|
9089
|
+
pieces.push({
|
|
9090
|
+
type: "format",
|
|
9091
|
+
index: overlapStart,
|
|
9092
|
+
length: overlapEnd - overlapStart,
|
|
9093
|
+
attributes: reduced
|
|
9094
|
+
});
|
|
9095
|
+
}
|
|
9096
|
+
if (overlapEnd < end) {
|
|
9097
|
+
pieces.push({
|
|
9098
|
+
type: "format",
|
|
9099
|
+
index: overlapEnd,
|
|
9100
|
+
length: end - overlapEnd,
|
|
9101
|
+
attributes: op.attributes
|
|
9102
|
+
});
|
|
9103
|
+
}
|
|
9104
|
+
return pieces;
|
|
9105
|
+
}
|
|
9106
|
+
}
|
|
9107
|
+
function transformSingle(op, over, order) {
|
|
9108
|
+
switch (op.type) {
|
|
9109
|
+
case "insert":
|
|
9110
|
+
return transformInsert(op, over, order);
|
|
9111
|
+
case "delete":
|
|
9112
|
+
return transformDelete(op, over);
|
|
9113
|
+
case "format":
|
|
9114
|
+
return transformFormat(op, over, order);
|
|
9115
|
+
}
|
|
9116
|
+
}
|
|
9117
|
+
function transformTextOperationsX(a, b, order) {
|
|
9118
|
+
if (a.length === 0 || b.length === 0) {
|
|
9119
|
+
return [[...a], [...b]];
|
|
9120
|
+
}
|
|
9121
|
+
if (a.length === 1 && b.length === 1) {
|
|
9122
|
+
return [
|
|
9123
|
+
transformSingle(a[0], b[0], order),
|
|
9124
|
+
transformSingle(b[0], a[0], oppositeOrder(order))
|
|
9125
|
+
];
|
|
9126
|
+
}
|
|
9127
|
+
if (a.length > 1) {
|
|
9128
|
+
const [headA1, b1] = transformTextOperationsX([a[0]], b, order);
|
|
9129
|
+
const [restA1, b2] = transformTextOperationsX(a.slice(1), b1, order);
|
|
9130
|
+
return [[...headA1, ...restA1], b2];
|
|
9131
|
+
}
|
|
9132
|
+
const [a1, headB1] = transformTextOperationsX(a, [b[0]], order);
|
|
9133
|
+
const [a2, restB1] = transformTextOperationsX(a1, b.slice(1), order);
|
|
9134
|
+
return [a2, [...headB1, ...restB1]];
|
|
9135
|
+
}
|
|
9136
|
+
function transformTextOperations(ops, over, order) {
|
|
9137
|
+
return transformTextOperationsX(ops, over, order)[0];
|
|
9138
|
+
}
|
|
9139
|
+
function textOperationsEqual(a, b) {
|
|
9140
|
+
return a === b || stableStringify(a) === stableStringify(b);
|
|
9141
|
+
}
|
|
9142
|
+
function applyTextOperationsToSegments(segments, ops) {
|
|
9143
|
+
let next = [...segments];
|
|
9144
|
+
for (const op of ops) {
|
|
9145
|
+
if (op.type === "insert") {
|
|
9146
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9147
|
+
next = applyInsert(next, index, op.text, op.attributes);
|
|
9148
|
+
} else if (op.type === "delete") {
|
|
9149
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9150
|
+
const clipped = clipRange(index, op.length, textLength(next));
|
|
9151
|
+
next = applyDelete(next, clipped.index, clipped.length).segments;
|
|
9152
|
+
} else {
|
|
9153
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9154
|
+
const clipped = clipRange(index, op.length, textLength(next));
|
|
9155
|
+
next = applyFormat(next, clipped.index, clipped.length, op.attributes);
|
|
9156
|
+
}
|
|
9157
|
+
}
|
|
9158
|
+
return next;
|
|
9159
|
+
}
|
|
9160
|
+
function applyLiveTextOperations(data, ops) {
|
|
9161
|
+
return segmentsToData(
|
|
9162
|
+
applyTextOperationsToSegments(dataToSegments(data), ops)
|
|
9163
|
+
);
|
|
9164
|
+
}
|
|
9165
|
+
function invertTextOperations(segments, ops) {
|
|
9166
|
+
let shadow = [...segments];
|
|
9167
|
+
const reverse = [];
|
|
9168
|
+
for (const op of ops) {
|
|
9169
|
+
if (op.type === "insert") {
|
|
9170
|
+
shadow = applyInsert(shadow, op.index, op.text, op.attributes);
|
|
9171
|
+
reverse.unshift({
|
|
9172
|
+
type: "delete",
|
|
9173
|
+
index: op.index,
|
|
9174
|
+
length: op.text.length
|
|
9175
|
+
});
|
|
9176
|
+
} else if (op.type === "delete") {
|
|
9177
|
+
const deletedSegments = extractDeletedSegments(
|
|
9178
|
+
shadow,
|
|
9179
|
+
op.index,
|
|
9180
|
+
op.length
|
|
9181
|
+
);
|
|
9182
|
+
shadow = applyDelete(shadow, op.index, op.length).segments;
|
|
9183
|
+
const inserts = [];
|
|
9184
|
+
let insertIndex = op.index;
|
|
9185
|
+
for (const segment of deletedSegments) {
|
|
9186
|
+
inserts.push({
|
|
9187
|
+
type: "insert",
|
|
9188
|
+
index: insertIndex,
|
|
9189
|
+
text: segment.text,
|
|
9190
|
+
attributes: segment.attributes
|
|
9191
|
+
});
|
|
9192
|
+
insertIndex += segment.text.length;
|
|
9193
|
+
}
|
|
9194
|
+
for (let index = inserts.length - 1; index >= 0; index--) {
|
|
9195
|
+
reverse.unshift(inserts[index]);
|
|
9196
|
+
}
|
|
9197
|
+
} else {
|
|
9198
|
+
const inverse = formatReverseOperations(
|
|
9199
|
+
shadow,
|
|
9200
|
+
op.index,
|
|
9201
|
+
op.length,
|
|
9202
|
+
op.attributes
|
|
9203
|
+
);
|
|
9204
|
+
shadow = applyFormat(shadow, op.index, op.length, op.attributes);
|
|
9205
|
+
reverse.unshift(...inverse.reverse());
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
return reverse;
|
|
9209
|
+
}
|
|
9210
|
+
|
|
9211
|
+
// src/crdts/LiveText.ts
|
|
9212
|
+
var ACCEPTED_OPS_HISTORY_LIMIT = 1e3;
|
|
9213
|
+
var LiveText = class _LiveText extends AbstractCrdt {
|
|
9214
|
+
/** The local document: #confirmed ⊕ #inFlightOps ⊕ #queuedOps. */
|
|
9215
|
+
#segments;
|
|
9216
|
+
/** The server-confirmed document (only authoritative ops applied). */
|
|
9217
|
+
#confirmed;
|
|
9218
|
+
#version;
|
|
9219
|
+
/** The op currently awaiting server acknowledgement (at most one). */
|
|
9220
|
+
#inFlightOpId;
|
|
9221
|
+
/** Its ops, continuously re-expressed against current server state. */
|
|
9222
|
+
#inFlightOps = [];
|
|
9223
|
+
/** Local edits made while an op is in flight; sent after the ack. */
|
|
9224
|
+
#queuedOps = [];
|
|
9225
|
+
#acceptedOps = [];
|
|
9226
|
+
/**
|
|
9227
|
+
* Creates a new LiveText document.
|
|
9228
|
+
*
|
|
9229
|
+
* @param textOrData Initial plain text, or an array of `[text]` /
|
|
9230
|
+
* `[text, attributes]` segments. Defaults to an empty document.
|
|
9231
|
+
*
|
|
9232
|
+
* @example
|
|
9233
|
+
* new LiveText();
|
|
9234
|
+
* new LiveText("Hello world");
|
|
9235
|
+
* new LiveText([["Hello ", { bold: true }], ["world"]]);
|
|
9236
|
+
*/
|
|
9237
|
+
constructor(textOrData = "", version = 0) {
|
|
9238
|
+
super();
|
|
9239
|
+
this.#segments = typeof textOrData === "string" ? textOrData.length === 0 ? [] : [{ text: textOrData }] : dataToSegments(textOrData);
|
|
9240
|
+
this.#confirmed = [...this.#segments];
|
|
9241
|
+
this.#version = version;
|
|
9242
|
+
Object.defineProperty(this, kInternal, {
|
|
9243
|
+
value: {
|
|
9244
|
+
encodeIndex: (localIndex) => this.#encodeIndex(localIndex),
|
|
9245
|
+
decodeIndex: (index, fromVersion) => this.#decodeIndex(index, fromVersion)
|
|
9246
|
+
},
|
|
9247
|
+
enumerable: false
|
|
9248
|
+
});
|
|
9249
|
+
}
|
|
9250
|
+
get version() {
|
|
9251
|
+
return this.#version;
|
|
9252
|
+
}
|
|
9253
|
+
get length() {
|
|
9254
|
+
return textLength(this.#segments);
|
|
9255
|
+
}
|
|
9256
|
+
/** @internal */
|
|
9257
|
+
static _deserialize([id, item], _parentToChildren, pool) {
|
|
9258
|
+
const text = new _LiveText(item.data, item.version);
|
|
9259
|
+
text._attach(id, pool);
|
|
9260
|
+
return text;
|
|
9261
|
+
}
|
|
9262
|
+
/** @internal */
|
|
9263
|
+
_toOps(parentId, parentKey) {
|
|
9264
|
+
if (this._id === void 0) {
|
|
9265
|
+
throw new Error("Cannot serialize LiveText if it is not attached");
|
|
9266
|
+
}
|
|
9267
|
+
return [
|
|
9268
|
+
{
|
|
9269
|
+
type: OpCode.CREATE_TEXT,
|
|
9270
|
+
id: this._id,
|
|
9271
|
+
parentId,
|
|
9272
|
+
parentKey,
|
|
9273
|
+
data: this.toJSON(),
|
|
9274
|
+
version: this.#version
|
|
9275
|
+
}
|
|
9276
|
+
];
|
|
9277
|
+
}
|
|
9278
|
+
/** @internal */
|
|
9279
|
+
_serialize() {
|
|
9280
|
+
if (this.parent.type !== "HasParent") {
|
|
9281
|
+
throw new Error("Cannot serialize LiveText if parent is missing");
|
|
9282
|
+
}
|
|
9283
|
+
return {
|
|
9284
|
+
type: CrdtType.TEXT,
|
|
9285
|
+
parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
|
|
9286
|
+
parentKey: this.parent.key,
|
|
9287
|
+
data: this.toJSON(),
|
|
9288
|
+
version: this.#version
|
|
9289
|
+
};
|
|
9290
|
+
}
|
|
9291
|
+
/** @internal */
|
|
9292
|
+
_attachChild(_op) {
|
|
9293
|
+
throw new Error("LiveText cannot contain child nodes");
|
|
9294
|
+
}
|
|
9295
|
+
/** @internal */
|
|
9296
|
+
_detachChild(_crdt) {
|
|
9297
|
+
throw new Error("LiveText cannot contain child nodes");
|
|
9298
|
+
}
|
|
9299
|
+
/** @internal */
|
|
9300
|
+
_apply(op, isLocal) {
|
|
9301
|
+
if (op.type !== OpCode.UPDATE_TEXT) {
|
|
9302
|
+
return super._apply(op, isLocal);
|
|
9303
|
+
}
|
|
9304
|
+
if (isLocal) {
|
|
9305
|
+
return this.#applyLocal(op);
|
|
9306
|
+
}
|
|
9307
|
+
if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
|
|
9308
|
+
return this.#applyAck(op);
|
|
9309
|
+
}
|
|
9310
|
+
if (op.opId !== void 0 && this.#acceptedOps.some((entry) => entry.opId === op.opId)) {
|
|
9311
|
+
this.#version = Math.max(this.#version, _nullishCoalesce(op.version, () => ( op.baseVersion + 1)));
|
|
9312
|
+
return { modified: false };
|
|
9313
|
+
}
|
|
9314
|
+
return this.#applyRemote(op);
|
|
9315
|
+
}
|
|
9316
|
+
/**
|
|
9317
|
+
* Inserts text at the given index.
|
|
9318
|
+
*
|
|
9319
|
+
* @param index Character index at which to insert. Values outside the
|
|
9320
|
+
* document range are clipped.
|
|
9321
|
+
* @param text Text to insert.
|
|
9322
|
+
* @param attributes Optional inline attributes for the inserted text.
|
|
9323
|
+
*
|
|
9324
|
+
* @example
|
|
9325
|
+
* const text = new LiveText("Hello");
|
|
9326
|
+
* text.insert(5, " world");
|
|
9327
|
+
* text.insert(0, "Say: ", { italic: true });
|
|
9328
|
+
*/
|
|
9329
|
+
insert(index, text, attributes) {
|
|
9330
|
+
const clippedIndex = Math.max(0, Math.min(index, this.length));
|
|
9331
|
+
this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
|
|
9332
|
+
}
|
|
9333
|
+
/**
|
|
9334
|
+
* Deletes `length` characters starting at `index`.
|
|
9335
|
+
*
|
|
9336
|
+
* @example
|
|
9337
|
+
* const text = new LiveText("Hello world");
|
|
9338
|
+
* text.delete(5, 6); // "Hello"
|
|
9339
|
+
*/
|
|
9340
|
+
delete(index, length) {
|
|
9341
|
+
const clipped = clipRange(index, length, this.length);
|
|
9342
|
+
if (clipped.length === 0) {
|
|
9343
|
+
return;
|
|
9344
|
+
}
|
|
9345
|
+
this.#dispatch([
|
|
9346
|
+
{ type: "delete", index: clipped.index, length: clipped.length }
|
|
9347
|
+
]);
|
|
9348
|
+
}
|
|
9349
|
+
/**
|
|
9350
|
+
* Replaces a range of text with new text.
|
|
9351
|
+
*
|
|
9352
|
+
* @example
|
|
9353
|
+
* const text = new LiveText("Hello world");
|
|
9354
|
+
* text.replace(0, 5, "Hi"); // "Hi world"
|
|
9355
|
+
*/
|
|
9356
|
+
replace(index, length, text, attributes) {
|
|
9357
|
+
const clipped = clipRange(index, length, this.length);
|
|
9358
|
+
const ops = [];
|
|
9359
|
+
if (clipped.length > 0) {
|
|
9360
|
+
ops.push({
|
|
9361
|
+
type: "delete",
|
|
9362
|
+
index: clipped.index,
|
|
9363
|
+
length: clipped.length
|
|
9364
|
+
});
|
|
9365
|
+
}
|
|
9366
|
+
if (text.length > 0) {
|
|
9367
|
+
ops.push({ type: "insert", index: clipped.index, text, attributes });
|
|
9368
|
+
}
|
|
9369
|
+
this.#dispatch(ops);
|
|
9370
|
+
}
|
|
9371
|
+
/**
|
|
9372
|
+
* Encode a local-document index (an offset into this LiveText's current
|
|
9373
|
+
* #segments, which CodeMirror or any consumer mirrors as its document)
|
|
9374
|
+
* into server-confirmed coordinates suitable for broadcasting to peers via
|
|
9375
|
+
* presence or any other side channel.
|
|
9376
|
+
*
|
|
9377
|
+
* The returned index is in this LiveText's current #confirmed coordinates
|
|
9378
|
+
* — that is, with this client's local pending ops inverse-mapped out.
|
|
9379
|
+
* Pair it with the current {@link LiveText.version} when sending so the
|
|
9380
|
+
* receiver can call {@link PrivateLiveTextApi.decodeIndex} to land the
|
|
9381
|
+
* position in their own local document coordinates regardless of their
|
|
9382
|
+
* private pending ops.
|
|
9383
|
+
*
|
|
9384
|
+
* Index ambiguity at boundaries is resolved by an inverse-of-forward
|
|
9385
|
+
* convention: a position at or before a local insertion is reported as
|
|
9386
|
+
* the position right before the insertion in #confirmed; a position past
|
|
9387
|
+
* the insertion shifts left by the insertion's length. Positions inside
|
|
9388
|
+
* an own-pending insertion collapse to the insertion point.
|
|
9389
|
+
*/
|
|
9390
|
+
#encodeIndex(localIndex) {
|
|
9391
|
+
let mapped = Math.max(0, Math.min(localIndex, this.length));
|
|
9392
|
+
mapped = inverseMapTextIndexThroughOperations(mapped, this.#queuedOps);
|
|
9393
|
+
mapped = inverseMapTextIndexThroughOperations(mapped, this.#inFlightOps);
|
|
9394
|
+
return mapped;
|
|
9395
|
+
}
|
|
9396
|
+
/**
|
|
9397
|
+
* Decode an `(index, fromVersion)` pair produced by
|
|
9398
|
+
* {@link PrivateLiveTextApi.encodeIndex} — typically on a peer — into an
|
|
9399
|
+
* offset in this LiveText's current local document (an index suitable for
|
|
9400
|
+
* placing a CodeMirror marker, an annotation anchor, or anything else that
|
|
9401
|
+
* lives over #segments).
|
|
9402
|
+
*
|
|
9403
|
+
* Composes the accepted ops applied since `fromVersion` (drawn from
|
|
9404
|
+
* #acceptedOps in locally-applied form) with this client's own local
|
|
9405
|
+
* pending ops, in that order. The result is in current #segments
|
|
9406
|
+
* coordinates.
|
|
9407
|
+
*
|
|
9408
|
+
* Returns `null` when the position cannot be decoded against the current
|
|
9409
|
+
* state:
|
|
9410
|
+
* - `fromVersion` is greater than this LiveText's current version: the
|
|
9411
|
+
* peer is ahead of us. The caller should park the message and retry
|
|
9412
|
+
* after more accepted ops arrive.
|
|
9413
|
+
* - `fromVersion` falls outside the retained accepted-ops history. This
|
|
9414
|
+
* only happens after very long-lived disconnections; the caller can
|
|
9415
|
+
* fall back to using the raw index and letting subsequent local
|
|
9416
|
+
* transactions map it (with bounded drift).
|
|
9417
|
+
*/
|
|
9418
|
+
#decodeIndex(index, fromVersion) {
|
|
9419
|
+
if (fromVersion > this.#version) {
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9422
|
+
if (fromVersion < this.#version) {
|
|
9423
|
+
const oldest = _optionalChain([this, 'access', _216 => _216.#acceptedOps, 'access', _217 => _217[0], 'optionalAccess', _218 => _218.version]);
|
|
9424
|
+
if (oldest === void 0 || oldest > fromVersion + 1) {
|
|
9425
|
+
return null;
|
|
9426
|
+
}
|
|
9427
|
+
}
|
|
9428
|
+
let mapped = index;
|
|
9429
|
+
for (const entry of this.#acceptedOps) {
|
|
9430
|
+
if (entry.version <= fromVersion) continue;
|
|
9431
|
+
if (entry.version > this.#version) break;
|
|
9432
|
+
if (entry.ops.length === 0) continue;
|
|
9433
|
+
mapped = mapTextIndexThroughOperations(mapped, entry.ops);
|
|
9434
|
+
}
|
|
9435
|
+
mapped = mapTextIndexThroughOperations(mapped, this.#inFlightOps);
|
|
9436
|
+
mapped = mapTextIndexThroughOperations(mapped, this.#queuedOps);
|
|
9437
|
+
return Math.max(0, Math.min(mapped, this.length));
|
|
9438
|
+
}
|
|
9439
|
+
/**
|
|
9440
|
+
* Applies or removes inline attributes on a range of text.
|
|
9441
|
+
*
|
|
9442
|
+
* Set an attribute to `null` to remove it from the range.
|
|
9443
|
+
*
|
|
9444
|
+
* @example
|
|
9445
|
+
* const text = new LiveText("Hello world");
|
|
9446
|
+
* text.format(0, 5, { bold: true });
|
|
9447
|
+
* text.format(0, 5, { bold: null });
|
|
9448
|
+
*/
|
|
9449
|
+
format(index, length, attributes) {
|
|
9450
|
+
const clipped = clipRange(index, length, this.length);
|
|
9451
|
+
if (clipped.length === 0) {
|
|
9452
|
+
return;
|
|
9453
|
+
}
|
|
9454
|
+
this.#dispatch([
|
|
9455
|
+
{
|
|
9456
|
+
type: "format",
|
|
9457
|
+
index: clipped.index,
|
|
9458
|
+
length: clipped.length,
|
|
9459
|
+
attributes
|
|
9460
|
+
}
|
|
9461
|
+
]);
|
|
9462
|
+
}
|
|
9463
|
+
/** Local edits made through the public API. */
|
|
9464
|
+
#dispatch(ops) {
|
|
9465
|
+
if (ops.length === 0) {
|
|
9466
|
+
return;
|
|
9467
|
+
}
|
|
9468
|
+
_optionalChain([this, 'access', _219 => _219._pool, 'optionalAccess', _220 => _220.assertStorageIsWritable, 'call', _221 => _221()]);
|
|
9469
|
+
const attached = this._pool !== void 0 && this._id !== void 0;
|
|
9470
|
+
const reverse = attached ? this.#invertOperations(ops) : [];
|
|
9471
|
+
const changes = this.#applyOperationsLocally(ops);
|
|
9472
|
+
if (!attached) {
|
|
9473
|
+
return;
|
|
9474
|
+
}
|
|
9475
|
+
const pool = nn(this._pool);
|
|
9476
|
+
const id = nn(this._id);
|
|
9477
|
+
const updates = /* @__PURE__ */ new Map([
|
|
9478
|
+
[
|
|
9479
|
+
id,
|
|
9480
|
+
{
|
|
9481
|
+
type: "LiveText",
|
|
9482
|
+
node: this,
|
|
9483
|
+
version: this.#version,
|
|
9484
|
+
updates: changes
|
|
9485
|
+
}
|
|
9486
|
+
]
|
|
9487
|
+
]);
|
|
9488
|
+
if (this.#inFlightOpId === void 0) {
|
|
9489
|
+
const opId = pool.generateOpId();
|
|
9490
|
+
this.#inFlightOpId = opId;
|
|
9491
|
+
this.#inFlightOps = [...ops];
|
|
9492
|
+
pool.dispatch(
|
|
9493
|
+
[
|
|
9494
|
+
{
|
|
9495
|
+
type: OpCode.UPDATE_TEXT,
|
|
9496
|
+
id,
|
|
9497
|
+
opId,
|
|
9498
|
+
baseVersion: this.#version,
|
|
9499
|
+
ops: [...ops]
|
|
9500
|
+
}
|
|
9501
|
+
],
|
|
9502
|
+
reverse,
|
|
9503
|
+
updates
|
|
9504
|
+
);
|
|
9505
|
+
} else {
|
|
9506
|
+
this.#queuedOps.push(...ops);
|
|
9507
|
+
pool.dispatch([], reverse, updates, { clearRedoStack: true });
|
|
9508
|
+
}
|
|
9509
|
+
}
|
|
9510
|
+
/**
|
|
9511
|
+
* A local replay of an existing wire op: an undo/redo frame, or an
|
|
9512
|
+
* unacknowledged op re-sent after a reconnect.
|
|
9513
|
+
*/
|
|
9514
|
+
#applyLocal(op) {
|
|
9515
|
+
const mutableOp = op;
|
|
9516
|
+
if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
|
|
9517
|
+
this.#inFlightOps = [...this.#inFlightOps, ...this.#queuedOps];
|
|
9518
|
+
this.#queuedOps = [];
|
|
9519
|
+
mutableOp.baseVersion = this.#version;
|
|
9520
|
+
mutableOp.ops = [...this.#inFlightOps];
|
|
9521
|
+
return { modified: false };
|
|
9522
|
+
}
|
|
9523
|
+
let ops = op.ops;
|
|
9524
|
+
for (const entry of this.#acceptedOps) {
|
|
9525
|
+
if (entry.version > op.baseVersion && entry.ops.length > 0) {
|
|
9526
|
+
ops = transformTextOperations(ops, entry.ops, "after");
|
|
9527
|
+
}
|
|
9528
|
+
}
|
|
9529
|
+
const reverse = this.#invertOperations(ops);
|
|
9530
|
+
const changes = this.#applyOperationsLocally(ops);
|
|
9531
|
+
if (this.#inFlightOpId === void 0 && ops.length > 0) {
|
|
9532
|
+
this.#inFlightOpId = nn(op.opId, "Local ops must have an opId");
|
|
9533
|
+
this.#inFlightOps = [...ops];
|
|
9534
|
+
mutableOp.baseVersion = this.#version;
|
|
9535
|
+
mutableOp.ops = [...ops];
|
|
9536
|
+
} else {
|
|
9537
|
+
this.#queuedOps.push(...ops);
|
|
9538
|
+
mutableOp.baseVersion = this.#version;
|
|
9539
|
+
mutableOp.ops = [];
|
|
9540
|
+
}
|
|
9541
|
+
if (changes.length === 0) {
|
|
9542
|
+
return { modified: false };
|
|
9543
|
+
}
|
|
9544
|
+
return {
|
|
9545
|
+
reverse,
|
|
9546
|
+
modified: {
|
|
9547
|
+
type: "LiveText",
|
|
9548
|
+
node: this,
|
|
9549
|
+
version: this.#version,
|
|
9550
|
+
updates: changes
|
|
9551
|
+
}
|
|
9552
|
+
};
|
|
9553
|
+
}
|
|
9554
|
+
/** Server acknowledgement of our in-flight op. */
|
|
9555
|
+
#applyAck(op) {
|
|
9556
|
+
const ackedVersion = _nullishCoalesce(op.version, () => ( Math.max(this.#version, op.baseVersion + 1)));
|
|
9557
|
+
const predicted = this.#inFlightOps;
|
|
9558
|
+
const opId = this.#inFlightOpId;
|
|
9559
|
+
this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
|
|
9560
|
+
this.#inFlightOpId = void 0;
|
|
9561
|
+
this.#inFlightOps = [];
|
|
9562
|
+
let appliedOps = [];
|
|
9563
|
+
let result = { modified: false };
|
|
9564
|
+
if (!textOperationsEqual(op.ops, predicted)) {
|
|
9565
|
+
error2(
|
|
9566
|
+
"LiveText: acknowledgement did not match the local prediction; resynchronizing"
|
|
9567
|
+
);
|
|
9568
|
+
const rebuilt = this.#rebuildLocalFromConfirmed();
|
|
9569
|
+
appliedOps = rebuilt.appliedOps;
|
|
9570
|
+
if (rebuilt.changes.length > 0) {
|
|
9571
|
+
result = {
|
|
9572
|
+
reverse: [],
|
|
9573
|
+
modified: {
|
|
9574
|
+
type: "LiveText",
|
|
9575
|
+
node: this,
|
|
9576
|
+
version: ackedVersion,
|
|
9577
|
+
updates: rebuilt.changes
|
|
9578
|
+
}
|
|
9579
|
+
};
|
|
9580
|
+
}
|
|
9581
|
+
}
|
|
9582
|
+
this.#version = Math.max(this.#version, ackedVersion);
|
|
9583
|
+
this.#recordAccepted(ackedVersion, appliedOps, opId);
|
|
9584
|
+
this.#flushQueued();
|
|
9585
|
+
return result;
|
|
9586
|
+
}
|
|
9587
|
+
/** An accepted op from another client (or a server-fabricated fix op). */
|
|
9588
|
+
#applyRemote(op) {
|
|
9589
|
+
const version = _nullishCoalesce(op.version, () => ( this.#version + 1));
|
|
9590
|
+
this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
|
|
9591
|
+
const [overInFlight, inFlight] = transformTextOperationsX(
|
|
9592
|
+
op.ops,
|
|
9593
|
+
this.#inFlightOps,
|
|
9594
|
+
"before"
|
|
9595
|
+
);
|
|
9596
|
+
const [applied, queued] = transformTextOperationsX(
|
|
9597
|
+
overInFlight,
|
|
9598
|
+
this.#queuedOps,
|
|
9599
|
+
"before"
|
|
9600
|
+
);
|
|
9601
|
+
this.#inFlightOps = inFlight;
|
|
9602
|
+
this.#queuedOps = queued;
|
|
9603
|
+
this.#recordAccepted(version, applied, op.opId);
|
|
9604
|
+
if (applied.length === 0) {
|
|
9605
|
+
this.#version = Math.max(this.#version, version);
|
|
9606
|
+
return { modified: false };
|
|
9607
|
+
}
|
|
9608
|
+
const reverse = this.#invertOperations(applied);
|
|
9609
|
+
const changes = this.#applyOperationsLocally(applied);
|
|
9610
|
+
this.#version = Math.max(this.#version, version);
|
|
9611
|
+
return {
|
|
9612
|
+
reverse,
|
|
9613
|
+
modified: {
|
|
9614
|
+
type: "LiveText",
|
|
9615
|
+
node: this,
|
|
9616
|
+
version: this.#version,
|
|
9617
|
+
updates: changes
|
|
9618
|
+
}
|
|
9619
|
+
};
|
|
9620
|
+
}
|
|
9621
|
+
/** Send the queued ops as the next in-flight op (after an ack). */
|
|
9622
|
+
#flushQueued() {
|
|
9623
|
+
if (this.#queuedOps.length === 0 || this._pool === void 0 || this._id === void 0) {
|
|
9624
|
+
return;
|
|
9625
|
+
}
|
|
9626
|
+
const opId = this._pool.generateOpId();
|
|
9627
|
+
this.#inFlightOpId = opId;
|
|
9628
|
+
this.#inFlightOps = this.#queuedOps;
|
|
9629
|
+
this.#queuedOps = [];
|
|
9630
|
+
this._pool.dispatch(
|
|
9631
|
+
[
|
|
9632
|
+
{
|
|
9633
|
+
type: OpCode.UPDATE_TEXT,
|
|
9634
|
+
id: this._id,
|
|
9635
|
+
opId,
|
|
9636
|
+
baseVersion: this.#version,
|
|
9637
|
+
ops: [...this.#inFlightOps]
|
|
9638
|
+
}
|
|
9639
|
+
],
|
|
9640
|
+
[],
|
|
9641
|
+
/* @__PURE__ */ new Map(),
|
|
9642
|
+
// The local content was already applied (and made undoable) when the
|
|
9643
|
+
// edits happened; this is purely an outbound flush.
|
|
9644
|
+
{ clearRedoStack: false }
|
|
9645
|
+
);
|
|
9646
|
+
}
|
|
9647
|
+
/**
|
|
9648
|
+
* Rebuild the local document as confirmed ⊕ queued ops, returning the
|
|
9649
|
+
* coarse delta that was applied. Only used by defensive recovery paths.
|
|
9650
|
+
*/
|
|
9651
|
+
#rebuildLocalFromConfirmed() {
|
|
9652
|
+
const before2 = this.#segments;
|
|
9653
|
+
const after2 = applyTextOperationsToSegments(this.#confirmed, [
|
|
9654
|
+
...this.#inFlightOps,
|
|
9655
|
+
...this.#queuedOps
|
|
9656
|
+
]);
|
|
9657
|
+
if (stableStringify(segmentsToData(before2)) === stableStringify(segmentsToData(after2))) {
|
|
9658
|
+
this.#segments = after2;
|
|
9659
|
+
return { appliedOps: [], changes: [] };
|
|
9660
|
+
}
|
|
9661
|
+
const beforeText = before2.map((segment) => segment.text).join("");
|
|
9662
|
+
this.#segments = after2;
|
|
9663
|
+
this.invalidate();
|
|
9664
|
+
const appliedOps = [];
|
|
9665
|
+
const changes = [];
|
|
9666
|
+
if (beforeText.length > 0) {
|
|
9667
|
+
appliedOps.push({ type: "delete", index: 0, length: beforeText.length });
|
|
9668
|
+
changes.push({
|
|
9669
|
+
type: "delete",
|
|
9670
|
+
index: 0,
|
|
9671
|
+
length: beforeText.length,
|
|
9672
|
+
deletedText: beforeText
|
|
9673
|
+
});
|
|
9674
|
+
}
|
|
9675
|
+
let index = 0;
|
|
9676
|
+
for (const segment of after2) {
|
|
9677
|
+
appliedOps.push({
|
|
9678
|
+
type: "insert",
|
|
9679
|
+
index,
|
|
9680
|
+
text: segment.text,
|
|
9681
|
+
attributes: segment.attributes
|
|
9682
|
+
});
|
|
9683
|
+
changes.push({
|
|
9684
|
+
type: "insert",
|
|
9685
|
+
index,
|
|
9686
|
+
text: segment.text,
|
|
9687
|
+
attributes: segment.attributes
|
|
9688
|
+
});
|
|
9689
|
+
index += segment.text.length;
|
|
9690
|
+
}
|
|
9691
|
+
return { appliedOps, changes };
|
|
9692
|
+
}
|
|
9693
|
+
/**
|
|
9694
|
+
* Reconcile this node against an authoritative storage snapshot (e.g.
|
|
9695
|
+
* after a reconnect). The confirmed state and version are replaced by the
|
|
9696
|
+
* snapshot's; pending (in-flight + queued) ops are preserved on top and
|
|
9697
|
+
* will be re-sent by the offline-ops replay.
|
|
9698
|
+
*
|
|
9699
|
+
* @internal
|
|
9700
|
+
*/
|
|
9701
|
+
_resyncText(data, version) {
|
|
9702
|
+
this.#confirmed = dataToSegments(data);
|
|
9703
|
+
this.#version = version;
|
|
9704
|
+
this.#acceptedOps = [];
|
|
9705
|
+
const rebuilt = this.#rebuildLocalFromConfirmed();
|
|
9706
|
+
if (rebuilt.changes.length === 0) {
|
|
9707
|
+
return void 0;
|
|
9708
|
+
}
|
|
9709
|
+
return {
|
|
9710
|
+
type: "LiveText",
|
|
9711
|
+
node: this,
|
|
9712
|
+
version: this.#version,
|
|
9713
|
+
updates: rebuilt.changes
|
|
9714
|
+
};
|
|
9715
|
+
}
|
|
9716
|
+
/**
|
|
9717
|
+
* Called when the server rejected one of our ops. Drops all pending state
|
|
9718
|
+
* for this node (edits queued behind a rejected op cannot be trusted
|
|
9719
|
+
* either); the room follows up with a storage resync.
|
|
9720
|
+
*
|
|
9721
|
+
* @internal
|
|
9722
|
+
*/
|
|
9723
|
+
_rejectPendingOp(opId) {
|
|
9724
|
+
if (opId !== this.#inFlightOpId) {
|
|
9725
|
+
return;
|
|
9726
|
+
}
|
|
9727
|
+
this.#inFlightOpId = void 0;
|
|
9728
|
+
this.#inFlightOps = [];
|
|
9729
|
+
this.#queuedOps = [];
|
|
9730
|
+
}
|
|
9731
|
+
#recordAccepted(version, ops, opId) {
|
|
9732
|
+
if (this.#acceptedOps.some((entry) => entry.version === version)) {
|
|
9733
|
+
return;
|
|
9734
|
+
}
|
|
9735
|
+
this.#acceptedOps.push({ version, opId, ops: [...ops] });
|
|
9736
|
+
this.#acceptedOps.sort((left, right) => left.version - right.version);
|
|
9737
|
+
if (this.#acceptedOps.length > ACCEPTED_OPS_HISTORY_LIMIT) {
|
|
9738
|
+
this.#acceptedOps.splice(
|
|
9739
|
+
0,
|
|
9740
|
+
this.#acceptedOps.length - ACCEPTED_OPS_HISTORY_LIMIT
|
|
9741
|
+
);
|
|
9742
|
+
}
|
|
9743
|
+
}
|
|
9744
|
+
#applyOperationsLocally(ops) {
|
|
9745
|
+
const changes = [];
|
|
9746
|
+
for (const op of ops) {
|
|
9747
|
+
if (op.type === "insert") {
|
|
9748
|
+
this.#segments = applyInsert(
|
|
9749
|
+
this.#segments,
|
|
9750
|
+
op.index,
|
|
9751
|
+
op.text,
|
|
9752
|
+
op.attributes
|
|
9753
|
+
);
|
|
9754
|
+
changes.push({
|
|
9755
|
+
type: "insert",
|
|
9756
|
+
index: op.index,
|
|
9757
|
+
text: op.text,
|
|
9758
|
+
attributes: op.attributes
|
|
9759
|
+
});
|
|
9760
|
+
} else if (op.type === "delete") {
|
|
9761
|
+
const result = applyDelete(this.#segments, op.index, op.length);
|
|
9762
|
+
this.#segments = result.segments;
|
|
9763
|
+
changes.push({
|
|
9764
|
+
type: "delete",
|
|
9765
|
+
index: op.index,
|
|
9766
|
+
length: op.length,
|
|
9767
|
+
deletedText: result.deletedText
|
|
9768
|
+
});
|
|
9769
|
+
} else {
|
|
9770
|
+
this.#segments = applyFormat(
|
|
9771
|
+
this.#segments,
|
|
9772
|
+
op.index,
|
|
9773
|
+
op.length,
|
|
9774
|
+
op.attributes
|
|
9775
|
+
);
|
|
9776
|
+
changes.push({
|
|
9777
|
+
type: "format",
|
|
9778
|
+
index: op.index,
|
|
9779
|
+
length: op.length,
|
|
9780
|
+
attributes: op.attributes
|
|
9781
|
+
});
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
this.invalidate();
|
|
9785
|
+
return changes;
|
|
9786
|
+
}
|
|
9787
|
+
#invertOperations(ops) {
|
|
9788
|
+
return [
|
|
9789
|
+
{
|
|
9790
|
+
type: OpCode.UPDATE_TEXT,
|
|
9791
|
+
id: nn(this._id),
|
|
9792
|
+
baseVersion: this.#version,
|
|
9793
|
+
ops: invertTextOperations(this.#segments, ops)
|
|
9794
|
+
}
|
|
9795
|
+
];
|
|
9796
|
+
}
|
|
9797
|
+
/** Returns the plain text content without attributes. Equivalent to joining the text from each segment in {@link LiveText.toJSON}. */
|
|
9798
|
+
toString() {
|
|
9799
|
+
return this.#segments.map((segment) => segment.text).join("");
|
|
9800
|
+
}
|
|
9801
|
+
/**
|
|
9802
|
+
* Returns a JSON-compatible snapshot of the document as a {@link LiveTextData}
|
|
9803
|
+
* array.
|
|
9804
|
+
*
|
|
9805
|
+
* @example
|
|
9806
|
+
* new LiveText([["Hello ", { bold: true }], ["world"]]).toJSON();
|
|
9807
|
+
* // [["Hello ", { bold: true }], ["world"]]
|
|
9808
|
+
*/
|
|
9809
|
+
toJSON() {
|
|
9810
|
+
return super.toJSON();
|
|
9811
|
+
}
|
|
9812
|
+
/** @internal */
|
|
9813
|
+
_toJSON() {
|
|
9814
|
+
return segmentsToData(this.#segments);
|
|
9815
|
+
}
|
|
9816
|
+
/** @internal */
|
|
9817
|
+
toTreeNode(key) {
|
|
9818
|
+
return super.toTreeNode(key);
|
|
9819
|
+
}
|
|
9820
|
+
/** @internal */
|
|
9821
|
+
_toTreeNode(key) {
|
|
9822
|
+
const nodeId = _nullishCoalesce(this._id, () => ( nanoid()));
|
|
9823
|
+
const payload = this.toJSON().map(
|
|
9824
|
+
(segment, index) => ({
|
|
9825
|
+
type: "Json",
|
|
9826
|
+
id: `${nodeId}:${index}`,
|
|
9827
|
+
key: String(index),
|
|
9828
|
+
payload: segment
|
|
9829
|
+
})
|
|
9830
|
+
);
|
|
9831
|
+
payload.push({
|
|
9832
|
+
type: "Json",
|
|
9833
|
+
id: `${nodeId}:version`,
|
|
9834
|
+
key: "version",
|
|
9835
|
+
payload: this.version
|
|
9836
|
+
});
|
|
9837
|
+
return {
|
|
9838
|
+
type: "LiveText",
|
|
9839
|
+
id: nodeId,
|
|
9840
|
+
key,
|
|
9841
|
+
payload
|
|
9842
|
+
};
|
|
9843
|
+
}
|
|
9844
|
+
clone() {
|
|
9845
|
+
return new _LiveText(this.toJSON(), this.#version);
|
|
9846
|
+
}
|
|
9847
|
+
};
|
|
9848
|
+
|
|
8713
9849
|
// src/crdts/liveblocks-helpers.ts
|
|
8714
9850
|
function creationOpToLiveNode(op) {
|
|
8715
9851
|
return lsonToLiveNode(creationOpToLson(op));
|
|
@@ -8724,6 +9860,8 @@ function creationOpToLson(op) {
|
|
|
8724
9860
|
return new LiveMap();
|
|
8725
9861
|
case OpCode.CREATE_LIST:
|
|
8726
9862
|
return new LiveList([]);
|
|
9863
|
+
case OpCode.CREATE_TEXT:
|
|
9864
|
+
return new LiveText(op.data, op.version);
|
|
8727
9865
|
default:
|
|
8728
9866
|
return assertNever(op, "Unknown creation Op");
|
|
8729
9867
|
}
|
|
@@ -8746,6 +9884,8 @@ function deserialize(node, parentToChildren, pool) {
|
|
|
8746
9884
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8747
9885
|
} else if (isRegisterStorageNode(node)) {
|
|
8748
9886
|
return LiveRegister._deserialize(node, parentToChildren, pool);
|
|
9887
|
+
} else if (isTextStorageNode(node)) {
|
|
9888
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8749
9889
|
} else {
|
|
8750
9890
|
throw new Error("Unexpected CRDT type");
|
|
8751
9891
|
}
|
|
@@ -8759,12 +9899,14 @@ function deserializeToLson(node, parentToChildren, pool) {
|
|
|
8759
9899
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8760
9900
|
} else if (isRegisterStorageNode(node)) {
|
|
8761
9901
|
return node[1].data;
|
|
9902
|
+
} else if (isTextStorageNode(node)) {
|
|
9903
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8762
9904
|
} else {
|
|
8763
9905
|
throw new Error("Unexpected CRDT type");
|
|
8764
9906
|
}
|
|
8765
9907
|
}
|
|
8766
9908
|
function isLiveStructure(value) {
|
|
8767
|
-
return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
|
|
9909
|
+
return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
|
|
8768
9910
|
}
|
|
8769
9911
|
function isLiveNode(value) {
|
|
8770
9912
|
return isLiveStructure(value) || isLiveRegister(value);
|
|
@@ -8778,6 +9920,9 @@ function isLiveMap(value) {
|
|
|
8778
9920
|
function isLiveObject(value) {
|
|
8779
9921
|
return value instanceof LiveObject;
|
|
8780
9922
|
}
|
|
9923
|
+
function isLiveText(value) {
|
|
9924
|
+
return value instanceof LiveText;
|
|
9925
|
+
}
|
|
8781
9926
|
function isLiveRegister(value) {
|
|
8782
9927
|
return value instanceof LiveRegister;
|
|
8783
9928
|
}
|
|
@@ -8787,14 +9932,14 @@ function cloneLson(value) {
|
|
|
8787
9932
|
function liveNodeToLson(obj) {
|
|
8788
9933
|
if (obj instanceof LiveRegister) {
|
|
8789
9934
|
return obj.data;
|
|
8790
|
-
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
|
|
9935
|
+
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
|
|
8791
9936
|
return obj;
|
|
8792
9937
|
} else {
|
|
8793
9938
|
return assertNever(obj, "Unknown AbstractCrdt");
|
|
8794
9939
|
}
|
|
8795
9940
|
}
|
|
8796
9941
|
function lsonToLiveNode(value) {
|
|
8797
|
-
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
|
|
9942
|
+
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
|
|
8798
9943
|
return value;
|
|
8799
9944
|
} else {
|
|
8800
9945
|
return new LiveRegister(value);
|
|
@@ -8937,6 +10082,16 @@ function diffNodeMap(prev, next) {
|
|
|
8937
10082
|
parentKey: crdt.parentKey
|
|
8938
10083
|
});
|
|
8939
10084
|
break;
|
|
10085
|
+
case CrdtType.TEXT:
|
|
10086
|
+
ops.push({
|
|
10087
|
+
type: OpCode.CREATE_TEXT,
|
|
10088
|
+
id,
|
|
10089
|
+
parentId: crdt.parentId,
|
|
10090
|
+
parentKey: crdt.parentKey,
|
|
10091
|
+
data: crdt.data,
|
|
10092
|
+
version: crdt.version
|
|
10093
|
+
});
|
|
10094
|
+
break;
|
|
8940
10095
|
}
|
|
8941
10096
|
}
|
|
8942
10097
|
});
|
|
@@ -8969,19 +10124,43 @@ function mergeListStorageUpdates(first, second) {
|
|
|
8969
10124
|
updates: updates.concat(second.updates)
|
|
8970
10125
|
};
|
|
8971
10126
|
}
|
|
10127
|
+
function mergeTextStorageUpdates(first, second) {
|
|
10128
|
+
return {
|
|
10129
|
+
...second,
|
|
10130
|
+
updates: first.updates.concat(second.updates)
|
|
10131
|
+
};
|
|
10132
|
+
}
|
|
8972
10133
|
function mergeStorageUpdates(first, second) {
|
|
8973
10134
|
if (first === void 0) {
|
|
8974
10135
|
return second;
|
|
8975
10136
|
}
|
|
10137
|
+
let merged;
|
|
8976
10138
|
if (first.type === "LiveObject" && second.type === "LiveObject") {
|
|
8977
|
-
|
|
10139
|
+
merged = mergeObjectStorageUpdates(first, second);
|
|
8978
10140
|
} else if (first.type === "LiveMap" && second.type === "LiveMap") {
|
|
8979
|
-
|
|
10141
|
+
merged = mergeMapStorageUpdates(first, second);
|
|
8980
10142
|
} else if (first.type === "LiveList" && second.type === "LiveList") {
|
|
8981
|
-
|
|
10143
|
+
merged = mergeListStorageUpdates(first, second);
|
|
10144
|
+
} else if (first.type === "LiveText" && second.type === "LiveText") {
|
|
10145
|
+
merged = mergeTextStorageUpdates(first, second);
|
|
8982
10146
|
} else {
|
|
10147
|
+
merged = second;
|
|
10148
|
+
}
|
|
10149
|
+
const sa = first[kStorageUpdateSource];
|
|
10150
|
+
const sb = second[kStorageUpdateSource];
|
|
10151
|
+
if (sa !== void 0 || sb !== void 0) {
|
|
10152
|
+
if (_optionalChain([sa, 'optionalAccess', _222 => _222.origin]) === "remote" || _optionalChain([sb, 'optionalAccess', _223 => _223.origin]) === "remote") {
|
|
10153
|
+
merged[kStorageUpdateSource] = { origin: "remote" };
|
|
10154
|
+
} else if (_optionalChain([sa, 'optionalAccess', _224 => _224.via]) === "history" || _optionalChain([sb, 'optionalAccess', _225 => _225.via]) === "history") {
|
|
10155
|
+
const historySource = _optionalChain([sb, 'optionalAccess', _226 => _226.via]) === "history" ? sb : _optionalChain([sa, 'optionalAccess', _227 => _227.via]) === "history" ? sa : void 0;
|
|
10156
|
+
if (_optionalChain([historySource, 'optionalAccess', _228 => _228.via]) === "history") {
|
|
10157
|
+
merged[kStorageUpdateSource] = historySource;
|
|
10158
|
+
}
|
|
10159
|
+
} else {
|
|
10160
|
+
merged[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
10161
|
+
}
|
|
8983
10162
|
}
|
|
8984
|
-
return
|
|
10163
|
+
return merged;
|
|
8985
10164
|
}
|
|
8986
10165
|
|
|
8987
10166
|
// src/devtools/bridge.ts
|
|
@@ -8997,7 +10176,7 @@ function sendToPanel(message, options) {
|
|
|
8997
10176
|
...message,
|
|
8998
10177
|
source: "liveblocks-devtools-client"
|
|
8999
10178
|
};
|
|
9000
|
-
if (!(_optionalChain([options, 'optionalAccess',
|
|
10179
|
+
if (!(_optionalChain([options, 'optionalAccess', _229 => _229.force]) || _bridgeActive)) {
|
|
9001
10180
|
return;
|
|
9002
10181
|
}
|
|
9003
10182
|
window.postMessage(fullMsg, "*");
|
|
@@ -9005,7 +10184,7 @@ function sendToPanel(message, options) {
|
|
|
9005
10184
|
var eventSource = makeEventSource();
|
|
9006
10185
|
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
9007
10186
|
window.addEventListener("message", (event) => {
|
|
9008
|
-
if (event.source === window && _optionalChain([event, 'access',
|
|
10187
|
+
if (event.source === window && _optionalChain([event, 'access', _230 => _230.data, 'optionalAccess', _231 => _231.source]) === "liveblocks-devtools-panel") {
|
|
9009
10188
|
eventSource.notify(event.data);
|
|
9010
10189
|
} else {
|
|
9011
10190
|
}
|
|
@@ -9147,7 +10326,7 @@ function fullSync(room) {
|
|
|
9147
10326
|
msg: "room::sync::full",
|
|
9148
10327
|
roomId: room.id,
|
|
9149
10328
|
status: room.getStatus(),
|
|
9150
|
-
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess',
|
|
10329
|
+
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _232 => _232.toTreeNode, 'call', _233 => _233("root"), 'access', _234 => _234.payload]), () => ( null)),
|
|
9151
10330
|
me,
|
|
9152
10331
|
others
|
|
9153
10332
|
});
|
|
@@ -9834,15 +11013,15 @@ function installBackgroundTabSpy() {
|
|
|
9834
11013
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
9835
11014
|
const inBackgroundSince = { current: null };
|
|
9836
11015
|
function onVisibilityChange() {
|
|
9837
|
-
if (_optionalChain([doc, 'optionalAccess',
|
|
11016
|
+
if (_optionalChain([doc, 'optionalAccess', _235 => _235.visibilityState]) === "hidden") {
|
|
9838
11017
|
inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
|
|
9839
11018
|
} else {
|
|
9840
11019
|
inBackgroundSince.current = null;
|
|
9841
11020
|
}
|
|
9842
11021
|
}
|
|
9843
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11022
|
+
_optionalChain([doc, 'optionalAccess', _236 => _236.addEventListener, 'call', _237 => _237("visibilitychange", onVisibilityChange)]);
|
|
9844
11023
|
const unsub = () => {
|
|
9845
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11024
|
+
_optionalChain([doc, 'optionalAccess', _238 => _238.removeEventListener, 'call', _239 => _239("visibilitychange", onVisibilityChange)]);
|
|
9846
11025
|
};
|
|
9847
11026
|
return [inBackgroundSince, unsub];
|
|
9848
11027
|
}
|
|
@@ -9925,6 +11104,8 @@ function createRoom(options, config) {
|
|
|
9925
11104
|
activeBatch: null,
|
|
9926
11105
|
unacknowledgedOps
|
|
9927
11106
|
};
|
|
11107
|
+
let nextHistoryItemId = 0;
|
|
11108
|
+
let historyDisabled = 0;
|
|
9928
11109
|
const nodeMapBuffer = makeNodeMapBuffer();
|
|
9929
11110
|
const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
|
|
9930
11111
|
let lastTokenKey;
|
|
@@ -10009,7 +11190,10 @@ function createRoom(options, config) {
|
|
|
10009
11190
|
}
|
|
10010
11191
|
}
|
|
10011
11192
|
});
|
|
10012
|
-
function onDispatch(ops, reverse, storageUpdates) {
|
|
11193
|
+
function onDispatch(ops, reverse, storageUpdates, options2) {
|
|
11194
|
+
for (const value of storageUpdates.values()) {
|
|
11195
|
+
value[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
11196
|
+
}
|
|
10013
11197
|
if (context.activeBatch) {
|
|
10014
11198
|
for (const op of ops) {
|
|
10015
11199
|
context.activeBatch.ops.push(op);
|
|
@@ -10028,15 +11212,17 @@ function createRoom(options, config) {
|
|
|
10028
11212
|
if (reverse.length > 0) {
|
|
10029
11213
|
addToUndoStack(reverse);
|
|
10030
11214
|
}
|
|
11215
|
+
if (_nullishCoalesce(_optionalChain([options2, 'optionalAccess', _240 => _240.clearRedoStack]), () => ( ops.length > 0))) {
|
|
11216
|
+
clearRedoStack();
|
|
11217
|
+
}
|
|
10031
11218
|
if (ops.length > 0) {
|
|
10032
|
-
context.redoStack.length = 0;
|
|
10033
11219
|
dispatchOps(ops);
|
|
10034
11220
|
}
|
|
10035
11221
|
notify({ storageUpdates });
|
|
10036
11222
|
}
|
|
10037
11223
|
}
|
|
10038
11224
|
function isStorageWritable() {
|
|
10039
|
-
const permissionMatrix = _optionalChain([context, 'access',
|
|
11225
|
+
const permissionMatrix = _optionalChain([context, 'access', _241 => _241.dynamicSessionInfoSig, 'access', _242 => _242.get, 'call', _243 => _243(), 'optionalAccess', _244 => _244.permissionMatrix]);
|
|
10040
11226
|
return permissionMatrix !== void 0 ? hasPermissionAccess(permissionMatrix, "storage", "write") : true;
|
|
10041
11227
|
}
|
|
10042
11228
|
const eventHub = {
|
|
@@ -10049,6 +11235,7 @@ function createRoom(options, config) {
|
|
|
10049
11235
|
others: makeEventSource(),
|
|
10050
11236
|
storageBatch: makeEventSource(),
|
|
10051
11237
|
history: makeEventSource(),
|
|
11238
|
+
privateHistory: makeEventSource(),
|
|
10052
11239
|
storageDidLoad: makeEventSource(),
|
|
10053
11240
|
storageStatus: makeEventSource(),
|
|
10054
11241
|
ydoc: makeEventSource(),
|
|
@@ -10141,6 +11328,23 @@ function createRoom(options, config) {
|
|
|
10141
11328
|
}
|
|
10142
11329
|
const ops = diffNodeMap(currentItems, nodes);
|
|
10143
11330
|
const result = applyRemoteOps(ops);
|
|
11331
|
+
for (const [id, crdt] of nodes) {
|
|
11332
|
+
if (crdt.type === CrdtType.TEXT) {
|
|
11333
|
+
const node = context.pool.nodes.get(id);
|
|
11334
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11335
|
+
const update = node._resyncText(crdt.data, crdt.version);
|
|
11336
|
+
if (update !== void 0) {
|
|
11337
|
+
result.updates.storageUpdates.set(
|
|
11338
|
+
id,
|
|
11339
|
+
mergeStorageUpdates(
|
|
11340
|
+
result.updates.storageUpdates.get(id),
|
|
11341
|
+
update
|
|
11342
|
+
)
|
|
11343
|
+
);
|
|
11344
|
+
}
|
|
11345
|
+
}
|
|
11346
|
+
}
|
|
11347
|
+
}
|
|
10144
11348
|
notify(result.updates);
|
|
10145
11349
|
} else {
|
|
10146
11350
|
context.root = LiveObject._fromItems(
|
|
@@ -10148,7 +11352,7 @@ function createRoom(options, config) {
|
|
|
10148
11352
|
context.pool
|
|
10149
11353
|
);
|
|
10150
11354
|
}
|
|
10151
|
-
const canWrite = _nullishCoalesce(_optionalChain([self, 'access',
|
|
11355
|
+
const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _245 => _245.get, 'call', _246 => _246(), 'optionalAccess', _247 => _247.canWrite]), () => ( true));
|
|
10152
11356
|
const root = context.root;
|
|
10153
11357
|
disableHistory(() => {
|
|
10154
11358
|
for (const key in context.initialStorage) {
|
|
@@ -10164,11 +11368,26 @@ function createRoom(options, config) {
|
|
|
10164
11368
|
}
|
|
10165
11369
|
});
|
|
10166
11370
|
}
|
|
11371
|
+
function notifyPrivateHistory(event) {
|
|
11372
|
+
if (historyDisabled > 0) return;
|
|
11373
|
+
eventHub.privateHistory.notify(event);
|
|
11374
|
+
}
|
|
11375
|
+
function clearRedoStack() {
|
|
11376
|
+
if (context.redoStack.length === 0) return;
|
|
11377
|
+
const ids = context.redoStack.map((item) => item.id);
|
|
11378
|
+
context.redoStack.length = 0;
|
|
11379
|
+
notifyPrivateHistory({ action: "discard", ids });
|
|
11380
|
+
}
|
|
10167
11381
|
function _addToRealUndoStack(frames) {
|
|
10168
11382
|
if (context.undoStack.length >= 50) {
|
|
10169
|
-
context.undoStack.shift();
|
|
11383
|
+
const evicted = context.undoStack.shift();
|
|
11384
|
+
if (evicted !== void 0) {
|
|
11385
|
+
notifyPrivateHistory({ action: "discard", ids: [evicted.id] });
|
|
11386
|
+
}
|
|
10170
11387
|
}
|
|
10171
|
-
|
|
11388
|
+
const id = nextHistoryItemId++;
|
|
11389
|
+
context.undoStack.push({ id, frames });
|
|
11390
|
+
notifyPrivateHistory({ action: "push", id });
|
|
10172
11391
|
onHistoryChange();
|
|
10173
11392
|
}
|
|
10174
11393
|
function addToUndoStack(frames) {
|
|
@@ -10206,7 +11425,7 @@ function createRoom(options, config) {
|
|
|
10206
11425
|
"Internal. Tried to get connection id but connection was never open"
|
|
10207
11426
|
);
|
|
10208
11427
|
}
|
|
10209
|
-
function applyLocalOps(frames) {
|
|
11428
|
+
function applyLocalOps(frames, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
10210
11429
|
const [pframes, ops] = partition(
|
|
10211
11430
|
frames,
|
|
10212
11431
|
(f) => f.type === "presence"
|
|
@@ -10218,7 +11437,8 @@ function createRoom(options, config) {
|
|
|
10218
11437
|
pframes,
|
|
10219
11438
|
opsWithOpIds,
|
|
10220
11439
|
/* isLocal */
|
|
10221
|
-
true
|
|
11440
|
+
true,
|
|
11441
|
+
localStorageUpdateSource
|
|
10222
11442
|
);
|
|
10223
11443
|
return { opsToEmit: opsWithOpIds, reverse, updates };
|
|
10224
11444
|
}
|
|
@@ -10230,7 +11450,7 @@ function createRoom(options, config) {
|
|
|
10230
11450
|
false
|
|
10231
11451
|
);
|
|
10232
11452
|
}
|
|
10233
|
-
function applyOps(pframes, ops, isLocal) {
|
|
11453
|
+
function applyOps(pframes, ops, isLocal, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
10234
11454
|
const output = {
|
|
10235
11455
|
reverse: new Deque(),
|
|
10236
11456
|
storageUpdates: /* @__PURE__ */ new Map(),
|
|
@@ -10268,6 +11488,7 @@ function createRoom(options, config) {
|
|
|
10268
11488
|
}
|
|
10269
11489
|
const applyOpResult = applyOp(op, source);
|
|
10270
11490
|
if (applyOpResult.modified) {
|
|
11491
|
+
applyOpResult.modified[kStorageUpdateSource] = source === 1 /* THEIRS */ ? { origin: "remote" } : localStorageUpdateSource;
|
|
10271
11492
|
const nodeId = applyOpResult.modified.node._id;
|
|
10272
11493
|
if (!(nodeId && createdNodeIds.has(nodeId))) {
|
|
10273
11494
|
output.storageUpdates.set(
|
|
@@ -10279,7 +11500,7 @@ function createRoom(options, config) {
|
|
|
10279
11500
|
);
|
|
10280
11501
|
output.reverse.pushLeft(applyOpResult.reverse);
|
|
10281
11502
|
}
|
|
10282
|
-
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
|
|
11503
|
+
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
|
|
10283
11504
|
createdNodeIds.add(op.id);
|
|
10284
11505
|
}
|
|
10285
11506
|
}
|
|
@@ -10299,6 +11520,7 @@ function createRoom(options, config) {
|
|
|
10299
11520
|
switch (op.type) {
|
|
10300
11521
|
case OpCode.DELETE_OBJECT_KEY:
|
|
10301
11522
|
case OpCode.UPDATE_OBJECT:
|
|
11523
|
+
case OpCode.UPDATE_TEXT:
|
|
10302
11524
|
case OpCode.DELETE_CRDT: {
|
|
10303
11525
|
const node = context.pool.nodes.get(op.id);
|
|
10304
11526
|
if (node === void 0) {
|
|
@@ -10323,6 +11545,7 @@ function createRoom(options, config) {
|
|
|
10323
11545
|
case OpCode.CREATE_OBJECT:
|
|
10324
11546
|
case OpCode.CREATE_LIST:
|
|
10325
11547
|
case OpCode.CREATE_MAP:
|
|
11548
|
+
case OpCode.CREATE_TEXT:
|
|
10326
11549
|
case OpCode.CREATE_REGISTER: {
|
|
10327
11550
|
if (op.parentId === void 0) {
|
|
10328
11551
|
return { modified: false };
|
|
@@ -10353,7 +11576,7 @@ function createRoom(options, config) {
|
|
|
10353
11576
|
}
|
|
10354
11577
|
context.myPresence.patch(patch);
|
|
10355
11578
|
if (context.activeBatch) {
|
|
10356
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11579
|
+
if (_optionalChain([options2, 'optionalAccess', _248 => _248.addToHistory])) {
|
|
10357
11580
|
context.activeBatch.reverseOps.pushLeft({
|
|
10358
11581
|
type: "presence",
|
|
10359
11582
|
data: oldValues
|
|
@@ -10362,7 +11585,7 @@ function createRoom(options, config) {
|
|
|
10362
11585
|
context.activeBatch.updates.presence = true;
|
|
10363
11586
|
} else {
|
|
10364
11587
|
flushNowOrSoon();
|
|
10365
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11588
|
+
if (_optionalChain([options2, 'optionalAccess', _249 => _249.addToHistory])) {
|
|
10366
11589
|
addToUndoStack([{ type: "presence", data: oldValues }]);
|
|
10367
11590
|
}
|
|
10368
11591
|
notify({ presence: true });
|
|
@@ -10541,11 +11764,11 @@ function createRoom(options, config) {
|
|
|
10541
11764
|
break;
|
|
10542
11765
|
}
|
|
10543
11766
|
case ServerMsgCode.STORAGE_CHUNK:
|
|
10544
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
11767
|
+
_optionalChain([stopwatch, 'optionalAccess', _250 => _250.lap, 'call', _251 => _251()]);
|
|
10545
11768
|
nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
|
|
10546
11769
|
break;
|
|
10547
11770
|
case ServerMsgCode.STORAGE_STREAM_END: {
|
|
10548
|
-
const timing = _optionalChain([stopwatch, 'optionalAccess',
|
|
11771
|
+
const timing = _optionalChain([stopwatch, 'optionalAccess', _252 => _252.stop, 'call', _253 => _253()]);
|
|
10549
11772
|
if (timing) {
|
|
10550
11773
|
const ms = (v) => `${v.toFixed(1)}ms`;
|
|
10551
11774
|
const rest = timing.laps.slice(1);
|
|
@@ -10572,16 +11795,37 @@ function createRoom(options, config) {
|
|
|
10572
11795
|
}
|
|
10573
11796
|
break;
|
|
10574
11797
|
}
|
|
10575
|
-
// Receiving a RejectedOps message
|
|
10576
|
-
//
|
|
10577
|
-
//
|
|
10578
|
-
//
|
|
11798
|
+
// Receiving a RejectedOps message means the server refused some of
|
|
11799
|
+
// our ops, so our optimistic local state is out of sync with the
|
|
11800
|
+
// server. For LiveText ops this is a normal (if rare) situation —
|
|
11801
|
+
// e.g. a client that was offline long enough to fall outside the
|
|
11802
|
+
// server's retained history window — and we can recover: drop the
|
|
11803
|
+
// rejected pending state and re-fetch the authoritative storage
|
|
11804
|
+
// snapshot. For other ops (e.g. permission rejections), rolling back
|
|
11805
|
+
// particular Ops is hard/impossible, so we keep the old behavior of
|
|
11806
|
+
// accepting the out-of-sync reality and surfacing an error.
|
|
10579
11807
|
case ServerMsgCode.REJECT_STORAGE_OP: {
|
|
10580
11808
|
errorWithTitle(
|
|
10581
11809
|
"Storage mutation rejection error",
|
|
10582
11810
|
message.reason
|
|
10583
11811
|
);
|
|
10584
|
-
|
|
11812
|
+
let needsStorageResync = false;
|
|
11813
|
+
for (const opId of message.opIds) {
|
|
11814
|
+
const rejectedOp = context.unacknowledgedOps.get(opId);
|
|
11815
|
+
context.unacknowledgedOps.delete(opId);
|
|
11816
|
+
context.buffer.storageOperations = context.buffer.storageOperations.filter((op) => op.opId !== opId);
|
|
11817
|
+
if (rejectedOp !== void 0 && rejectedOp.type === OpCode.UPDATE_TEXT) {
|
|
11818
|
+
const node = context.pool.nodes.get(rejectedOp.id);
|
|
11819
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11820
|
+
node._rejectPendingOp(opId);
|
|
11821
|
+
needsStorageResync = true;
|
|
11822
|
+
}
|
|
11823
|
+
}
|
|
11824
|
+
}
|
|
11825
|
+
if (needsStorageResync) {
|
|
11826
|
+
refreshStorage();
|
|
11827
|
+
flushNowOrSoon();
|
|
11828
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
10585
11829
|
throw new Error(
|
|
10586
11830
|
`Storage mutations rejected by server: ${message.reason}`
|
|
10587
11831
|
);
|
|
@@ -10680,11 +11924,11 @@ function createRoom(options, config) {
|
|
|
10680
11924
|
} else if (pendingFeedsRequests.has(requestId)) {
|
|
10681
11925
|
const pending = pendingFeedsRequests.get(requestId);
|
|
10682
11926
|
pendingFeedsRequests.delete(requestId);
|
|
10683
|
-
_optionalChain([pending, 'optionalAccess',
|
|
11927
|
+
_optionalChain([pending, 'optionalAccess', _254 => _254.reject, 'call', _255 => _255(err)]);
|
|
10684
11928
|
} else if (pendingFeedMessagesRequests.has(requestId)) {
|
|
10685
11929
|
const pending = pendingFeedMessagesRequests.get(requestId);
|
|
10686
11930
|
pendingFeedMessagesRequests.delete(requestId);
|
|
10687
|
-
_optionalChain([pending, 'optionalAccess',
|
|
11931
|
+
_optionalChain([pending, 'optionalAccess', _256 => _256.reject, 'call', _257 => _257(err)]);
|
|
10688
11932
|
}
|
|
10689
11933
|
eventHub.feeds.notify(message);
|
|
10690
11934
|
break;
|
|
@@ -10838,10 +12082,10 @@ function createRoom(options, config) {
|
|
|
10838
12082
|
timeoutId,
|
|
10839
12083
|
kind,
|
|
10840
12084
|
feedId,
|
|
10841
|
-
messageId: _optionalChain([options2, 'optionalAccess',
|
|
10842
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12085
|
+
messageId: _optionalChain([options2, 'optionalAccess', _258 => _258.messageId]),
|
|
12086
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _259 => _259.expectedClientMessageId])
|
|
10843
12087
|
});
|
|
10844
|
-
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess',
|
|
12088
|
+
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _260 => _260.expectedClientMessageId]) === void 0) {
|
|
10845
12089
|
const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
|
|
10846
12090
|
q.push(requestId);
|
|
10847
12091
|
pendingAddMessageFifoByFeed.set(feedId, q);
|
|
@@ -10892,10 +12136,10 @@ function createRoom(options, config) {
|
|
|
10892
12136
|
}
|
|
10893
12137
|
if (!matched) {
|
|
10894
12138
|
const q = pendingAddMessageFifoByFeed.get(message.feedId);
|
|
10895
|
-
const headId = _optionalChain([q, 'optionalAccess',
|
|
12139
|
+
const headId = _optionalChain([q, 'optionalAccess', _261 => _261[0]]);
|
|
10896
12140
|
if (headId !== void 0) {
|
|
10897
12141
|
const pending = pendingFeedMutations.get(headId);
|
|
10898
|
-
if (_optionalChain([pending, 'optionalAccess',
|
|
12142
|
+
if (_optionalChain([pending, 'optionalAccess', _262 => _262.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
|
|
10899
12143
|
settleFeedMutation(headId, "ok");
|
|
10900
12144
|
}
|
|
10901
12145
|
}
|
|
@@ -10931,7 +12175,7 @@ function createRoom(options, config) {
|
|
|
10931
12175
|
const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
|
|
10932
12176
|
createOrUpdateRootFromMessage(nodes);
|
|
10933
12177
|
applyAndSendOfflineOps(unacknowledgedOps2);
|
|
10934
|
-
_optionalChain([_resolveStoragePromise, 'optionalCall',
|
|
12178
|
+
_optionalChain([_resolveStoragePromise, 'optionalCall', _263 => _263()]);
|
|
10935
12179
|
notifyStorageStatus();
|
|
10936
12180
|
eventHub.storageDidLoad.notify();
|
|
10937
12181
|
}
|
|
@@ -10940,7 +12184,7 @@ function createRoom(options, config) {
|
|
|
10940
12184
|
if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
|
|
10941
12185
|
messages.push({ type: ClientMsgCode.FETCH_STORAGE });
|
|
10942
12186
|
nodeMapBuffer.take();
|
|
10943
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
12187
|
+
_optionalChain([stopwatch, 'optionalAccess', _264 => _264.start, 'call', _265 => _265()]);
|
|
10944
12188
|
}
|
|
10945
12189
|
}
|
|
10946
12190
|
function startLoadingStorage() {
|
|
@@ -10994,10 +12238,10 @@ function createRoom(options, config) {
|
|
|
10994
12238
|
const message = {
|
|
10995
12239
|
type: ClientMsgCode.FETCH_FEEDS,
|
|
10996
12240
|
requestId,
|
|
10997
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
10998
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
10999
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
11000
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
12241
|
+
cursor: _optionalChain([options2, 'optionalAccess', _266 => _266.cursor]),
|
|
12242
|
+
since: _optionalChain([options2, 'optionalAccess', _267 => _267.since]),
|
|
12243
|
+
limit: _optionalChain([options2, 'optionalAccess', _268 => _268.limit]),
|
|
12244
|
+
metadata: _optionalChain([options2, 'optionalAccess', _269 => _269.metadata])
|
|
11001
12245
|
};
|
|
11002
12246
|
context.buffer.messages.push(message);
|
|
11003
12247
|
flushNowOrSoon();
|
|
@@ -11017,9 +12261,9 @@ function createRoom(options, config) {
|
|
|
11017
12261
|
type: ClientMsgCode.FETCH_FEED_MESSAGES,
|
|
11018
12262
|
requestId,
|
|
11019
12263
|
feedId,
|
|
11020
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
11021
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
11022
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
12264
|
+
cursor: _optionalChain([options2, 'optionalAccess', _270 => _270.cursor]),
|
|
12265
|
+
since: _optionalChain([options2, 'optionalAccess', _271 => _271.since]),
|
|
12266
|
+
limit: _optionalChain([options2, 'optionalAccess', _272 => _272.limit])
|
|
11023
12267
|
};
|
|
11024
12268
|
context.buffer.messages.push(message);
|
|
11025
12269
|
flushNowOrSoon();
|
|
@@ -11038,8 +12282,8 @@ function createRoom(options, config) {
|
|
|
11038
12282
|
type: ClientMsgCode.ADD_FEED,
|
|
11039
12283
|
requestId,
|
|
11040
12284
|
feedId,
|
|
11041
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
11042
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12285
|
+
metadata: _optionalChain([options2, 'optionalAccess', _273 => _273.metadata]),
|
|
12286
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _274 => _274.createdAt])
|
|
11043
12287
|
};
|
|
11044
12288
|
context.buffer.messages.push(message);
|
|
11045
12289
|
flushNowOrSoon();
|
|
@@ -11073,15 +12317,15 @@ function createRoom(options, config) {
|
|
|
11073
12317
|
function addFeedMessage(feedId, data, options2) {
|
|
11074
12318
|
const requestId = nanoid();
|
|
11075
12319
|
const promise = registerFeedMutation(requestId, "add-message", feedId, {
|
|
11076
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12320
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _275 => _275.id])
|
|
11077
12321
|
});
|
|
11078
12322
|
const message = {
|
|
11079
12323
|
type: ClientMsgCode.ADD_FEED_MESSAGE,
|
|
11080
12324
|
requestId,
|
|
11081
12325
|
feedId,
|
|
11082
12326
|
data,
|
|
11083
|
-
id: _optionalChain([options2, 'optionalAccess',
|
|
11084
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12327
|
+
id: _optionalChain([options2, 'optionalAccess', _276 => _276.id]),
|
|
12328
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _277 => _277.createdAt])
|
|
11085
12329
|
};
|
|
11086
12330
|
context.buffer.messages.push(message);
|
|
11087
12331
|
flushNowOrSoon();
|
|
@@ -11098,7 +12342,7 @@ function createRoom(options, config) {
|
|
|
11098
12342
|
feedId,
|
|
11099
12343
|
messageId,
|
|
11100
12344
|
data,
|
|
11101
|
-
updatedAt: _optionalChain([options2, 'optionalAccess',
|
|
12345
|
+
updatedAt: _optionalChain([options2, 'optionalAccess', _278 => _278.updatedAt])
|
|
11102
12346
|
};
|
|
11103
12347
|
context.buffer.messages.push(message);
|
|
11104
12348
|
flushNowOrSoon();
|
|
@@ -11123,14 +12367,19 @@ function createRoom(options, config) {
|
|
|
11123
12367
|
if (context.activeBatch) {
|
|
11124
12368
|
throw new Error("undo is not allowed during a batch");
|
|
11125
12369
|
}
|
|
11126
|
-
const
|
|
11127
|
-
if (
|
|
12370
|
+
const item = context.undoStack.pop();
|
|
12371
|
+
if (item === void 0) {
|
|
11128
12372
|
return;
|
|
11129
12373
|
}
|
|
11130
12374
|
context.pausedHistory = null;
|
|
11131
|
-
const result = applyLocalOps(frames
|
|
12375
|
+
const result = applyLocalOps(item.frames, {
|
|
12376
|
+
origin: "local",
|
|
12377
|
+
via: "history",
|
|
12378
|
+
action: "undo"
|
|
12379
|
+
});
|
|
12380
|
+
context.redoStack.push({ id: item.id, frames: result.reverse });
|
|
12381
|
+
notifyPrivateHistory({ action: "undo", id: item.id });
|
|
11132
12382
|
notify(result.updates);
|
|
11133
|
-
context.redoStack.push(result.reverse);
|
|
11134
12383
|
onHistoryChange();
|
|
11135
12384
|
for (const op of result.opsToEmit) {
|
|
11136
12385
|
context.buffer.storageOperations.push(op);
|
|
@@ -11141,14 +12390,19 @@ function createRoom(options, config) {
|
|
|
11141
12390
|
if (context.activeBatch) {
|
|
11142
12391
|
throw new Error("redo is not allowed during a batch");
|
|
11143
12392
|
}
|
|
11144
|
-
const
|
|
11145
|
-
if (
|
|
12393
|
+
const item = context.redoStack.pop();
|
|
12394
|
+
if (item === void 0) {
|
|
11146
12395
|
return;
|
|
11147
12396
|
}
|
|
11148
12397
|
context.pausedHistory = null;
|
|
11149
|
-
const result = applyLocalOps(frames
|
|
12398
|
+
const result = applyLocalOps(item.frames, {
|
|
12399
|
+
origin: "local",
|
|
12400
|
+
via: "history",
|
|
12401
|
+
action: "redo"
|
|
12402
|
+
});
|
|
12403
|
+
context.undoStack.push({ id: item.id, frames: result.reverse });
|
|
12404
|
+
notifyPrivateHistory({ action: "redo", id: item.id });
|
|
11150
12405
|
notify(result.updates);
|
|
11151
|
-
context.undoStack.push(result.reverse);
|
|
11152
12406
|
onHistoryChange();
|
|
11153
12407
|
for (const op of result.opsToEmit) {
|
|
11154
12408
|
context.buffer.storageOperations.push(op);
|
|
@@ -11158,6 +12412,8 @@ function createRoom(options, config) {
|
|
|
11158
12412
|
function clear() {
|
|
11159
12413
|
context.undoStack.length = 0;
|
|
11160
12414
|
context.redoStack.length = 0;
|
|
12415
|
+
notifyPrivateHistory({ action: "clear" });
|
|
12416
|
+
onHistoryChange();
|
|
11161
12417
|
}
|
|
11162
12418
|
function batch2(callback) {
|
|
11163
12419
|
if (context.activeBatch) {
|
|
@@ -11186,7 +12442,7 @@ function createRoom(options, config) {
|
|
|
11186
12442
|
commitPausedHistoryToUndoStack();
|
|
11187
12443
|
}
|
|
11188
12444
|
if (currentBatch.ops.length > 0) {
|
|
11189
|
-
|
|
12445
|
+
clearRedoStack();
|
|
11190
12446
|
}
|
|
11191
12447
|
if (currentBatch.ops.length > 0) {
|
|
11192
12448
|
dispatchOps(currentBatch.ops);
|
|
@@ -11215,7 +12471,6 @@ function createRoom(options, config) {
|
|
|
11215
12471
|
}
|
|
11216
12472
|
commitPausedHistoryToUndoStack();
|
|
11217
12473
|
}
|
|
11218
|
-
let historyDisabled = 0;
|
|
11219
12474
|
function disableHistory(fn) {
|
|
11220
12475
|
const origUndo = context.undoStack;
|
|
11221
12476
|
const origRedo = context.redoStack;
|
|
@@ -11305,8 +12560,8 @@ function createRoom(options, config) {
|
|
|
11305
12560
|
async function getThreads(options2) {
|
|
11306
12561
|
return httpClient.getThreads({
|
|
11307
12562
|
roomId,
|
|
11308
|
-
query: _optionalChain([options2, 'optionalAccess',
|
|
11309
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
12563
|
+
query: _optionalChain([options2, 'optionalAccess', _279 => _279.query]),
|
|
12564
|
+
cursor: _optionalChain([options2, 'optionalAccess', _280 => _280.cursor])
|
|
11310
12565
|
});
|
|
11311
12566
|
}
|
|
11312
12567
|
async function getThread(threadId) {
|
|
@@ -11428,7 +12683,7 @@ function createRoom(options, config) {
|
|
|
11428
12683
|
function getSubscriptionSettings(options2) {
|
|
11429
12684
|
return httpClient.getSubscriptionSettings({
|
|
11430
12685
|
roomId,
|
|
11431
|
-
signal: _optionalChain([options2, 'optionalAccess',
|
|
12686
|
+
signal: _optionalChain([options2, 'optionalAccess', _281 => _281.signal])
|
|
11432
12687
|
});
|
|
11433
12688
|
}
|
|
11434
12689
|
function updateSubscriptionSettings(settings) {
|
|
@@ -11450,24 +12705,39 @@ function createRoom(options, config) {
|
|
|
11450
12705
|
{
|
|
11451
12706
|
[kInternal]: {
|
|
11452
12707
|
get presenceBuffer() {
|
|
11453
|
-
return deepClone(_nullishCoalesce(_optionalChain([context, 'access',
|
|
12708
|
+
return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _282 => _282.buffer, 'access', _283 => _283.presenceUpdates, 'optionalAccess', _284 => _284.data]), () => ( null)));
|
|
11454
12709
|
},
|
|
11455
12710
|
// prettier-ignore
|
|
11456
12711
|
get undoStack() {
|
|
11457
|
-
return
|
|
12712
|
+
return structuredClone(
|
|
12713
|
+
context.undoStack.map((item) => ({
|
|
12714
|
+
id: item.id,
|
|
12715
|
+
frames: item.frames
|
|
12716
|
+
}))
|
|
12717
|
+
);
|
|
12718
|
+
},
|
|
12719
|
+
// prettier-ignore
|
|
12720
|
+
get redoStack() {
|
|
12721
|
+
return structuredClone(
|
|
12722
|
+
context.redoStack.map((item) => ({
|
|
12723
|
+
id: item.id,
|
|
12724
|
+
frames: item.frames
|
|
12725
|
+
}))
|
|
12726
|
+
);
|
|
11458
12727
|
},
|
|
11459
12728
|
// prettier-ignore
|
|
11460
12729
|
get nodeCount() {
|
|
11461
12730
|
return context.pool.nodes.size;
|
|
11462
12731
|
},
|
|
11463
12732
|
// prettier-ignore
|
|
12733
|
+
history: eventHub.privateHistory.observable,
|
|
11464
12734
|
getYjsProvider() {
|
|
11465
12735
|
return context.yjsProvider;
|
|
11466
12736
|
},
|
|
11467
12737
|
setYjsProvider(newProvider) {
|
|
11468
|
-
_optionalChain([context, 'access',
|
|
12738
|
+
_optionalChain([context, 'access', _285 => _285.yjsProvider, 'optionalAccess', _286 => _286.off, 'call', _287 => _287("status", yjsStatusDidChange)]);
|
|
11469
12739
|
context.yjsProvider = newProvider;
|
|
11470
|
-
_optionalChain([newProvider, 'optionalAccess',
|
|
12740
|
+
_optionalChain([newProvider, 'optionalAccess', _288 => _288.on, 'call', _289 => _289("status", yjsStatusDidChange)]);
|
|
11471
12741
|
context.yjsProviderDidChange.notify();
|
|
11472
12742
|
},
|
|
11473
12743
|
yjsProviderDidChange: context.yjsProviderDidChange.observable,
|
|
@@ -11527,7 +12797,7 @@ ${dumpPool(
|
|
|
11527
12797
|
source.dispose();
|
|
11528
12798
|
}
|
|
11529
12799
|
eventHub.roomWillDestroy.notify();
|
|
11530
|
-
_optionalChain([context, 'access',
|
|
12800
|
+
_optionalChain([context, 'access', _290 => _290.yjsProvider, 'optionalAccess', _291 => _291.off, 'call', _292 => _292("status", yjsStatusDidChange)]);
|
|
11531
12801
|
syncSourceForStorage.destroy();
|
|
11532
12802
|
syncSourceForYjs.destroy();
|
|
11533
12803
|
uninstallBgTabSpy();
|
|
@@ -11689,7 +12959,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
|
|
|
11689
12959
|
}
|
|
11690
12960
|
if (isLiveNode(first)) {
|
|
11691
12961
|
const node = first;
|
|
11692
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
12962
|
+
if (_optionalChain([options, 'optionalAccess', _293 => _293.isDeep])) {
|
|
11693
12963
|
const storageCallback = second;
|
|
11694
12964
|
return subscribeToLiveStructureDeeply(node, storageCallback);
|
|
11695
12965
|
} else {
|
|
@@ -11779,8 +13049,8 @@ function createClient(options) {
|
|
|
11779
13049
|
const authManager = createAuthManager(options, (token) => {
|
|
11780
13050
|
currentUserId.set(() => token.uid);
|
|
11781
13051
|
});
|
|
11782
|
-
const fetchPolyfill = _optionalChain([clientOptions, 'access',
|
|
11783
|
-
_optionalChain([globalThis, 'access',
|
|
13052
|
+
const fetchPolyfill = _optionalChain([clientOptions, 'access', _294 => _294.polyfills, 'optionalAccess', _295 => _295.fetch]) || /* istanbul ignore next */
|
|
13053
|
+
_optionalChain([globalThis, 'access', _296 => _296.fetch, 'optionalAccess', _297 => _297.bind, 'call', _298 => _298(globalThis)]);
|
|
11784
13054
|
const httpClient = createApiClient({
|
|
11785
13055
|
baseUrl,
|
|
11786
13056
|
fetchPolyfill,
|
|
@@ -11797,7 +13067,7 @@ function createClient(options) {
|
|
|
11797
13067
|
delegates: {
|
|
11798
13068
|
createSocket: makeCreateSocketDelegateForAi(
|
|
11799
13069
|
baseUrl,
|
|
11800
|
-
_optionalChain([clientOptions, 'access',
|
|
13070
|
+
_optionalChain([clientOptions, 'access', _299 => _299.polyfills, 'optionalAccess', _300 => _300.WebSocket])
|
|
11801
13071
|
),
|
|
11802
13072
|
authenticate: async () => {
|
|
11803
13073
|
const resp = await authManager.getAuthValue({
|
|
@@ -11868,7 +13138,7 @@ function createClient(options) {
|
|
|
11868
13138
|
createSocket: makeCreateSocketDelegateForRoom(
|
|
11869
13139
|
roomId,
|
|
11870
13140
|
baseUrl,
|
|
11871
|
-
_optionalChain([clientOptions, 'access',
|
|
13141
|
+
_optionalChain([clientOptions, 'access', _301 => _301.polyfills, 'optionalAccess', _302 => _302.WebSocket])
|
|
11872
13142
|
),
|
|
11873
13143
|
authenticate: makeAuthDelegateForRoom(roomId, authManager)
|
|
11874
13144
|
})),
|
|
@@ -11890,7 +13160,7 @@ function createClient(options) {
|
|
|
11890
13160
|
const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
|
|
11891
13161
|
if (shouldConnect) {
|
|
11892
13162
|
if (typeof atob === "undefined") {
|
|
11893
|
-
if (_optionalChain([clientOptions, 'access',
|
|
13163
|
+
if (_optionalChain([clientOptions, 'access', _303 => _303.polyfills, 'optionalAccess', _304 => _304.atob]) === void 0) {
|
|
11894
13164
|
throw new Error(
|
|
11895
13165
|
"You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
|
|
11896
13166
|
);
|
|
@@ -11902,7 +13172,7 @@ function createClient(options) {
|
|
|
11902
13172
|
return leaseRoom(newRoomDetails);
|
|
11903
13173
|
}
|
|
11904
13174
|
function getRoom(roomId) {
|
|
11905
|
-
const room = _optionalChain([roomsById, 'access',
|
|
13175
|
+
const room = _optionalChain([roomsById, 'access', _305 => _305.get, 'call', _306 => _306(roomId), 'optionalAccess', _307 => _307.room]);
|
|
11906
13176
|
return room ? room : null;
|
|
11907
13177
|
}
|
|
11908
13178
|
function logout() {
|
|
@@ -11918,7 +13188,7 @@ function createClient(options) {
|
|
|
11918
13188
|
const batchedResolveUsers = new Batch(
|
|
11919
13189
|
async (batchedUserIds) => {
|
|
11920
13190
|
const userIds = batchedUserIds.flat();
|
|
11921
|
-
const users = await _optionalChain([resolveUsers, 'optionalCall',
|
|
13191
|
+
const users = await _optionalChain([resolveUsers, 'optionalCall', _308 => _308({ userIds })]);
|
|
11922
13192
|
warnOnceIf(
|
|
11923
13193
|
!resolveUsers,
|
|
11924
13194
|
"Set the resolveUsers option in createClient to specify user info."
|
|
@@ -11935,7 +13205,7 @@ function createClient(options) {
|
|
|
11935
13205
|
const batchedResolveRoomsInfo = new Batch(
|
|
11936
13206
|
async (batchedRoomIds) => {
|
|
11937
13207
|
const roomIds = batchedRoomIds.flat();
|
|
11938
|
-
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall',
|
|
13208
|
+
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _309 => _309({ roomIds })]);
|
|
11939
13209
|
warnOnceIf(
|
|
11940
13210
|
!resolveRoomsInfo,
|
|
11941
13211
|
"Set the resolveRoomsInfo option in createClient to specify room info."
|
|
@@ -11952,7 +13222,7 @@ function createClient(options) {
|
|
|
11952
13222
|
const batchedResolveGroupsInfo = new Batch(
|
|
11953
13223
|
async (batchedGroupIds) => {
|
|
11954
13224
|
const groupIds = batchedGroupIds.flat();
|
|
11955
|
-
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall',
|
|
13225
|
+
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _310 => _310({ groupIds })]);
|
|
11956
13226
|
warnOnceIf(
|
|
11957
13227
|
!resolveGroupsInfo,
|
|
11958
13228
|
"Set the resolveGroupsInfo option in createClient to specify group info."
|
|
@@ -12011,7 +13281,7 @@ function createClient(options) {
|
|
|
12011
13281
|
}
|
|
12012
13282
|
};
|
|
12013
13283
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12014
|
-
_optionalChain([win, 'optionalAccess',
|
|
13284
|
+
_optionalChain([win, 'optionalAccess', _311 => _311.addEventListener, 'call', _312 => _312("beforeunload", maybePreventClose)]);
|
|
12015
13285
|
}
|
|
12016
13286
|
async function getNotificationSettings(options2) {
|
|
12017
13287
|
const plainSettings = await httpClient.getNotificationSettings(options2);
|
|
@@ -12139,7 +13409,7 @@ var commentBodyElementsTypes = {
|
|
|
12139
13409
|
mention: "inline"
|
|
12140
13410
|
};
|
|
12141
13411
|
function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
12142
|
-
if (!body || !_optionalChain([body, 'optionalAccess',
|
|
13412
|
+
if (!body || !_optionalChain([body, 'optionalAccess', _313 => _313.content])) {
|
|
12143
13413
|
return;
|
|
12144
13414
|
}
|
|
12145
13415
|
const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
|
|
@@ -12149,13 +13419,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
|
12149
13419
|
for (const block of body.content) {
|
|
12150
13420
|
if (type === "all" || type === "block") {
|
|
12151
13421
|
if (guard(block)) {
|
|
12152
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13422
|
+
_optionalChain([visitor, 'optionalCall', _314 => _314(block)]);
|
|
12153
13423
|
}
|
|
12154
13424
|
}
|
|
12155
13425
|
if (type === "all" || type === "inline") {
|
|
12156
13426
|
for (const inline of block.children) {
|
|
12157
13427
|
if (guard(inline)) {
|
|
12158
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13428
|
+
_optionalChain([visitor, 'optionalCall', _315 => _315(inline)]);
|
|
12159
13429
|
}
|
|
12160
13430
|
}
|
|
12161
13431
|
}
|
|
@@ -12325,7 +13595,7 @@ var stringifyCommentBodyPlainElements = {
|
|
|
12325
13595
|
text: ({ element }) => element.text,
|
|
12326
13596
|
link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
|
|
12327
13597
|
mention: ({ element, user, group }) => {
|
|
12328
|
-
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13598
|
+
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _316 => _316.name]), () => ( _optionalChain([group, 'optionalAccess', _317 => _317.name]))), () => ( element.id))}`;
|
|
12329
13599
|
}
|
|
12330
13600
|
};
|
|
12331
13601
|
var stringifyCommentBodyHtmlElements = {
|
|
@@ -12355,7 +13625,7 @@ var stringifyCommentBodyHtmlElements = {
|
|
|
12355
13625
|
return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
|
|
12356
13626
|
},
|
|
12357
13627
|
mention: ({ element, user, group }) => {
|
|
12358
|
-
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess',
|
|
13628
|
+
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _318 => _318.name]) ? html`${_optionalChain([user, 'optionalAccess', _319 => _319.name])}` : _optionalChain([group, 'optionalAccess', _320 => _320.name]) ? html`${_optionalChain([group, 'optionalAccess', _321 => _321.name])}` : element.id}</span>`;
|
|
12359
13629
|
}
|
|
12360
13630
|
};
|
|
12361
13631
|
var stringifyCommentBodyMarkdownElements = {
|
|
@@ -12385,20 +13655,20 @@ var stringifyCommentBodyMarkdownElements = {
|
|
|
12385
13655
|
return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
|
|
12386
13656
|
},
|
|
12387
13657
|
mention: ({ element, user, group }) => {
|
|
12388
|
-
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13658
|
+
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _322 => _322.name]), () => ( _optionalChain([group, 'optionalAccess', _323 => _323.name]))), () => ( element.id))}`;
|
|
12389
13659
|
}
|
|
12390
13660
|
};
|
|
12391
13661
|
async function stringifyCommentBody(body, options) {
|
|
12392
|
-
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
12393
|
-
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13662
|
+
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _324 => _324.format]), () => ( "plain"));
|
|
13663
|
+
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _325 => _325.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
|
|
12394
13664
|
const elements = {
|
|
12395
13665
|
...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
|
|
12396
|
-
..._optionalChain([options, 'optionalAccess',
|
|
13666
|
+
..._optionalChain([options, 'optionalAccess', _326 => _326.elements])
|
|
12397
13667
|
};
|
|
12398
13668
|
const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
|
|
12399
13669
|
body,
|
|
12400
|
-
_optionalChain([options, 'optionalAccess',
|
|
12401
|
-
_optionalChain([options, 'optionalAccess',
|
|
13670
|
+
_optionalChain([options, 'optionalAccess', _327 => _327.resolveUsers]),
|
|
13671
|
+
_optionalChain([options, 'optionalAccess', _328 => _328.resolveGroupsInfo])
|
|
12402
13672
|
);
|
|
12403
13673
|
const blocks = body.content.flatMap((block, blockIndex) => {
|
|
12404
13674
|
switch (block.type) {
|
|
@@ -12480,6 +13750,12 @@ function toPlainLson(lson) {
|
|
|
12480
13750
|
liveblocksType: "LiveList",
|
|
12481
13751
|
data: [...lson].map((item) => toPlainLson(item))
|
|
12482
13752
|
};
|
|
13753
|
+
} else if (lson instanceof LiveText) {
|
|
13754
|
+
return {
|
|
13755
|
+
liveblocksType: "LiveText",
|
|
13756
|
+
data: lson.toJSON(),
|
|
13757
|
+
version: lson.version
|
|
13758
|
+
};
|
|
12483
13759
|
} else {
|
|
12484
13760
|
return lson;
|
|
12485
13761
|
}
|
|
@@ -12533,9 +13809,9 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12533
13809
|
const startTime = performance.now();
|
|
12534
13810
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
12535
13811
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12536
|
-
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13812
|
+
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _329 => _329.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
|
|
12537
13813
|
const context = {
|
|
12538
|
-
inForeground: _optionalChain([doc, 'optionalAccess',
|
|
13814
|
+
inForeground: _optionalChain([doc, 'optionalAccess', _330 => _330.visibilityState]) !== "hidden",
|
|
12539
13815
|
lastSuccessfulPollAt: startTime,
|
|
12540
13816
|
count: 0,
|
|
12541
13817
|
backoff: 0
|
|
@@ -12616,11 +13892,11 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12616
13892
|
pollNowIfStale();
|
|
12617
13893
|
}
|
|
12618
13894
|
function onVisibilityChange() {
|
|
12619
|
-
setInForeground(_optionalChain([doc, 'optionalAccess',
|
|
13895
|
+
setInForeground(_optionalChain([doc, 'optionalAccess', _331 => _331.visibilityState]) !== "hidden");
|
|
12620
13896
|
}
|
|
12621
|
-
_optionalChain([doc, 'optionalAccess',
|
|
12622
|
-
_optionalChain([win, 'optionalAccess',
|
|
12623
|
-
_optionalChain([win, 'optionalAccess',
|
|
13897
|
+
_optionalChain([doc, 'optionalAccess', _332 => _332.addEventListener, 'call', _333 => _333("visibilitychange", onVisibilityChange)]);
|
|
13898
|
+
_optionalChain([win, 'optionalAccess', _334 => _334.addEventListener, 'call', _335 => _335("online", onVisibilityChange)]);
|
|
13899
|
+
_optionalChain([win, 'optionalAccess', _336 => _336.addEventListener, 'call', _337 => _337("focus", pollNowIfStale)]);
|
|
12624
13900
|
fsm.start();
|
|
12625
13901
|
return {
|
|
12626
13902
|
inc,
|
|
@@ -12765,5 +14041,10 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
|
12765
14041
|
|
|
12766
14042
|
|
|
12767
14043
|
|
|
12768
|
-
|
|
14044
|
+
|
|
14045
|
+
|
|
14046
|
+
|
|
14047
|
+
|
|
14048
|
+
|
|
14049
|
+
exports.ClientMsgCode = ClientMsgCode; exports.CrdtType = CrdtType; exports.DefaultMap = DefaultMap; exports.Deque = Deque; exports.DerivedSignal = DerivedSignal; exports.FeedRequestErrorCode = FeedRequestErrorCode; exports.HttpError = HttpError; exports.LiveList = LiveList; exports.LiveMap = LiveMap; exports.LiveObject = LiveObject; exports.LiveText = LiveText; exports.LiveblocksError = LiveblocksError; exports.MENTION_CHARACTER = MENTION_CHARACTER; exports.MutableSignal = MutableSignal; exports.OpCode = OpCode; exports.Permission = Permission; exports.Promise_withResolvers = Promise_withResolvers; exports.ServerMsgCode = ServerMsgCode; exports.Signal = Signal; exports.SortedList = SortedList; exports.TextEditorType = TextEditorType; exports.WebsocketCloseCodes = WebsocketCloseCodes; exports.applyLiveTextOperations = applyLiveTextOperations; exports.asPos = asPos; exports.assert = assert; exports.assertNever = assertNever; exports.autoRetry = autoRetry; exports.b64decode = b64decode; exports.batch = batch; exports.checkBounds = checkBounds; exports.chunk = chunk; exports.cloneLson = cloneLson; exports.compactNodesToNodeStream = compactNodesToNodeStream; exports.compactObject = compactObject; exports.console = fancy_console_exports; exports.convertToCommentData = convertToCommentData; exports.convertToCommentUserReaction = convertToCommentUserReaction; exports.convertToGroupData = convertToGroupData; exports.convertToInboxNotificationData = convertToInboxNotificationData; exports.convertToSubscriptionData = convertToSubscriptionData; exports.convertToThreadData = convertToThreadData; exports.convertToUserSubscriptionData = convertToUserSubscriptionData; exports.createClient = createClient; exports.createCommentAttachmentId = createCommentAttachmentId; exports.createCommentId = createCommentId; exports.createInboxNotificationId = createInboxNotificationId; exports.createManagedPool = createManagedPool; exports.createNotificationSettings = createNotificationSettings; exports.createThreadId = createThreadId; exports.deepLiveify = deepLiveify; exports.defineAiTool = defineAiTool; exports.deprecate = deprecate; exports.deprecateIf = deprecateIf; exports.detectDupes = detectDupes; exports.entries = entries; exports.errorIf = errorIf; exports.findLastIndex = findLastIndex; exports.freeze = freeze; exports.generateUrl = generateUrl; exports.getMentionsFromCommentBody = getMentionsFromCommentBody; exports.getSubscriptionKey = getSubscriptionKey; exports.hasPermissionAccess = hasPermissionAccess; exports.html = html; exports.htmlSafe = htmlSafe; exports.isCommentBodyLink = isCommentBodyLink; exports.isCommentBodyMention = isCommentBodyMention; exports.isCommentBodyText = isCommentBodyText; exports.isJsonArray = isJsonArray; exports.isJsonObject = isJsonObject; exports.isJsonScalar = isJsonScalar; exports.isListStorageNode = isListStorageNode; exports.isLiveNode = isLiveNode; exports.isMapStorageNode = isMapStorageNode; exports.isNotificationChannelEnabled = isNotificationChannelEnabled; exports.isNumberOperator = isNumberOperator; exports.isObjectStorageNode = isObjectStorageNode; exports.isPlainObject = isPlainObject; exports.isRegisterStorageNode = isRegisterStorageNode; exports.isRootStorageNode = isRootStorageNode; exports.isStartsWithOperator = isStartsWithOperator; exports.isTextStorageNode = isTextStorageNode; exports.isUrl = isUrl; exports.kInternal = kInternal; exports.kStorageUpdateSource = kStorageUpdateSource; exports.keys = keys; exports.makeAbortController = makeAbortController; exports.makeEventSource = makeEventSource; exports.makePoller = makePoller; exports.makePosition = makePosition; exports.mapValues = mapValues; exports.memoizeOnSuccess = memoizeOnSuccess; exports.mergeRoomPermissionScopes = mergeRoomPermissionScopes; exports.nanoid = nanoid; exports.nn = nn; exports.nodeStreamToCompactNodes = nodeStreamToCompactNodes; exports.normalizeRoomAccesses = normalizeRoomAccesses; exports.normalizeRoomPermissions = normalizeRoomPermissions; exports.normalizeUpdateRoomAccesses = normalizeUpdateRoomAccesses; exports.objectToQuery = objectToQuery; exports.patchNotificationSettings = patchNotificationSettings; exports.permissionMatrixFromScopes = permissionMatrixFromScopes; exports.raise = raise; exports.resolveMentionsInCommentBody = resolveMentionsInCommentBody; exports.sanitizeUrl = sanitizeUrl; exports.shallow = shallow; exports.shallow2 = shallow2; exports.stableStringify = stableStringify; exports.stringifyCommentBody = stringifyCommentBody; exports.throwUsageError = throwUsageError; exports.toPlainLson = toPlainLson; exports.transformTextOperations = transformTextOperations; exports.tryParseJson = tryParseJson; exports.url = url; exports.urljoin = urljoin; exports.validatePermissionsSet = validatePermissionsSet; exports.wait = wait; exports.warnOnce = warnOnce; exports.warnOnceIf = warnOnceIf; exports.withTimeout = withTimeout;
|
|
12769
14050
|
//# sourceMappingURL=index.cjs.map
|