@liveblocks/server 1.6.2 → 1.8.0-pre1
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 +292 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -21
- package/dist/index.d.ts +55 -21
- package/dist/index.js +279 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -12,12 +12,12 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
12
12
|
// src/decoders/ClientMsg.ts
|
|
13
13
|
import { ClientMsgCode } from "@liveblocks/core";
|
|
14
14
|
import {
|
|
15
|
-
array as
|
|
15
|
+
array as array3,
|
|
16
16
|
boolean,
|
|
17
17
|
constant as constant2,
|
|
18
18
|
jsonObject,
|
|
19
19
|
nonEmptyString,
|
|
20
|
-
number,
|
|
20
|
+
number as number2,
|
|
21
21
|
object as object2,
|
|
22
22
|
optional as optional3,
|
|
23
23
|
string as string3,
|
|
@@ -158,14 +158,28 @@ var jsonObjectYolo = jsonYolo.refine(
|
|
|
158
158
|
// src/decoders/Op.ts
|
|
159
159
|
import { OpCode } from "@liveblocks/core";
|
|
160
160
|
import {
|
|
161
|
+
array as array2,
|
|
161
162
|
constant,
|
|
163
|
+
either as either2,
|
|
164
|
+
number,
|
|
162
165
|
object,
|
|
163
166
|
oneOf,
|
|
164
167
|
optional as optional2,
|
|
168
|
+
sized as sized2,
|
|
169
|
+
startsWith,
|
|
165
170
|
string as string2,
|
|
166
|
-
taggedUnion
|
|
171
|
+
taggedUnion,
|
|
172
|
+
tuple
|
|
167
173
|
} from "decoders";
|
|
168
174
|
var intent = oneOf(["set", "push"]);
|
|
175
|
+
var storageFileId = sized2(startsWith("fl_"), { size: 24 });
|
|
176
|
+
var fileSize = number.refine(
|
|
177
|
+
(value) => Number.isSafeInteger(value) && value >= 0,
|
|
178
|
+
"Must be a valid file size"
|
|
179
|
+
);
|
|
180
|
+
var liveTextVersion = number.reject(
|
|
181
|
+
(value) => Number.isSafeInteger(value) && value >= 0 ? null : "Must be a non-negative safe integer"
|
|
182
|
+
);
|
|
169
183
|
var updateObjectOp = object({
|
|
170
184
|
type: constant(OpCode.UPDATE_OBJECT),
|
|
171
185
|
opId: string2,
|
|
@@ -210,6 +224,64 @@ var createRegisterOp = object({
|
|
|
210
224
|
intent: optional2(intent),
|
|
211
225
|
deletedId: optional2(string2)
|
|
212
226
|
});
|
|
227
|
+
var liveTextSegment = either2(
|
|
228
|
+
tuple(string2),
|
|
229
|
+
tuple(string2, jsonObjectYolo)
|
|
230
|
+
);
|
|
231
|
+
var liveTextData = array2(liveTextSegment);
|
|
232
|
+
var textOperation = taggedUnion("type", {
|
|
233
|
+
insert: object({
|
|
234
|
+
type: constant("insert"),
|
|
235
|
+
index: number,
|
|
236
|
+
text: string2,
|
|
237
|
+
attributes: optional2(jsonObjectYolo)
|
|
238
|
+
}),
|
|
239
|
+
delete: object({
|
|
240
|
+
type: constant("delete"),
|
|
241
|
+
index: number,
|
|
242
|
+
length: number
|
|
243
|
+
}),
|
|
244
|
+
format: object({
|
|
245
|
+
type: constant("format"),
|
|
246
|
+
index: number,
|
|
247
|
+
length: number,
|
|
248
|
+
attributes: jsonObjectYolo
|
|
249
|
+
})
|
|
250
|
+
});
|
|
251
|
+
var createTextOp = object({
|
|
252
|
+
type: constant(OpCode.CREATE_TEXT),
|
|
253
|
+
opId: string2,
|
|
254
|
+
id: string2,
|
|
255
|
+
parentId: string2,
|
|
256
|
+
parentKey: string2,
|
|
257
|
+
data: liveTextData,
|
|
258
|
+
version: liveTextVersion,
|
|
259
|
+
intent: optional2(intent),
|
|
260
|
+
deletedId: optional2(string2)
|
|
261
|
+
});
|
|
262
|
+
var createFileOp = object({
|
|
263
|
+
type: constant(OpCode.CREATE_FILE),
|
|
264
|
+
opId: string2,
|
|
265
|
+
id: string2,
|
|
266
|
+
parentId: string2,
|
|
267
|
+
parentKey: string2,
|
|
268
|
+
data: object({
|
|
269
|
+
id: storageFileId,
|
|
270
|
+
name: string2,
|
|
271
|
+
size: fileSize,
|
|
272
|
+
mimeType: string2
|
|
273
|
+
}),
|
|
274
|
+
intent: optional2(intent),
|
|
275
|
+
deletedId: optional2(string2)
|
|
276
|
+
});
|
|
277
|
+
var updateTextOp = object({
|
|
278
|
+
type: constant(OpCode.UPDATE_TEXT),
|
|
279
|
+
opId: string2,
|
|
280
|
+
id: string2,
|
|
281
|
+
baseVersion: liveTextVersion,
|
|
282
|
+
version: optional2(liveTextVersion),
|
|
283
|
+
ops: array2(textOperation)
|
|
284
|
+
});
|
|
213
285
|
var deleteCrdtOp = object({
|
|
214
286
|
type: constant(OpCode.DELETE_CRDT),
|
|
215
287
|
opId: string2,
|
|
@@ -233,6 +305,9 @@ var op = taggedUnion("type", {
|
|
|
233
305
|
[OpCode.CREATE_LIST]: createListOp,
|
|
234
306
|
[OpCode.CREATE_MAP]: createMapOp,
|
|
235
307
|
[OpCode.CREATE_REGISTER]: createRegisterOp,
|
|
308
|
+
[OpCode.CREATE_TEXT]: createTextOp,
|
|
309
|
+
[OpCode.UPDATE_TEXT]: updateTextOp,
|
|
310
|
+
[OpCode.CREATE_FILE]: createFileOp,
|
|
236
311
|
[OpCode.DELETE_CRDT]: deleteCrdtOp,
|
|
237
312
|
[OpCode.SET_PARENT_KEY]: setParentKeyOp,
|
|
238
313
|
[OpCode.DELETE_OBJECT_KEY]: deleteObjectKeyOp
|
|
@@ -247,7 +322,7 @@ var ROOT_YDOC_ID = "root";
|
|
|
247
322
|
var updatePresenceClientMsg = object2({
|
|
248
323
|
type: constant2(ClientMsgCode.UPDATE_PRESENCE),
|
|
249
324
|
data: jsonObjectYolo,
|
|
250
|
-
targetActor: optional3(
|
|
325
|
+
targetActor: optional3(number2)
|
|
251
326
|
});
|
|
252
327
|
var broadcastEventClientMsg = object2({
|
|
253
328
|
type: constant2(ClientMsgCode.BROADCAST_EVENT),
|
|
@@ -258,7 +333,7 @@ var fetchStorageClientMsg = object2({
|
|
|
258
333
|
});
|
|
259
334
|
var updateStorageClientMsg = object2({
|
|
260
335
|
type: constant2(ClientMsgCode.UPDATE_STORAGE),
|
|
261
|
-
ops:
|
|
336
|
+
ops: array3(op)
|
|
262
337
|
});
|
|
263
338
|
var fetchYDocClientMsg = object2({
|
|
264
339
|
type: constant2(ClientMsgCode.FETCH_YDOC),
|
|
@@ -278,8 +353,8 @@ var fetchFeedsClientMsg = object2({
|
|
|
278
353
|
type: constant2(FeedMsgCode.FETCH_FEEDS),
|
|
279
354
|
requestId: string3,
|
|
280
355
|
cursor: optional3(string3),
|
|
281
|
-
since: optional3(
|
|
282
|
-
limit: optional3(
|
|
356
|
+
since: optional3(number2),
|
|
357
|
+
limit: optional3(number2),
|
|
283
358
|
metadata: fetchFeedsMetadataFilterDecoder
|
|
284
359
|
});
|
|
285
360
|
var fetchFeedMessagesClientMsg = object2({
|
|
@@ -287,14 +362,14 @@ var fetchFeedMessagesClientMsg = object2({
|
|
|
287
362
|
requestId: string3,
|
|
288
363
|
feedId: nonEmptyString,
|
|
289
364
|
cursor: optional3(string3),
|
|
290
|
-
since: optional3(
|
|
291
|
-
limit: optional3(
|
|
365
|
+
since: optional3(number2),
|
|
366
|
+
limit: optional3(number2)
|
|
292
367
|
});
|
|
293
368
|
var addFeedClientMsg = object2({
|
|
294
369
|
type: constant2(FeedMsgCode.ADD_FEED),
|
|
295
370
|
feedId: string3,
|
|
296
371
|
metadata: optionalFeedMetadataDecoder,
|
|
297
|
-
timestamp: optional3(
|
|
372
|
+
timestamp: optional3(number2),
|
|
298
373
|
requestId: optional3(string3)
|
|
299
374
|
});
|
|
300
375
|
var updateFeedClientMsg = object2({
|
|
@@ -313,7 +388,7 @@ var addFeedMessageClientMsg = object2({
|
|
|
313
388
|
feedId: string3,
|
|
314
389
|
data: jsonObject,
|
|
315
390
|
id: optional3(string3),
|
|
316
|
-
timestamp: optional3(
|
|
391
|
+
timestamp: optional3(number2),
|
|
317
392
|
requestId: optional3(string3)
|
|
318
393
|
});
|
|
319
394
|
var updateFeedMessageClientMsg = object2({
|
|
@@ -321,7 +396,7 @@ var updateFeedMessageClientMsg = object2({
|
|
|
321
396
|
feedId: string3,
|
|
322
397
|
messageId: string3,
|
|
323
398
|
data: jsonObject,
|
|
324
|
-
timestamp: optional3(
|
|
399
|
+
timestamp: optional3(number2),
|
|
325
400
|
requestId: optional3(string3)
|
|
326
401
|
});
|
|
327
402
|
var deleteFeedMessageClientMsg = object2({
|
|
@@ -372,6 +447,10 @@ function buildNode(snapshot2, id) {
|
|
|
372
447
|
return buildList(snapshot2, id);
|
|
373
448
|
} else if (node.type === CrdtType.MAP) {
|
|
374
449
|
return buildMap(snapshot2, id);
|
|
450
|
+
} else if (node.type === CrdtType.TEXT) {
|
|
451
|
+
return node.data;
|
|
452
|
+
} else if (node.type === CrdtType.FILE) {
|
|
453
|
+
return node.data;
|
|
375
454
|
} else {
|
|
376
455
|
return node.data;
|
|
377
456
|
}
|
|
@@ -415,6 +494,10 @@ function* emit(snapshot2, id) {
|
|
|
415
494
|
yield* emitMap(snapshot2, id);
|
|
416
495
|
} else if (node.type === CrdtType.REGISTER) {
|
|
417
496
|
yield JSON.stringify(node.data);
|
|
497
|
+
} else if (node.type === CrdtType.TEXT) {
|
|
498
|
+
yield JSON.stringify(node.data);
|
|
499
|
+
} else if (node.type === CrdtType.FILE) {
|
|
500
|
+
yield JSON.stringify(node.data);
|
|
418
501
|
}
|
|
419
502
|
}
|
|
420
503
|
function* emitObject(snapshot2, id, staticJson) {
|
|
@@ -486,6 +569,29 @@ function* iterJson(key, data, parent, state) {
|
|
|
486
569
|
case "LiveMap":
|
|
487
570
|
yield* iterMap(key, data.data, parent, state);
|
|
488
571
|
return;
|
|
572
|
+
case "LiveText":
|
|
573
|
+
yield [
|
|
574
|
+
generateId(state),
|
|
575
|
+
{
|
|
576
|
+
type: CrdtType2.TEXT,
|
|
577
|
+
data: data.data,
|
|
578
|
+
version: data.version ?? 0,
|
|
579
|
+
parentId: parent[0],
|
|
580
|
+
parentKey: key
|
|
581
|
+
}
|
|
582
|
+
];
|
|
583
|
+
return;
|
|
584
|
+
case "LiveFile":
|
|
585
|
+
yield [
|
|
586
|
+
generateId(state),
|
|
587
|
+
{
|
|
588
|
+
type: CrdtType2.FILE,
|
|
589
|
+
data: data.data,
|
|
590
|
+
parentId: parent[0],
|
|
591
|
+
parentKey: key
|
|
592
|
+
}
|
|
593
|
+
];
|
|
594
|
+
return;
|
|
489
595
|
// istanbul ignore next
|
|
490
596
|
default:
|
|
491
597
|
assertNever(data, "Unknown `liveblocksType` field");
|
|
@@ -570,6 +676,13 @@ function buildNode2(snapshot2, id) {
|
|
|
570
676
|
return buildList2(snapshot2, id);
|
|
571
677
|
} else if (node.type === CrdtType2.MAP) {
|
|
572
678
|
return buildMap2(snapshot2, id);
|
|
679
|
+
} else if (node.type === CrdtType2.TEXT) {
|
|
680
|
+
return {
|
|
681
|
+
liveblocksType: "LiveText",
|
|
682
|
+
data: node.data
|
|
683
|
+
};
|
|
684
|
+
} else if (node.type === CrdtType2.FILE) {
|
|
685
|
+
return { liveblocksType: "LiveFile", data: node.data };
|
|
573
686
|
} else {
|
|
574
687
|
return node.data ?? null;
|
|
575
688
|
}
|
|
@@ -616,6 +729,15 @@ function* emit2(snapshot2, id) {
|
|
|
616
729
|
yield* emitMap2(snapshot2, id);
|
|
617
730
|
} else if (node.type === CrdtType2.REGISTER) {
|
|
618
731
|
yield JSON.stringify(node.data ?? null);
|
|
732
|
+
} else if (node.type === CrdtType2.TEXT) {
|
|
733
|
+
yield JSON.stringify({
|
|
734
|
+
liveblocksType: "LiveText",
|
|
735
|
+
data: node.data
|
|
736
|
+
});
|
|
737
|
+
} else if (node.type === CrdtType2.FILE) {
|
|
738
|
+
yield '{"liveblocksType":"LiveFile","data":';
|
|
739
|
+
yield JSON.stringify(node.data);
|
|
740
|
+
yield "}";
|
|
619
741
|
}
|
|
620
742
|
}
|
|
621
743
|
function* emitObject2(snapshot2, id, staticJson) {
|
|
@@ -837,7 +959,7 @@ import {
|
|
|
837
959
|
WebsocketCloseCodes as CloseCode
|
|
838
960
|
} from "@liveblocks/core";
|
|
839
961
|
import { Mutex, tryAcquire } from "async-mutex";
|
|
840
|
-
import { array as
|
|
962
|
+
import { array as array4, formatInline } from "decoders";
|
|
841
963
|
import { chunkedByCost } from "itertools";
|
|
842
964
|
import { nanoid as nanoid2 } from "nanoid";
|
|
843
965
|
|
|
@@ -1012,9 +1134,10 @@ function buildReverseLookup(nodes) {
|
|
|
1012
1134
|
delete node.data[key];
|
|
1013
1135
|
}
|
|
1014
1136
|
}
|
|
1015
|
-
|
|
1137
|
+
const isLeafNode = node.type === CrdtType4.REGISTER || node.type === CrdtType4.FILE || node.type === CrdtType4.TEXT;
|
|
1138
|
+
if (!isLeafNode) {
|
|
1016
1139
|
queue.push(...revNodes.valuesAt(nodeId));
|
|
1017
|
-
} else {
|
|
1140
|
+
} else if (node.type === CrdtType4.REGISTER) {
|
|
1018
1141
|
const parent = nodes.get(node.parentId);
|
|
1019
1142
|
if (parent?.type === CrdtType4.OBJECT) {
|
|
1020
1143
|
continue;
|
|
@@ -1035,7 +1158,6 @@ function hasStaticDataAt(node, key) {
|
|
|
1035
1158
|
return node.type === CrdtType4.OBJECT && Object.prototype.hasOwnProperty.call(node.data, key) && node.data[key] !== void 0;
|
|
1036
1159
|
}
|
|
1037
1160
|
var InMemoryDriver = class {
|
|
1038
|
-
// Key: `${feedId}:${messageId}`
|
|
1039
1161
|
constructor(options) {
|
|
1040
1162
|
__publicField(this, "_nextActor");
|
|
1041
1163
|
__publicField(this, "_nodes");
|
|
@@ -1045,12 +1167,15 @@ var InMemoryDriver = class {
|
|
|
1045
1167
|
__publicField(this, "_leasedSessions");
|
|
1046
1168
|
__publicField(this, "_feeds");
|
|
1047
1169
|
__publicField(this, "_feedMessages");
|
|
1170
|
+
// Key: `${feedId}:${messageId}`
|
|
1171
|
+
__publicField(this, "_liveTextHistory");
|
|
1048
1172
|
this._nodes = /* @__PURE__ */ new Map();
|
|
1049
1173
|
this._metadb = /* @__PURE__ */ new Map();
|
|
1050
1174
|
this._ydb = /* @__PURE__ */ new Map();
|
|
1051
1175
|
this._leasedSessions = /* @__PURE__ */ new Map();
|
|
1052
1176
|
this._feeds = /* @__PURE__ */ new Map();
|
|
1053
1177
|
this._feedMessages = /* @__PURE__ */ new Map();
|
|
1178
|
+
this._liveTextHistory = /* @__PURE__ */ new Map();
|
|
1054
1179
|
this._nextActor = options?.initialActor ?? -1;
|
|
1055
1180
|
for (const [key, value] of options?.initialNodes ?? []) {
|
|
1056
1181
|
this._nodes.set(key, value);
|
|
@@ -1066,10 +1191,40 @@ var InMemoryDriver = class {
|
|
|
1066
1191
|
DANGEROUSLY_reset_nodes(doc) {
|
|
1067
1192
|
this.reinitialize();
|
|
1068
1193
|
this._nodes.clear();
|
|
1194
|
+
this._liveTextHistory.clear();
|
|
1069
1195
|
for (const [id, node] of plainLsonToNodeStream(doc)) {
|
|
1070
1196
|
this._nodes.set(id, node);
|
|
1071
1197
|
}
|
|
1072
1198
|
}
|
|
1199
|
+
get_live_text_history_since(nodeId, version) {
|
|
1200
|
+
return (this._liveTextHistory.get(nodeId) ?? []).filter((entry) => entry.version > version).sort((left, right) => left.version - right.version).map((entry) => ({ ...entry, ops: [...entry.ops] }));
|
|
1201
|
+
}
|
|
1202
|
+
get_live_text_history_by_op_id(nodeId, opId) {
|
|
1203
|
+
const entry = (this._liveTextHistory.get(nodeId) ?? []).find(
|
|
1204
|
+
(item) => item.opId === opId
|
|
1205
|
+
);
|
|
1206
|
+
return entry === void 0 ? void 0 : { ...entry, ops: [...entry.ops] };
|
|
1207
|
+
}
|
|
1208
|
+
append_live_text_history(entry) {
|
|
1209
|
+
const history = this._liveTextHistory.get(entry.nodeId) ?? [];
|
|
1210
|
+
history.push({ ...entry, ops: [...entry.ops] });
|
|
1211
|
+
history.sort((left, right) => left.version - right.version);
|
|
1212
|
+
this._liveTextHistory.set(entry.nodeId, history);
|
|
1213
|
+
}
|
|
1214
|
+
purge_live_text_history_before(nodeId, minVersionToKeep) {
|
|
1215
|
+
const history = this._liveTextHistory.get(nodeId);
|
|
1216
|
+
if (history === void 0) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
const retained = history.filter(
|
|
1220
|
+
(entry) => entry.version >= minVersionToKeep
|
|
1221
|
+
);
|
|
1222
|
+
if (retained.length === 0) {
|
|
1223
|
+
this._liveTextHistory.delete(nodeId);
|
|
1224
|
+
} else {
|
|
1225
|
+
this._liveTextHistory.set(nodeId, retained);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1073
1228
|
get_meta(key) {
|
|
1074
1229
|
return this._metadb.get(key);
|
|
1075
1230
|
}
|
|
@@ -1345,6 +1500,7 @@ var InMemoryDriver = class {
|
|
|
1345
1500
|
}
|
|
1346
1501
|
_loadNodesApi() {
|
|
1347
1502
|
const nodes = this._nodes;
|
|
1503
|
+
const liveTextHistory = this._liveTextHistory;
|
|
1348
1504
|
if (!nodes.has("root")) {
|
|
1349
1505
|
nodes.set("root", { type: CrdtType4.OBJECT, data: {} });
|
|
1350
1506
|
}
|
|
@@ -1377,6 +1533,9 @@ var InMemoryDriver = class {
|
|
|
1377
1533
|
if (node.type === CrdtType4.REGISTER && parentNode.type === CrdtType4.OBJECT) {
|
|
1378
1534
|
throw new Error("Cannot add register under object");
|
|
1379
1535
|
}
|
|
1536
|
+
if (parentNode.type === CrdtType4.FILE) {
|
|
1537
|
+
throw new Error("Cannot add child under file");
|
|
1538
|
+
}
|
|
1380
1539
|
const conflictingSiblingId = revNodes.get(node.parentId, node.parentKey);
|
|
1381
1540
|
if (conflictingSiblingId !== id) {
|
|
1382
1541
|
const parentNode2 = nodes.get(node.parentId);
|
|
@@ -1432,6 +1591,7 @@ var InMemoryDriver = class {
|
|
|
1432
1591
|
const currid = queue.pop();
|
|
1433
1592
|
queue.push(...revNodes.valuesAt(currid));
|
|
1434
1593
|
nodes.delete(currid);
|
|
1594
|
+
liveTextHistory.delete(currid);
|
|
1435
1595
|
revNodes.deleteAll(currid);
|
|
1436
1596
|
}
|
|
1437
1597
|
}
|
|
@@ -1551,24 +1711,27 @@ function makeNewInMemoryDriver(options) {
|
|
|
1551
1711
|
|
|
1552
1712
|
// src/Storage.ts
|
|
1553
1713
|
import {
|
|
1714
|
+
applyLiveTextOperations,
|
|
1554
1715
|
asPos as asPos2,
|
|
1555
1716
|
assertNever as assertNever2,
|
|
1556
1717
|
CrdtType as CrdtType5,
|
|
1557
1718
|
makePosition as makePosition2,
|
|
1558
|
-
OpCode as OpCode2
|
|
1719
|
+
OpCode as OpCode2,
|
|
1720
|
+
transformTextOperations
|
|
1559
1721
|
} from "@liveblocks/core";
|
|
1722
|
+
var LIVE_TEXT_HISTORY_LIMIT = 1e3;
|
|
1723
|
+
var LIVE_TEXT_HISTORY_TOO_OLD_REASON = "LiveText operation is older than retained history";
|
|
1560
1724
|
function accept(op2, fix) {
|
|
1561
1725
|
return { action: "accepted", op: op2, fix };
|
|
1562
1726
|
}
|
|
1563
1727
|
function ignore(ignoredOp) {
|
|
1564
1728
|
return { action: "ignored", ignoredOpId: ignoredOp.opId };
|
|
1565
1729
|
}
|
|
1566
|
-
function
|
|
1567
|
-
return {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
};
|
|
1730
|
+
function reject(op2, reason) {
|
|
1731
|
+
return { action: "rejected", opIds: [op2.opId], reason };
|
|
1732
|
+
}
|
|
1733
|
+
function rectify(ackOp, fix) {
|
|
1734
|
+
return { action: "rectified", ackOp, fix };
|
|
1572
1735
|
}
|
|
1573
1736
|
function nodeFromCreateChildOp(op2) {
|
|
1574
1737
|
switch (op2.type) {
|
|
@@ -1598,6 +1761,21 @@ function nodeFromCreateChildOp(op2) {
|
|
|
1598
1761
|
parentKey: op2.parentKey,
|
|
1599
1762
|
data: op2.data
|
|
1600
1763
|
};
|
|
1764
|
+
case OpCode2.CREATE_TEXT:
|
|
1765
|
+
return {
|
|
1766
|
+
type: CrdtType5.TEXT,
|
|
1767
|
+
parentId: op2.parentId,
|
|
1768
|
+
parentKey: op2.parentKey,
|
|
1769
|
+
data: op2.data,
|
|
1770
|
+
version: op2.version
|
|
1771
|
+
};
|
|
1772
|
+
case OpCode2.CREATE_FILE:
|
|
1773
|
+
return {
|
|
1774
|
+
type: CrdtType5.FILE,
|
|
1775
|
+
parentId: op2.parentId,
|
|
1776
|
+
parentKey: op2.parentKey,
|
|
1777
|
+
data: op2.data
|
|
1778
|
+
};
|
|
1601
1779
|
// istanbul ignore next
|
|
1602
1780
|
default:
|
|
1603
1781
|
return assertNever2(op2, "Unknown op code");
|
|
@@ -1639,9 +1817,13 @@ var Storage = class {
|
|
|
1639
1817
|
case OpCode2.CREATE_MAP:
|
|
1640
1818
|
case OpCode2.CREATE_REGISTER:
|
|
1641
1819
|
case OpCode2.CREATE_OBJECT:
|
|
1820
|
+
case OpCode2.CREATE_TEXT:
|
|
1821
|
+
case OpCode2.CREATE_FILE:
|
|
1642
1822
|
return this.applyCreateOp(op2);
|
|
1643
1823
|
case OpCode2.UPDATE_OBJECT:
|
|
1644
1824
|
return this.applyUpdateObjectOp(op2);
|
|
1825
|
+
case OpCode2.UPDATE_TEXT:
|
|
1826
|
+
return this.applyUpdateTextOp(op2);
|
|
1645
1827
|
case OpCode2.SET_PARENT_KEY:
|
|
1646
1828
|
return this.applySetParentKeyOp(op2);
|
|
1647
1829
|
case OpCode2.DELETE_OBJECT_KEY:
|
|
@@ -1662,7 +1844,14 @@ var Storage = class {
|
|
|
1662
1844
|
if (op2.intent === "push") {
|
|
1663
1845
|
const stored = this.driver.get_node(op2.id);
|
|
1664
1846
|
if (stored?.parentId !== void 0 && this.driver.get_node(stored.parentId)?.type === CrdtType5.LIST) {
|
|
1665
|
-
return rectify(
|
|
1847
|
+
return rectify(
|
|
1848
|
+
{ ...op2, parentKey: stored.parentKey },
|
|
1849
|
+
{
|
|
1850
|
+
type: OpCode2.SET_PARENT_KEY,
|
|
1851
|
+
id: op2.id,
|
|
1852
|
+
parentKey: stored.parentKey
|
|
1853
|
+
}
|
|
1854
|
+
);
|
|
1666
1855
|
}
|
|
1667
1856
|
}
|
|
1668
1857
|
return ignore(op2);
|
|
@@ -1684,6 +1873,8 @@ var Storage = class {
|
|
|
1684
1873
|
case CrdtType5.LIST:
|
|
1685
1874
|
return this.createChildAsListItem(op2, node);
|
|
1686
1875
|
case CrdtType5.REGISTER:
|
|
1876
|
+
case CrdtType5.TEXT:
|
|
1877
|
+
case CrdtType5.FILE:
|
|
1687
1878
|
return ignore(op2);
|
|
1688
1879
|
// istanbul ignore next
|
|
1689
1880
|
default:
|
|
@@ -1740,6 +1931,59 @@ var Storage = class {
|
|
|
1740
1931
|
this.driver.set_object_data(op2.id, op2.data, true);
|
|
1741
1932
|
return accept(op2);
|
|
1742
1933
|
}
|
|
1934
|
+
applyUpdateTextOp(op2) {
|
|
1935
|
+
const node = this.driver.get_node(op2.id);
|
|
1936
|
+
if (node?.type !== CrdtType5.TEXT) {
|
|
1937
|
+
return ignore(op2);
|
|
1938
|
+
}
|
|
1939
|
+
const duplicate = this.driver.get_live_text_history_by_op_id(
|
|
1940
|
+
op2.id,
|
|
1941
|
+
op2.opId
|
|
1942
|
+
);
|
|
1943
|
+
if (duplicate !== void 0) {
|
|
1944
|
+
return rectify({
|
|
1945
|
+
...op2,
|
|
1946
|
+
baseVersion: duplicate.baseVersion,
|
|
1947
|
+
version: duplicate.version,
|
|
1948
|
+
ops: [...duplicate.ops]
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
if (op2.ops.length === 0) {
|
|
1952
|
+
return ignore(op2);
|
|
1953
|
+
}
|
|
1954
|
+
if (op2.baseVersion > node.version) {
|
|
1955
|
+
return reject(op2, "LiveText operation base version is ahead of storage");
|
|
1956
|
+
}
|
|
1957
|
+
const history = op2.baseVersion < node.version ? this.driver.get_live_text_history_since(op2.id, op2.baseVersion) : [];
|
|
1958
|
+
if (op2.baseVersion < node.version && history.length !== node.version - op2.baseVersion) {
|
|
1959
|
+
return reject(op2, LIVE_TEXT_HISTORY_TOO_OLD_REASON);
|
|
1960
|
+
}
|
|
1961
|
+
const acceptedOps = history.flatMap((entry) => entry.ops);
|
|
1962
|
+
const ops = acceptedOps.length > 0 ? transformTextOperations(op2.ops, acceptedOps, "after") : op2.ops;
|
|
1963
|
+
const version = node.version + 1;
|
|
1964
|
+
const data = applyLiveTextOperations(node.data, ops);
|
|
1965
|
+
this.driver.set_child(
|
|
1966
|
+
op2.id,
|
|
1967
|
+
{
|
|
1968
|
+
...node,
|
|
1969
|
+
data,
|
|
1970
|
+
version
|
|
1971
|
+
},
|
|
1972
|
+
true
|
|
1973
|
+
);
|
|
1974
|
+
this.driver.append_live_text_history({
|
|
1975
|
+
nodeId: op2.id,
|
|
1976
|
+
baseVersion: node.version,
|
|
1977
|
+
version,
|
|
1978
|
+
opId: op2.opId,
|
|
1979
|
+
ops: [...ops]
|
|
1980
|
+
});
|
|
1981
|
+
this.driver.purge_live_text_history_before(
|
|
1982
|
+
op2.id,
|
|
1983
|
+
Math.max(0, version - LIVE_TEXT_HISTORY_LIMIT + 1)
|
|
1984
|
+
);
|
|
1985
|
+
return accept({ ...op2, baseVersion: node.version, version, ops: [...ops] });
|
|
1986
|
+
}
|
|
1743
1987
|
applyDeleteCrdtOp(op2) {
|
|
1744
1988
|
this.driver.delete_node(op2.id);
|
|
1745
1989
|
return accept(op2);
|
|
@@ -2157,7 +2401,7 @@ function isLeasedSessionExpired(leasedSession) {
|
|
|
2157
2401
|
|
|
2158
2402
|
// src/Room.ts
|
|
2159
2403
|
var MB = 1024 * 1024;
|
|
2160
|
-
var messagesDecoder =
|
|
2404
|
+
var messagesDecoder = array4(clientMsgDecoder);
|
|
2161
2405
|
var ServerMsgCode2 = { ...CoreServerMsgCode, ...FeedMsgCode };
|
|
2162
2406
|
var HIGHEST_PROTOCOL_VERSION = Math.max(
|
|
2163
2407
|
...Object.values(ProtocolVersion).filter(
|
|
@@ -3128,9 +3372,11 @@ var Room = class {
|
|
|
3128
3372
|
case "ignored":
|
|
3129
3373
|
return r.ignoredOpId !== void 0 ? [ackIgnoredOp(r.ignoredOpId)] : [];
|
|
3130
3374
|
case "rectified":
|
|
3131
|
-
return [r.ackOp, r.fix];
|
|
3375
|
+
return r.fix !== void 0 ? [r.ackOp, r.fix] : [r.ackOp];
|
|
3132
3376
|
case "accepted":
|
|
3133
3377
|
return r.fix !== void 0 ? [r.fix] : [];
|
|
3378
|
+
case "rejected":
|
|
3379
|
+
return [];
|
|
3134
3380
|
// istanbul ignore next
|
|
3135
3381
|
default:
|
|
3136
3382
|
return assertNever3(r, "Unhandled case");
|
|
@@ -3153,6 +3399,13 @@ var Room = class {
|
|
|
3153
3399
|
ops: opsToSendBack
|
|
3154
3400
|
});
|
|
3155
3401
|
}
|
|
3402
|
+
for (const rejected of result.filter((r) => r.action === "rejected")) {
|
|
3403
|
+
replyImmediately({
|
|
3404
|
+
type: ServerMsgCode2.REJECT_STORAGE_OP,
|
|
3405
|
+
opIds: rejected.opIds,
|
|
3406
|
+
reason: rejected.reason
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3156
3409
|
if (opsToForward.length > 0) {
|
|
3157
3410
|
const p$ = this.hooks.postClientMsgStorageDidUpdate?.(ctx, session);
|
|
3158
3411
|
if (p$) defer(p$);
|