@liveblocks/core 3.22.0 → 3.23.0-exp2
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 +1399 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +367 -32
- package/dist/index.d.ts +367 -32
- package/dist/index.js +1327 -39
- 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.23.0-exp2";
|
|
10
10
|
var PKG_FORMAT = "cjs";
|
|
11
11
|
|
|
12
12
|
// src/dupe-detection.ts
|
|
@@ -3879,6 +3879,7 @@ var ManagedSocket = class {
|
|
|
3879
3879
|
|
|
3880
3880
|
// src/internal.ts
|
|
3881
3881
|
var kInternal = /* @__PURE__ */ Symbol();
|
|
3882
|
+
var kStorageUpdateSource = /* @__PURE__ */ Symbol();
|
|
3882
3883
|
|
|
3883
3884
|
// src/lib/IncrementalJsonParser.ts
|
|
3884
3885
|
var EMPTY_OBJECT = Object.freeze({});
|
|
@@ -5837,13 +5838,15 @@ var OpCode = Object.freeze({
|
|
|
5837
5838
|
DELETE_CRDT: 5,
|
|
5838
5839
|
DELETE_OBJECT_KEY: 6,
|
|
5839
5840
|
CREATE_MAP: 7,
|
|
5840
|
-
CREATE_REGISTER: 8
|
|
5841
|
+
CREATE_REGISTER: 8,
|
|
5842
|
+
CREATE_TEXT: 9,
|
|
5843
|
+
UPDATE_TEXT: 10
|
|
5841
5844
|
});
|
|
5842
5845
|
function isIgnoredOp(op) {
|
|
5843
5846
|
return op.type === OpCode.DELETE_CRDT && op.id === "ACK";
|
|
5844
5847
|
}
|
|
5845
5848
|
function isCreateOp(op) {
|
|
5846
|
-
return op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_REGISTER || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_LIST;
|
|
5849
|
+
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;
|
|
5847
5850
|
}
|
|
5848
5851
|
|
|
5849
5852
|
// src/protocol/StorageNode.ts
|
|
@@ -5851,7 +5854,8 @@ var CrdtType = Object.freeze({
|
|
|
5851
5854
|
OBJECT: 0,
|
|
5852
5855
|
LIST: 1,
|
|
5853
5856
|
MAP: 2,
|
|
5854
|
-
REGISTER: 3
|
|
5857
|
+
REGISTER: 3,
|
|
5858
|
+
TEXT: 4
|
|
5855
5859
|
});
|
|
5856
5860
|
function isRootStorageNode(node) {
|
|
5857
5861
|
return node[0] === "root";
|
|
@@ -5868,6 +5872,9 @@ function isMapStorageNode(node) {
|
|
|
5868
5872
|
function isRegisterStorageNode(node) {
|
|
5869
5873
|
return node[1].type === CrdtType.REGISTER;
|
|
5870
5874
|
}
|
|
5875
|
+
function isTextStorageNode(node) {
|
|
5876
|
+
return node[1].type === CrdtType.TEXT;
|
|
5877
|
+
}
|
|
5871
5878
|
function isCompactRootNode(node) {
|
|
5872
5879
|
return node[0] === "root";
|
|
5873
5880
|
}
|
|
@@ -5890,6 +5897,9 @@ function* compactNodesToNodeStream(compactNodes) {
|
|
|
5890
5897
|
case CrdtType.REGISTER:
|
|
5891
5898
|
yield [cnode[0], { type: CrdtType.REGISTER, parentId: cnode[2], parentKey: cnode[3], data: cnode[4] }];
|
|
5892
5899
|
break;
|
|
5900
|
+
case CrdtType.TEXT:
|
|
5901
|
+
yield [cnode[0], { type: CrdtType.TEXT, parentId: cnode[2], parentKey: cnode[3], data: cnode[4], version: cnode[5] }];
|
|
5902
|
+
break;
|
|
5893
5903
|
default:
|
|
5894
5904
|
}
|
|
5895
5905
|
}
|
|
@@ -5918,6 +5928,17 @@ function* nodeStreamToCompactNodes(nodes) {
|
|
|
5918
5928
|
const id = node[0];
|
|
5919
5929
|
const crdt = node[1];
|
|
5920
5930
|
yield [id, CrdtType.REGISTER, crdt.parentId, crdt.parentKey, crdt.data];
|
|
5931
|
+
} else if (isTextStorageNode(node)) {
|
|
5932
|
+
const id = node[0];
|
|
5933
|
+
const crdt = node[1];
|
|
5934
|
+
yield [
|
|
5935
|
+
id,
|
|
5936
|
+
CrdtType.TEXT,
|
|
5937
|
+
crdt.parentId,
|
|
5938
|
+
crdt.parentKey,
|
|
5939
|
+
crdt.data,
|
|
5940
|
+
crdt.version
|
|
5941
|
+
];
|
|
5921
5942
|
} else {
|
|
5922
5943
|
}
|
|
5923
5944
|
}
|
|
@@ -6120,6 +6141,10 @@ ${parentKey}`;
|
|
|
6120
6141
|
get size() {
|
|
6121
6142
|
return this.#byOpId.size;
|
|
6122
6143
|
}
|
|
6144
|
+
/** The still-unacknowledged op with the given opId, if any. */
|
|
6145
|
+
get(opId) {
|
|
6146
|
+
return this.#byOpId.get(opId);
|
|
6147
|
+
}
|
|
6123
6148
|
/**
|
|
6124
6149
|
* Mark the given Op as still unacknowledged.
|
|
6125
6150
|
*/
|
|
@@ -6219,8 +6244,8 @@ function createManagedPool(options) {
|
|
|
6219
6244
|
deleteNode: (id) => void nodes.delete(id),
|
|
6220
6245
|
generateId: () => `${getCurrentConnectionId()}:${clock++}`,
|
|
6221
6246
|
generateOpId: () => `${getCurrentConnectionId()}:${opClock++}`,
|
|
6222
|
-
dispatch(ops, reverse, storageUpdates) {
|
|
6223
|
-
_optionalChain([onDispatch, 'optionalCall', _129 => _129(ops, reverse, storageUpdates)]);
|
|
6247
|
+
dispatch(ops, reverse, storageUpdates, options2) {
|
|
6248
|
+
_optionalChain([onDispatch, 'optionalCall', _129 => _129(ops, reverse, storageUpdates, options2)]);
|
|
6224
6249
|
},
|
|
6225
6250
|
assertStorageIsWritable: () => {
|
|
6226
6251
|
if (!isStorageWritable()) {
|
|
@@ -6243,10 +6268,17 @@ function Orphaned(oldKey, oldPos = asPos(oldKey)) {
|
|
|
6243
6268
|
return Object.freeze({ type: "Orphaned", oldKey, oldPos });
|
|
6244
6269
|
}
|
|
6245
6270
|
var AbstractCrdt = class {
|
|
6246
|
-
// ^^^^^^^^^^^^ TODO: Make this an interface
|
|
6247
6271
|
#pool;
|
|
6248
6272
|
#id;
|
|
6249
6273
|
#parent = NoParent;
|
|
6274
|
+
constructor() {
|
|
6275
|
+
Object.defineProperty(this, kInternal, {
|
|
6276
|
+
value: {
|
|
6277
|
+
getId: () => this.#id
|
|
6278
|
+
},
|
|
6279
|
+
enumerable: false
|
|
6280
|
+
});
|
|
6281
|
+
}
|
|
6250
6282
|
/** @internal */
|
|
6251
6283
|
_getParentKeyOrThrow() {
|
|
6252
6284
|
switch (this.parent.type) {
|
|
@@ -8776,6 +8808,1114 @@ var LiveObject = (_class2 = class _LiveObject extends AbstractCrdt {
|
|
|
8776
8808
|
}
|
|
8777
8809
|
}, _class2.__initStatic(), _class2);
|
|
8778
8810
|
|
|
8811
|
+
// src/crdts/liveTextOps.ts
|
|
8812
|
+
function attributesEqual(left, right) {
|
|
8813
|
+
if (left === right) {
|
|
8814
|
+
return true;
|
|
8815
|
+
}
|
|
8816
|
+
if (left === void 0 || right === void 0) {
|
|
8817
|
+
return false;
|
|
8818
|
+
}
|
|
8819
|
+
const leftKeys = Object.keys(left);
|
|
8820
|
+
const rightKeys = Object.keys(right);
|
|
8821
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
8822
|
+
return false;
|
|
8823
|
+
}
|
|
8824
|
+
for (const key of leftKeys) {
|
|
8825
|
+
if (left[key] !== right[key]) {
|
|
8826
|
+
return false;
|
|
8827
|
+
}
|
|
8828
|
+
}
|
|
8829
|
+
return true;
|
|
8830
|
+
}
|
|
8831
|
+
function cloneAttributes(attributes) {
|
|
8832
|
+
return attributes === void 0 ? void 0 : freeze({ ...attributes });
|
|
8833
|
+
}
|
|
8834
|
+
function normalizeSegments(segments) {
|
|
8835
|
+
const normalized = [];
|
|
8836
|
+
for (const segment of segments) {
|
|
8837
|
+
if (segment.text.length === 0) {
|
|
8838
|
+
continue;
|
|
8839
|
+
}
|
|
8840
|
+
const last = normalized.at(-1);
|
|
8841
|
+
const attributes = cloneAttributes(segment.attributes);
|
|
8842
|
+
if (last !== void 0 && attributesEqual(last.attributes, attributes)) {
|
|
8843
|
+
last.text += segment.text;
|
|
8844
|
+
} else {
|
|
8845
|
+
normalized.push({ text: segment.text, attributes });
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
return normalized;
|
|
8849
|
+
}
|
|
8850
|
+
function dataToSegments(data) {
|
|
8851
|
+
return normalizeSegments(
|
|
8852
|
+
data.map(([text, attributes]) => ({
|
|
8853
|
+
text,
|
|
8854
|
+
attributes
|
|
8855
|
+
}))
|
|
8856
|
+
);
|
|
8857
|
+
}
|
|
8858
|
+
function segmentsToData(segments) {
|
|
8859
|
+
return segments.map(
|
|
8860
|
+
(segment) => segment.attributes === void 0 ? [segment.text] : [segment.text, { ...segment.attributes }]
|
|
8861
|
+
);
|
|
8862
|
+
}
|
|
8863
|
+
function textLength(segments) {
|
|
8864
|
+
return segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
8865
|
+
}
|
|
8866
|
+
function splitSegmentsAt(segments, index) {
|
|
8867
|
+
const result = [];
|
|
8868
|
+
let offset = 0;
|
|
8869
|
+
for (const segment of segments) {
|
|
8870
|
+
const end = offset + segment.text.length;
|
|
8871
|
+
if (index > offset && index < end) {
|
|
8872
|
+
const before2 = segment.text.slice(0, index - offset);
|
|
8873
|
+
const after2 = segment.text.slice(index - offset);
|
|
8874
|
+
result.push({ text: before2, attributes: segment.attributes });
|
|
8875
|
+
result.push({ text: after2, attributes: segment.attributes });
|
|
8876
|
+
} else {
|
|
8877
|
+
result.push({ text: segment.text, attributes: segment.attributes });
|
|
8878
|
+
}
|
|
8879
|
+
offset = end;
|
|
8880
|
+
}
|
|
8881
|
+
return result;
|
|
8882
|
+
}
|
|
8883
|
+
function clipRange(index, length, contentLength) {
|
|
8884
|
+
const clippedIndex = Math.max(0, Math.min(index, contentLength));
|
|
8885
|
+
const clippedEnd = Math.max(
|
|
8886
|
+
clippedIndex,
|
|
8887
|
+
Math.min(index + length, contentLength)
|
|
8888
|
+
);
|
|
8889
|
+
return { index: clippedIndex, length: clippedEnd - clippedIndex };
|
|
8890
|
+
}
|
|
8891
|
+
function applyInsert(segments, index, text, attributes) {
|
|
8892
|
+
if (text.length === 0) {
|
|
8893
|
+
return normalizeSegments(segments);
|
|
8894
|
+
}
|
|
8895
|
+
const split = splitSegmentsAt(segments, index);
|
|
8896
|
+
const result = [];
|
|
8897
|
+
let offset = 0;
|
|
8898
|
+
let inserted = false;
|
|
8899
|
+
for (const segment of split) {
|
|
8900
|
+
if (!inserted && offset === index) {
|
|
8901
|
+
result.push({ text, attributes });
|
|
8902
|
+
inserted = true;
|
|
8903
|
+
}
|
|
8904
|
+
result.push(segment);
|
|
8905
|
+
offset += segment.text.length;
|
|
8906
|
+
}
|
|
8907
|
+
if (!inserted) {
|
|
8908
|
+
result.push({ text, attributes });
|
|
8909
|
+
}
|
|
8910
|
+
return normalizeSegments(result);
|
|
8911
|
+
}
|
|
8912
|
+
function extractDeletedSegments(segments, index, length) {
|
|
8913
|
+
const split = splitSegmentsAt(
|
|
8914
|
+
splitSegmentsAt(segments, index),
|
|
8915
|
+
index + length
|
|
8916
|
+
);
|
|
8917
|
+
const deleted = [];
|
|
8918
|
+
let offset = 0;
|
|
8919
|
+
for (const segment of split) {
|
|
8920
|
+
const end = offset + segment.text.length;
|
|
8921
|
+
if (offset >= index && end <= index + length) {
|
|
8922
|
+
deleted.push({
|
|
8923
|
+
text: segment.text,
|
|
8924
|
+
attributes: segment.attributes
|
|
8925
|
+
});
|
|
8926
|
+
}
|
|
8927
|
+
offset = end;
|
|
8928
|
+
}
|
|
8929
|
+
return normalizeSegments(deleted);
|
|
8930
|
+
}
|
|
8931
|
+
function applyDelete(segments, index, length) {
|
|
8932
|
+
const deletedSegments = extractDeletedSegments(segments, index, length);
|
|
8933
|
+
const split = splitSegmentsAt(
|
|
8934
|
+
splitSegmentsAt(segments, index),
|
|
8935
|
+
index + length
|
|
8936
|
+
);
|
|
8937
|
+
const result = [];
|
|
8938
|
+
let offset = 0;
|
|
8939
|
+
let deletedText = "";
|
|
8940
|
+
for (const segment of split) {
|
|
8941
|
+
const end = offset + segment.text.length;
|
|
8942
|
+
if (offset >= index && end <= index + length) {
|
|
8943
|
+
deletedText += segment.text;
|
|
8944
|
+
} else {
|
|
8945
|
+
result.push(segment);
|
|
8946
|
+
}
|
|
8947
|
+
offset = end;
|
|
8948
|
+
}
|
|
8949
|
+
return {
|
|
8950
|
+
segments: normalizeSegments(result),
|
|
8951
|
+
deletedText,
|
|
8952
|
+
deletedSegments
|
|
8953
|
+
};
|
|
8954
|
+
}
|
|
8955
|
+
function applyFormat(segments, index, length, attributes) {
|
|
8956
|
+
const split = splitSegmentsAt(
|
|
8957
|
+
splitSegmentsAt(segments, index),
|
|
8958
|
+
index + length
|
|
8959
|
+
);
|
|
8960
|
+
const result = [];
|
|
8961
|
+
let offset = 0;
|
|
8962
|
+
for (const segment of split) {
|
|
8963
|
+
const end = offset + segment.text.length;
|
|
8964
|
+
if (offset >= index && end <= index + length) {
|
|
8965
|
+
const nextAttributes = {
|
|
8966
|
+
..._nullishCoalesce(segment.attributes, () => ( {}))
|
|
8967
|
+
};
|
|
8968
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
8969
|
+
if (value === null) {
|
|
8970
|
+
delete nextAttributes[key];
|
|
8971
|
+
} else {
|
|
8972
|
+
nextAttributes[key] = value;
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
result.push({
|
|
8976
|
+
text: segment.text,
|
|
8977
|
+
attributes: Object.keys(nextAttributes).length === 0 ? void 0 : freeze(nextAttributes)
|
|
8978
|
+
});
|
|
8979
|
+
} else {
|
|
8980
|
+
result.push(segment);
|
|
8981
|
+
}
|
|
8982
|
+
offset = end;
|
|
8983
|
+
}
|
|
8984
|
+
return normalizeSegments(result);
|
|
8985
|
+
}
|
|
8986
|
+
function formatReverseOperations(segments, index, length, patch) {
|
|
8987
|
+
const split = splitSegmentsAt(
|
|
8988
|
+
splitSegmentsAt(segments, index),
|
|
8989
|
+
index + length
|
|
8990
|
+
);
|
|
8991
|
+
const result = [];
|
|
8992
|
+
let offset = 0;
|
|
8993
|
+
for (const segment of split) {
|
|
8994
|
+
const end = offset + segment.text.length;
|
|
8995
|
+
if (offset >= index && end <= index + length) {
|
|
8996
|
+
const attributes = {};
|
|
8997
|
+
for (const key of Object.keys(patch)) {
|
|
8998
|
+
attributes[key] = _nullishCoalesce(_optionalChain([segment, 'access', _217 => _217.attributes, 'optionalAccess', _218 => _218[key]]), () => ( null));
|
|
8999
|
+
}
|
|
9000
|
+
result.push({
|
|
9001
|
+
type: "format",
|
|
9002
|
+
index: offset,
|
|
9003
|
+
length: segment.text.length,
|
|
9004
|
+
attributes
|
|
9005
|
+
});
|
|
9006
|
+
}
|
|
9007
|
+
offset = end;
|
|
9008
|
+
}
|
|
9009
|
+
return result;
|
|
9010
|
+
}
|
|
9011
|
+
function mapIndexThroughOperation(index, op) {
|
|
9012
|
+
if (op.type === "insert") {
|
|
9013
|
+
return op.index <= index ? index + op.text.length : index;
|
|
9014
|
+
} else if (op.type === "delete") {
|
|
9015
|
+
if (op.index >= index) {
|
|
9016
|
+
return index;
|
|
9017
|
+
}
|
|
9018
|
+
return Math.max(op.index, index - op.length);
|
|
9019
|
+
} else {
|
|
9020
|
+
return index;
|
|
9021
|
+
}
|
|
9022
|
+
}
|
|
9023
|
+
function mapTextIndexThroughOperations(index, ops) {
|
|
9024
|
+
let mapped = index;
|
|
9025
|
+
for (const op of ops) {
|
|
9026
|
+
mapped = mapIndexThroughOperation(mapped, op);
|
|
9027
|
+
}
|
|
9028
|
+
return mapped;
|
|
9029
|
+
}
|
|
9030
|
+
function inverseMapIndexThroughOperation(index, op) {
|
|
9031
|
+
if (op.type === "insert") {
|
|
9032
|
+
if (index <= op.index) {
|
|
9033
|
+
return index;
|
|
9034
|
+
}
|
|
9035
|
+
return Math.max(op.index, index - op.text.length);
|
|
9036
|
+
} else if (op.type === "delete") {
|
|
9037
|
+
return op.index <= index ? index + op.length : index;
|
|
9038
|
+
} else {
|
|
9039
|
+
return index;
|
|
9040
|
+
}
|
|
9041
|
+
}
|
|
9042
|
+
function inverseMapTextIndexThroughOperations(index, ops) {
|
|
9043
|
+
let mapped = index;
|
|
9044
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
9045
|
+
mapped = inverseMapIndexThroughOperation(mapped, ops[i]);
|
|
9046
|
+
}
|
|
9047
|
+
return mapped;
|
|
9048
|
+
}
|
|
9049
|
+
function oppositeOrder(order) {
|
|
9050
|
+
return order === "before" ? "after" : "before";
|
|
9051
|
+
}
|
|
9052
|
+
function mapIndexOverDelete(index, deleteIndex, deleteLength) {
|
|
9053
|
+
if (deleteIndex >= index) {
|
|
9054
|
+
return index;
|
|
9055
|
+
}
|
|
9056
|
+
return Math.max(deleteIndex, index - deleteLength);
|
|
9057
|
+
}
|
|
9058
|
+
function transformInsert(op, over, order) {
|
|
9059
|
+
if (over.type === "insert") {
|
|
9060
|
+
const shifts = over.index < op.index || over.index === op.index && order === "after";
|
|
9061
|
+
return [shifts ? { ...op, index: op.index + over.text.length } : { ...op }];
|
|
9062
|
+
} else if (over.type === "delete") {
|
|
9063
|
+
return [
|
|
9064
|
+
{ ...op, index: mapIndexOverDelete(op.index, over.index, over.length) }
|
|
9065
|
+
];
|
|
9066
|
+
} else {
|
|
9067
|
+
return [{ ...op }];
|
|
9068
|
+
}
|
|
9069
|
+
}
|
|
9070
|
+
function transformDelete(op, over) {
|
|
9071
|
+
const start = op.index;
|
|
9072
|
+
const end = op.index + op.length;
|
|
9073
|
+
if (over.type === "insert") {
|
|
9074
|
+
const at = over.index;
|
|
9075
|
+
const len = over.text.length;
|
|
9076
|
+
if (at <= start) {
|
|
9077
|
+
return [{ ...op, index: start + len }];
|
|
9078
|
+
}
|
|
9079
|
+
if (at >= end) {
|
|
9080
|
+
return [{ ...op }];
|
|
9081
|
+
}
|
|
9082
|
+
return [
|
|
9083
|
+
{ type: "delete", index: start, length: at - start },
|
|
9084
|
+
{ type: "delete", index: start + len, length: end - at }
|
|
9085
|
+
];
|
|
9086
|
+
} else if (over.type === "delete") {
|
|
9087
|
+
const newStart = mapIndexOverDelete(start, over.index, over.length);
|
|
9088
|
+
const newEnd = mapIndexOverDelete(end, over.index, over.length);
|
|
9089
|
+
return newEnd - newStart > 0 ? [{ type: "delete", index: newStart, length: newEnd - newStart }] : [];
|
|
9090
|
+
} else {
|
|
9091
|
+
return [{ ...op }];
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
function transformFormat(op, over, order) {
|
|
9095
|
+
const start = op.index;
|
|
9096
|
+
const end = op.index + op.length;
|
|
9097
|
+
if (over.type === "insert") {
|
|
9098
|
+
const at = over.index;
|
|
9099
|
+
const len = over.text.length;
|
|
9100
|
+
if (at <= start) {
|
|
9101
|
+
return [{ ...op, index: start + len }];
|
|
9102
|
+
}
|
|
9103
|
+
if (at >= end) {
|
|
9104
|
+
return [{ ...op }];
|
|
9105
|
+
}
|
|
9106
|
+
return [
|
|
9107
|
+
{
|
|
9108
|
+
type: "format",
|
|
9109
|
+
index: start,
|
|
9110
|
+
length: at - start,
|
|
9111
|
+
attributes: op.attributes
|
|
9112
|
+
},
|
|
9113
|
+
{
|
|
9114
|
+
type: "format",
|
|
9115
|
+
index: at + len,
|
|
9116
|
+
length: end - at,
|
|
9117
|
+
attributes: op.attributes
|
|
9118
|
+
}
|
|
9119
|
+
];
|
|
9120
|
+
} else if (over.type === "delete") {
|
|
9121
|
+
const newStart = mapIndexOverDelete(start, over.index, over.length);
|
|
9122
|
+
const newEnd = mapIndexOverDelete(end, over.index, over.length);
|
|
9123
|
+
return newEnd - newStart > 0 ? [
|
|
9124
|
+
{
|
|
9125
|
+
type: "format",
|
|
9126
|
+
index: newStart,
|
|
9127
|
+
length: newEnd - newStart,
|
|
9128
|
+
attributes: op.attributes
|
|
9129
|
+
}
|
|
9130
|
+
] : [];
|
|
9131
|
+
} else {
|
|
9132
|
+
if (order === "after") {
|
|
9133
|
+
return [{ ...op }];
|
|
9134
|
+
}
|
|
9135
|
+
const overlapStart = Math.max(start, over.index);
|
|
9136
|
+
const overlapEnd = Math.min(end, over.index + over.length);
|
|
9137
|
+
if (overlapStart >= overlapEnd) {
|
|
9138
|
+
return [{ ...op }];
|
|
9139
|
+
}
|
|
9140
|
+
const hasConflict = Object.keys(op.attributes).some(
|
|
9141
|
+
(key) => key in over.attributes
|
|
9142
|
+
);
|
|
9143
|
+
if (!hasConflict) {
|
|
9144
|
+
return [{ ...op }];
|
|
9145
|
+
}
|
|
9146
|
+
const reduced = {};
|
|
9147
|
+
for (const [key, value] of Object.entries(op.attributes)) {
|
|
9148
|
+
if (!(key in over.attributes)) {
|
|
9149
|
+
reduced[key] = value;
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
const pieces = [];
|
|
9153
|
+
if (start < overlapStart) {
|
|
9154
|
+
pieces.push({
|
|
9155
|
+
type: "format",
|
|
9156
|
+
index: start,
|
|
9157
|
+
length: overlapStart - start,
|
|
9158
|
+
attributes: op.attributes
|
|
9159
|
+
});
|
|
9160
|
+
}
|
|
9161
|
+
if (Object.keys(reduced).length > 0) {
|
|
9162
|
+
pieces.push({
|
|
9163
|
+
type: "format",
|
|
9164
|
+
index: overlapStart,
|
|
9165
|
+
length: overlapEnd - overlapStart,
|
|
9166
|
+
attributes: reduced
|
|
9167
|
+
});
|
|
9168
|
+
}
|
|
9169
|
+
if (overlapEnd < end) {
|
|
9170
|
+
pieces.push({
|
|
9171
|
+
type: "format",
|
|
9172
|
+
index: overlapEnd,
|
|
9173
|
+
length: end - overlapEnd,
|
|
9174
|
+
attributes: op.attributes
|
|
9175
|
+
});
|
|
9176
|
+
}
|
|
9177
|
+
return pieces;
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
function transformSingle(op, over, order) {
|
|
9181
|
+
switch (op.type) {
|
|
9182
|
+
case "insert":
|
|
9183
|
+
return transformInsert(op, over, order);
|
|
9184
|
+
case "delete":
|
|
9185
|
+
return transformDelete(op, over);
|
|
9186
|
+
case "format":
|
|
9187
|
+
return transformFormat(op, over, order);
|
|
9188
|
+
}
|
|
9189
|
+
}
|
|
9190
|
+
function transformTextOperationsX(a, b, order) {
|
|
9191
|
+
if (a.length === 0 || b.length === 0) {
|
|
9192
|
+
return [[...a], [...b]];
|
|
9193
|
+
}
|
|
9194
|
+
if (a.length === 1 && b.length === 1) {
|
|
9195
|
+
return [
|
|
9196
|
+
transformSingle(a[0], b[0], order),
|
|
9197
|
+
transformSingle(b[0], a[0], oppositeOrder(order))
|
|
9198
|
+
];
|
|
9199
|
+
}
|
|
9200
|
+
if (a.length > 1) {
|
|
9201
|
+
const [headA1, b1] = transformTextOperationsX([a[0]], b, order);
|
|
9202
|
+
const [restA1, b2] = transformTextOperationsX(a.slice(1), b1, order);
|
|
9203
|
+
return [[...headA1, ...restA1], b2];
|
|
9204
|
+
}
|
|
9205
|
+
const [a1, headB1] = transformTextOperationsX(a, [b[0]], order);
|
|
9206
|
+
const [a2, restB1] = transformTextOperationsX(a1, b.slice(1), order);
|
|
9207
|
+
return [a2, [...headB1, ...restB1]];
|
|
9208
|
+
}
|
|
9209
|
+
function transformTextOperations(ops, over, order) {
|
|
9210
|
+
return transformTextOperationsX(ops, over, order)[0];
|
|
9211
|
+
}
|
|
9212
|
+
function textOperationsEqual(a, b) {
|
|
9213
|
+
return a === b || stableStringify(a) === stableStringify(b);
|
|
9214
|
+
}
|
|
9215
|
+
function applyTextOperationsToSegments(segments, ops) {
|
|
9216
|
+
let next = [...segments];
|
|
9217
|
+
for (const op of ops) {
|
|
9218
|
+
if (op.type === "insert") {
|
|
9219
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9220
|
+
next = applyInsert(next, index, op.text, op.attributes);
|
|
9221
|
+
} else if (op.type === "delete") {
|
|
9222
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9223
|
+
const clipped = clipRange(index, op.length, textLength(next));
|
|
9224
|
+
next = applyDelete(next, clipped.index, clipped.length).segments;
|
|
9225
|
+
} else {
|
|
9226
|
+
const index = Math.max(0, Math.min(op.index, textLength(next)));
|
|
9227
|
+
const clipped = clipRange(index, op.length, textLength(next));
|
|
9228
|
+
next = applyFormat(next, clipped.index, clipped.length, op.attributes);
|
|
9229
|
+
}
|
|
9230
|
+
}
|
|
9231
|
+
return next;
|
|
9232
|
+
}
|
|
9233
|
+
function applyLiveTextOperations(data, ops) {
|
|
9234
|
+
return segmentsToData(
|
|
9235
|
+
applyTextOperationsToSegments(dataToSegments(data), ops)
|
|
9236
|
+
);
|
|
9237
|
+
}
|
|
9238
|
+
function invertTextOperations(segments, ops) {
|
|
9239
|
+
let shadow = [...segments];
|
|
9240
|
+
const reverse = [];
|
|
9241
|
+
for (const op of ops) {
|
|
9242
|
+
if (op.type === "insert") {
|
|
9243
|
+
shadow = applyInsert(shadow, op.index, op.text, op.attributes);
|
|
9244
|
+
reverse.unshift({
|
|
9245
|
+
type: "delete",
|
|
9246
|
+
index: op.index,
|
|
9247
|
+
length: op.text.length
|
|
9248
|
+
});
|
|
9249
|
+
} else if (op.type === "delete") {
|
|
9250
|
+
const deletedSegments = extractDeletedSegments(
|
|
9251
|
+
shadow,
|
|
9252
|
+
op.index,
|
|
9253
|
+
op.length
|
|
9254
|
+
);
|
|
9255
|
+
shadow = applyDelete(shadow, op.index, op.length).segments;
|
|
9256
|
+
const inserts = [];
|
|
9257
|
+
let insertIndex = op.index;
|
|
9258
|
+
for (const segment of deletedSegments) {
|
|
9259
|
+
inserts.push({
|
|
9260
|
+
type: "insert",
|
|
9261
|
+
index: insertIndex,
|
|
9262
|
+
text: segment.text,
|
|
9263
|
+
attributes: segment.attributes
|
|
9264
|
+
});
|
|
9265
|
+
insertIndex += segment.text.length;
|
|
9266
|
+
}
|
|
9267
|
+
for (let index = inserts.length - 1; index >= 0; index--) {
|
|
9268
|
+
reverse.unshift(inserts[index]);
|
|
9269
|
+
}
|
|
9270
|
+
} else {
|
|
9271
|
+
const inverse = formatReverseOperations(
|
|
9272
|
+
shadow,
|
|
9273
|
+
op.index,
|
|
9274
|
+
op.length,
|
|
9275
|
+
op.attributes
|
|
9276
|
+
);
|
|
9277
|
+
shadow = applyFormat(shadow, op.index, op.length, op.attributes);
|
|
9278
|
+
reverse.unshift(...inverse.reverse());
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
9281
|
+
return reverse;
|
|
9282
|
+
}
|
|
9283
|
+
|
|
9284
|
+
// src/crdts/LiveText.ts
|
|
9285
|
+
var ACCEPTED_OPS_HISTORY_LIMIT = 1e3;
|
|
9286
|
+
var LiveText = class _LiveText extends AbstractCrdt {
|
|
9287
|
+
/** The local document: #confirmed ⊕ #inFlightOps ⊕ #queuedOps. */
|
|
9288
|
+
#segments;
|
|
9289
|
+
/** The server-confirmed document (only authoritative ops applied). */
|
|
9290
|
+
#confirmed;
|
|
9291
|
+
#version;
|
|
9292
|
+
/** The op currently awaiting server acknowledgement (at most one). */
|
|
9293
|
+
#inFlightOpId;
|
|
9294
|
+
/** Its ops, continuously re-expressed against current server state. */
|
|
9295
|
+
#inFlightOps = [];
|
|
9296
|
+
/** Local edits made while an op is in flight; sent after the ack. */
|
|
9297
|
+
#queuedOps = [];
|
|
9298
|
+
#acceptedOps = [];
|
|
9299
|
+
/**
|
|
9300
|
+
* Creates a new LiveText document.
|
|
9301
|
+
*
|
|
9302
|
+
* @param textOrData Initial plain text, or an array of `[text]` /
|
|
9303
|
+
* `[text, attributes]` segments. Defaults to an empty document.
|
|
9304
|
+
*
|
|
9305
|
+
* @example
|
|
9306
|
+
* new LiveText();
|
|
9307
|
+
* new LiveText("Hello world");
|
|
9308
|
+
* new LiveText([["Hello ", { bold: true }], ["world"]]);
|
|
9309
|
+
*/
|
|
9310
|
+
constructor(textOrData = "", version = 0) {
|
|
9311
|
+
super();
|
|
9312
|
+
this.#segments = typeof textOrData === "string" ? textOrData.length === 0 ? [] : [{ text: textOrData }] : dataToSegments(textOrData);
|
|
9313
|
+
this.#confirmed = [...this.#segments];
|
|
9314
|
+
this.#version = version;
|
|
9315
|
+
Object.assign(this[kInternal], {
|
|
9316
|
+
encodeIndex: (localIndex) => this.#encodeIndex(localIndex),
|
|
9317
|
+
decodeIndex: (index, fromVersion) => this.#decodeIndex(index, fromVersion)
|
|
9318
|
+
});
|
|
9319
|
+
}
|
|
9320
|
+
get version() {
|
|
9321
|
+
return this.#version;
|
|
9322
|
+
}
|
|
9323
|
+
get length() {
|
|
9324
|
+
return textLength(this.#segments);
|
|
9325
|
+
}
|
|
9326
|
+
/** @internal */
|
|
9327
|
+
static _deserialize([id, item], _parentToChildren, pool) {
|
|
9328
|
+
const text = new _LiveText(item.data, item.version);
|
|
9329
|
+
text._attach(id, pool);
|
|
9330
|
+
return text;
|
|
9331
|
+
}
|
|
9332
|
+
/** @internal */
|
|
9333
|
+
_toOps(parentId, parentKey) {
|
|
9334
|
+
if (this._id === void 0) {
|
|
9335
|
+
throw new Error("Cannot serialize LiveText if it is not attached");
|
|
9336
|
+
}
|
|
9337
|
+
return [
|
|
9338
|
+
{
|
|
9339
|
+
type: OpCode.CREATE_TEXT,
|
|
9340
|
+
id: this._id,
|
|
9341
|
+
parentId,
|
|
9342
|
+
parentKey,
|
|
9343
|
+
data: this.toJSON(),
|
|
9344
|
+
version: this.#version
|
|
9345
|
+
}
|
|
9346
|
+
];
|
|
9347
|
+
}
|
|
9348
|
+
/** @internal */
|
|
9349
|
+
_serialize() {
|
|
9350
|
+
if (this.parent.type !== "HasParent") {
|
|
9351
|
+
throw new Error("Cannot serialize LiveText if parent is missing");
|
|
9352
|
+
}
|
|
9353
|
+
return {
|
|
9354
|
+
type: CrdtType.TEXT,
|
|
9355
|
+
parentId: nn(this.parent.node._id, "Parent node expected to have ID"),
|
|
9356
|
+
parentKey: this.parent.key,
|
|
9357
|
+
data: this.toJSON(),
|
|
9358
|
+
version: this.#version
|
|
9359
|
+
};
|
|
9360
|
+
}
|
|
9361
|
+
/** @internal */
|
|
9362
|
+
_attachChild(_op) {
|
|
9363
|
+
throw new Error("LiveText cannot contain child nodes");
|
|
9364
|
+
}
|
|
9365
|
+
/** @internal */
|
|
9366
|
+
_detachChild(_crdt) {
|
|
9367
|
+
throw new Error("LiveText cannot contain child nodes");
|
|
9368
|
+
}
|
|
9369
|
+
/** @internal */
|
|
9370
|
+
_apply(op, isLocal) {
|
|
9371
|
+
if (op.type !== OpCode.UPDATE_TEXT) {
|
|
9372
|
+
return super._apply(op, isLocal);
|
|
9373
|
+
}
|
|
9374
|
+
if (isLocal) {
|
|
9375
|
+
return this.#applyLocal(op);
|
|
9376
|
+
}
|
|
9377
|
+
if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
|
|
9378
|
+
return this.#applyAck(op);
|
|
9379
|
+
}
|
|
9380
|
+
if (op.opId !== void 0 && this.#acceptedOps.some((entry) => entry.opId === op.opId)) {
|
|
9381
|
+
this.#version = Math.max(this.#version, _nullishCoalesce(op.version, () => ( op.baseVersion + 1)));
|
|
9382
|
+
return { modified: false };
|
|
9383
|
+
}
|
|
9384
|
+
return this.#applyRemote(op);
|
|
9385
|
+
}
|
|
9386
|
+
/**
|
|
9387
|
+
* Inserts text at the given index.
|
|
9388
|
+
*
|
|
9389
|
+
* @param index Character index at which to insert. Values outside the
|
|
9390
|
+
* document range are clipped.
|
|
9391
|
+
* @param text Text to insert.
|
|
9392
|
+
* @param attributes Optional inline attributes for the inserted text.
|
|
9393
|
+
*
|
|
9394
|
+
* @example
|
|
9395
|
+
* const text = new LiveText("Hello");
|
|
9396
|
+
* text.insert(5, " world");
|
|
9397
|
+
* text.insert(0, "Say: ", { italic: true });
|
|
9398
|
+
*/
|
|
9399
|
+
insert(index, text, attributes) {
|
|
9400
|
+
const clippedIndex = Math.max(0, Math.min(index, this.length));
|
|
9401
|
+
this.#dispatch([{ type: "insert", index: clippedIndex, text, attributes }]);
|
|
9402
|
+
}
|
|
9403
|
+
/**
|
|
9404
|
+
* Deletes `length` characters starting at `index`.
|
|
9405
|
+
*
|
|
9406
|
+
* @example
|
|
9407
|
+
* const text = new LiveText("Hello world");
|
|
9408
|
+
* text.delete(5, 6); // "Hello"
|
|
9409
|
+
*/
|
|
9410
|
+
delete(index, length) {
|
|
9411
|
+
const clipped = clipRange(index, length, this.length);
|
|
9412
|
+
if (clipped.length === 0) {
|
|
9413
|
+
return;
|
|
9414
|
+
}
|
|
9415
|
+
this.#dispatch([
|
|
9416
|
+
{ type: "delete", index: clipped.index, length: clipped.length }
|
|
9417
|
+
]);
|
|
9418
|
+
}
|
|
9419
|
+
/**
|
|
9420
|
+
* Replaces a range of text with new text.
|
|
9421
|
+
*
|
|
9422
|
+
* @example
|
|
9423
|
+
* const text = new LiveText("Hello world");
|
|
9424
|
+
* text.replace(0, 5, "Hi"); // "Hi world"
|
|
9425
|
+
*/
|
|
9426
|
+
replace(index, length, text, attributes) {
|
|
9427
|
+
const clipped = clipRange(index, length, this.length);
|
|
9428
|
+
const ops = [];
|
|
9429
|
+
if (clipped.length > 0) {
|
|
9430
|
+
ops.push({
|
|
9431
|
+
type: "delete",
|
|
9432
|
+
index: clipped.index,
|
|
9433
|
+
length: clipped.length
|
|
9434
|
+
});
|
|
9435
|
+
}
|
|
9436
|
+
if (text.length > 0) {
|
|
9437
|
+
ops.push({ type: "insert", index: clipped.index, text, attributes });
|
|
9438
|
+
}
|
|
9439
|
+
this.#dispatch(ops);
|
|
9440
|
+
}
|
|
9441
|
+
/**
|
|
9442
|
+
* Encode a local-document index (an offset into this LiveText's current
|
|
9443
|
+
* #segments, which CodeMirror or any consumer mirrors as its document)
|
|
9444
|
+
* into server-confirmed coordinates suitable for broadcasting to peers via
|
|
9445
|
+
* presence or any other side channel.
|
|
9446
|
+
*
|
|
9447
|
+
* The returned index is in this LiveText's current #confirmed coordinates
|
|
9448
|
+
* — that is, with this client's local pending ops inverse-mapped out.
|
|
9449
|
+
* Pair it with the current {@link LiveText.version} when sending so the
|
|
9450
|
+
* receiver can call {@link PrivateLiveTextApi.decodeIndex} to land the
|
|
9451
|
+
* position in their own local document coordinates regardless of their
|
|
9452
|
+
* private pending ops.
|
|
9453
|
+
*
|
|
9454
|
+
* Index ambiguity at boundaries is resolved by an inverse-of-forward
|
|
9455
|
+
* convention: a position at or before a local insertion is reported as
|
|
9456
|
+
* the position right before the insertion in #confirmed; a position past
|
|
9457
|
+
* the insertion shifts left by the insertion's length. Positions inside
|
|
9458
|
+
* an own-pending insertion collapse to the insertion point.
|
|
9459
|
+
*/
|
|
9460
|
+
#encodeIndex(localIndex) {
|
|
9461
|
+
let mapped = Math.max(0, Math.min(localIndex, this.length));
|
|
9462
|
+
mapped = inverseMapTextIndexThroughOperations(mapped, this.#queuedOps);
|
|
9463
|
+
mapped = inverseMapTextIndexThroughOperations(mapped, this.#inFlightOps);
|
|
9464
|
+
return mapped;
|
|
9465
|
+
}
|
|
9466
|
+
/**
|
|
9467
|
+
* Decode an `(index, fromVersion)` pair produced by
|
|
9468
|
+
* {@link PrivateLiveTextApi.encodeIndex} — typically on a peer — into an
|
|
9469
|
+
* offset in this LiveText's current local document (an index suitable for
|
|
9470
|
+
* placing a CodeMirror marker, an annotation anchor, or anything else that
|
|
9471
|
+
* lives over #segments).
|
|
9472
|
+
*
|
|
9473
|
+
* Composes the accepted ops applied since `fromVersion` (drawn from
|
|
9474
|
+
* #acceptedOps in locally-applied form) with this client's own local
|
|
9475
|
+
* pending ops, in that order. The result is in current #segments
|
|
9476
|
+
* coordinates.
|
|
9477
|
+
*
|
|
9478
|
+
* Returns `null` when the position cannot be decoded against the current
|
|
9479
|
+
* state:
|
|
9480
|
+
* - `fromVersion` is greater than this LiveText's current version: the
|
|
9481
|
+
* peer is ahead of us. The caller should park the message and retry
|
|
9482
|
+
* after more accepted ops arrive.
|
|
9483
|
+
* - `fromVersion` falls outside the retained accepted-ops history. This
|
|
9484
|
+
* only happens after very long-lived disconnections; the caller can
|
|
9485
|
+
* fall back to using the raw index and letting subsequent local
|
|
9486
|
+
* transactions map it (with bounded drift).
|
|
9487
|
+
*/
|
|
9488
|
+
#decodeIndex(index, fromVersion) {
|
|
9489
|
+
if (fromVersion > this.#version) {
|
|
9490
|
+
return null;
|
|
9491
|
+
}
|
|
9492
|
+
if (fromVersion < this.#version) {
|
|
9493
|
+
const oldest = _optionalChain([this, 'access', _219 => _219.#acceptedOps, 'access', _220 => _220[0], 'optionalAccess', _221 => _221.version]);
|
|
9494
|
+
if (oldest === void 0 || oldest > fromVersion + 1) {
|
|
9495
|
+
return null;
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
let mapped = index;
|
|
9499
|
+
for (const entry of this.#acceptedOps) {
|
|
9500
|
+
if (entry.version <= fromVersion) continue;
|
|
9501
|
+
if (entry.version > this.#version) break;
|
|
9502
|
+
if (entry.ops.length === 0) continue;
|
|
9503
|
+
mapped = mapTextIndexThroughOperations(mapped, entry.ops);
|
|
9504
|
+
}
|
|
9505
|
+
mapped = mapTextIndexThroughOperations(mapped, this.#inFlightOps);
|
|
9506
|
+
mapped = mapTextIndexThroughOperations(mapped, this.#queuedOps);
|
|
9507
|
+
return Math.max(0, Math.min(mapped, this.length));
|
|
9508
|
+
}
|
|
9509
|
+
/**
|
|
9510
|
+
* Applies or removes inline attributes on a range of text.
|
|
9511
|
+
*
|
|
9512
|
+
* Set an attribute to `null` to remove it from the range.
|
|
9513
|
+
*
|
|
9514
|
+
* @example
|
|
9515
|
+
* const text = new LiveText("Hello world");
|
|
9516
|
+
* text.format(0, 5, { bold: true });
|
|
9517
|
+
* text.format(0, 5, { bold: null });
|
|
9518
|
+
*/
|
|
9519
|
+
format(index, length, attributes) {
|
|
9520
|
+
const clipped = clipRange(index, length, this.length);
|
|
9521
|
+
if (clipped.length === 0) {
|
|
9522
|
+
return;
|
|
9523
|
+
}
|
|
9524
|
+
this.#dispatch([
|
|
9525
|
+
{
|
|
9526
|
+
type: "format",
|
|
9527
|
+
index: clipped.index,
|
|
9528
|
+
length: clipped.length,
|
|
9529
|
+
attributes
|
|
9530
|
+
}
|
|
9531
|
+
]);
|
|
9532
|
+
}
|
|
9533
|
+
/** Local edits made through the public API. */
|
|
9534
|
+
#dispatch(ops) {
|
|
9535
|
+
if (ops.length === 0) {
|
|
9536
|
+
return;
|
|
9537
|
+
}
|
|
9538
|
+
_optionalChain([this, 'access', _222 => _222._pool, 'optionalAccess', _223 => _223.assertStorageIsWritable, 'call', _224 => _224()]);
|
|
9539
|
+
const attached = this._pool !== void 0 && this._id !== void 0;
|
|
9540
|
+
const reverse = attached ? this.#invertOperations(ops) : [];
|
|
9541
|
+
const changes = this.#applyOperationsLocally(ops);
|
|
9542
|
+
if (!attached) {
|
|
9543
|
+
return;
|
|
9544
|
+
}
|
|
9545
|
+
const pool = nn(this._pool);
|
|
9546
|
+
const id = nn(this._id);
|
|
9547
|
+
const updates = /* @__PURE__ */ new Map([
|
|
9548
|
+
[
|
|
9549
|
+
id,
|
|
9550
|
+
{
|
|
9551
|
+
type: "LiveText",
|
|
9552
|
+
node: this,
|
|
9553
|
+
version: this.#version,
|
|
9554
|
+
updates: changes
|
|
9555
|
+
}
|
|
9556
|
+
]
|
|
9557
|
+
]);
|
|
9558
|
+
if (this.#inFlightOpId === void 0) {
|
|
9559
|
+
const opId = pool.generateOpId();
|
|
9560
|
+
this.#inFlightOpId = opId;
|
|
9561
|
+
this.#inFlightOps = [...ops];
|
|
9562
|
+
pool.dispatch(
|
|
9563
|
+
[
|
|
9564
|
+
{
|
|
9565
|
+
type: OpCode.UPDATE_TEXT,
|
|
9566
|
+
id,
|
|
9567
|
+
opId,
|
|
9568
|
+
baseVersion: this.#version,
|
|
9569
|
+
ops: [...ops]
|
|
9570
|
+
}
|
|
9571
|
+
],
|
|
9572
|
+
reverse,
|
|
9573
|
+
updates
|
|
9574
|
+
);
|
|
9575
|
+
} else {
|
|
9576
|
+
this.#queuedOps.push(...ops);
|
|
9577
|
+
pool.dispatch([], reverse, updates, { clearRedoStack: true });
|
|
9578
|
+
}
|
|
9579
|
+
}
|
|
9580
|
+
/**
|
|
9581
|
+
* A local replay of an existing wire op: an undo/redo frame, or an
|
|
9582
|
+
* unacknowledged op re-sent after a reconnect.
|
|
9583
|
+
*/
|
|
9584
|
+
#applyLocal(op) {
|
|
9585
|
+
const mutableOp = op;
|
|
9586
|
+
if (op.opId !== void 0 && op.opId === this.#inFlightOpId) {
|
|
9587
|
+
this.#inFlightOps = [...this.#inFlightOps, ...this.#queuedOps];
|
|
9588
|
+
this.#queuedOps = [];
|
|
9589
|
+
mutableOp.baseVersion = this.#version;
|
|
9590
|
+
mutableOp.ops = [...this.#inFlightOps];
|
|
9591
|
+
return { modified: false };
|
|
9592
|
+
}
|
|
9593
|
+
let ops = op.ops;
|
|
9594
|
+
for (const entry of this.#acceptedOps) {
|
|
9595
|
+
if (entry.version > op.baseVersion && entry.ops.length > 0) {
|
|
9596
|
+
ops = transformTextOperations(ops, entry.ops, "after");
|
|
9597
|
+
}
|
|
9598
|
+
}
|
|
9599
|
+
const reverse = this.#invertOperations(ops);
|
|
9600
|
+
const changes = this.#applyOperationsLocally(ops);
|
|
9601
|
+
if (this.#inFlightOpId === void 0 && ops.length > 0) {
|
|
9602
|
+
this.#inFlightOpId = nn(op.opId, "Local ops must have an opId");
|
|
9603
|
+
this.#inFlightOps = [...ops];
|
|
9604
|
+
mutableOp.baseVersion = this.#version;
|
|
9605
|
+
mutableOp.ops = [...ops];
|
|
9606
|
+
} else {
|
|
9607
|
+
this.#queuedOps.push(...ops);
|
|
9608
|
+
mutableOp.baseVersion = this.#version;
|
|
9609
|
+
mutableOp.ops = [];
|
|
9610
|
+
}
|
|
9611
|
+
if (changes.length === 0) {
|
|
9612
|
+
return { modified: false };
|
|
9613
|
+
}
|
|
9614
|
+
return {
|
|
9615
|
+
reverse,
|
|
9616
|
+
modified: {
|
|
9617
|
+
type: "LiveText",
|
|
9618
|
+
node: this,
|
|
9619
|
+
version: this.#version,
|
|
9620
|
+
updates: changes
|
|
9621
|
+
}
|
|
9622
|
+
};
|
|
9623
|
+
}
|
|
9624
|
+
/** Server acknowledgement of our in-flight op. */
|
|
9625
|
+
#applyAck(op) {
|
|
9626
|
+
const ackedVersion = _nullishCoalesce(op.version, () => ( Math.max(this.#version, op.baseVersion + 1)));
|
|
9627
|
+
const predicted = this.#inFlightOps;
|
|
9628
|
+
const opId = this.#inFlightOpId;
|
|
9629
|
+
this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
|
|
9630
|
+
this.#inFlightOpId = void 0;
|
|
9631
|
+
this.#inFlightOps = [];
|
|
9632
|
+
let appliedOps = [];
|
|
9633
|
+
let result = { modified: false };
|
|
9634
|
+
if (!textOperationsEqual(op.ops, predicted)) {
|
|
9635
|
+
error2(
|
|
9636
|
+
"LiveText: acknowledgement did not match the local prediction; resynchronizing"
|
|
9637
|
+
);
|
|
9638
|
+
const rebuilt = this.#rebuildLocalFromConfirmed();
|
|
9639
|
+
appliedOps = rebuilt.appliedOps;
|
|
9640
|
+
if (rebuilt.changes.length > 0) {
|
|
9641
|
+
result = {
|
|
9642
|
+
reverse: [],
|
|
9643
|
+
modified: {
|
|
9644
|
+
type: "LiveText",
|
|
9645
|
+
node: this,
|
|
9646
|
+
version: ackedVersion,
|
|
9647
|
+
updates: rebuilt.changes
|
|
9648
|
+
}
|
|
9649
|
+
};
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9652
|
+
this.#version = Math.max(this.#version, ackedVersion);
|
|
9653
|
+
this.#recordAccepted(ackedVersion, appliedOps, opId);
|
|
9654
|
+
this.#flushQueued();
|
|
9655
|
+
return result;
|
|
9656
|
+
}
|
|
9657
|
+
/** An accepted op from another client (or a server-fabricated fix op). */
|
|
9658
|
+
#applyRemote(op) {
|
|
9659
|
+
const version = _nullishCoalesce(op.version, () => ( this.#version + 1));
|
|
9660
|
+
this.#confirmed = applyTextOperationsToSegments(this.#confirmed, op.ops);
|
|
9661
|
+
const [overInFlight, inFlight] = transformTextOperationsX(
|
|
9662
|
+
op.ops,
|
|
9663
|
+
this.#inFlightOps,
|
|
9664
|
+
"before"
|
|
9665
|
+
);
|
|
9666
|
+
const [applied, queued] = transformTextOperationsX(
|
|
9667
|
+
overInFlight,
|
|
9668
|
+
this.#queuedOps,
|
|
9669
|
+
"before"
|
|
9670
|
+
);
|
|
9671
|
+
this.#inFlightOps = inFlight;
|
|
9672
|
+
this.#queuedOps = queued;
|
|
9673
|
+
this.#recordAccepted(version, applied, op.opId);
|
|
9674
|
+
if (applied.length === 0) {
|
|
9675
|
+
this.#version = Math.max(this.#version, version);
|
|
9676
|
+
return { modified: false };
|
|
9677
|
+
}
|
|
9678
|
+
const reverse = this.#invertOperations(applied);
|
|
9679
|
+
const changes = this.#applyOperationsLocally(applied);
|
|
9680
|
+
this.#version = Math.max(this.#version, version);
|
|
9681
|
+
return {
|
|
9682
|
+
reverse,
|
|
9683
|
+
modified: {
|
|
9684
|
+
type: "LiveText",
|
|
9685
|
+
node: this,
|
|
9686
|
+
version: this.#version,
|
|
9687
|
+
updates: changes
|
|
9688
|
+
}
|
|
9689
|
+
};
|
|
9690
|
+
}
|
|
9691
|
+
/** Send the queued ops as the next in-flight op (after an ack). */
|
|
9692
|
+
#flushQueued() {
|
|
9693
|
+
if (this.#queuedOps.length === 0 || this._pool === void 0 || this._id === void 0) {
|
|
9694
|
+
return;
|
|
9695
|
+
}
|
|
9696
|
+
const opId = this._pool.generateOpId();
|
|
9697
|
+
this.#inFlightOpId = opId;
|
|
9698
|
+
this.#inFlightOps = this.#queuedOps;
|
|
9699
|
+
this.#queuedOps = [];
|
|
9700
|
+
this._pool.dispatch(
|
|
9701
|
+
[
|
|
9702
|
+
{
|
|
9703
|
+
type: OpCode.UPDATE_TEXT,
|
|
9704
|
+
id: this._id,
|
|
9705
|
+
opId,
|
|
9706
|
+
baseVersion: this.#version,
|
|
9707
|
+
ops: [...this.#inFlightOps]
|
|
9708
|
+
}
|
|
9709
|
+
],
|
|
9710
|
+
[],
|
|
9711
|
+
/* @__PURE__ */ new Map(),
|
|
9712
|
+
// The local content was already applied (and made undoable) when the
|
|
9713
|
+
// edits happened; this is purely an outbound flush.
|
|
9714
|
+
{ clearRedoStack: false }
|
|
9715
|
+
);
|
|
9716
|
+
}
|
|
9717
|
+
/**
|
|
9718
|
+
* Rebuild the local document as confirmed ⊕ queued ops, returning the
|
|
9719
|
+
* coarse delta that was applied. Only used by defensive recovery paths.
|
|
9720
|
+
*/
|
|
9721
|
+
#rebuildLocalFromConfirmed() {
|
|
9722
|
+
const before2 = this.#segments;
|
|
9723
|
+
const after2 = applyTextOperationsToSegments(this.#confirmed, [
|
|
9724
|
+
...this.#inFlightOps,
|
|
9725
|
+
...this.#queuedOps
|
|
9726
|
+
]);
|
|
9727
|
+
if (stableStringify(segmentsToData(before2)) === stableStringify(segmentsToData(after2))) {
|
|
9728
|
+
this.#segments = after2;
|
|
9729
|
+
return { appliedOps: [], changes: [] };
|
|
9730
|
+
}
|
|
9731
|
+
const beforeText = before2.map((segment) => segment.text).join("");
|
|
9732
|
+
this.#segments = after2;
|
|
9733
|
+
this.invalidate();
|
|
9734
|
+
const appliedOps = [];
|
|
9735
|
+
const changes = [];
|
|
9736
|
+
if (beforeText.length > 0) {
|
|
9737
|
+
appliedOps.push({ type: "delete", index: 0, length: beforeText.length });
|
|
9738
|
+
changes.push({
|
|
9739
|
+
type: "delete",
|
|
9740
|
+
index: 0,
|
|
9741
|
+
length: beforeText.length,
|
|
9742
|
+
deletedText: beforeText
|
|
9743
|
+
});
|
|
9744
|
+
}
|
|
9745
|
+
let index = 0;
|
|
9746
|
+
for (const segment of after2) {
|
|
9747
|
+
appliedOps.push({
|
|
9748
|
+
type: "insert",
|
|
9749
|
+
index,
|
|
9750
|
+
text: segment.text,
|
|
9751
|
+
attributes: segment.attributes
|
|
9752
|
+
});
|
|
9753
|
+
changes.push({
|
|
9754
|
+
type: "insert",
|
|
9755
|
+
index,
|
|
9756
|
+
text: segment.text,
|
|
9757
|
+
attributes: segment.attributes
|
|
9758
|
+
});
|
|
9759
|
+
index += segment.text.length;
|
|
9760
|
+
}
|
|
9761
|
+
return { appliedOps, changes };
|
|
9762
|
+
}
|
|
9763
|
+
/**
|
|
9764
|
+
* Reconcile this node against an authoritative storage snapshot (e.g.
|
|
9765
|
+
* after a reconnect). The confirmed state and version are replaced by the
|
|
9766
|
+
* snapshot's; pending (in-flight + queued) ops are preserved on top and
|
|
9767
|
+
* will be re-sent by the offline-ops replay.
|
|
9768
|
+
*
|
|
9769
|
+
* @internal
|
|
9770
|
+
*/
|
|
9771
|
+
_resyncText(data, version) {
|
|
9772
|
+
this.#confirmed = dataToSegments(data);
|
|
9773
|
+
this.#version = version;
|
|
9774
|
+
this.#acceptedOps = [];
|
|
9775
|
+
const rebuilt = this.#rebuildLocalFromConfirmed();
|
|
9776
|
+
if (rebuilt.changes.length === 0) {
|
|
9777
|
+
return void 0;
|
|
9778
|
+
}
|
|
9779
|
+
return {
|
|
9780
|
+
type: "LiveText",
|
|
9781
|
+
node: this,
|
|
9782
|
+
version: this.#version,
|
|
9783
|
+
updates: rebuilt.changes
|
|
9784
|
+
};
|
|
9785
|
+
}
|
|
9786
|
+
/**
|
|
9787
|
+
* Called when the server rejected one of our ops. Drops all pending state
|
|
9788
|
+
* for this node (edits queued behind a rejected op cannot be trusted
|
|
9789
|
+
* either); the room follows up with a storage resync.
|
|
9790
|
+
*
|
|
9791
|
+
* @internal
|
|
9792
|
+
*/
|
|
9793
|
+
_rejectPendingOp(opId) {
|
|
9794
|
+
if (opId !== this.#inFlightOpId) {
|
|
9795
|
+
return;
|
|
9796
|
+
}
|
|
9797
|
+
this.#inFlightOpId = void 0;
|
|
9798
|
+
this.#inFlightOps = [];
|
|
9799
|
+
this.#queuedOps = [];
|
|
9800
|
+
}
|
|
9801
|
+
#recordAccepted(version, ops, opId) {
|
|
9802
|
+
if (this.#acceptedOps.some((entry) => entry.version === version)) {
|
|
9803
|
+
return;
|
|
9804
|
+
}
|
|
9805
|
+
this.#acceptedOps.push({ version, opId, ops: [...ops] });
|
|
9806
|
+
this.#acceptedOps.sort((left, right) => left.version - right.version);
|
|
9807
|
+
if (this.#acceptedOps.length > ACCEPTED_OPS_HISTORY_LIMIT) {
|
|
9808
|
+
this.#acceptedOps.splice(
|
|
9809
|
+
0,
|
|
9810
|
+
this.#acceptedOps.length - ACCEPTED_OPS_HISTORY_LIMIT
|
|
9811
|
+
);
|
|
9812
|
+
}
|
|
9813
|
+
}
|
|
9814
|
+
#applyOperationsLocally(ops) {
|
|
9815
|
+
const changes = [];
|
|
9816
|
+
for (const op of ops) {
|
|
9817
|
+
if (op.type === "insert") {
|
|
9818
|
+
this.#segments = applyInsert(
|
|
9819
|
+
this.#segments,
|
|
9820
|
+
op.index,
|
|
9821
|
+
op.text,
|
|
9822
|
+
op.attributes
|
|
9823
|
+
);
|
|
9824
|
+
changes.push({
|
|
9825
|
+
type: "insert",
|
|
9826
|
+
index: op.index,
|
|
9827
|
+
text: op.text,
|
|
9828
|
+
attributes: op.attributes
|
|
9829
|
+
});
|
|
9830
|
+
} else if (op.type === "delete") {
|
|
9831
|
+
const result = applyDelete(this.#segments, op.index, op.length);
|
|
9832
|
+
this.#segments = result.segments;
|
|
9833
|
+
changes.push({
|
|
9834
|
+
type: "delete",
|
|
9835
|
+
index: op.index,
|
|
9836
|
+
length: op.length,
|
|
9837
|
+
deletedText: result.deletedText
|
|
9838
|
+
});
|
|
9839
|
+
} else {
|
|
9840
|
+
this.#segments = applyFormat(
|
|
9841
|
+
this.#segments,
|
|
9842
|
+
op.index,
|
|
9843
|
+
op.length,
|
|
9844
|
+
op.attributes
|
|
9845
|
+
);
|
|
9846
|
+
changes.push({
|
|
9847
|
+
type: "format",
|
|
9848
|
+
index: op.index,
|
|
9849
|
+
length: op.length,
|
|
9850
|
+
attributes: op.attributes
|
|
9851
|
+
});
|
|
9852
|
+
}
|
|
9853
|
+
}
|
|
9854
|
+
this.invalidate();
|
|
9855
|
+
return changes;
|
|
9856
|
+
}
|
|
9857
|
+
#invertOperations(ops) {
|
|
9858
|
+
return [
|
|
9859
|
+
{
|
|
9860
|
+
type: OpCode.UPDATE_TEXT,
|
|
9861
|
+
id: nn(this._id),
|
|
9862
|
+
baseVersion: this.#version,
|
|
9863
|
+
ops: invertTextOperations(this.#segments, ops)
|
|
9864
|
+
}
|
|
9865
|
+
];
|
|
9866
|
+
}
|
|
9867
|
+
/** Returns the plain text content without attributes. Equivalent to joining the text from each segment in {@link LiveText.toJSON}. */
|
|
9868
|
+
toString() {
|
|
9869
|
+
return this.#segments.map((segment) => segment.text).join("");
|
|
9870
|
+
}
|
|
9871
|
+
/**
|
|
9872
|
+
* Returns a JSON-compatible snapshot of the document as a {@link LiveTextData}
|
|
9873
|
+
* array.
|
|
9874
|
+
*
|
|
9875
|
+
* @example
|
|
9876
|
+
* new LiveText([["Hello ", { bold: true }], ["world"]]).toJSON();
|
|
9877
|
+
* // [["Hello ", { bold: true }], ["world"]]
|
|
9878
|
+
*/
|
|
9879
|
+
toJSON() {
|
|
9880
|
+
return super.toJSON();
|
|
9881
|
+
}
|
|
9882
|
+
/** @internal */
|
|
9883
|
+
_toJSON() {
|
|
9884
|
+
return segmentsToData(this.#segments);
|
|
9885
|
+
}
|
|
9886
|
+
/** @internal */
|
|
9887
|
+
toTreeNode(key) {
|
|
9888
|
+
return super.toTreeNode(key);
|
|
9889
|
+
}
|
|
9890
|
+
/** @internal */
|
|
9891
|
+
_toTreeNode(key) {
|
|
9892
|
+
const nodeId = _nullishCoalesce(this._id, () => ( nanoid()));
|
|
9893
|
+
const payload = this.toJSON().map(
|
|
9894
|
+
(segment, index) => ({
|
|
9895
|
+
type: "Json",
|
|
9896
|
+
id: `${nodeId}:${index}`,
|
|
9897
|
+
key: String(index),
|
|
9898
|
+
payload: segment
|
|
9899
|
+
})
|
|
9900
|
+
);
|
|
9901
|
+
payload.push({
|
|
9902
|
+
type: "Json",
|
|
9903
|
+
id: `${nodeId}:version`,
|
|
9904
|
+
key: "version",
|
|
9905
|
+
payload: this.version
|
|
9906
|
+
});
|
|
9907
|
+
return {
|
|
9908
|
+
type: "LiveText",
|
|
9909
|
+
id: nodeId,
|
|
9910
|
+
key,
|
|
9911
|
+
payload
|
|
9912
|
+
};
|
|
9913
|
+
}
|
|
9914
|
+
clone() {
|
|
9915
|
+
return new _LiveText(this.toJSON(), this.#version);
|
|
9916
|
+
}
|
|
9917
|
+
};
|
|
9918
|
+
|
|
8779
9919
|
// src/crdts/liveblocks-helpers.ts
|
|
8780
9920
|
function creationOpToLiveNode(op) {
|
|
8781
9921
|
return lsonToLiveNode(creationOpToLson(op));
|
|
@@ -8790,6 +9930,8 @@ function creationOpToLson(op) {
|
|
|
8790
9930
|
return new LiveMap();
|
|
8791
9931
|
case OpCode.CREATE_LIST:
|
|
8792
9932
|
return new LiveList([]);
|
|
9933
|
+
case OpCode.CREATE_TEXT:
|
|
9934
|
+
return new LiveText(op.data, op.version);
|
|
8793
9935
|
default:
|
|
8794
9936
|
return assertNever(op, "Unknown creation Op");
|
|
8795
9937
|
}
|
|
@@ -8822,6 +9964,8 @@ function deserialize(node, parentToChildren, pool) {
|
|
|
8822
9964
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8823
9965
|
} else if (isRegisterStorageNode(node)) {
|
|
8824
9966
|
return LiveRegister._deserialize(node, parentToChildren, pool);
|
|
9967
|
+
} else if (isTextStorageNode(node)) {
|
|
9968
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8825
9969
|
} else {
|
|
8826
9970
|
throw new Error("Unexpected CRDT type");
|
|
8827
9971
|
}
|
|
@@ -8835,12 +9979,14 @@ function deserializeToLson(node, parentToChildren, pool) {
|
|
|
8835
9979
|
return LiveMap._deserialize(node, parentToChildren, pool);
|
|
8836
9980
|
} else if (isRegisterStorageNode(node)) {
|
|
8837
9981
|
return node[1].data;
|
|
9982
|
+
} else if (isTextStorageNode(node)) {
|
|
9983
|
+
return LiveText._deserialize(node, parentToChildren, pool);
|
|
8838
9984
|
} else {
|
|
8839
9985
|
throw new Error("Unexpected CRDT type");
|
|
8840
9986
|
}
|
|
8841
9987
|
}
|
|
8842
9988
|
function isLiveStructure(value) {
|
|
8843
|
-
return isLiveList(value) || isLiveMap(value) || isLiveObject(value);
|
|
9989
|
+
return isLiveList(value) || isLiveMap(value) || isLiveObject(value) || isLiveText(value);
|
|
8844
9990
|
}
|
|
8845
9991
|
function isLiveNode(value) {
|
|
8846
9992
|
return isLiveStructure(value) || isLiveRegister(value);
|
|
@@ -8854,6 +10000,9 @@ function isLiveMap(value) {
|
|
|
8854
10000
|
function isLiveObject(value) {
|
|
8855
10001
|
return value instanceof LiveObject;
|
|
8856
10002
|
}
|
|
10003
|
+
function isLiveText(value) {
|
|
10004
|
+
return value instanceof LiveText;
|
|
10005
|
+
}
|
|
8857
10006
|
function isLiveRegister(value) {
|
|
8858
10007
|
return value instanceof LiveRegister;
|
|
8859
10008
|
}
|
|
@@ -8863,14 +10012,14 @@ function cloneLson(value) {
|
|
|
8863
10012
|
function liveNodeToLson(obj) {
|
|
8864
10013
|
if (obj instanceof LiveRegister) {
|
|
8865
10014
|
return obj.data;
|
|
8866
|
-
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject) {
|
|
10015
|
+
} else if (obj instanceof LiveList || obj instanceof LiveMap || obj instanceof LiveObject || obj instanceof LiveText) {
|
|
8867
10016
|
return obj;
|
|
8868
10017
|
} else {
|
|
8869
10018
|
return assertNever(obj, "Unknown AbstractCrdt");
|
|
8870
10019
|
}
|
|
8871
10020
|
}
|
|
8872
10021
|
function lsonToLiveNode(value) {
|
|
8873
|
-
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList) {
|
|
10022
|
+
if (value instanceof LiveObject || value instanceof LiveMap || value instanceof LiveList || value instanceof LiveText) {
|
|
8874
10023
|
return value;
|
|
8875
10024
|
} else {
|
|
8876
10025
|
return new LiveRegister(value);
|
|
@@ -8990,6 +10139,16 @@ function diffNodeMap(prev, next) {
|
|
|
8990
10139
|
parentKey: crdt.parentKey
|
|
8991
10140
|
});
|
|
8992
10141
|
break;
|
|
10142
|
+
case CrdtType.TEXT:
|
|
10143
|
+
ops.push({
|
|
10144
|
+
type: OpCode.CREATE_TEXT,
|
|
10145
|
+
id,
|
|
10146
|
+
parentId: crdt.parentId,
|
|
10147
|
+
parentKey: crdt.parentKey,
|
|
10148
|
+
data: crdt.data,
|
|
10149
|
+
version: crdt.version
|
|
10150
|
+
});
|
|
10151
|
+
break;
|
|
8993
10152
|
}
|
|
8994
10153
|
}
|
|
8995
10154
|
next.forEach((crdt, id) => {
|
|
@@ -9060,19 +10219,43 @@ function mergeListStorageUpdates(first, second) {
|
|
|
9060
10219
|
updates: updates.concat(second.updates)
|
|
9061
10220
|
};
|
|
9062
10221
|
}
|
|
10222
|
+
function mergeTextStorageUpdates(first, second) {
|
|
10223
|
+
return {
|
|
10224
|
+
...second,
|
|
10225
|
+
updates: first.updates.concat(second.updates)
|
|
10226
|
+
};
|
|
10227
|
+
}
|
|
9063
10228
|
function mergeStorageUpdates(first, second) {
|
|
9064
10229
|
if (first === void 0) {
|
|
9065
10230
|
return second;
|
|
9066
10231
|
}
|
|
10232
|
+
let merged;
|
|
9067
10233
|
if (first.type === "LiveObject" && second.type === "LiveObject") {
|
|
9068
|
-
|
|
10234
|
+
merged = mergeObjectStorageUpdates(first, second);
|
|
9069
10235
|
} else if (first.type === "LiveMap" && second.type === "LiveMap") {
|
|
9070
|
-
|
|
10236
|
+
merged = mergeMapStorageUpdates(first, second);
|
|
9071
10237
|
} else if (first.type === "LiveList" && second.type === "LiveList") {
|
|
9072
|
-
|
|
10238
|
+
merged = mergeListStorageUpdates(first, second);
|
|
10239
|
+
} else if (first.type === "LiveText" && second.type === "LiveText") {
|
|
10240
|
+
merged = mergeTextStorageUpdates(first, second);
|
|
9073
10241
|
} else {
|
|
10242
|
+
merged = second;
|
|
10243
|
+
}
|
|
10244
|
+
const sa = first[kStorageUpdateSource];
|
|
10245
|
+
const sb = second[kStorageUpdateSource];
|
|
10246
|
+
if (sa !== void 0 || sb !== void 0) {
|
|
10247
|
+
if (_optionalChain([sa, 'optionalAccess', _225 => _225.origin]) === "remote" || _optionalChain([sb, 'optionalAccess', _226 => _226.origin]) === "remote") {
|
|
10248
|
+
merged[kStorageUpdateSource] = { origin: "remote" };
|
|
10249
|
+
} else if (_optionalChain([sa, 'optionalAccess', _227 => _227.via]) === "history" || _optionalChain([sb, 'optionalAccess', _228 => _228.via]) === "history") {
|
|
10250
|
+
const historySource = _optionalChain([sb, 'optionalAccess', _229 => _229.via]) === "history" ? sb : _optionalChain([sa, 'optionalAccess', _230 => _230.via]) === "history" ? sa : void 0;
|
|
10251
|
+
if (_optionalChain([historySource, 'optionalAccess', _231 => _231.via]) === "history") {
|
|
10252
|
+
merged[kStorageUpdateSource] = historySource;
|
|
10253
|
+
}
|
|
10254
|
+
} else {
|
|
10255
|
+
merged[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
10256
|
+
}
|
|
9074
10257
|
}
|
|
9075
|
-
return
|
|
10258
|
+
return merged;
|
|
9076
10259
|
}
|
|
9077
10260
|
|
|
9078
10261
|
// src/devtools/bridge.ts
|
|
@@ -9088,7 +10271,7 @@ function sendToPanel(message, options) {
|
|
|
9088
10271
|
...message,
|
|
9089
10272
|
source: "liveblocks-devtools-client"
|
|
9090
10273
|
};
|
|
9091
|
-
if (!(_optionalChain([options, 'optionalAccess',
|
|
10274
|
+
if (!(_optionalChain([options, 'optionalAccess', _232 => _232.force]) || _bridgeActive)) {
|
|
9092
10275
|
return;
|
|
9093
10276
|
}
|
|
9094
10277
|
window.postMessage(fullMsg, "*");
|
|
@@ -9096,7 +10279,7 @@ function sendToPanel(message, options) {
|
|
|
9096
10279
|
var eventSource = makeEventSource();
|
|
9097
10280
|
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
9098
10281
|
window.addEventListener("message", (event) => {
|
|
9099
|
-
if (event.source === window && _optionalChain([event, 'access',
|
|
10282
|
+
if (event.source === window && _optionalChain([event, 'access', _233 => _233.data, 'optionalAccess', _234 => _234.source]) === "liveblocks-devtools-panel") {
|
|
9100
10283
|
eventSource.notify(event.data);
|
|
9101
10284
|
} else {
|
|
9102
10285
|
}
|
|
@@ -9238,7 +10421,7 @@ function fullSync(room) {
|
|
|
9238
10421
|
msg: "room::sync::full",
|
|
9239
10422
|
roomId: room.id,
|
|
9240
10423
|
status: room.getStatus(),
|
|
9241
|
-
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess',
|
|
10424
|
+
storage: _nullishCoalesce(_optionalChain([root, 'optionalAccess', _235 => _235.toTreeNode, 'call', _236 => _236("root"), 'access', _237 => _237.payload]), () => ( null)),
|
|
9242
10425
|
me,
|
|
9243
10426
|
others
|
|
9244
10427
|
});
|
|
@@ -9925,15 +11108,15 @@ function installBackgroundTabSpy() {
|
|
|
9925
11108
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
9926
11109
|
const inBackgroundSince = { current: null };
|
|
9927
11110
|
function onVisibilityChange() {
|
|
9928
|
-
if (_optionalChain([doc, 'optionalAccess',
|
|
11111
|
+
if (_optionalChain([doc, 'optionalAccess', _238 => _238.visibilityState]) === "hidden") {
|
|
9929
11112
|
inBackgroundSince.current = _nullishCoalesce(inBackgroundSince.current, () => ( Date.now()));
|
|
9930
11113
|
} else {
|
|
9931
11114
|
inBackgroundSince.current = null;
|
|
9932
11115
|
}
|
|
9933
11116
|
}
|
|
9934
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11117
|
+
_optionalChain([doc, 'optionalAccess', _239 => _239.addEventListener, 'call', _240 => _240("visibilitychange", onVisibilityChange)]);
|
|
9935
11118
|
const unsub = () => {
|
|
9936
|
-
_optionalChain([doc, 'optionalAccess',
|
|
11119
|
+
_optionalChain([doc, 'optionalAccess', _241 => _241.removeEventListener, 'call', _242 => _242("visibilitychange", onVisibilityChange)]);
|
|
9937
11120
|
};
|
|
9938
11121
|
return [inBackgroundSince, unsub];
|
|
9939
11122
|
}
|
|
@@ -9957,7 +11140,7 @@ function makeNodeMapBuffer() {
|
|
|
9957
11140
|
function topLevelKeysOf(nodes) {
|
|
9958
11141
|
const keys2 = /* @__PURE__ */ new Set();
|
|
9959
11142
|
const root = nodes.get("root");
|
|
9960
|
-
for (const key in _optionalChain([root, 'optionalAccess',
|
|
11143
|
+
for (const key in _optionalChain([root, 'optionalAccess', _243 => _243.data])) {
|
|
9961
11144
|
keys2.add(key);
|
|
9962
11145
|
}
|
|
9963
11146
|
for (const node of nodes.values()) {
|
|
@@ -10029,6 +11212,8 @@ function createRoom(options, config) {
|
|
|
10029
11212
|
activeBatch: null,
|
|
10030
11213
|
unacknowledgedOps
|
|
10031
11214
|
};
|
|
11215
|
+
let nextHistoryItemId = 0;
|
|
11216
|
+
let historyDisabled = 0;
|
|
10032
11217
|
const nodeMapBuffer = makeNodeMapBuffer();
|
|
10033
11218
|
const stopwatch = config.enableDebugLogging ? makeStopWatch() : void 0;
|
|
10034
11219
|
let lastTokenKey;
|
|
@@ -10113,7 +11298,10 @@ function createRoom(options, config) {
|
|
|
10113
11298
|
}
|
|
10114
11299
|
}
|
|
10115
11300
|
});
|
|
10116
|
-
function onDispatch(ops, reverse, storageUpdates) {
|
|
11301
|
+
function onDispatch(ops, reverse, storageUpdates, options2) {
|
|
11302
|
+
for (const value of storageUpdates.values()) {
|
|
11303
|
+
value[kStorageUpdateSource] = { origin: "local", via: "mutation" };
|
|
11304
|
+
}
|
|
10117
11305
|
if (context.activeBatch) {
|
|
10118
11306
|
for (const op of ops) {
|
|
10119
11307
|
context.activeBatch.ops.push(op);
|
|
@@ -10128,19 +11316,24 @@ function createRoom(options, config) {
|
|
|
10128
11316
|
);
|
|
10129
11317
|
}
|
|
10130
11318
|
context.activeBatch.reverseOps.pushLeft(reverse);
|
|
11319
|
+
if (_optionalChain([options2, 'optionalAccess', _244 => _244.clearRedoStack])) {
|
|
11320
|
+
context.activeBatch.clearRedoStack = true;
|
|
11321
|
+
}
|
|
10131
11322
|
} else {
|
|
10132
11323
|
if (reverse.length > 0) {
|
|
10133
11324
|
addToUndoStack(reverse);
|
|
10134
11325
|
}
|
|
11326
|
+
if (_nullishCoalesce(_optionalChain([options2, 'optionalAccess', _245 => _245.clearRedoStack]), () => ( ops.length > 0))) {
|
|
11327
|
+
clearRedoStack();
|
|
11328
|
+
}
|
|
10135
11329
|
if (ops.length > 0) {
|
|
10136
|
-
context.redoStack.length = 0;
|
|
10137
11330
|
dispatchOps(ops);
|
|
10138
11331
|
}
|
|
10139
11332
|
notify({ storageUpdates });
|
|
10140
11333
|
}
|
|
10141
11334
|
}
|
|
10142
11335
|
function isStorageWritable() {
|
|
10143
|
-
const permissionMatrix = _optionalChain([context, 'access',
|
|
11336
|
+
const permissionMatrix = _optionalChain([context, 'access', _246 => _246.dynamicSessionInfoSig, 'access', _247 => _247.get, 'call', _248 => _248(), 'optionalAccess', _249 => _249.permissionMatrix]);
|
|
10144
11337
|
return permissionMatrix !== void 0 ? hasPermissionAccess(permissionMatrix, "storage", "write") : true;
|
|
10145
11338
|
}
|
|
10146
11339
|
const eventHub = {
|
|
@@ -10153,6 +11346,7 @@ function createRoom(options, config) {
|
|
|
10153
11346
|
others: makeEventSource(),
|
|
10154
11347
|
storageBatch: makeEventSource(),
|
|
10155
11348
|
history: makeEventSource(),
|
|
11349
|
+
privateHistory: makeEventSource(),
|
|
10156
11350
|
storageDidLoad: makeEventSource(),
|
|
10157
11351
|
storageStatus: makeEventSource(),
|
|
10158
11352
|
ydoc: makeEventSource(),
|
|
@@ -10253,6 +11447,23 @@ function createRoom(options, config) {
|
|
|
10253
11447
|
}
|
|
10254
11448
|
if (context.root !== void 0) {
|
|
10255
11449
|
const result = applyRemoteOps(diffCurrentStorageAgainst(nodes));
|
|
11450
|
+
for (const [id, crdt] of nodes) {
|
|
11451
|
+
if (crdt.type === CrdtType.TEXT) {
|
|
11452
|
+
const node = context.pool.nodes.get(id);
|
|
11453
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11454
|
+
const update = node._resyncText(crdt.data, crdt.version);
|
|
11455
|
+
if (update !== void 0) {
|
|
11456
|
+
result.updates.storageUpdates.set(
|
|
11457
|
+
id,
|
|
11458
|
+
mergeStorageUpdates(
|
|
11459
|
+
result.updates.storageUpdates.get(id),
|
|
11460
|
+
update
|
|
11461
|
+
)
|
|
11462
|
+
);
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11465
|
+
}
|
|
11466
|
+
}
|
|
10256
11467
|
notify(result.updates);
|
|
10257
11468
|
} else {
|
|
10258
11469
|
context.root = LiveObject._fromItems(
|
|
@@ -10260,7 +11471,7 @@ function createRoom(options, config) {
|
|
|
10260
11471
|
context.pool
|
|
10261
11472
|
);
|
|
10262
11473
|
}
|
|
10263
|
-
const canWrite = _nullishCoalesce(_optionalChain([self, 'access',
|
|
11474
|
+
const canWrite = _nullishCoalesce(_optionalChain([self, 'access', _250 => _250.get, 'call', _251 => _251(), 'optionalAccess', _252 => _252.canWrite]), () => ( true));
|
|
10264
11475
|
const serverTopLevelKeys = topLevelKeysOf(nodes);
|
|
10265
11476
|
const root = context.root;
|
|
10266
11477
|
disableHistory(() => {
|
|
@@ -10277,6 +11488,16 @@ function createRoom(options, config) {
|
|
|
10277
11488
|
}
|
|
10278
11489
|
});
|
|
10279
11490
|
}
|
|
11491
|
+
function notifyPrivateHistory(event) {
|
|
11492
|
+
if (historyDisabled > 0) return;
|
|
11493
|
+
eventHub.privateHistory.notify(event);
|
|
11494
|
+
}
|
|
11495
|
+
function clearRedoStack() {
|
|
11496
|
+
if (context.redoStack.length === 0) return;
|
|
11497
|
+
const ids = context.redoStack.map((item) => item.id);
|
|
11498
|
+
context.redoStack.length = 0;
|
|
11499
|
+
notifyPrivateHistory({ action: "discard", ids });
|
|
11500
|
+
}
|
|
10280
11501
|
function reconcileStorageWithNodes(nodes) {
|
|
10281
11502
|
if (context.root === void 0) {
|
|
10282
11503
|
throw new Error("Cannot reconcile storage before it is loaded");
|
|
@@ -10301,9 +11522,14 @@ function createRoom(options, config) {
|
|
|
10301
11522
|
}
|
|
10302
11523
|
function _addToRealUndoStack(frames) {
|
|
10303
11524
|
if (context.undoStack.length >= 50) {
|
|
10304
|
-
context.undoStack.shift();
|
|
11525
|
+
const evicted = context.undoStack.shift();
|
|
11526
|
+
if (evicted !== void 0) {
|
|
11527
|
+
notifyPrivateHistory({ action: "discard", ids: [evicted.id] });
|
|
11528
|
+
}
|
|
10305
11529
|
}
|
|
10306
|
-
|
|
11530
|
+
const id = nextHistoryItemId++;
|
|
11531
|
+
context.undoStack.push({ id, frames });
|
|
11532
|
+
notifyPrivateHistory({ action: "push", id });
|
|
10307
11533
|
onHistoryChange();
|
|
10308
11534
|
}
|
|
10309
11535
|
function addToUndoStack(frames) {
|
|
@@ -10341,7 +11567,7 @@ function createRoom(options, config) {
|
|
|
10341
11567
|
"Internal. Tried to get connection id but connection was never open"
|
|
10342
11568
|
);
|
|
10343
11569
|
}
|
|
10344
|
-
function applyLocalOps(frames) {
|
|
11570
|
+
function applyLocalOps(frames, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
10345
11571
|
const [pframes, ops] = partition(
|
|
10346
11572
|
frames,
|
|
10347
11573
|
(f) => f.type === "presence"
|
|
@@ -10353,7 +11579,8 @@ function createRoom(options, config) {
|
|
|
10353
11579
|
pframes,
|
|
10354
11580
|
opsWithOpIds,
|
|
10355
11581
|
/* isLocal */
|
|
10356
|
-
true
|
|
11582
|
+
true,
|
|
11583
|
+
localStorageUpdateSource
|
|
10357
11584
|
);
|
|
10358
11585
|
return { opsToEmit: opsWithOpIds, reverse, updates };
|
|
10359
11586
|
}
|
|
@@ -10365,7 +11592,7 @@ function createRoom(options, config) {
|
|
|
10365
11592
|
false
|
|
10366
11593
|
);
|
|
10367
11594
|
}
|
|
10368
|
-
function applyOps(pframes, ops, isLocal) {
|
|
11595
|
+
function applyOps(pframes, ops, isLocal, localStorageUpdateSource = { origin: "local", via: "mutation" }) {
|
|
10369
11596
|
const output = {
|
|
10370
11597
|
reverse: new Deque(),
|
|
10371
11598
|
storageUpdates: /* @__PURE__ */ new Map(),
|
|
@@ -10403,6 +11630,7 @@ function createRoom(options, config) {
|
|
|
10403
11630
|
}
|
|
10404
11631
|
const applyOpResult = applyOp(op, source);
|
|
10405
11632
|
if (applyOpResult.modified) {
|
|
11633
|
+
applyOpResult.modified[kStorageUpdateSource] = source === 1 /* THEIRS */ ? { origin: "remote" } : localStorageUpdateSource;
|
|
10406
11634
|
const nodeId = applyOpResult.modified.node._id;
|
|
10407
11635
|
if (!(nodeId && createdNodeIds.has(nodeId))) {
|
|
10408
11636
|
output.storageUpdates.set(
|
|
@@ -10414,7 +11642,7 @@ function createRoom(options, config) {
|
|
|
10414
11642
|
);
|
|
10415
11643
|
output.reverse.pushLeft(applyOpResult.reverse);
|
|
10416
11644
|
}
|
|
10417
|
-
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT) {
|
|
11645
|
+
if (op.type === OpCode.CREATE_LIST || op.type === OpCode.CREATE_MAP || op.type === OpCode.CREATE_OBJECT || op.type === OpCode.CREATE_TEXT) {
|
|
10418
11646
|
createdNodeIds.add(op.id);
|
|
10419
11647
|
}
|
|
10420
11648
|
}
|
|
@@ -10434,6 +11662,7 @@ function createRoom(options, config) {
|
|
|
10434
11662
|
switch (op.type) {
|
|
10435
11663
|
case OpCode.DELETE_OBJECT_KEY:
|
|
10436
11664
|
case OpCode.UPDATE_OBJECT:
|
|
11665
|
+
case OpCode.UPDATE_TEXT:
|
|
10437
11666
|
case OpCode.DELETE_CRDT: {
|
|
10438
11667
|
const node = context.pool.nodes.get(op.id);
|
|
10439
11668
|
if (node === void 0) {
|
|
@@ -10458,6 +11687,7 @@ function createRoom(options, config) {
|
|
|
10458
11687
|
case OpCode.CREATE_OBJECT:
|
|
10459
11688
|
case OpCode.CREATE_LIST:
|
|
10460
11689
|
case OpCode.CREATE_MAP:
|
|
11690
|
+
case OpCode.CREATE_TEXT:
|
|
10461
11691
|
case OpCode.CREATE_REGISTER: {
|
|
10462
11692
|
if (op.parentId === void 0) {
|
|
10463
11693
|
return { modified: false };
|
|
@@ -10492,7 +11722,7 @@ function createRoom(options, config) {
|
|
|
10492
11722
|
}
|
|
10493
11723
|
context.myPresence.patch(patch);
|
|
10494
11724
|
if (context.activeBatch) {
|
|
10495
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11725
|
+
if (_optionalChain([options2, 'optionalAccess', _253 => _253.addToHistory])) {
|
|
10496
11726
|
context.activeBatch.reverseOps.pushLeft({
|
|
10497
11727
|
type: "presence",
|
|
10498
11728
|
data: oldValues
|
|
@@ -10501,7 +11731,7 @@ function createRoom(options, config) {
|
|
|
10501
11731
|
context.activeBatch.updates.presence = true;
|
|
10502
11732
|
} else {
|
|
10503
11733
|
flushNowOrSoon();
|
|
10504
|
-
if (_optionalChain([options2, 'optionalAccess',
|
|
11734
|
+
if (_optionalChain([options2, 'optionalAccess', _254 => _254.addToHistory])) {
|
|
10505
11735
|
addToUndoStack([{ type: "presence", data: oldValues }]);
|
|
10506
11736
|
}
|
|
10507
11737
|
notify({ presence: true });
|
|
@@ -10680,11 +11910,11 @@ function createRoom(options, config) {
|
|
|
10680
11910
|
break;
|
|
10681
11911
|
}
|
|
10682
11912
|
case ServerMsgCode.STORAGE_CHUNK:
|
|
10683
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
11913
|
+
_optionalChain([stopwatch, 'optionalAccess', _255 => _255.lap, 'call', _256 => _256()]);
|
|
10684
11914
|
nodeMapBuffer.append(compactNodesToNodeStream(message.nodes));
|
|
10685
11915
|
break;
|
|
10686
11916
|
case ServerMsgCode.STORAGE_STREAM_END: {
|
|
10687
|
-
const timing = _optionalChain([stopwatch, 'optionalAccess',
|
|
11917
|
+
const timing = _optionalChain([stopwatch, 'optionalAccess', _257 => _257.stop, 'call', _258 => _258()]);
|
|
10688
11918
|
if (timing) {
|
|
10689
11919
|
const ms = (v) => `${v.toFixed(1)}ms`;
|
|
10690
11920
|
const rest = timing.laps.slice(1);
|
|
@@ -10711,16 +11941,37 @@ function createRoom(options, config) {
|
|
|
10711
11941
|
}
|
|
10712
11942
|
break;
|
|
10713
11943
|
}
|
|
10714
|
-
// Receiving a RejectedOps message
|
|
10715
|
-
//
|
|
10716
|
-
//
|
|
10717
|
-
//
|
|
11944
|
+
// Receiving a RejectedOps message means the server refused some of
|
|
11945
|
+
// our ops, so our optimistic local state is out of sync with the
|
|
11946
|
+
// server. For LiveText ops this is a normal (if rare) situation —
|
|
11947
|
+
// e.g. a client that was offline long enough to fall outside the
|
|
11948
|
+
// server's retained history window — and we can recover: drop the
|
|
11949
|
+
// rejected pending state and re-fetch the authoritative storage
|
|
11950
|
+
// snapshot. For other ops (e.g. permission rejections), rolling back
|
|
11951
|
+
// particular Ops is hard/impossible, so we keep the old behavior of
|
|
11952
|
+
// accepting the out-of-sync reality and surfacing an error.
|
|
10718
11953
|
case ServerMsgCode.REJECT_STORAGE_OP: {
|
|
10719
11954
|
errorWithTitle(
|
|
10720
11955
|
"Storage mutation rejection error",
|
|
10721
11956
|
message.reason
|
|
10722
11957
|
);
|
|
10723
|
-
|
|
11958
|
+
let needsStorageResync = false;
|
|
11959
|
+
for (const opId of message.opIds) {
|
|
11960
|
+
const rejectedOp = context.unacknowledgedOps.get(opId);
|
|
11961
|
+
context.unacknowledgedOps.delete(opId);
|
|
11962
|
+
context.buffer.storageOperations = context.buffer.storageOperations.filter((op) => op.opId !== opId);
|
|
11963
|
+
if (rejectedOp !== void 0 && rejectedOp.type === OpCode.UPDATE_TEXT) {
|
|
11964
|
+
const node = context.pool.nodes.get(rejectedOp.id);
|
|
11965
|
+
if (node !== void 0 && isLiveText(node)) {
|
|
11966
|
+
node._rejectPendingOp(opId);
|
|
11967
|
+
needsStorageResync = true;
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11970
|
+
}
|
|
11971
|
+
if (needsStorageResync) {
|
|
11972
|
+
refreshStorage();
|
|
11973
|
+
flushNowOrSoon();
|
|
11974
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
10724
11975
|
throw new Error(
|
|
10725
11976
|
`Storage mutations rejected by server: ${message.reason}`
|
|
10726
11977
|
);
|
|
@@ -10819,11 +12070,11 @@ function createRoom(options, config) {
|
|
|
10819
12070
|
} else if (pendingFeedsRequests.has(requestId)) {
|
|
10820
12071
|
const pending = pendingFeedsRequests.get(requestId);
|
|
10821
12072
|
pendingFeedsRequests.delete(requestId);
|
|
10822
|
-
_optionalChain([pending, 'optionalAccess',
|
|
12073
|
+
_optionalChain([pending, 'optionalAccess', _259 => _259.reject, 'call', _260 => _260(err)]);
|
|
10823
12074
|
} else if (pendingFeedMessagesRequests.has(requestId)) {
|
|
10824
12075
|
const pending = pendingFeedMessagesRequests.get(requestId);
|
|
10825
12076
|
pendingFeedMessagesRequests.delete(requestId);
|
|
10826
|
-
_optionalChain([pending, 'optionalAccess',
|
|
12077
|
+
_optionalChain([pending, 'optionalAccess', _261 => _261.reject, 'call', _262 => _262(err)]);
|
|
10827
12078
|
}
|
|
10828
12079
|
eventHub.feeds.notify(message);
|
|
10829
12080
|
break;
|
|
@@ -10977,10 +12228,10 @@ function createRoom(options, config) {
|
|
|
10977
12228
|
timeoutId,
|
|
10978
12229
|
kind,
|
|
10979
12230
|
feedId,
|
|
10980
|
-
messageId: _optionalChain([options2, 'optionalAccess',
|
|
10981
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12231
|
+
messageId: _optionalChain([options2, 'optionalAccess', _263 => _263.messageId]),
|
|
12232
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _264 => _264.expectedClientMessageId])
|
|
10982
12233
|
});
|
|
10983
|
-
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess',
|
|
12234
|
+
if (kind === "add-message" && _optionalChain([options2, 'optionalAccess', _265 => _265.expectedClientMessageId]) === void 0) {
|
|
10984
12235
|
const q = _nullishCoalesce(pendingAddMessageFifoByFeed.get(feedId), () => ( []));
|
|
10985
12236
|
q.push(requestId);
|
|
10986
12237
|
pendingAddMessageFifoByFeed.set(feedId, q);
|
|
@@ -11031,10 +12282,10 @@ function createRoom(options, config) {
|
|
|
11031
12282
|
}
|
|
11032
12283
|
if (!matched) {
|
|
11033
12284
|
const q = pendingAddMessageFifoByFeed.get(message.feedId);
|
|
11034
|
-
const headId = _optionalChain([q, 'optionalAccess',
|
|
12285
|
+
const headId = _optionalChain([q, 'optionalAccess', _266 => _266[0]]);
|
|
11035
12286
|
if (headId !== void 0) {
|
|
11036
12287
|
const pending = pendingFeedMutations.get(headId);
|
|
11037
|
-
if (_optionalChain([pending, 'optionalAccess',
|
|
12288
|
+
if (_optionalChain([pending, 'optionalAccess', _267 => _267.kind]) === "add-message" && pending.expectedClientMessageId === void 0) {
|
|
11038
12289
|
settleFeedMutation(headId, "ok");
|
|
11039
12290
|
}
|
|
11040
12291
|
}
|
|
@@ -11070,7 +12321,7 @@ function createRoom(options, config) {
|
|
|
11070
12321
|
const unacknowledgedOps2 = [...context.unacknowledgedOps.values()];
|
|
11071
12322
|
createOrUpdateRootFromMessage(nodes);
|
|
11072
12323
|
applyAndSendOfflineOps(unacknowledgedOps2);
|
|
11073
|
-
_optionalChain([_resolveStoragePromise, 'optionalCall',
|
|
12324
|
+
_optionalChain([_resolveStoragePromise, 'optionalCall', _268 => _268()]);
|
|
11074
12325
|
notifyStorageStatus();
|
|
11075
12326
|
eventHub.storageDidLoad.notify();
|
|
11076
12327
|
}
|
|
@@ -11079,7 +12330,7 @@ function createRoom(options, config) {
|
|
|
11079
12330
|
if (!messages.some((msg) => msg.type === ClientMsgCode.FETCH_STORAGE)) {
|
|
11080
12331
|
messages.push({ type: ClientMsgCode.FETCH_STORAGE });
|
|
11081
12332
|
nodeMapBuffer.take();
|
|
11082
|
-
_optionalChain([stopwatch, 'optionalAccess',
|
|
12333
|
+
_optionalChain([stopwatch, 'optionalAccess', _269 => _269.start, 'call', _270 => _270()]);
|
|
11083
12334
|
}
|
|
11084
12335
|
}
|
|
11085
12336
|
function startLoadingStorage() {
|
|
@@ -11133,10 +12384,10 @@ function createRoom(options, config) {
|
|
|
11133
12384
|
const message = {
|
|
11134
12385
|
type: ClientMsgCode.FETCH_FEEDS,
|
|
11135
12386
|
requestId,
|
|
11136
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
11137
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
11138
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
11139
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
12387
|
+
cursor: _optionalChain([options2, 'optionalAccess', _271 => _271.cursor]),
|
|
12388
|
+
since: _optionalChain([options2, 'optionalAccess', _272 => _272.since]),
|
|
12389
|
+
limit: _optionalChain([options2, 'optionalAccess', _273 => _273.limit]),
|
|
12390
|
+
metadata: _optionalChain([options2, 'optionalAccess', _274 => _274.metadata])
|
|
11140
12391
|
};
|
|
11141
12392
|
context.buffer.messages.push(message);
|
|
11142
12393
|
flushNowOrSoon();
|
|
@@ -11156,9 +12407,9 @@ function createRoom(options, config) {
|
|
|
11156
12407
|
type: ClientMsgCode.FETCH_FEED_MESSAGES,
|
|
11157
12408
|
requestId,
|
|
11158
12409
|
feedId,
|
|
11159
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
11160
|
-
since: _optionalChain([options2, 'optionalAccess',
|
|
11161
|
-
limit: _optionalChain([options2, 'optionalAccess',
|
|
12410
|
+
cursor: _optionalChain([options2, 'optionalAccess', _275 => _275.cursor]),
|
|
12411
|
+
since: _optionalChain([options2, 'optionalAccess', _276 => _276.since]),
|
|
12412
|
+
limit: _optionalChain([options2, 'optionalAccess', _277 => _277.limit])
|
|
11162
12413
|
};
|
|
11163
12414
|
context.buffer.messages.push(message);
|
|
11164
12415
|
flushNowOrSoon();
|
|
@@ -11177,8 +12428,8 @@ function createRoom(options, config) {
|
|
|
11177
12428
|
type: ClientMsgCode.ADD_FEED,
|
|
11178
12429
|
requestId,
|
|
11179
12430
|
feedId,
|
|
11180
|
-
metadata: _optionalChain([options2, 'optionalAccess',
|
|
11181
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12431
|
+
metadata: _optionalChain([options2, 'optionalAccess', _278 => _278.metadata]),
|
|
12432
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _279 => _279.createdAt])
|
|
11182
12433
|
};
|
|
11183
12434
|
context.buffer.messages.push(message);
|
|
11184
12435
|
flushNowOrSoon();
|
|
@@ -11212,15 +12463,15 @@ function createRoom(options, config) {
|
|
|
11212
12463
|
function addFeedMessage(feedId, data, options2) {
|
|
11213
12464
|
const requestId = nanoid();
|
|
11214
12465
|
const promise = registerFeedMutation(requestId, "add-message", feedId, {
|
|
11215
|
-
expectedClientMessageId: _optionalChain([options2, 'optionalAccess',
|
|
12466
|
+
expectedClientMessageId: _optionalChain([options2, 'optionalAccess', _280 => _280.id])
|
|
11216
12467
|
});
|
|
11217
12468
|
const message = {
|
|
11218
12469
|
type: ClientMsgCode.ADD_FEED_MESSAGE,
|
|
11219
12470
|
requestId,
|
|
11220
12471
|
feedId,
|
|
11221
12472
|
data,
|
|
11222
|
-
id: _optionalChain([options2, 'optionalAccess',
|
|
11223
|
-
createdAt: _optionalChain([options2, 'optionalAccess',
|
|
12473
|
+
id: _optionalChain([options2, 'optionalAccess', _281 => _281.id]),
|
|
12474
|
+
createdAt: _optionalChain([options2, 'optionalAccess', _282 => _282.createdAt])
|
|
11224
12475
|
};
|
|
11225
12476
|
context.buffer.messages.push(message);
|
|
11226
12477
|
flushNowOrSoon();
|
|
@@ -11237,7 +12488,7 @@ function createRoom(options, config) {
|
|
|
11237
12488
|
feedId,
|
|
11238
12489
|
messageId,
|
|
11239
12490
|
data,
|
|
11240
|
-
updatedAt: _optionalChain([options2, 'optionalAccess',
|
|
12491
|
+
updatedAt: _optionalChain([options2, 'optionalAccess', _283 => _283.updatedAt])
|
|
11241
12492
|
};
|
|
11242
12493
|
context.buffer.messages.push(message);
|
|
11243
12494
|
flushNowOrSoon();
|
|
@@ -11262,14 +12513,19 @@ function createRoom(options, config) {
|
|
|
11262
12513
|
if (context.activeBatch) {
|
|
11263
12514
|
throw new Error("undo is not allowed during a batch");
|
|
11264
12515
|
}
|
|
11265
|
-
const
|
|
11266
|
-
if (
|
|
12516
|
+
const item = context.undoStack.pop();
|
|
12517
|
+
if (item === void 0) {
|
|
11267
12518
|
return;
|
|
11268
12519
|
}
|
|
11269
12520
|
context.pausedHistory = null;
|
|
11270
|
-
const result = applyLocalOps(frames
|
|
12521
|
+
const result = applyLocalOps(item.frames, {
|
|
12522
|
+
origin: "local",
|
|
12523
|
+
via: "history",
|
|
12524
|
+
action: "undo"
|
|
12525
|
+
});
|
|
12526
|
+
context.redoStack.push({ id: item.id, frames: result.reverse });
|
|
12527
|
+
notifyPrivateHistory({ action: "undo", id: item.id });
|
|
11271
12528
|
notify(result.updates);
|
|
11272
|
-
context.redoStack.push(result.reverse);
|
|
11273
12529
|
onHistoryChange();
|
|
11274
12530
|
for (const op of result.opsToEmit) {
|
|
11275
12531
|
context.buffer.storageOperations.push(op);
|
|
@@ -11280,14 +12536,19 @@ function createRoom(options, config) {
|
|
|
11280
12536
|
if (context.activeBatch) {
|
|
11281
12537
|
throw new Error("redo is not allowed during a batch");
|
|
11282
12538
|
}
|
|
11283
|
-
const
|
|
11284
|
-
if (
|
|
12539
|
+
const item = context.redoStack.pop();
|
|
12540
|
+
if (item === void 0) {
|
|
11285
12541
|
return;
|
|
11286
12542
|
}
|
|
11287
12543
|
context.pausedHistory = null;
|
|
11288
|
-
const result = applyLocalOps(frames
|
|
12544
|
+
const result = applyLocalOps(item.frames, {
|
|
12545
|
+
origin: "local",
|
|
12546
|
+
via: "history",
|
|
12547
|
+
action: "redo"
|
|
12548
|
+
});
|
|
12549
|
+
context.undoStack.push({ id: item.id, frames: result.reverse });
|
|
12550
|
+
notifyPrivateHistory({ action: "redo", id: item.id });
|
|
11289
12551
|
notify(result.updates);
|
|
11290
|
-
context.undoStack.push(result.reverse);
|
|
11291
12552
|
onHistoryChange();
|
|
11292
12553
|
for (const op of result.opsToEmit) {
|
|
11293
12554
|
context.buffer.storageOperations.push(op);
|
|
@@ -11297,6 +12558,8 @@ function createRoom(options, config) {
|
|
|
11297
12558
|
function clear() {
|
|
11298
12559
|
context.undoStack.length = 0;
|
|
11299
12560
|
context.redoStack.length = 0;
|
|
12561
|
+
notifyPrivateHistory({ action: "clear" });
|
|
12562
|
+
onHistoryChange();
|
|
11300
12563
|
}
|
|
11301
12564
|
function batch2(callback) {
|
|
11302
12565
|
if (context.activeBatch) {
|
|
@@ -11324,8 +12587,8 @@ function createRoom(options, config) {
|
|
|
11324
12587
|
if (currentBatch.scheduleHistoryResume) {
|
|
11325
12588
|
commitPausedHistoryToUndoStack();
|
|
11326
12589
|
}
|
|
11327
|
-
if (currentBatch.ops.length > 0) {
|
|
11328
|
-
|
|
12590
|
+
if (currentBatch.ops.length > 0 || currentBatch.clearRedoStack) {
|
|
12591
|
+
clearRedoStack();
|
|
11329
12592
|
}
|
|
11330
12593
|
if (currentBatch.ops.length > 0) {
|
|
11331
12594
|
dispatchOps(currentBatch.ops);
|
|
@@ -11354,7 +12617,6 @@ function createRoom(options, config) {
|
|
|
11354
12617
|
}
|
|
11355
12618
|
commitPausedHistoryToUndoStack();
|
|
11356
12619
|
}
|
|
11357
|
-
let historyDisabled = 0;
|
|
11358
12620
|
function disableHistory(fn) {
|
|
11359
12621
|
const origUndo = context.undoStack;
|
|
11360
12622
|
const origRedo = context.redoStack;
|
|
@@ -11444,8 +12706,8 @@ function createRoom(options, config) {
|
|
|
11444
12706
|
async function getThreads(options2) {
|
|
11445
12707
|
return httpClient.getThreads({
|
|
11446
12708
|
roomId,
|
|
11447
|
-
query: _optionalChain([options2, 'optionalAccess',
|
|
11448
|
-
cursor: _optionalChain([options2, 'optionalAccess',
|
|
12709
|
+
query: _optionalChain([options2, 'optionalAccess', _284 => _284.query]),
|
|
12710
|
+
cursor: _optionalChain([options2, 'optionalAccess', _285 => _285.cursor])
|
|
11449
12711
|
});
|
|
11450
12712
|
}
|
|
11451
12713
|
async function getThread(threadId) {
|
|
@@ -11568,7 +12830,7 @@ function createRoom(options, config) {
|
|
|
11568
12830
|
function getSubscriptionSettings(options2) {
|
|
11569
12831
|
return httpClient.getSubscriptionSettings({
|
|
11570
12832
|
roomId,
|
|
11571
|
-
signal: _optionalChain([options2, 'optionalAccess',
|
|
12833
|
+
signal: _optionalChain([options2, 'optionalAccess', _286 => _286.signal])
|
|
11572
12834
|
});
|
|
11573
12835
|
}
|
|
11574
12836
|
function updateSubscriptionSettings(settings) {
|
|
@@ -11590,30 +12852,45 @@ function createRoom(options, config) {
|
|
|
11590
12852
|
{
|
|
11591
12853
|
[kInternal]: {
|
|
11592
12854
|
get presenceBuffer() {
|
|
11593
|
-
return deepClone(_nullishCoalesce(_optionalChain([context, 'access',
|
|
12855
|
+
return deepClone(_nullishCoalesce(_optionalChain([context, 'access', _287 => _287.buffer, 'access', _288 => _288.presenceUpdates, 'optionalAccess', _289 => _289.data]), () => ( null)));
|
|
11594
12856
|
},
|
|
11595
12857
|
// prettier-ignore
|
|
11596
12858
|
get undoStack() {
|
|
11597
|
-
return
|
|
12859
|
+
return structuredClone(
|
|
12860
|
+
context.undoStack.map((item) => ({
|
|
12861
|
+
id: item.id,
|
|
12862
|
+
frames: item.frames
|
|
12863
|
+
}))
|
|
12864
|
+
);
|
|
12865
|
+
},
|
|
12866
|
+
// prettier-ignore
|
|
12867
|
+
get redoStack() {
|
|
12868
|
+
return structuredClone(
|
|
12869
|
+
context.redoStack.map((item) => ({
|
|
12870
|
+
id: item.id,
|
|
12871
|
+
frames: item.frames
|
|
12872
|
+
}))
|
|
12873
|
+
);
|
|
11598
12874
|
},
|
|
11599
12875
|
// prettier-ignore
|
|
11600
12876
|
get nodeCount() {
|
|
11601
12877
|
return context.pool.nodes.size;
|
|
11602
12878
|
},
|
|
11603
12879
|
// prettier-ignore
|
|
12880
|
+
history: eventHub.privateHistory.observable,
|
|
11604
12881
|
getYjsProvider() {
|
|
11605
12882
|
return context.yjsProvider;
|
|
11606
12883
|
},
|
|
11607
12884
|
setYjsProvider(newProvider) {
|
|
11608
|
-
_optionalChain([context, 'access',
|
|
12885
|
+
_optionalChain([context, 'access', _290 => _290.yjsProvider, 'optionalAccess', _291 => _291.off, 'call', _292 => _292("status", yjsStatusDidChange)]);
|
|
11609
12886
|
context.yjsProvider = newProvider;
|
|
11610
|
-
_optionalChain([newProvider, 'optionalAccess',
|
|
12887
|
+
_optionalChain([newProvider, 'optionalAccess', _293 => _293.on, 'call', _294 => _294("status", yjsStatusDidChange)]);
|
|
11611
12888
|
context.yjsProviderDidChange.notify();
|
|
11612
12889
|
},
|
|
11613
12890
|
yjsProviderDidChange: context.yjsProviderDidChange.observable,
|
|
11614
12891
|
// send metadata when using a text editor
|
|
11615
12892
|
reportTextEditor,
|
|
11616
|
-
getPermissionMatrix: () => _optionalChain([context, 'access',
|
|
12893
|
+
getPermissionMatrix: () => _optionalChain([context, 'access', _295 => _295.dynamicSessionInfoSig, 'access', _296 => _296.get, 'call', _297 => _297(), 'optionalAccess', _298 => _298.permissionMatrix]),
|
|
11617
12894
|
// create a text mention when using a text editor
|
|
11618
12895
|
createTextMention,
|
|
11619
12896
|
// delete a text mention when using a text editor
|
|
@@ -11675,7 +12952,7 @@ ${dumpPool(
|
|
|
11675
12952
|
source.dispose();
|
|
11676
12953
|
}
|
|
11677
12954
|
eventHub.roomWillDestroy.notify();
|
|
11678
|
-
_optionalChain([context, 'access',
|
|
12955
|
+
_optionalChain([context, 'access', _299 => _299.yjsProvider, 'optionalAccess', _300 => _300.off, 'call', _301 => _301("status", yjsStatusDidChange)]);
|
|
11679
12956
|
syncSourceForStorage.destroy();
|
|
11680
12957
|
syncSourceForYjs.destroy();
|
|
11681
12958
|
uninstallBgTabSpy();
|
|
@@ -11837,7 +13114,7 @@ function makeClassicSubscribeFn(roomId, events, errorEvents) {
|
|
|
11837
13114
|
}
|
|
11838
13115
|
if (isLiveNode(first)) {
|
|
11839
13116
|
const node = first;
|
|
11840
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
13117
|
+
if (_optionalChain([options, 'optionalAccess', _302 => _302.isDeep])) {
|
|
11841
13118
|
const storageCallback = second;
|
|
11842
13119
|
return subscribeToLiveStructureDeeply(node, storageCallback);
|
|
11843
13120
|
} else {
|
|
@@ -11927,8 +13204,8 @@ function createClient(options) {
|
|
|
11927
13204
|
const authManager = createAuthManager(options, (token) => {
|
|
11928
13205
|
currentUserId.set(() => token.uid);
|
|
11929
13206
|
});
|
|
11930
|
-
const fetchPolyfill = _optionalChain([clientOptions, 'access',
|
|
11931
|
-
_optionalChain([globalThis, 'access',
|
|
13207
|
+
const fetchPolyfill = _optionalChain([clientOptions, 'access', _303 => _303.polyfills, 'optionalAccess', _304 => _304.fetch]) || /* istanbul ignore next */
|
|
13208
|
+
_optionalChain([globalThis, 'access', _305 => _305.fetch, 'optionalAccess', _306 => _306.bind, 'call', _307 => _307(globalThis)]);
|
|
11932
13209
|
const httpClient = createApiClient({
|
|
11933
13210
|
baseUrl,
|
|
11934
13211
|
fetchPolyfill,
|
|
@@ -11945,7 +13222,7 @@ function createClient(options) {
|
|
|
11945
13222
|
delegates: {
|
|
11946
13223
|
createSocket: makeCreateSocketDelegateForAi(
|
|
11947
13224
|
baseUrl,
|
|
11948
|
-
_optionalChain([clientOptions, 'access',
|
|
13225
|
+
_optionalChain([clientOptions, 'access', _308 => _308.polyfills, 'optionalAccess', _309 => _309.WebSocket])
|
|
11949
13226
|
),
|
|
11950
13227
|
authenticate: async () => {
|
|
11951
13228
|
const resp = await authManager.getAuthValue({
|
|
@@ -12016,7 +13293,7 @@ function createClient(options) {
|
|
|
12016
13293
|
createSocket: makeCreateSocketDelegateForRoom(
|
|
12017
13294
|
roomId,
|
|
12018
13295
|
baseUrl,
|
|
12019
|
-
_optionalChain([clientOptions, 'access',
|
|
13296
|
+
_optionalChain([clientOptions, 'access', _310 => _310.polyfills, 'optionalAccess', _311 => _311.WebSocket])
|
|
12020
13297
|
),
|
|
12021
13298
|
authenticate: makeAuthDelegateForRoom(roomId, authManager)
|
|
12022
13299
|
})),
|
|
@@ -12038,7 +13315,7 @@ function createClient(options) {
|
|
|
12038
13315
|
const shouldConnect = _nullishCoalesce(options2.autoConnect, () => ( true));
|
|
12039
13316
|
if (shouldConnect) {
|
|
12040
13317
|
if (typeof atob === "undefined") {
|
|
12041
|
-
if (_optionalChain([clientOptions, 'access',
|
|
13318
|
+
if (_optionalChain([clientOptions, 'access', _312 => _312.polyfills, 'optionalAccess', _313 => _313.atob]) === void 0) {
|
|
12042
13319
|
throw new Error(
|
|
12043
13320
|
"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"
|
|
12044
13321
|
);
|
|
@@ -12050,7 +13327,7 @@ function createClient(options) {
|
|
|
12050
13327
|
return leaseRoom(newRoomDetails);
|
|
12051
13328
|
}
|
|
12052
13329
|
function getRoom(roomId) {
|
|
12053
|
-
const room = _optionalChain([roomsById, 'access',
|
|
13330
|
+
const room = _optionalChain([roomsById, 'access', _314 => _314.get, 'call', _315 => _315(roomId), 'optionalAccess', _316 => _316.room]);
|
|
12054
13331
|
return room ? room : null;
|
|
12055
13332
|
}
|
|
12056
13333
|
function logout() {
|
|
@@ -12066,7 +13343,7 @@ function createClient(options) {
|
|
|
12066
13343
|
const batchedResolveUsers = new Batch(
|
|
12067
13344
|
async (batchedUserIds) => {
|
|
12068
13345
|
const userIds = batchedUserIds.flat();
|
|
12069
|
-
const users = await _optionalChain([resolveUsers, 'optionalCall',
|
|
13346
|
+
const users = await _optionalChain([resolveUsers, 'optionalCall', _317 => _317({ userIds })]);
|
|
12070
13347
|
warnOnceIf(
|
|
12071
13348
|
!resolveUsers,
|
|
12072
13349
|
"Set the resolveUsers option in createClient to specify user info."
|
|
@@ -12083,7 +13360,7 @@ function createClient(options) {
|
|
|
12083
13360
|
const batchedResolveRoomsInfo = new Batch(
|
|
12084
13361
|
async (batchedRoomIds) => {
|
|
12085
13362
|
const roomIds = batchedRoomIds.flat();
|
|
12086
|
-
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall',
|
|
13363
|
+
const roomsInfo = await _optionalChain([resolveRoomsInfo, 'optionalCall', _318 => _318({ roomIds })]);
|
|
12087
13364
|
warnOnceIf(
|
|
12088
13365
|
!resolveRoomsInfo,
|
|
12089
13366
|
"Set the resolveRoomsInfo option in createClient to specify room info."
|
|
@@ -12100,7 +13377,7 @@ function createClient(options) {
|
|
|
12100
13377
|
const batchedResolveGroupsInfo = new Batch(
|
|
12101
13378
|
async (batchedGroupIds) => {
|
|
12102
13379
|
const groupIds = batchedGroupIds.flat();
|
|
12103
|
-
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall',
|
|
13380
|
+
const groupsInfo = await _optionalChain([resolveGroupsInfo, 'optionalCall', _319 => _319({ groupIds })]);
|
|
12104
13381
|
warnOnceIf(
|
|
12105
13382
|
!resolveGroupsInfo,
|
|
12106
13383
|
"Set the resolveGroupsInfo option in createClient to specify group info."
|
|
@@ -12159,7 +13436,7 @@ function createClient(options) {
|
|
|
12159
13436
|
}
|
|
12160
13437
|
};
|
|
12161
13438
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12162
|
-
_optionalChain([win, 'optionalAccess',
|
|
13439
|
+
_optionalChain([win, 'optionalAccess', _320 => _320.addEventListener, 'call', _321 => _321("beforeunload", maybePreventClose)]);
|
|
12163
13440
|
}
|
|
12164
13441
|
async function getNotificationSettings(options2) {
|
|
12165
13442
|
const plainSettings = await httpClient.getNotificationSettings(options2);
|
|
@@ -12287,7 +13564,7 @@ var commentBodyElementsTypes = {
|
|
|
12287
13564
|
mention: "inline"
|
|
12288
13565
|
};
|
|
12289
13566
|
function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
12290
|
-
if (!body || !_optionalChain([body, 'optionalAccess',
|
|
13567
|
+
if (!body || !_optionalChain([body, 'optionalAccess', _322 => _322.content])) {
|
|
12291
13568
|
return;
|
|
12292
13569
|
}
|
|
12293
13570
|
const element = typeof elementOrVisitor === "string" ? elementOrVisitor : void 0;
|
|
@@ -12297,13 +13574,13 @@ function traverseCommentBody(body, elementOrVisitor, possiblyVisitor) {
|
|
|
12297
13574
|
for (const block of body.content) {
|
|
12298
13575
|
if (type === "all" || type === "block") {
|
|
12299
13576
|
if (guard(block)) {
|
|
12300
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13577
|
+
_optionalChain([visitor, 'optionalCall', _323 => _323(block)]);
|
|
12301
13578
|
}
|
|
12302
13579
|
}
|
|
12303
13580
|
if (type === "all" || type === "inline") {
|
|
12304
13581
|
for (const inline of block.children) {
|
|
12305
13582
|
if (guard(inline)) {
|
|
12306
|
-
_optionalChain([visitor, 'optionalCall',
|
|
13583
|
+
_optionalChain([visitor, 'optionalCall', _324 => _324(inline)]);
|
|
12307
13584
|
}
|
|
12308
13585
|
}
|
|
12309
13586
|
}
|
|
@@ -12473,7 +13750,7 @@ var stringifyCommentBodyPlainElements = {
|
|
|
12473
13750
|
text: ({ element }) => element.text,
|
|
12474
13751
|
link: ({ element }) => _nullishCoalesce(element.text, () => ( element.url)),
|
|
12475
13752
|
mention: ({ element, user, group }) => {
|
|
12476
|
-
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13753
|
+
return `@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _325 => _325.name]), () => ( _optionalChain([group, 'optionalAccess', _326 => _326.name]))), () => ( element.id))}`;
|
|
12477
13754
|
}
|
|
12478
13755
|
};
|
|
12479
13756
|
var stringifyCommentBodyHtmlElements = {
|
|
@@ -12503,7 +13780,7 @@ var stringifyCommentBodyHtmlElements = {
|
|
|
12503
13780
|
return html`<a href="${href}" target="_blank" rel="noopener noreferrer">${element.text ? html`${element.text}` : element.url}</a>`;
|
|
12504
13781
|
},
|
|
12505
13782
|
mention: ({ element, user, group }) => {
|
|
12506
|
-
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess',
|
|
13783
|
+
return html`<span data-mention>@${_optionalChain([user, 'optionalAccess', _327 => _327.name]) ? html`${_optionalChain([user, 'optionalAccess', _328 => _328.name])}` : _optionalChain([group, 'optionalAccess', _329 => _329.name]) ? html`${_optionalChain([group, 'optionalAccess', _330 => _330.name])}` : element.id}</span>`;
|
|
12507
13784
|
}
|
|
12508
13785
|
};
|
|
12509
13786
|
var stringifyCommentBodyMarkdownElements = {
|
|
@@ -12533,20 +13810,20 @@ var stringifyCommentBodyMarkdownElements = {
|
|
|
12533
13810
|
return markdown`[${_nullishCoalesce(element.text, () => ( element.url))}](${href})`;
|
|
12534
13811
|
},
|
|
12535
13812
|
mention: ({ element, user, group }) => {
|
|
12536
|
-
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess',
|
|
13813
|
+
return markdown`@${_nullishCoalesce(_nullishCoalesce(_optionalChain([user, 'optionalAccess', _331 => _331.name]), () => ( _optionalChain([group, 'optionalAccess', _332 => _332.name]))), () => ( element.id))}`;
|
|
12537
13814
|
}
|
|
12538
13815
|
};
|
|
12539
13816
|
async function stringifyCommentBody(body, options) {
|
|
12540
|
-
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
12541
|
-
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13817
|
+
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _333 => _333.format]), () => ( "plain"));
|
|
13818
|
+
const separator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _334 => _334.separator]), () => ( (format === "markdown" ? "\n\n" : "\n")));
|
|
12542
13819
|
const elements = {
|
|
12543
13820
|
...format === "html" ? stringifyCommentBodyHtmlElements : format === "markdown" ? stringifyCommentBodyMarkdownElements : stringifyCommentBodyPlainElements,
|
|
12544
|
-
..._optionalChain([options, 'optionalAccess',
|
|
13821
|
+
..._optionalChain([options, 'optionalAccess', _335 => _335.elements])
|
|
12545
13822
|
};
|
|
12546
13823
|
const { users: resolvedUsers, groups: resolvedGroupsInfo } = await resolveMentionsInCommentBody(
|
|
12547
13824
|
body,
|
|
12548
|
-
_optionalChain([options, 'optionalAccess',
|
|
12549
|
-
_optionalChain([options, 'optionalAccess',
|
|
13825
|
+
_optionalChain([options, 'optionalAccess', _336 => _336.resolveUsers]),
|
|
13826
|
+
_optionalChain([options, 'optionalAccess', _337 => _337.resolveGroupsInfo])
|
|
12550
13827
|
);
|
|
12551
13828
|
const blocks = body.content.flatMap((block, blockIndex) => {
|
|
12552
13829
|
switch (block.type) {
|
|
@@ -12628,6 +13905,12 @@ function toPlainLson(lson) {
|
|
|
12628
13905
|
liveblocksType: "LiveList",
|
|
12629
13906
|
data: [...lson].map((item) => toPlainLson(item))
|
|
12630
13907
|
};
|
|
13908
|
+
} else if (lson instanceof LiveText) {
|
|
13909
|
+
return {
|
|
13910
|
+
liveblocksType: "LiveText",
|
|
13911
|
+
data: lson.toJSON(),
|
|
13912
|
+
version: lson.version
|
|
13913
|
+
};
|
|
12631
13914
|
} else {
|
|
12632
13915
|
return lson;
|
|
12633
13916
|
}
|
|
@@ -12681,9 +13964,9 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12681
13964
|
const startTime = performance.now();
|
|
12682
13965
|
const doc = typeof document !== "undefined" ? document : void 0;
|
|
12683
13966
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
12684
|
-
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
13967
|
+
const maxStaleTimeMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _338 => _338.maxStaleTimeMs]), () => ( Number.POSITIVE_INFINITY));
|
|
12685
13968
|
const context = {
|
|
12686
|
-
inForeground: _optionalChain([doc, 'optionalAccess',
|
|
13969
|
+
inForeground: _optionalChain([doc, 'optionalAccess', _339 => _339.visibilityState]) !== "hidden",
|
|
12687
13970
|
lastSuccessfulPollAt: startTime,
|
|
12688
13971
|
count: 0,
|
|
12689
13972
|
backoff: 0
|
|
@@ -12764,11 +14047,11 @@ function makePoller(callback, intervalMs, options) {
|
|
|
12764
14047
|
pollNowIfStale();
|
|
12765
14048
|
}
|
|
12766
14049
|
function onVisibilityChange() {
|
|
12767
|
-
setInForeground(_optionalChain([doc, 'optionalAccess',
|
|
14050
|
+
setInForeground(_optionalChain([doc, 'optionalAccess', _340 => _340.visibilityState]) !== "hidden");
|
|
12768
14051
|
}
|
|
12769
|
-
_optionalChain([doc, 'optionalAccess',
|
|
12770
|
-
_optionalChain([win, 'optionalAccess',
|
|
12771
|
-
_optionalChain([win, 'optionalAccess',
|
|
14052
|
+
_optionalChain([doc, 'optionalAccess', _341 => _341.addEventListener, 'call', _342 => _342("visibilitychange", onVisibilityChange)]);
|
|
14053
|
+
_optionalChain([win, 'optionalAccess', _343 => _343.addEventListener, 'call', _344 => _344("online", onVisibilityChange)]);
|
|
14054
|
+
_optionalChain([win, 'optionalAccess', _345 => _345.addEventListener, 'call', _346 => _346("focus", pollNowIfStale)]);
|
|
12772
14055
|
fsm.start();
|
|
12773
14056
|
return {
|
|
12774
14057
|
inc,
|
|
@@ -12913,5 +14196,10 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
|
12913
14196
|
|
|
12914
14197
|
|
|
12915
14198
|
|
|
12916
|
-
|
|
14199
|
+
|
|
14200
|
+
|
|
14201
|
+
|
|
14202
|
+
|
|
14203
|
+
|
|
14204
|
+
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;
|
|
12917
14205
|
//# sourceMappingURL=index.cjs.map
|