@liveblocks/core 3.20.0 → 3.21.0-exp1
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 +1376 -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 +1306 -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-exp1";
|
|
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,1105 @@ 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 {
|
|
9819
|
+
type: "LiveText",
|
|
9820
|
+
id: _nullishCoalesce(this._id, () => ( nanoid())),
|
|
9821
|
+
key,
|
|
9822
|
+
payload: [
|
|
9823
|
+
{
|
|
9824
|
+
type: "Json",
|
|
9825
|
+
id: `${_nullishCoalesce(this._id, () => ( nanoid()))}:text`,
|
|
9826
|
+
key: "text",
|
|
9827
|
+
payload: this.toString()
|
|
9828
|
+
}
|
|
9829
|
+
]
|
|
9830
|
+
};
|
|
9831
|
+
}
|
|
9832
|
+
clone() {
|
|
9833
|
+
return new _LiveText(this.toJSON(), this.#version);
|
|
9834
|
+
}
|
|
9835
|
+
};
|
|
9836
|
+
|
|
8713
9837
|
// src/crdts/liveblocks-helpers.ts
|
|
8714
9838
|
function creationOpToLiveNode(op) {
|
|
8715
9839
|
return lsonToLiveNode(creationOpToLson(op));
|
|
@@ -8724,6 +9848,8 @@ function creationOpToLson(op) {
|
|
|
8724
9848
|
return new LiveMap();
|
|
8725
9849
|
case OpCode.CREATE_LIST:
|
|
8726
9850
|
return new LiveList([]);
|
|
9851
|
+
case OpCode.CREATE_TEXT:
|
|
9852
|
+
return new LiveText(op.data, op.version);
|
|
8727
9853
|
default:
|
|
8728
9854
|
return assertNever(op, "Unknown creation Op");
|
|
8729
9855
|
}
|
|
@@ -8746,6 +9872,8 @@ function deserialize(node, parentToChildren, pool) {
|
|
|
8746
9872
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8747
9873
|
} else if (isRegisterStorageNode(node)) {
|
|
8748
9874
|
return LiveRegister._deserialize(node, parentToChildren, pool);
|
|
9875
|
+
} else if (isTextStorageNode(node)) {
|
|
9876
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8749
9877
|
} else {
|
|
8750
9878
|
throw new Error("Unexpected CRDT type");
|
|
8751
9879
|
}
|
|
@@ -8759,12 +9887,14 @@ function deserializeToLson(node, parentToChildren, pool) {
|
|
|
8759
9887
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8760
9888
|
} else if (isRegisterStorageNode(node)) {
|
|
8761
9889
|
return node[1].data;
|
|
9890
|
+
} else if (isTextStorageNode(node)) {
|
|
9891
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8762
9892
|
} else {
|
|
8763
9893
|
throw new Error("Unexpected CRDT type");
|
|
8764
9894
|
}
|
|
8765
9895
|
}
|
|
8766
9896
|
function isLiveStructure(value) {
|
|
8767
|
-
return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
|
|
9897
|
+
return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
|
|
8768
9898
|
}
|
|
8769
9899
|
function isLiveNode(value) {
|
|
8770
9900
|
return isLiveStructure(value) || isLiveRegister(value);
|
|
@@ -8778,6 +9908,9 @@ function isLiveMap(value) {
|
|
|
8778
9908
|
function isLiveObject(value) {
|
|
8779
9909
|
return value instanceof LiveObject;
|
|
8780
9910
|
}
|
|
9911
|
+
function isLiveText(value) {
|
|
9912
|
+
return value instanceof LiveText;
|
|
9913
|
+
}
|
|
8781
9914
|
function isLiveRegister(value) {
|
|
8782
9915
|
return value instanceof LiveRegister;
|
|
8783
9916
|
}
|
|
@@ -8787,14 +9920,14 @@ function cloneLson(value) {
|
|
|
8787
9920
|
function liveNodeToLson(obj) {
|
|
8788
9921
|
if (obj instanceof LiveRegister) {
|
|
8789
9922
|
return obj.data;
|
|
8790
|
-
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
|
|
9923
|
+
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
|
|
8791
9924
|
return obj;
|
|
8792
9925
|
} else {
|
|
8793
9926
|
return assertNever(obj, "Unknown AbstractCrdt");
|
|
8794
9927
|
}
|
|
8795
9928
|
}
|
|
8796
9929
|
function lsonToLiveNode(value) {
|
|
8797
|
-
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
|
|
9930
|
+
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
|
|
8798
9931
|
return value;
|
|
8799
9932
|
} else {
|
|
8800
9933
|
return new LiveRegister(value);
|
|
@@ -8937,6 +10070,16 @@ function diffNodeMap(prev, next) {
|
|
|
8937
10070
|
parentKey: crdt.parentKey
|
|
8938
10071
|
});
|
|
8939
10072
|
break;
|
|
10073
|
+
case CrdtType.TEXT:
|
|
10074
|
+
ops.push({
|
|
10075
|
+
type: OpCode.CREATE_TEXT,
|
|
10076
|
+
id,
|
|
10077
|
+
parentId: crdt.parentId,
|
|
10078
|
+
parentKey: crdt.parentKey,
|
|
10079
|
+
data: crdt.data,
|
|
10080
|
+
version: crdt.version
|
|
10081
|
+
});
|
|
10082
|
+
break;
|
|
8940
10083
|
}
|
|
8941
10084
|
}
|
|
8942
10085
|
});
|
|
@@ -8969,19 +10112,43 @@ function mergeListStorageUpdates(first, second) {
|
|
|
8969
10112
|
updates: updates.concat(second.updates)
|
|
8970
10113
|
};
|
|
8971
10114
|
}
|
|
10115
|
+
function mergeTextStorageUpdates(first, second) {
|
|
10116
|
+
return {
|
|
10117
|
+
...second,
|
|
10118
|
+
updates: first.updates.concat(second.updates)
|
|
10119
|
+
};
|
|
10120
|
+
}
|
|
8972
10121
|
function mergeStorageUpdates(first, second) {
|
|
8973
10122
|
if (first === void 0) {
|
|
8974
10123
|
return second;
|
|
8975
10124
|
}
|
|
10125
|
+
let merged;
|
|
8976
10126
|
if (first.type === "LiveObject" && second.type === "LiveObject") {
|
|
8977
|
-
|
|
10127
|
+
merged = mergeObjectStorageUpdates(first, second);
|
|
8978
10128
|
} else if (first.type === "LiveMap" && second.type === "LiveMap") {
|
|
8979
|
-
|
|
10129
|
+
merged = mergeMapStorageUpdates(first, second);
|
|
8980
10130
|
} else if (first.type === "LiveList" && second.type === "LiveList") {
|
|
8981
|
-
|
|
10131
|
+
merged = mergeListStorageUpdates(first, second);
|
|
10132
|
+
} else if (first.type === "LiveText" && second.type === "LiveText") {
|
|
10133
|
+
merged = mergeTextStorageUpdates(first, second);
|
|
8982
10134
|
} else {
|
|
10135
|
+
merged = second;
|
|
10136
|
+
}
|
|
10137
|
+
const sa = first[kStorageUpdateSource];
|
|
10138
|
+
const sb = second[kStorageUpdateSource];
|
|
10139
|
+
if (sa !== void 0 || sb !== void 0) {
|
|
10140
|
+
if (_optionalChain([sa, 'optionalAccess', _222 => _222.origin]) === "remote" || _optionalChain([sb, 'optionalAccess', _223 => _223.origin]) === "remote") {
|
|
10141
|
+
merged[kStorageUpdateSource] = { origin: "remote" };
|
|
10142
|
+
} else if (_optionalChain([sa, 'optionalAccess', _224 => _224.via]) === "history" || _optionalChain([sb, 'optionalAccess', _225 => _225.via]) === "history") {
|
|
10143
|
+
const historySource = _optionalChain([sb, 'optionalAccess', _226 => _226.via]) === "history" ? sb : _optionalChain([sa, 'optionalAccess', _227 => _227.via]) === "history" ? sa : void 0;
|
|
10144
|
+
if (_optionalChain([historySource, 'optionalAccess', _228 => _228.via]) === "history") {
|
|
10145
|
+
merged[kStorageUpdateSource] = historySource;
|
|
10146
|
+
}
|
|
10147
|
+
} else {
|
|
10148
|
+
merged[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
10149
|
+
}
|
|
8983
10150
|
}
|
|
8984
|
-
return
|
|
10151
|
+
return merged;
|
|
8985
10152
|
}
|
|
8986
10153
|
|
|
8987
10154
|
// src/devtools/bridge.ts
|
|
@@ -8997,7 +10164,7 @@ function sendToPanel(message, options) {
|
|
|
8997
10164
|
...message,
|
|
8998
10165
|
source: "liveblocks-devtools-client"
|
|
8999
10166
|
};
|
|
9000
|
-
if (!(_optionalChain([options, 'optionalAccess',
|
|
10167
|
+
if (!(_optionalChain([options, 'optionalAccess', _229 => _229.force]) || _bridgeActive)) {
|
|
9001
10168
|
return;
|
|
9002
10169
|
}
|
|
9003
10170
|
window.postMessage(fullMsg, "*");
|
|
@@ -9005,7 +10172,7 @@ function sendToPanel(message, options) {
|
|
|
9005
10172
|
var eventSource = makeEventSource();
|
|
9006
10173
|
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
9007
10174
|
window.addEventListener("message", (event) => {
|
|
9008
|
-
if (event.source === window && _optionalChain([event, 'access',
|
|
10175
|
+
if (event.source === window && _optionalChain([event, 'access', _230 => _230.data, 'optionalAccess', _231 => _231.source]) === "liveblocks-devtools-panel") {
|
|
9009
10176
|
eventSource.notify(event.data);
|
|
9010
10177
|
} else {
|
|
9011
10178
|
}
|
|
@@ -9147,7 +10314,7 @@ function fullSync(room) {
|
|
|
9147
10314
|
msg: "room::sync::full",
|
|
9148
10315
|
roomId: room.id,
|
|
9149
10316
|
status: room.getStatus(),
|
|
9150
|
-
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess',
|
|
10317
|
+
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _232 => _232.toTreeNode, 'call', _233 => _233("root"), 'access', _234 => _234.payload]), () => ( null)),
|
|
9151
10318
|
me,
|
|
9152
10319
|
others
|
|
9153
10320
|
});
|
|
@@ -9834,15 +11001,15 @@ function installBackgroundTabSpy() {
|
|
|
9834
11001
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
9835
11002
|
const inBackgroundSince = { current: null };
|
|
9836
11003
|
function onVisibilityChange() {
|
|
9837
|
-
if (_optionalChain([doc, 'optionalAccess',
|
|
11004
|
+
if (_optionalChain([doc, 'optionalAccess', _235 => _235.visibilityState]) === "hidden") {
|
|
9838
11005
|
inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
|
|
9839
11006
|
} else {
|
|
9840
11007
|
inBackgroundSince.current = null;
|
|
9841
11008
|
}
|
|
9842
11009
|
}
|
|
9843
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11010
|
+
_optionalChain([doc, 'optionalAccess', _236 => _236.addEventListener, 'call', _237 => _237("visibilitychange", onVisibilityChange)]);
|
|
9844
11011
|
const unsub = () => {
|
|
9845
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11012
|
+
_optionalChain([doc, 'optionalAccess', _238 => _238.removeEventListener, 'call', _239 => _239("visibilitychange", onVisibilityChange)]);
|
|
9846
11013
|
};
|
|
9847
11014
|
return [inBackgroundSince, unsub];
|
|
9848
11015
|
}
|
|
@@ -9925,6 +11092,8 @@ function createRoom(options, config) {
|
|
|
9925
11092
|
activeBatch: null,
|
|
9926
11093
|
unacknowledgedOps
|
|
9927
11094
|
};
|
|
11095
|
+
let nextHistoryItemId = 0;
|
|
11096
|
+
let historyDisabled = 0;
|
|
9928
11097
|
const nodeMapBuffer = makeNodeMapBuffer();
|
|
9929
11098
|
const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
|
|
9930
11099
|
let lastTokenKey;
|
|
@@ -10009,7 +11178,10 @@ function createRoom(options, config) {
|
|
|
10009
11178
|
}
|
|
10010
11179
|
}
|
|
10011
11180
|
});
|
|
10012
|
-
function onDispatch(ops, reverse, storageUpdates) {
|
|
11181
|
+
function onDispatch(ops, reverse, storageUpdates, options2) {
|
|
11182
|
+
for (const value of storageUpdates.values()) {
|
|
11183
|
+
value[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
11184
|
+
}
|
|
10013
11185
|
if (context.activeBatch) {
|
|
10014
11186
|
for (const op of ops) {
|
|
10015
11187
|
context.activeBatch.ops.push(op);
|
|
@@ -10028,15 +11200,17 @@ function createRoom(options, config) {
|
|
|
10028
11200
|
if (reverse.length > 0) {
|
|
10029
11201
|
addToUndoStack(reverse);
|
|
10030
11202
|
}
|
|
11203
|
+
if (_nullishCoalesce(_optionalChain([options2, 'optionalAccess', _240 => _240.clearRedoStack]), () => ( ops.length > 0))) {
|
|
11204
|
+
clearRedoStack();
|
|
11205
|
+
}
|
|
10031
11206
|
if (ops.length > 0) {
|
|
10032
|
-
context.redoStack.length = 0;
|
|
10033
11207
|
dispatchOps(ops);
|
|
10034
11208
|
}
|
|
10035
11209
|
notify({ storageUpdates });
|
|
10036
11210
|
}
|
|
10037
11211
|
}
|
|
10038
11212
|
function isStorageWritable() {
|
|
10039
|
-
const permissionMatrix = _optionalChain([context, 'access',
|
|
11213
|
+
const permissionMatrix = _optionalChain([context, 'access', _241 => _241.dynamicSessionInfoSig, 'access', _242 => _242.get, 'call', _243 => _243(), 'optionalAccess', _244 => _244.permissionMatrix]);
|
|
10040
11214
|
return permissionMatrix !== void 0 ? hasPermissionAccess(permissionMatrix, "storage", "write") : true;
|
|
10041
11215
|
}
|
|
10042
11216
|
const eventHub = {
|
|
@@ -10049,6 +11223,7 @@ function createRoom(options, config) {
|
|
|
10049
11223
|
others: makeEventSource(),
|
|
10050
11224
|
storageBatch: makeEventSource(),
|
|
10051
11225
|
history: makeEventSource(),
|
|
11226
|
+
privateHistory: makeEventSource(),
|
|
10052
11227
|
storageDidLoad: makeEventSource(),
|
|
10053
11228
|
storageStatus: makeEventSource(),
|
|
10054
11229
|
ydoc: makeEventSource(),
|
|
@@ -10141,6 +11316,23 @@ function createRoom(options, config) {
|
|
|
10141
11316
|
}
|
|
10142
11317
|
const ops = diffNodeMap(currentItems, nodes);
|
|
10143
11318
|
const result = applyRemoteOps(ops);
|
|
11319
|
+
for (const [id, crdt] of nodes) {
|
|
11320
|
+
if (crdt.type === CrdtType.TEXT) {
|
|
11321
|
+
const node = context.pool.nodes.get(id);
|
|
11322
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11323
|
+
const update = node._resyncText(crdt.data, crdt.version);
|
|
11324
|
+
if (update !== void 0) {
|
|
11325
|
+
result.updates.storageUpdates.set(
|
|
11326
|
+
id,
|
|
11327
|
+
mergeStorageUpdates(
|
|
11328
|
+
result.updates.storageUpdates.get(id),
|
|
11329
|
+
update
|
|
11330
|
+
)
|
|
11331
|
+
);
|
|
11332
|
+
}
|
|
11333
|
+
}
|
|
11334
|
+
}
|
|
11335
|
+
}
|
|
10144
11336
|
notify(result.updates);
|
|
10145
11337
|
} else {
|
|
10146
11338
|
context.root = LiveObject._fromItems(
|
|
@@ -10148,7 +11340,7 @@ function createRoom(options, config) {
|
|
|
10148
11340
|
context.pool
|
|
10149
11341
|
);
|
|
10150
11342
|
}
|
|
10151
|
-
const canWrite = _nullishCoalesce(_optionalChain([self, 'access',
|
|
11343
|
+
const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _245 => _245.get, 'call', _246 => _246(), 'optionalAccess', _247 => _247.canWrite]), () => ( true));
|
|
10152
11344
|
const root = context.root;
|
|
10153
11345
|
disableHistory(() => {
|
|
10154
11346
|
for (const key in context.initialStorage) {
|
|
@@ -10164,11 +11356,26 @@ function createRoom(options, config) {
|
|
|
10164
11356
|
}
|
|
10165
11357
|
});
|
|
10166
11358
|
}
|
|
11359
|
+
function notifyPrivateHistory(event) {
|
|
11360
|
+
if (historyDisabled > 0) return;
|
|
11361
|
+
eventHub.privateHistory.notify(event);
|
|
11362
|
+
}
|
|
11363
|
+
function clearRedoStack() {
|
|
11364
|
+
if (context.redoStack.length === 0) return;
|
|
11365
|
+
const ids = context.redoStack.map((item) => item.id);
|
|
11366
|
+
context.redoStack.length = 0;
|
|
11367
|
+
notifyPrivateHistory({ action: "discard", ids });
|
|
11368
|
+
}
|
|
10167
11369
|
function _addToRealUndoStack(frames) {
|
|
10168
11370
|
if (context.undoStack.length >= 50) {
|
|
10169
|
-
context.undoStack.shift();
|
|
11371
|
+
const evicted = context.undoStack.shift();
|
|
11372
|
+
if (evicted !== void 0) {
|
|
11373
|
+
notifyPrivateHistory({ action: "discard", ids: [evicted.id] });
|
|
11374
|
+
}
|
|
10170
11375
|
}
|
|
10171
|
-
|
|
11376
|
+
const id = nextHistoryItemId++;
|
|
11377
|
+
context.undoStack.push({ id, frames });
|
|
11378
|
+
notifyPrivateHistory({ action: "push", id });
|
|
10172
11379
|
onHistoryChange();
|
|
10173
11380
|
}
|
|
10174
11381
|
function addToUndoStack(frames) {
|
|
@@ -10206,7 +11413,7 @@ function createRoom(options, config) {
|
|
|
10206
11413
|
"Internal. Tried to get connection id but connection was never open"
|
|
10207
11414
|
);
|
|
10208
11415
|
}
|
|
10209
|
-
function applyLocalOps(frames) {
|
|
11416
|
+
function applyLocalOps(frames, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
10210
11417
|
const [pframes, ops] = partition(
|
|
10211
11418
|
frames,
|
|
10212
11419
|
(f) => f.type === "presence"
|
|
@@ -10218,7 +11425,8 @@ function createRoom(options, config) {
|
|
|
10218
11425
|
pframes,
|
|
10219
11426
|
opsWithOpIds,
|
|
10220
11427
|
/* isLocal */
|
|
10221
|
-
true
|
|
11428
|
+
true,
|
|
11429
|
+
localStorageUpdateSource
|
|
10222
11430
|
);
|
|
10223
11431
|
return { opsToEmit: opsWithOpIds, reverse, updates };
|
|
10224
11432
|
}
|
|
@@ -10230,7 +11438,7 @@ function createRoom(options, config) {
|
|
|
10230
11438
|
false
|
|
10231
11439
|
);
|
|
10232
11440
|
}
|
|
10233
|
-
function applyOps(pframes, ops, isLocal) {
|
|
11441
|
+
function applyOps(pframes, ops, isLocal, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
10234
11442
|
const output = {
|
|
10235
11443
|
reverse: new Deque(),
|
|
10236
11444
|
storageUpdates: /* @__PURE__ */ new Map(),
|
|
@@ -10268,6 +11476,7 @@ function createRoom(options, config) {
|
|
|
10268
11476
|
}
|
|
10269
11477
|
const applyOpResult = applyOp(op, source);
|
|
10270
11478
|
if (applyOpResult.modified) {
|
|
11479
|
+
applyOpResult.modified[kStorageUpdateSource] = source === 1 /* THEIRS */ ? { origin: "remote" } : localStorageUpdateSource;
|
|
10271
11480
|
const nodeId = applyOpResult.modified.node._id;
|
|
10272
11481
|
if (!(nodeId && createdNodeIds.has(nodeId))) {
|
|
10273
11482
|
output.storageUpdates.set(
|
|
@@ -10279,7 +11488,7 @@ function createRoom(options, config) {
|
|
|
10279
11488
|
);
|
|
10280
11489
|
output.reverse.pushLeft(applyOpResult.reverse);
|
|
10281
11490
|
}
|
|
10282
|
-
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
|
|
11491
|
+
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
|
|
10283
11492
|
createdNodeIds.add(op.id);
|
|
10284
11493
|
}
|
|
10285
11494
|
}
|
|
@@ -10299,6 +11508,7 @@ function createRoom(options, config) {
|
|
|
10299
11508
|
switch (op.type) {
|
|
10300
11509
|
case OpCode.DELETE_OBJECT_KEY:
|
|
10301
11510
|
case OpCode.UPDATE_OBJECT:
|
|
11511
|
+
case OpCode.UPDATE_TEXT:
|
|
10302
11512
|
case OpCode.DELETE_CRDT: {
|
|
10303
11513
|
const node = context.pool.nodes.get(op.id);
|
|
10304
11514
|
if (node === void 0) {
|
|
@@ -10323,6 +11533,7 @@ function createRoom(options, config) {
|
|
|
10323
11533
|
case OpCode.CREATE_OBJECT:
|
|
10324
11534
|
case OpCode.CREATE_LIST:
|
|
10325
11535
|
case OpCode.CREATE_MAP:
|
|
11536
|
+
case OpCode.CREATE_TEXT:
|
|
10326
11537
|
case OpCode.CREATE_REGISTER: {
|
|
10327
11538
|
if (op.parentId === void 0) {
|
|
10328
11539
|
return { modified: false };
|
|
@@ -10353,7 +11564,7 @@ function createRoom(options, config) {
|
|
|
10353
11564
|
}
|
|
10354
11565
|
context.myPresence.patch(patch);
|
|
10355
11566
|
if (context.activeBatch) {
|
|
10356
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11567
|
+
if (_optionalChain([options2, 'optionalAccess', _248 => _248.addToHistory])) {
|
|
10357
11568
|
context.activeBatch.reverseOps.pushLeft({
|
|
10358
11569
|
type: "presence",
|
|
10359
11570
|
data: oldValues
|
|
@@ -10362,7 +11573,7 @@ function createRoom(options, config) {
|
|
|
10362
11573
|
context.activeBatch.updates.presence = true;
|
|
10363
11574
|
} else {
|
|
10364
11575
|
flushNowOrSoon();
|
|
10365
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11576
|
+
if (_optionalChain([options2, 'optionalAccess', _249 => _249.addToHistory])) {
|
|
10366
11577
|
addToUndoStack([{ type: "presence", data: oldValues }]);
|
|
10367
11578
|
}
|
|
10368
11579
|
notify({ presence: true });
|
|
@@ -10541,11 +11752,11 @@ function createRoom(options, config) {
|
|
|
10541
11752
|
break;
|
|
10542
11753
|
}
|
|
10543
11754
|
case ServerMsgCode.STORAGE_CHUNK:
|
|
10544
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
11755
|
+
_optionalChain([stopwatch, 'optionalAccess', _250 => _250.lap, 'call', _251 => _251()]);
|
|
10545
11756
|
nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
|
|
10546
11757
|
break;
|
|
10547
11758
|
case ServerMsgCode.STORAGE_STREAM_END: {
|
|
10548
|
-
const timing = _optionalChain([stopwatch, 'optionalAccess',
|
|
11759
|
+
const timing = _optionalChain([stopwatch, 'optionalAccess', _252 => _252.stop, 'call', _253 => _253()]);
|
|
10549
11760
|
if (timing) {
|
|
10550
11761
|
const ms = (v) => `${v.toFixed(1)}ms`;
|
|
10551
11762
|
const rest = timing.laps.slice(1);
|
|
@@ -10572,16 +11783,37 @@ function createRoom(options, config) {
|
|
|
10572
11783
|
}
|
|
10573
11784
|
break;
|
|
10574
11785
|
}
|
|
10575
|
-
// Receiving a RejectedOps message
|
|
10576
|
-
//
|
|
10577
|
-
//
|
|
10578
|
-
//
|
|
11786
|
+
// Receiving a RejectedOps message means the server refused some of
|
|
11787
|
+
// our ops, so our optimistic local state is out of sync with the
|
|
11788
|
+
// server. For LiveText ops this is a normal (if rare) situation —
|
|
11789
|
+
// e.g. a client that was offline long enough to fall outside the
|
|
11790
|
+
// server's retained history window — and we can recover: drop the
|
|
11791
|
+
// rejected pending state and re-fetch the authoritative storage
|
|
11792
|
+
// snapshot. For other ops (e.g. permission rejections), rolling back
|
|
11793
|
+
// particular Ops is hard/impossible, so we keep the old behavior of
|
|
11794
|
+
// accepting the out-of-sync reality and surfacing an error.
|
|
10579
11795
|
case ServerMsgCode.REJECT_STORAGE_OP: {
|
|
10580
11796
|
errorWithTitle(
|
|
10581
11797
|
"Storage mutation rejection error",
|
|
10582
11798
|
message.reason
|
|
10583
11799
|
);
|
|
10584
|
-
|
|
11800
|
+
let needsStorageResync = false;
|
|
11801
|
+
for (const opId of message.opIds) {
|
|
11802
|
+
const rejectedOp = context.unacknowledgedOps.get(opId);
|
|
11803
|
+
context.unacknowledgedOps.delete(opId);
|
|
11804
|
+
context.buffer.storageOperations = context.buffer.storageOperations.filter((op) => op.opId !== opId);
|
|
11805
|
+
if (rejectedOp !== void 0 && rejectedOp.type === OpCode.UPDATE_TEXT) {
|
|
11806
|
+
const node = context.pool.nodes.get(rejectedOp.id);
|
|
11807
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11808
|
+
node._rejectPendingOp(opId);
|
|
11809
|
+
needsStorageResync = true;
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11813
|
+
if (needsStorageResync) {
|
|
11814
|
+
refreshStorage();
|
|
11815
|
+
flushNowOrSoon();
|
|
11816
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
10585
11817
|
throw new Error(
|
|
10586
11818
|
`Storage mutations rejected by server: ${message.reason}`
|
|
10587
11819
|
);
|
|
@@ -10680,11 +11912,11 @@ function createRoom(options, config) {
|
|
|
10680
11912
|
} else if (pendingFeedsRequests.has(requestId)) {
|
|
10681
11913
|
const pending = pendingFeedsRequests.get(requestId);
|
|
10682
11914
|
pendingFeedsRequests.delete(requestId);
|
|
10683
|
-
_optionalChain([pending, 'optionalAccess',
|
|
11915
|
+
_optionalChain([pending, 'optionalAccess', _254 => _254.reject, 'call', _255 => _255(err)]);
|
|
10684
11916
|
} else if (pendingFeedMessagesRequests.has(requestId)) {
|
|
10685
11917
|
const pending = pendingFeedMessagesRequests.get(requestId);
|
|
10686
11918
|
pendingFeedMessagesRequests.delete(requestId);
|
|
10687
|
-
_optionalChain([pending, 'optionalAccess',
|
|
11919
|
+
_optionalChain([pending, 'optionalAccess', _256 => _256.reject, 'call', _257 => _257(err)]);
|
|
10688
11920
|
}
|
|
10689
11921
|
eventHub.feeds.notify(message);
|
|
10690
11922
|
break;
|
|
@@ -10838,10 +12070,10 @@ function createRoom(options, config) {
|
|
|
10838
12070
|
timeoutId,
|
|
10839
12071
|
kind,
|
|
10840
12072
|
feedId,
|
|
10841
|
-
messageId: _optionalChain([options2, 'optionalAccess',
|
|
10842
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12073
|
+
messageId: _optionalChain([options2, 'optionalAccess', _258 => _258.messageId]),
|
|
12074
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _259 => _259.expectedClientMessageId])
|
|
10843
12075
|
});
|
|
10844
|
-
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess',
|
|
12076
|
+
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _260 => _260.expectedClientMessageId]) === void 0) {
|
|
10845
12077
|
const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
|
|
10846
12078
|
q.push(requestId);
|
|
10847
12079
|
pendingAddMessageFifoByFeed.set(feedId, q);
|
|
@@ -10892,10 +12124,10 @@ function createRoom(options, config) {
|
|
|
10892
12124
|
}
|
|
10893
12125
|
if (!matched) {
|
|
10894
12126
|
const q = pendingAddMessageFifoByFeed.get(message.feedId);
|
|
10895
|
-
const headId = _optionalChain([q, 'optionalAccess',
|
|
12127
|
+
const headId = _optionalChain([q, 'optionalAccess', _261 => _261[0]]);
|
|
10896
12128
|
if (headId !== void 0) {
|
|
10897
12129
|
const pending = pendingFeedMutations.get(headId);
|
|
10898
|
-
if (_optionalChain([pending, 'optionalAccess',
|
|
12130
|
+
if (_optionalChain([pending, 'optionalAccess', _262 => _262.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
|
|
10899
12131
|
settleFeedMutation(headId, "ok");
|
|
10900
12132
|
}
|
|
10901
12133
|
}
|
|
@@ -10931,7 +12163,7 @@ function createRoom(options, config) {
|
|
|
10931
12163
|
const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
|
|
10932
12164
|
createOrUpdateRootFromMessage(nodes);
|
|
10933
12165
|
applyAndSendOfflineOps(unacknowledgedOps2);
|
|
10934
|
-
_optionalChain([_resolveStoragePromise, 'optionalCall',
|
|
12166
|
+
_optionalChain([_resolveStoragePromise, 'optionalCall', _263 => _263()]);
|
|
10935
12167
|
notifyStorageStatus();
|
|
10936
12168
|
eventHub.storageDidLoad.notify();
|
|
10937
12169
|
}
|
|
@@ -10940,7 +12172,7 @@ function createRoom(options, config) {
|
|
|
10940
12172
|
if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
|
|
10941
12173
|
messages.push({ type: ClientMsgCode.FETCH_STORAGE });
|
|
10942
12174
|
nodeMapBuffer.take();
|
|
10943
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
12175
|
+
_optionalChain([stopwatch, 'optionalAccess', _264 => _264.start, 'call', _265 => _265()]);
|
|
10944
12176
|
}
|
|
10945
12177
|
}
|
|
10946
12178
|
function startLoadingStorage() {
|
|
@@ -10994,10 +12226,10 @@ function createRoom(options, config) {
|
|
|
10994
12226
|
const message = {
|
|
10995
12227
|
type: ClientMsgCode.FETCH_FEEDS,
|
|
10996
12228
|
requestId,
|
|
10997
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
10998
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
10999
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
11000
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
12229
|
+
cursor: _optionalChain([options2, 'optionalAccess', _266 => _266.cursor]),
|
|
12230
|
+
since: _optionalChain([options2, 'optionalAccess', _267 => _267.since]),
|
|
12231
|
+
limit: _optionalChain([options2, 'optionalAccess', _268 => _268.limit]),
|
|
12232
|
+
metadata: _optionalChain([options2, 'optionalAccess', _269 => _269.metadata])
|
|
11001
12233
|
};
|
|
11002
12234
|
context.buffer.messages.push(message);
|
|
11003
12235
|
flushNowOrSoon();
|
|
@@ -11017,9 +12249,9 @@ function createRoom(options, config) {
|
|
|
11017
12249
|
type: ClientMsgCode.FETCH_FEED_MESSAGES,
|
|
11018
12250
|
requestId,
|
|
11019
12251
|
feedId,
|
|
11020
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
11021
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
11022
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
12252
|
+
cursor: _optionalChain([options2, 'optionalAccess', _270 => _270.cursor]),
|
|
12253
|
+
since: _optionalChain([options2, 'optionalAccess', _271 => _271.since]),
|
|
12254
|
+
limit: _optionalChain([options2, 'optionalAccess', _272 => _272.limit])
|
|
11023
12255
|
};
|
|
11024
12256
|
context.buffer.messages.push(message);
|
|
11025
12257
|
flushNowOrSoon();
|
|
@@ -11038,8 +12270,8 @@ function createRoom(options, config) {
|
|
|
11038
12270
|
type: ClientMsgCode.ADD_FEED,
|
|
11039
12271
|
requestId,
|
|
11040
12272
|
feedId,
|
|
11041
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
11042
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12273
|
+
metadata: _optionalChain([options2, 'optionalAccess', _273 => _273.metadata]),
|
|
12274
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _274 => _274.createdAt])
|
|
11043
12275
|
};
|
|
11044
12276
|
context.buffer.messages.push(message);
|
|
11045
12277
|
flushNowOrSoon();
|
|
@@ -11073,15 +12305,15 @@ function createRoom(options, config) {
|
|
|
11073
12305
|
function addFeedMessage(feedId, data, options2) {
|
|
11074
12306
|
const requestId = nanoid();
|
|
11075
12307
|
const promise = registerFeedMutation(requestId, "add-message", feedId, {
|
|
11076
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12308
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _275 => _275.id])
|
|
11077
12309
|
});
|
|
11078
12310
|
const message = {
|
|
11079
12311
|
type: ClientMsgCode.ADD_FEED_MESSAGE,
|
|
11080
12312
|
requestId,
|
|
11081
12313
|
feedId,
|
|
11082
12314
|
data,
|
|
11083
|
-
id: _optionalChain([options2, 'optionalAccess',
|
|
11084
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12315
|
+
id: _optionalChain([options2, 'optionalAccess', _276 => _276.id]),
|
|
12316
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _277 => _277.createdAt])
|
|
11085
12317
|
};
|
|
11086
12318
|
context.buffer.messages.push(message);
|
|
11087
12319
|
flushNowOrSoon();
|
|
@@ -11098,7 +12330,7 @@ function createRoom(options, config) {
|
|
|
11098
12330
|
feedId,
|
|
11099
12331
|
messageId,
|
|
11100
12332
|
data,
|
|
11101
|
-
updatedAt: _optionalChain([options2, 'optionalAccess',
|
|
12333
|
+
updatedAt: _optionalChain([options2, 'optionalAccess', _278 => _278.updatedAt])
|
|
11102
12334
|
};
|
|
11103
12335
|
context.buffer.messages.push(message);
|
|
11104
12336
|
flushNowOrSoon();
|
|
@@ -11123,14 +12355,19 @@ function createRoom(options, config) {
|
|
|
11123
12355
|
if (context.activeBatch) {
|
|
11124
12356
|
throw new Error("undo is not allowed during a batch");
|
|
11125
12357
|
}
|
|
11126
|
-
const
|
|
11127
|
-
if (
|
|
12358
|
+
const item = context.undoStack.pop();
|
|
12359
|
+
if (item === void 0) {
|
|
11128
12360
|
return;
|
|
11129
12361
|
}
|
|
11130
12362
|
context.pausedHistory = null;
|
|
11131
|
-
const result = applyLocalOps(frames
|
|
12363
|
+
const result = applyLocalOps(item.frames, {
|
|
12364
|
+
origin: "local",
|
|
12365
|
+
via: "history",
|
|
12366
|
+
action: "undo"
|
|
12367
|
+
});
|
|
12368
|
+
context.redoStack.push({ id: item.id, frames: result.reverse });
|
|
12369
|
+
notifyPrivateHistory({ action: "undo", id: item.id });
|
|
11132
12370
|
notify(result.updates);
|
|
11133
|
-
context.redoStack.push(result.reverse);
|
|
11134
12371
|
onHistoryChange();
|
|
11135
12372
|
for (const op of result.opsToEmit) {
|
|
11136
12373
|
context.buffer.storageOperations.push(op);
|
|
@@ -11141,14 +12378,19 @@ function createRoom(options, config) {
|
|
|
11141
12378
|
if (context.activeBatch) {
|
|
11142
12379
|
throw new Error("redo is not allowed during a batch");
|
|
11143
12380
|
}
|
|
11144
|
-
const
|
|
11145
|
-
if (
|
|
12381
|
+
const item = context.redoStack.pop();
|
|
12382
|
+
if (item === void 0) {
|
|
11146
12383
|
return;
|
|
11147
12384
|
}
|
|
11148
12385
|
context.pausedHistory = null;
|
|
11149
|
-
const result = applyLocalOps(frames
|
|
12386
|
+
const result = applyLocalOps(item.frames, {
|
|
12387
|
+
origin: "local",
|
|
12388
|
+
via: "history",
|
|
12389
|
+
action: "redo"
|
|
12390
|
+
});
|
|
12391
|
+
context.undoStack.push({ id: item.id, frames: result.reverse });
|
|
12392
|
+
notifyPrivateHistory({ action: "redo", id: item.id });
|
|
11150
12393
|
notify(result.updates);
|
|
11151
|
-
context.undoStack.push(result.reverse);
|
|
11152
12394
|
onHistoryChange();
|
|
11153
12395
|
for (const op of result.opsToEmit) {
|
|
11154
12396
|
context.buffer.storageOperations.push(op);
|
|
@@ -11158,6 +12400,8 @@ function createRoom(options, config) {
|
|
|
11158
12400
|
function clear() {
|
|
11159
12401
|
context.undoStack.length = 0;
|
|
11160
12402
|
context.redoStack.length = 0;
|
|
12403
|
+
notifyPrivateHistory({ action: "clear" });
|
|
12404
|
+
onHistoryChange();
|
|
11161
12405
|
}
|
|
11162
12406
|
function batch2(callback) {
|
|
11163
12407
|
if (context.activeBatch) {
|
|
@@ -11186,7 +12430,7 @@ function createRoom(options, config) {
|
|
|
11186
12430
|
commitPausedHistoryToUndoStack();
|
|
11187
12431
|
}
|
|
11188
12432
|
if (currentBatch.ops.length > 0) {
|
|
11189
|
-
|
|
12433
|
+
clearRedoStack();
|
|
11190
12434
|
}
|
|
11191
12435
|
if (currentBatch.ops.length > 0) {
|
|
11192
12436
|
dispatchOps(currentBatch.ops);
|
|
@@ -11215,7 +12459,6 @@ function createRoom(options, config) {
|
|
|
11215
12459
|
}
|
|
11216
12460
|
commitPausedHistoryToUndoStack();
|
|
11217
12461
|
}
|
|
11218
|
-
let historyDisabled = 0;
|
|
11219
12462
|
function disableHistory(fn) {
|
|
11220
12463
|
const origUndo = context.undoStack;
|
|
11221
12464
|
const origRedo = context.redoStack;
|
|
@@ -11305,8 +12548,8 @@ function createRoom(options, config) {
|
|
|
11305
12548
|
async function getThreads(options2) {
|
|
11306
12549
|
return httpClient.getThreads({
|
|
11307
12550
|
roomId,
|
|
11308
|
-
query: _optionalChain([options2, 'optionalAccess',
|
|
11309
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
12551
|
+
query: _optionalChain([options2, 'optionalAccess', _279 => _279.query]),
|
|
12552
|
+
cursor: _optionalChain([options2, 'optionalAccess', _280 => _280.cursor])
|
|
11310
12553
|
});
|
|
11311
12554
|
}
|
|
11312
12555
|
async function getThread(threadId) {
|
|
@@ -11428,7 +12671,7 @@ function createRoom(options, config) {
|
|
|
11428
12671
|
function getSubscriptionSettings(options2) {
|
|
11429
12672
|
return httpClient.getSubscriptionSettings({
|
|
11430
12673
|
roomId,
|
|
11431
|
-
signal: _optionalChain([options2, 'optionalAccess',
|
|
12674
|
+
signal: _optionalChain([options2, 'optionalAccess', _281 => _281.signal])
|
|
11432
12675
|
});
|
|
11433
12676
|
}
|
|
11434
12677
|
function updateSubscriptionSettings(settings) {
|
|
@@ -11450,24 +12693,39 @@ function createRoom(options, config) {
|
|
|
11450
12693
|
{
|
|
11451
12694
|
[kInternal]: {
|
|
11452
12695
|
get presenceBuffer() {
|
|
11453
|
-
return deepClone(_nullishCoalesce(_optionalChain([context, 'access',
|
|
12696
|
+
return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _282 => _282.buffer, 'access', _283 => _283.presenceUpdates, 'optionalAccess', _284 => _284.data]), () => ( null)));
|
|
11454
12697
|
},
|
|
11455
12698
|
// prettier-ignore
|
|
11456
12699
|
get undoStack() {
|
|
11457
|
-
return
|
|
12700
|
+
return structuredClone(
|
|
12701
|
+
context.undoStack.map((item) => ({
|
|
12702
|
+
id: item.id,
|
|
12703
|
+
frames: item.frames
|
|
12704
|
+
}))
|
|
12705
|
+
);
|
|
12706
|
+
},
|
|
12707
|
+
// prettier-ignore
|
|
12708
|
+
get redoStack() {
|
|
12709
|
+
return structuredClone(
|
|
12710
|
+
context.redoStack.map((item) => ({
|
|
12711
|
+
id: item.id,
|
|
12712
|
+
frames: item.frames
|
|
12713
|
+
}))
|
|
12714
|
+
);
|
|
11458
12715
|
},
|
|
11459
12716
|
// prettier-ignore
|
|
11460
12717
|
get nodeCount() {
|
|
11461
12718
|
return context.pool.nodes.size;
|
|
11462
12719
|
},
|
|
11463
12720
|
// prettier-ignore
|
|
12721
|
+
history: eventHub.privateHistory.observable,
|
|
11464
12722
|
getYjsProvider() {
|
|
11465
12723
|
return context.yjsProvider;
|
|
11466
12724
|
},
|
|
11467
12725
|
setYjsProvider(newProvider) {
|
|
11468
|
-
_optionalChain([context, 'access',
|
|
12726
|
+
_optionalChain([context, 'access', _285 => _285.yjsProvider, 'optionalAccess', _286 => _286.off, 'call', _287 => _287("status", yjsStatusDidChange)]);
|
|
11469
12727
|
context.yjsProvider = newProvider;
|
|
11470
|
-
_optionalChain([newProvider, 'optionalAccess',
|
|
12728
|
+
_optionalChain([newProvider, 'optionalAccess', _288 => _288.on, 'call', _289 => _289("status", yjsStatusDidChange)]);
|
|
11471
12729
|
context.yjsProviderDidChange.notify();
|
|
11472
12730
|
},
|
|
11473
12731
|
yjsProviderDidChange: context.yjsProviderDidChange.observable,
|
|
@@ -11527,7 +12785,7 @@ ${dumpPool(
|
|
|
11527
12785
|
source.dispose();
|
|
11528
12786
|
}
|
|
11529
12787
|
eventHub.roomWillDestroy.notify();
|
|
11530
|
-
_optionalChain([context, 'access',
|
|
12788
|
+
_optionalChain([context, 'access', _290 => _290.yjsProvider, 'optionalAccess', _291 => _291.off, 'call', _292 => _292("status", yjsStatusDidChange)]);
|
|
11531
12789
|
syncSourceForStorage.destroy();
|
|
11532
12790
|
syncSourceForYjs.destroy();
|
|
11533
12791
|
uninstallBgTabSpy();
|
|
@@ -11689,7 +12947,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
|
|
|
11689
12947
|
}
|
|
11690
12948
|
if (isLiveNode(first)) {
|
|
11691
12949
|
const node = first;
|
|
11692
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
12950
|
+
if (_optionalChain([options, 'optionalAccess', _293 => _293.isDeep])) {
|
|
11693
12951
|
const storageCallback = second;
|
|
11694
12952
|
return subscribeToLiveStructureDeeply(node, storageCallback);
|
|
11695
12953
|
} else {
|
|
@@ -11779,8 +13037,8 @@ function createClient(options) {
|
|
|
11779
13037
|
const authManager = createAuthManager(options, (token) => {
|
|
11780
13038
|
currentUserId.set(() => token.uid);
|
|
11781
13039
|
});
|
|
11782
|
-
const fetchPolyfill = _optionalChain([clientOptions, 'access',
|
|
11783
|
-
_optionalChain([globalThis, 'access',
|
|
13040
|
+
const fetchPolyfill = _optionalChain([clientOptions, 'access', _294 => _294.polyfills, 'optionalAccess', _295 => _295.fetch]) || /* istanbul ignore next */
|
|
13041
|
+
_optionalChain([globalThis, 'access', _296 => _296.fetch, 'optionalAccess', _297 => _297.bind, 'call', _298 => _298(globalThis)]);
|
|
11784
13042
|
const httpClient = createApiClient({
|
|
11785
13043
|
baseUrl,
|
|
11786
13044
|
fetchPolyfill,
|
|
@@ -11797,7 +13055,7 @@ function createClient(options) {
|
|
|
11797
13055
|
delegates: {
|
|
11798
13056
|
createSocket: makeCreateSocketDelegateForAi(
|
|
11799
13057
|
baseUrl,
|
|
11800
|
-
_optionalChain([clientOptions, 'access',
|
|
13058
|
+
_optionalChain([clientOptions, 'access', _299 => _299.polyfills, 'optionalAccess', _300 => _300.WebSocket])
|
|
11801
13059
|
),
|
|
11802
13060
|
authenticate: async () => {
|
|
11803
13061
|
const resp = await authManager.getAuthValue({
|
|
@@ -11868,7 +13126,7 @@ function createClient(options) {
|
|
|
11868
13126
|
createSocket: makeCreateSocketDelegateForRoom(
|
|
11869
13127
|
roomId,
|
|
11870
13128
|
baseUrl,
|
|
11871
|
-
_optionalChain([clientOptions, 'access',
|
|
13129
|
+
_optionalChain([clientOptions, 'access', _301 => _301.polyfills, 'optionalAccess', _302 => _302.WebSocket])
|
|
11872
13130
|
),
|
|
11873
13131
|
authenticate: makeAuthDelegateForRoom(roomId, authManager)
|
|
11874
13132
|
})),
|
|
@@ -11890,7 +13148,7 @@ function createClient(options) {
|
|
|
11890
13148
|
const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
|
|
11891
13149
|
if (shouldConnect) {
|
|
11892
13150
|
if (typeof atob === "undefined") {
|
|
11893
|
-
if (_optionalChain([clientOptions, 'access',
|
|
13151
|
+
if (_optionalChain([clientOptions, 'access', _303 => _303.polyfills, 'optionalAccess', _304 => _304.atob]) === void 0) {
|
|
11894
13152
|
throw new Error(
|
|
11895
13153
|
"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
13154
|
);
|
|
@@ -11902,7 +13160,7 @@ function createClient(options) {
|
|
|
11902
13160
|
return leaseRoom(newRoomDetails);
|
|
11903
13161
|
}
|
|
11904
13162
|
function getRoom(roomId) {
|
|
11905
|
-
const room = _optionalChain([roomsById, 'access',
|
|
13163
|
+
const room = _optionalChain([roomsById, 'access', _305 => _305.get, 'call', _306 => _306(roomId), 'optionalAccess', _307 => _307.room]);
|
|
11906
13164
|
return room ? room : null;
|
|
11907
13165
|
}
|
|
11908
13166
|
function logout() {
|
|
@@ -11918,7 +13176,7 @@ function createClient(options) {
|
|
|
11918
13176
|
const batchedResolveUsers = new Batch(
|
|
11919
13177
|
async (batchedUserIds) => {
|
|
11920
13178
|
const userIds = batchedUserIds.flat();
|
|
11921
|
-
const users = await _optionalChain([resolveUsers, 'optionalCall',
|
|
13179
|
+
const users = await _optionalChain([resolveUsers, 'optionalCall', _308 => _308({ userIds })]);
|
|
11922
13180
|
warnOnceIf(
|
|
11923
13181
|
!resolveUsers,
|
|
11924
13182
|
"Set the resolveUsers option in createClient to specify user info."
|
|
@@ -11935,7 +13193,7 @@ function createClient(options) {
|
|
|
11935
13193
|
const batchedResolveRoomsInfo = new Batch(
|
|
11936
13194
|
async (batchedRoomIds) => {
|
|
11937
13195
|
const roomIds = batchedRoomIds.flat();
|
|
11938
|
-
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall',
|
|
13196
|
+
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _309 => _309({ roomIds })]);
|
|
11939
13197
|
warnOnceIf(
|
|
11940
13198
|
!resolveRoomsInfo,
|
|
11941
13199
|
"Set the resolveRoomsInfo option in createClient to specify room info."
|
|
@@ -11952,7 +13210,7 @@ function createClient(options) {
|
|
|
11952
13210
|
const batchedResolveGroupsInfo = new Batch(
|
|
11953
13211
|
async (batchedGroupIds) => {
|
|
11954
13212
|
const groupIds = batchedGroupIds.flat();
|
|
11955
|
-
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall',
|
|
13213
|
+
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _310 => _310({ groupIds })]);
|
|
11956
13214
|
warnOnceIf(
|
|
11957
13215
|
!resolveGroupsInfo,
|
|
11958
13216
|
"Set the resolveGroupsInfo option in createClient to specify group info."
|
|
@@ -12011,7 +13269,7 @@ function createClient(options) {
|
|
|
12011
13269
|
}
|
|
12012
13270
|
};
|
|
12013
13271
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12014
|
-
_optionalChain([win, 'optionalAccess',
|
|
13272
|
+
_optionalChain([win, 'optionalAccess', _311 => _311.addEventListener, 'call', _312 => _312("beforeunload", maybePreventClose)]);
|
|
12015
13273
|
}
|
|
12016
13274
|
async function getNotificationSettings(options2) {
|
|
12017
13275
|
const plainSettings = await httpClient.getNotificationSettings(options2);
|
|
@@ -12139,7 +13397,7 @@ var commentBodyElementsTypes = {
|
|
|
12139
13397
|
mention: "inline"
|
|
12140
13398
|
};
|
|
12141
13399
|
function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
12142
|
-
if (!body || !_optionalChain([body, 'optionalAccess',
|
|
13400
|
+
if (!body || !_optionalChain([body, 'optionalAccess', _313 => _313.content])) {
|
|
12143
13401
|
return;
|
|
12144
13402
|
}
|
|
12145
13403
|
const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
|
|
@@ -12149,13 +13407,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
|
12149
13407
|
for (const block of body.content) {
|
|
12150
13408
|
if (type === "all" || type === "block") {
|
|
12151
13409
|
if (guard(block)) {
|
|
12152
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13410
|
+
_optionalChain([visitor, 'optionalCall', _314 => _314(block)]);
|
|
12153
13411
|
}
|
|
12154
13412
|
}
|
|
12155
13413
|
if (type === "all" || type === "inline") {
|
|
12156
13414
|
for (const inline of block.children) {
|
|
12157
13415
|
if (guard(inline)) {
|
|
12158
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13416
|
+
_optionalChain([visitor, 'optionalCall', _315 => _315(inline)]);
|
|
12159
13417
|
}
|
|
12160
13418
|
}
|
|
12161
13419
|
}
|
|
@@ -12325,7 +13583,7 @@ var stringifyCommentBodyPlainElements = {
|
|
|
12325
13583
|
text: ({ element }) => element.text,
|
|
12326
13584
|
link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
|
|
12327
13585
|
mention: ({ element, user, group }) => {
|
|
12328
|
-
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13586
|
+
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _316 => _316.name]), () => ( _optionalChain([group, 'optionalAccess', _317 => _317.name]))), () => ( element.id))}`;
|
|
12329
13587
|
}
|
|
12330
13588
|
};
|
|
12331
13589
|
var stringifyCommentBodyHtmlElements = {
|
|
@@ -12355,7 +13613,7 @@ var stringifyCommentBodyHtmlElements = {
|
|
|
12355
13613
|
return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
|
|
12356
13614
|
},
|
|
12357
13615
|
mention: ({ element, user, group }) => {
|
|
12358
|
-
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess',
|
|
13616
|
+
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
13617
|
}
|
|
12360
13618
|
};
|
|
12361
13619
|
var stringifyCommentBodyMarkdownElements = {
|
|
@@ -12385,20 +13643,20 @@ var stringifyCommentBodyMarkdownElements = {
|
|
|
12385
13643
|
return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
|
|
12386
13644
|
},
|
|
12387
13645
|
mention: ({ element, user, group }) => {
|
|
12388
|
-
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13646
|
+
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _322 => _322.name]), () => ( _optionalChain([group, 'optionalAccess', _323 => _323.name]))), () => ( element.id))}`;
|
|
12389
13647
|
}
|
|
12390
13648
|
};
|
|
12391
13649
|
async function stringifyCommentBody(body, options) {
|
|
12392
|
-
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
12393
|
-
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13650
|
+
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _324 => _324.format]), () => ( "plain"));
|
|
13651
|
+
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _325 => _325.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
|
|
12394
13652
|
const elements = {
|
|
12395
13653
|
...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
|
|
12396
|
-
..._optionalChain([options, 'optionalAccess',
|
|
13654
|
+
..._optionalChain([options, 'optionalAccess', _326 => _326.elements])
|
|
12397
13655
|
};
|
|
12398
13656
|
const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
|
|
12399
13657
|
body,
|
|
12400
|
-
_optionalChain([options, 'optionalAccess',
|
|
12401
|
-
_optionalChain([options, 'optionalAccess',
|
|
13658
|
+
_optionalChain([options, 'optionalAccess', _327 => _327.resolveUsers]),
|
|
13659
|
+
_optionalChain([options, 'optionalAccess', _328 => _328.resolveGroupsInfo])
|
|
12402
13660
|
);
|
|
12403
13661
|
const blocks = body.content.flatMap((block, blockIndex) => {
|
|
12404
13662
|
switch (block.type) {
|
|
@@ -12480,6 +13738,12 @@ function toPlainLson(lson) {
|
|
|
12480
13738
|
liveblocksType: "LiveList",
|
|
12481
13739
|
data: [...lson].map((item) => toPlainLson(item))
|
|
12482
13740
|
};
|
|
13741
|
+
} else if (lson instanceof LiveText) {
|
|
13742
|
+
return {
|
|
13743
|
+
liveblocksType: "LiveText",
|
|
13744
|
+
data: lson.toJSON(),
|
|
13745
|
+
version: lson.version
|
|
13746
|
+
};
|
|
12483
13747
|
} else {
|
|
12484
13748
|
return lson;
|
|
12485
13749
|
}
|
|
@@ -12533,9 +13797,9 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12533
13797
|
const startTime = performance.now();
|
|
12534
13798
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
12535
13799
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12536
|
-
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13800
|
+
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _329 => _329.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
|
|
12537
13801
|
const context = {
|
|
12538
|
-
inForeground: _optionalChain([doc, 'optionalAccess',
|
|
13802
|
+
inForeground: _optionalChain([doc, 'optionalAccess', _330 => _330.visibilityState]) !== "hidden",
|
|
12539
13803
|
lastSuccessfulPollAt: startTime,
|
|
12540
13804
|
count: 0,
|
|
12541
13805
|
backoff: 0
|
|
@@ -12616,11 +13880,11 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12616
13880
|
pollNowIfStale();
|
|
12617
13881
|
}
|
|
12618
13882
|
function onVisibilityChange() {
|
|
12619
|
-
setInForeground(_optionalChain([doc, 'optionalAccess',
|
|
13883
|
+
setInForeground(_optionalChain([doc, 'optionalAccess', _331 => _331.visibilityState]) !== "hidden");
|
|
12620
13884
|
}
|
|
12621
|
-
_optionalChain([doc, 'optionalAccess',
|
|
12622
|
-
_optionalChain([win, 'optionalAccess',
|
|
12623
|
-
_optionalChain([win, 'optionalAccess',
|
|
13885
|
+
_optionalChain([doc, 'optionalAccess', _332 => _332.addEventListener, 'call', _333 => _333("visibilitychange", onVisibilityChange)]);
|
|
13886
|
+
_optionalChain([win, 'optionalAccess', _334 => _334.addEventListener, 'call', _335 => _335("online", onVisibilityChange)]);
|
|
13887
|
+
_optionalChain([win, 'optionalAccess', _336 => _336.addEventListener, 'call', _337 => _337("focus", pollNowIfStale)]);
|
|
12624
13888
|
fsm.start();
|
|
12625
13889
|
return {
|
|
12626
13890
|
inc,
|
|
@@ -12765,5 +14029,10 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
|
12765
14029
|
|
|
12766
14030
|
|
|
12767
14031
|
|
|
12768
|
-
|
|
14032
|
+
|
|
14033
|
+
|
|
14034
|
+
|
|
14035
|
+
|
|
14036
|
+
|
|
14037
|
+
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
14038
|
//# sourceMappingURL=index.cjs.map
|