@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.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 array2,
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(number)
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: array2(op)
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(number),
282
- limit: optional3(number),
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(number),
291
- limit: optional3(number)
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(number),
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(number),
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(number),
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 array3, formatInline } from "decoders";
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
- if (node.type !== CrdtType4.REGISTER) {
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 rectify(op2, parentKey) {
1567
- return {
1568
- action: "rectified",
1569
- ackOp: { ...op2, parentKey },
1570
- fix: { type: OpCode2.SET_PARENT_KEY, id: op2.id, parentKey }
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(op2, stored.parentKey);
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 = array3(clientMsgDecoder);
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$);