@liveblocks/core 3.16.0 → 3.17.0-rc1

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
@@ -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.16.0";
9
+ var PKG_VERSION = "3.17.0-rc1";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -6319,7 +6319,6 @@ var AbstractCrdt = class {
6319
6319
  }
6320
6320
  /**
6321
6321
  * @internal
6322
- *
6323
6322
  * Return an snapshot of this Live tree for use in DevTools.
6324
6323
  */
6325
6324
  toTreeNode(key) {
@@ -6329,6 +6328,14 @@ var AbstractCrdt = class {
6329
6328
  }
6330
6329
  return this.#cachedTreeNode;
6331
6330
  }
6331
+ /**
6332
+ * @private
6333
+ * Returns true if the cached immutable snapshot exists and is
6334
+ * reference-equal to the given value. Does not trigger a recompute.
6335
+ */
6336
+ immutableIs(value) {
6337
+ return this.#cachedImmutable !== void 0 && this.#cachedImmutable === value;
6338
+ }
6332
6339
  /**
6333
6340
  * Return an immutable snapshot of this Live node and its children.
6334
6341
  */
@@ -6728,7 +6735,10 @@ var LiveList = class _LiveList extends AbstractCrdt {
6728
6735
  };
6729
6736
  } else {
6730
6737
  if (indexOfItemWithSamePosition !== -1) {
6731
- nn(this.#items.removeAt(indexOfItemWithSamePosition));
6738
+ const displaced = nn(
6739
+ this.#items.removeAt(indexOfItemWithSamePosition)
6740
+ );
6741
+ this.#implicitlyDeletedItems.add(displaced);
6732
6742
  }
6733
6743
  const { newItem, newIndex } = this.#createAttachItemAndSort(
6734
6744
  op,
@@ -7876,10 +7886,115 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7876
7886
  }
7877
7887
  };
7878
7888
 
7889
+ // src/crdts/reconcile.ts
7890
+ function deepLiveify(value, config) {
7891
+ if (Array.isArray(value)) {
7892
+ return new LiveList(value.map((v) => deepLiveify(v, config)));
7893
+ } else if (isPlainObject(value)) {
7894
+ const init = {};
7895
+ const locals = {};
7896
+ for (const key in value) {
7897
+ const val = value[key];
7898
+ if (val === void 0) {
7899
+ continue;
7900
+ }
7901
+ const subConfig = isPlainObject(config) ? config[key] : config;
7902
+ if (subConfig === false) {
7903
+ locals[key] = val;
7904
+ } else if (subConfig === "atomic") {
7905
+ init[key] = val;
7906
+ } else {
7907
+ init[key] = deepLiveify(val, subConfig);
7908
+ }
7909
+ }
7910
+ const lo = new LiveObject(init);
7911
+ for (const key in locals) {
7912
+ lo.setLocal(key, locals[key]);
7913
+ }
7914
+ return lo;
7915
+ } else {
7916
+ return value;
7917
+ }
7918
+ }
7919
+ function reconcile(live, json, config) {
7920
+ if (isLiveObject(live) && isPlainObject(json)) {
7921
+ return reconcileLiveObject(live, json, config);
7922
+ } else if (isLiveList(live) && Array.isArray(json)) {
7923
+ return reconcileLiveList(live, json, config);
7924
+ } else if (isLiveMap(live) && isPlainObject(json)) {
7925
+ return reconcileLiveMap(live, config);
7926
+ } else {
7927
+ return deepLiveify(json, config);
7928
+ }
7929
+ }
7930
+ function reconcileLiveMap(_liveMap, _config) {
7931
+ throw new Error("Reconciling a LiveMap is not supported yet");
7932
+ }
7933
+ function reconcileLiveObject(liveObj, jsonObj, config) {
7934
+ const currentKeys = liveObj.keys();
7935
+ for (const key in jsonObj) {
7936
+ currentKeys.delete(key);
7937
+ const newVal = jsonObj[key];
7938
+ if (newVal === void 0) {
7939
+ liveObj.delete(key);
7940
+ continue;
7941
+ }
7942
+ const subConfig = isPlainObject(config) ? config[key] : config;
7943
+ if (subConfig === false) {
7944
+ liveObj.setLocal(key, newVal);
7945
+ } else if (subConfig === "atomic") {
7946
+ const curVal = liveObj.get(key);
7947
+ if (curVal !== newVal) {
7948
+ liveObj.set(key, newVal);
7949
+ }
7950
+ } else {
7951
+ const curVal = liveObj.get(key);
7952
+ if (curVal === void 0) {
7953
+ liveObj.set(key, deepLiveify(newVal, subConfig));
7954
+ } else if (isLiveStructure(curVal)) {
7955
+ const next = reconcile(curVal, newVal, subConfig);
7956
+ if (next !== curVal) {
7957
+ liveObj.set(key, next);
7958
+ }
7959
+ } else if (curVal !== newVal) {
7960
+ liveObj.set(key, deepLiveify(newVal, subConfig));
7961
+ }
7962
+ }
7963
+ }
7964
+ for (const key of currentKeys) {
7965
+ liveObj.delete(key);
7966
+ }
7967
+ return liveObj;
7968
+ }
7969
+ function reconcileLiveList(liveList, jsonArr, config) {
7970
+ const curLen = liveList.length;
7971
+ const newLen = jsonArr.length;
7972
+ for (let i = 0; i < Math.min(curLen, newLen); i++) {
7973
+ const curVal = liveList.get(i);
7974
+ const newVal = jsonArr[i];
7975
+ if (isLiveStructure(curVal)) {
7976
+ const next = reconcile(curVal, newVal, config);
7977
+ if (next !== curVal) {
7978
+ liveList.set(i, next);
7979
+ }
7980
+ } else if (curVal !== newVal) {
7981
+ liveList.set(i, deepLiveify(newVal, config));
7982
+ }
7983
+ }
7984
+ for (let i = curLen; i < newLen; i++) {
7985
+ liveList.push(deepLiveify(jsonArr[i], config));
7986
+ }
7987
+ for (let i = curLen - 1; i >= newLen; i--) {
7988
+ liveList.delete(i);
7989
+ }
7990
+ return liveList;
7991
+ }
7992
+
7879
7993
  // src/crdts/LiveObject.ts
7880
7994
  var MAX_LIVE_OBJECT_SIZE = 128 * 1024;
7881
7995
  var LiveObject = class _LiveObject extends AbstractCrdt {
7882
- #map;
7996
+ #synced;
7997
+ #local = /* @__PURE__ */ new Map();
7883
7998
  /**
7884
7999
  * Tracks unacknowledged local changes per property to preserve optimistic
7885
8000
  * updates. Maps property keys to their pending operation IDs.
@@ -7944,7 +8059,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
7944
8059
  value._setParentLink(this, key);
7945
8060
  }
7946
8061
  }
7947
- this.#map = new Map(Object.entries(o));
8062
+ this.#synced = new Map(Object.entries(o));
7948
8063
  }
7949
8064
  /** @internal */
7950
8065
  _toOps(parentId, parentKey) {
@@ -7960,7 +8075,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
7960
8075
  data: {}
7961
8076
  };
7962
8077
  ops.push(op);
7963
- for (const [key, value] of this.#map) {
8078
+ for (const [key, value] of this.#synced) {
7964
8079
  if (isLiveNode(value)) {
7965
8080
  for (const childOp of value._toOps(this._id, key)) {
7966
8081
  ops.push(childOp);
@@ -7989,7 +8104,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
7989
8104
  if (isLiveStructure(child)) {
7990
8105
  child._setParentLink(liveObj, crdt.parentKey);
7991
8106
  }
7992
- liveObj.#map.set(crdt.parentKey, child);
8107
+ liveObj.#synced.set(crdt.parentKey, child);
7993
8108
  liveObj.invalidate();
7994
8109
  }
7995
8110
  return liveObj;
@@ -7997,7 +8112,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
7997
8112
  /** @internal */
7998
8113
  _attach(id, pool) {
7999
8114
  super._attach(id, pool);
8000
- for (const [_key, value] of this.#map) {
8115
+ for (const [_key, value] of this.#synced) {
8001
8116
  if (isLiveNode(value)) {
8002
8117
  value._attach(pool.generateId(), pool);
8003
8118
  }
@@ -8026,7 +8141,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8026
8141
  return { modified: false };
8027
8142
  }
8028
8143
  const thisId = nn(this._id);
8029
- const previousValue = this.#map.get(key);
8144
+ const previousValue = this.#synced.get(key);
8030
8145
  let reverse;
8031
8146
  if (isLiveNode(previousValue)) {
8032
8147
  reverse = previousValue._toOps(thisId, key);
@@ -8042,7 +8157,8 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8042
8157
  }
8043
8158
  ];
8044
8159
  }
8045
- this.#map.set(key, child);
8160
+ this.#local.delete(key);
8161
+ this.#synced.set(key, child);
8046
8162
  this.invalidate();
8047
8163
  if (isLiveStructure(child)) {
8048
8164
  child._setParentLink(this, key);
@@ -8063,9 +8179,9 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8063
8179
  const id = nn(this._id);
8064
8180
  const parentKey = nn(child._parentKey);
8065
8181
  const reverse = child._toOps(id, parentKey);
8066
- for (const [key, value] of this.#map) {
8182
+ for (const [key, value] of this.#synced) {
8067
8183
  if (value === child) {
8068
- this.#map.delete(key);
8184
+ this.#synced.delete(key);
8069
8185
  this.invalidate();
8070
8186
  }
8071
8187
  }
@@ -8084,7 +8200,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8084
8200
  /** @internal */
8085
8201
  _detach() {
8086
8202
  super._detach();
8087
- for (const value of this.#map.values()) {
8203
+ for (const value of this.#synced.values()) {
8088
8204
  if (isLiveNode(value)) {
8089
8205
  value._detach();
8090
8206
  }
@@ -8102,7 +8218,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8102
8218
  /** @internal */
8103
8219
  _serialize() {
8104
8220
  const data = {};
8105
- for (const [key, value] of this.#map) {
8221
+ for (const [key, value] of this.#synced) {
8106
8222
  if (!isLiveNode(value)) {
8107
8223
  data[key] = value;
8108
8224
  }
@@ -8131,7 +8247,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8131
8247
  data: {}
8132
8248
  };
8133
8249
  for (const key in op.data) {
8134
- const oldValue = this.#map.get(key);
8250
+ const oldValue = this.#synced.get(key);
8135
8251
  if (isLiveNode(oldValue)) {
8136
8252
  for (const childOp of oldValue._toOps(id, key)) {
8137
8253
  reverse.push(childOp);
@@ -8159,13 +8275,14 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8159
8275
  } else {
8160
8276
  continue;
8161
8277
  }
8162
- const oldValue = this.#map.get(key);
8278
+ const oldValue = this.#synced.get(key);
8163
8279
  if (isLiveNode(oldValue)) {
8164
8280
  oldValue._detach();
8165
8281
  }
8166
8282
  isModified = true;
8167
8283
  updateDelta[key] = { type: "update" };
8168
- this.#map.set(key, value);
8284
+ this.#local.delete(key);
8285
+ this.#synced.set(key, value);
8169
8286
  this.invalidate();
8170
8287
  }
8171
8288
  if (Object.keys(reverseUpdate.data).length !== 0) {
@@ -8182,7 +8299,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8182
8299
  }
8183
8300
  #applyDeleteObjectKey(op, isLocal) {
8184
8301
  const key = op.key;
8185
- const oldValue = this.#map.get(key);
8302
+ const oldValue = this.#synced.get(key);
8186
8303
  if (oldValue === void 0) {
8187
8304
  return { modified: false };
8188
8305
  }
@@ -8203,7 +8320,8 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8203
8320
  }
8204
8321
  ];
8205
8322
  }
8206
- this.#map.delete(key);
8323
+ this.#local.delete(key);
8324
+ this.#synced.delete(key);
8207
8325
  this.invalidate();
8208
8326
  return {
8209
8327
  modified: {
@@ -8216,11 +8334,23 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8216
8334
  reverse
8217
8335
  };
8218
8336
  }
8337
+ /** @private */
8338
+ keys() {
8339
+ const result = new Set(this.#synced.keys());
8340
+ for (const key of this.#local.keys()) {
8341
+ result.add(key);
8342
+ }
8343
+ return result;
8344
+ }
8219
8345
  /**
8220
8346
  * Transform the LiveObject into a javascript object
8221
8347
  */
8222
8348
  toObject() {
8223
- return Object.fromEntries(this.#map);
8349
+ const result = Object.fromEntries(this.#synced);
8350
+ for (const [key, value] of this.#local) {
8351
+ result[key] = value;
8352
+ }
8353
+ return result;
8224
8354
  }
8225
8355
  /**
8226
8356
  * Adds or updates a property with a specified key and a value.
@@ -8228,49 +8358,109 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8228
8358
  * @param value The value of the property to add
8229
8359
  */
8230
8360
  set(key, value) {
8231
- this._pool?.assertStorageIsWritable();
8232
8361
  this.update({ [key]: value });
8233
8362
  }
8363
+ /**
8364
+ * @experimental
8365
+ *
8366
+ * Sets a local-only property that is not synchronized over the wire.
8367
+ * The value will be visible via get(), toObject(), and toImmutable() on
8368
+ * this client only. Other clients and the server will see `undefined`
8369
+ * for this key.
8370
+ *
8371
+ * Caveat: this method will not add changes to the undo/redo stack.
8372
+ */
8373
+ setLocal(key, value) {
8374
+ this._pool?.assertStorageIsWritable();
8375
+ const deleteResult = this.#prepareDelete(key);
8376
+ this.#local.set(key, value);
8377
+ this.invalidate();
8378
+ if (this._pool !== void 0 && this._id !== void 0) {
8379
+ const ops = deleteResult?.[0] ?? [];
8380
+ const reverse = deleteResult?.[1] ?? [];
8381
+ const storageUpdates = deleteResult?.[2] ?? /* @__PURE__ */ new Map();
8382
+ const existing = storageUpdates.get(this._id);
8383
+ storageUpdates.set(this._id, {
8384
+ node: this,
8385
+ type: "LiveObject",
8386
+ updates: {
8387
+ ...existing?.updates,
8388
+ [key]: { type: "update" }
8389
+ }
8390
+ });
8391
+ this._pool.dispatch(ops, reverse, storageUpdates);
8392
+ }
8393
+ }
8234
8394
  /**
8235
8395
  * Returns a specified property from the LiveObject.
8236
8396
  * @param key The key of the property to get
8237
8397
  */
8238
8398
  get(key) {
8239
- return this.#map.get(key);
8399
+ return this.#local.has(key) ? this.#local.get(key) : this.#synced.get(key);
8240
8400
  }
8241
8401
  /**
8242
- * Deletes a key from the LiveObject
8243
- * @param key The key of the property to delete
8402
+ * Removes a synced key, returning the ops, reverse ops, and storage updates
8403
+ * needed to notify the pool. Returns null if the key doesn't exist in
8404
+ * #synced or pool/id are unavailable. Does NOT dispatch.
8244
8405
  */
8245
- delete(key) {
8406
+ #prepareDelete(key) {
8246
8407
  this._pool?.assertStorageIsWritable();
8247
- const keyAsString = key;
8248
- const oldValue = this.#map.get(keyAsString);
8408
+ const k = key;
8409
+ if (this.#local.has(k) && !this.#synced.has(k)) {
8410
+ const oldValue2 = this.#local.get(k);
8411
+ this.#local.delete(k);
8412
+ this.invalidate();
8413
+ if (this._pool !== void 0 && this._id !== void 0) {
8414
+ const storageUpdates2 = /* @__PURE__ */ new Map();
8415
+ storageUpdates2.set(this._id, {
8416
+ node: this,
8417
+ type: "LiveObject",
8418
+ updates: {
8419
+ [k]: {
8420
+ type: "delete",
8421
+ deletedItem: oldValue2
8422
+ }
8423
+ }
8424
+ });
8425
+ return [[], [], storageUpdates2];
8426
+ }
8427
+ return null;
8428
+ }
8429
+ this.#local.delete(k);
8430
+ const oldValue = this.#synced.get(k);
8249
8431
  if (oldValue === void 0) {
8250
- return;
8432
+ return null;
8251
8433
  }
8252
8434
  if (this._pool === void 0 || this._id === void 0) {
8253
8435
  if (isLiveNode(oldValue)) {
8254
8436
  oldValue._detach();
8255
8437
  }
8256
- this.#map.delete(keyAsString);
8438
+ this.#synced.delete(k);
8257
8439
  this.invalidate();
8258
- return;
8440
+ return null;
8259
8441
  }
8442
+ const ops = [
8443
+ {
8444
+ type: OpCode.DELETE_OBJECT_KEY,
8445
+ key: k,
8446
+ id: this._id,
8447
+ opId: this._pool.generateOpId()
8448
+ }
8449
+ ];
8260
8450
  let reverse;
8261
8451
  if (isLiveNode(oldValue)) {
8262
8452
  oldValue._detach();
8263
- reverse = oldValue._toOps(this._id, keyAsString);
8453
+ reverse = oldValue._toOps(this._id, k);
8264
8454
  } else {
8265
8455
  reverse = [
8266
8456
  {
8267
8457
  type: OpCode.UPDATE_OBJECT,
8268
- data: { [keyAsString]: oldValue },
8458
+ data: { [k]: oldValue },
8269
8459
  id: this._id
8270
8460
  }
8271
8461
  ];
8272
8462
  }
8273
- this.#map.delete(keyAsString);
8463
+ this.#synced.delete(k);
8274
8464
  this.invalidate();
8275
8465
  const storageUpdates = /* @__PURE__ */ new Map();
8276
8466
  storageUpdates.set(this._id, {
@@ -8280,18 +8470,18 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8280
8470
  [key]: { type: "delete", deletedItem: oldValue }
8281
8471
  }
8282
8472
  });
8283
- this._pool.dispatch(
8284
- [
8285
- {
8286
- type: OpCode.DELETE_OBJECT_KEY,
8287
- key: keyAsString,
8288
- id: this._id,
8289
- opId: this._pool.generateOpId()
8290
- }
8291
- ],
8292
- reverse,
8293
- storageUpdates
8294
- );
8473
+ return [ops, reverse, storageUpdates];
8474
+ }
8475
+ /**
8476
+ * Deletes a key from the LiveObject
8477
+ * @param key The key of the property to delete
8478
+ */
8479
+ delete(key) {
8480
+ const result = this.#prepareDelete(key);
8481
+ if (result) {
8482
+ const [ops, reverse, storageUpdates] = result;
8483
+ this._pool?.dispatch(ops, reverse, storageUpdates);
8484
+ }
8295
8485
  }
8296
8486
  /**
8297
8487
  * Adds or updates multiple properties at once with an object.
@@ -8301,7 +8491,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8301
8491
  this._pool?.assertStorageIsWritable();
8302
8492
  if (_LiveObject.detectLargeObjects) {
8303
8493
  const data = {};
8304
- for (const [key, value] of this.#map) {
8494
+ for (const [key, value] of this.#synced) {
8305
8495
  if (!isLiveNode(value)) {
8306
8496
  data[key] = value;
8307
8497
  }
@@ -8330,14 +8520,15 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8330
8520
  if (newValue === void 0) {
8331
8521
  continue;
8332
8522
  }
8333
- const oldValue = this.#map.get(key);
8523
+ const oldValue = this.#synced.get(key);
8334
8524
  if (isLiveNode(oldValue)) {
8335
8525
  oldValue._detach();
8336
8526
  }
8337
8527
  if (isLiveNode(newValue)) {
8338
8528
  newValue._setParentLink(this, key);
8339
8529
  }
8340
- this.#map.set(key, newValue);
8530
+ this.#local.delete(key);
8531
+ this.#synced.set(key, newValue);
8341
8532
  this.invalidate();
8342
8533
  }
8343
8534
  return;
@@ -8357,7 +8548,10 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8357
8548
  if (newValue === void 0) {
8358
8549
  continue;
8359
8550
  }
8360
- const oldValue = this.#map.get(key);
8551
+ const oldValue = this.#synced.get(key);
8552
+ if (oldValue === newValue) {
8553
+ continue;
8554
+ }
8361
8555
  if (isLiveNode(oldValue)) {
8362
8556
  for (const childOp of oldValue._toOps(this._id, key)) {
8363
8557
  reverseOps.push(childOp);
@@ -8389,7 +8583,8 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8389
8583
  updatedProps[key] = newValue;
8390
8584
  this.#unackedOpsByKey.set(key, opId);
8391
8585
  }
8392
- this.#map.set(key, newValue);
8586
+ this.#local.delete(key);
8587
+ this.#synced.set(key, newValue);
8393
8588
  this.invalidate();
8394
8589
  updateDelta[key] = { type: "update" };
8395
8590
  }
@@ -8404,6 +8599,9 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8404
8599
  data: updatedProps
8405
8600
  });
8406
8601
  }
8602
+ if (ops.length === 0 && reverseOps.length === 0 && Object.keys(updateDelta).length === 0) {
8603
+ return;
8604
+ }
8407
8605
  const storageUpdates = /* @__PURE__ */ new Map();
8408
8606
  storageUpdates.set(this._id, {
8409
8607
  node: this,
@@ -8412,6 +8610,34 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8412
8610
  });
8413
8611
  this._pool.dispatch(ops, reverseOps, storageUpdates);
8414
8612
  }
8613
+ /**
8614
+ * Creates a new LiveObject from a plain JSON object, recursively converting
8615
+ * nested objects to LiveObjects and arrays to LiveLists. An optional
8616
+ * SyncConfig controls per-key behavior (local-only, atomic, or deep).
8617
+ *
8618
+ * @private
8619
+ */
8620
+ static from(obj, config) {
8621
+ if (!isPlainObject(obj)) throw new Error("Expected a JSON object");
8622
+ const liveObj = new _LiveObject({});
8623
+ liveObj.reconcile(obj, config);
8624
+ return liveObj;
8625
+ }
8626
+ /**
8627
+ * Reconciles a LiveObject tree to match the given JSON object and sync
8628
+ * config. Only mutates keys that actually changed. Recursively reconciles
8629
+ * the entire Live tree below it.
8630
+ *
8631
+ * @private
8632
+ */
8633
+ reconcile(jsonObj, config) {
8634
+ if (this.immutableIs(jsonObj)) return;
8635
+ if (!isPlainObject(jsonObj))
8636
+ throw new Error(
8637
+ "Reconciling the document root expects a plain object value"
8638
+ );
8639
+ reconcileLiveObject(this, jsonObj, config);
8640
+ }
8415
8641
  toImmutable() {
8416
8642
  return super.toImmutable();
8417
8643
  }
@@ -8426,7 +8652,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8426
8652
  type: "LiveObject",
8427
8653
  id: nodeId,
8428
8654
  key,
8429
- payload: Array.from(this.#map.entries()).map(
8655
+ payload: Array.from(this.#synced.entries()).map(
8430
8656
  ([key2, value]) => isLiveNode(value) ? value.toTreeNode(key2) : { type: "Json", id: `${nodeId}:${key2}`, key: key2, payload: value }
8431
8657
  )
8432
8658
  };
@@ -8434,20 +8660,27 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8434
8660
  /** @internal */
8435
8661
  _toImmutable() {
8436
8662
  const result = {};
8437
- for (const [key, val] of this.#map) {
8663
+ for (const [key, val] of this.#synced) {
8438
8664
  result[key] = isLiveStructure(val) ? val.toImmutable() : val;
8439
8665
  }
8666
+ for (const [key, val] of this.#local) {
8667
+ result[key] = val;
8668
+ }
8440
8669
  return process.env.NODE_ENV === "production" ? result : Object.freeze(result);
8441
8670
  }
8442
8671
  clone() {
8443
- return new _LiveObject(
8672
+ const cloned = new _LiveObject(
8444
8673
  Object.fromEntries(
8445
- Array.from(this.#map).map(([key, value]) => [
8674
+ Array.from(this.#synced).map(([key, value]) => [
8446
8675
  key,
8447
8676
  isLiveStructure(value) ? value.clone() : deepClone(value)
8448
8677
  ])
8449
8678
  )
8450
8679
  );
8680
+ for (const [key, value] of this.#local) {
8681
+ cloned.#local.set(key, deepClone(value));
8682
+ }
8683
+ return cloned;
8451
8684
  }
8452
8685
  };
8453
8686
 
@@ -9255,9 +9488,13 @@ function createRoom(options, config) {
9255
9488
  }
9256
9489
  context.activeBatch.reverseOps.pushLeft(reverse);
9257
9490
  } else {
9258
- addToUndoStack(reverse);
9259
- context.redoStack.length = 0;
9260
- dispatchOps(ops);
9491
+ if (reverse.length > 0) {
9492
+ addToUndoStack(reverse);
9493
+ }
9494
+ if (ops.length > 0) {
9495
+ context.redoStack.length = 0;
9496
+ dispatchOps(ops);
9497
+ }
9261
9498
  notify({ storageUpdates });
9262
9499
  }
9263
9500
  }
@@ -9367,19 +9604,20 @@ function createRoom(options, config) {
9367
9604
  );
9368
9605
  }
9369
9606
  const canWrite = self.get()?.canWrite ?? true;
9370
- const stackSizeBefore = context.undoStack.length;
9371
- for (const key in context.initialStorage) {
9372
- if (context.root.get(key) === void 0) {
9373
- if (canWrite) {
9374
- context.root.set(key, cloneLson(context.initialStorage[key]));
9375
- } else {
9376
- warn(
9377
- `Attempted to populate missing storage key '${key}', but current user has no write access`
9378
- );
9607
+ const root = context.root;
9608
+ withoutHistory(() => {
9609
+ for (const key in context.initialStorage) {
9610
+ if (root.get(key) === void 0) {
9611
+ if (canWrite) {
9612
+ root.set(key, cloneLson(context.initialStorage[key]));
9613
+ } else {
9614
+ warn(
9615
+ `Attempted to populate missing storage key '${key}', but current user has no write access`
9616
+ );
9617
+ }
9379
9618
  }
9380
9619
  }
9381
- }
9382
- context.undoStack.length = stackSizeBefore;
9620
+ });
9383
9621
  }
9384
9622
  function _addToRealUndoStack(frames) {
9385
9623
  if (context.undoStack.length >= 50) {
@@ -10397,7 +10635,8 @@ function createRoom(options, config) {
10397
10635
  presence: false,
10398
10636
  others: []
10399
10637
  },
10400
- reverseOps: new Deque()
10638
+ reverseOps: new Deque(),
10639
+ scheduleHistoryResume: false
10401
10640
  };
10402
10641
  try {
10403
10642
  returnValue = callback();
@@ -10407,6 +10646,9 @@ function createRoom(options, config) {
10407
10646
  if (currentBatch.reverseOps.length > 0) {
10408
10647
  addToUndoStack(Array.from(currentBatch.reverseOps));
10409
10648
  }
10649
+ if (currentBatch.scheduleHistoryResume) {
10650
+ commitPausedHistoryToUndoStack();
10651
+ }
10410
10652
  if (currentBatch.ops.length > 0) {
10411
10653
  context.redoStack.length = 0;
10412
10654
  }
@@ -10423,13 +10665,30 @@ function createRoom(options, config) {
10423
10665
  context.pausedHistory = new Deque();
10424
10666
  }
10425
10667
  }
10426
- function resumeHistory() {
10668
+ function commitPausedHistoryToUndoStack() {
10427
10669
  const frames = context.pausedHistory;
10428
10670
  context.pausedHistory = null;
10429
10671
  if (frames !== null && frames.length > 0) {
10430
10672
  _addToRealUndoStack(Array.from(frames));
10431
10673
  }
10432
10674
  }
10675
+ function resumeHistory() {
10676
+ if (context.activeBatch !== null) {
10677
+ context.activeBatch.scheduleHistoryResume = true;
10678
+ return;
10679
+ }
10680
+ commitPausedHistoryToUndoStack();
10681
+ }
10682
+ function withoutHistory(fn) {
10683
+ const undoBefore = context.undoStack.length;
10684
+ const redoBefore = context.redoStack.length;
10685
+ try {
10686
+ return fn();
10687
+ } finally {
10688
+ context.undoStack.length = undoBefore;
10689
+ context.redoStack.length = redoBefore;
10690
+ }
10691
+ }
10433
10692
  const syncSourceForStorage = config.createSyncSource();
10434
10693
  function getStorageStatus() {
10435
10694
  if (context.root === void 0) {
@@ -10735,7 +10994,10 @@ function createRoom(options, config) {
10735
10994
  canRedo,
10736
10995
  clear,
10737
10996
  pause: pauseHistory,
10738
- resume: resumeHistory
10997
+ resume: resumeHistory,
10998
+ [kInternal]: {
10999
+ withoutHistory
11000
+ }
10739
11001
  },
10740
11002
  fetchYDoc,
10741
11003
  fetchFeeds,
@@ -11697,24 +11959,7 @@ function lsonToJson(value) {
11697
11959
  }
11698
11960
  return value;
11699
11961
  }
11700
- function deepLiveify(value) {
11701
- if (Array.isArray(value)) {
11702
- return new LiveList(value.map(deepLiveify));
11703
- } else if (isPlainObject(value)) {
11704
- const init = {};
11705
- for (const key in value) {
11706
- const val = value[key];
11707
- if (val === void 0) {
11708
- continue;
11709
- }
11710
- init[key] = deepLiveify(val);
11711
- }
11712
- return new LiveObject(init);
11713
- } else {
11714
- return value;
11715
- }
11716
- }
11717
- function patchLiveList(liveList, prev, next) {
11962
+ function legacy_patchLiveList(liveList, prev, next) {
11718
11963
  let i = 0;
11719
11964
  let prevEnd = prev.length - 1;
11720
11965
  let nextEnd = next.length - 1;
@@ -11760,7 +12005,7 @@ function patchLiveList(liveList, prev, next) {
11760
12005
  nextNode = next[i];
11761
12006
  const liveListNode = liveList.get(i);
11762
12007
  if (isLiveObject(liveListNode) && isPlainObject(prevNode) && isPlainObject(nextNode)) {
11763
- patchLiveObject(liveListNode, prevNode, nextNode);
12008
+ legacy_patchLiveObject(liveListNode, prevNode, nextNode);
11764
12009
  } else {
11765
12010
  liveList.set(i, deepLiveify(nextNode));
11766
12011
  }
@@ -11777,7 +12022,7 @@ function patchLiveList(liveList, prev, next) {
11777
12022
  }
11778
12023
  }
11779
12024
  }
11780
- function patchLiveObjectKey(liveObject, key, prev, next) {
12025
+ function legacy_patchLiveObjectKey(liveObject, key, prev, next) {
11781
12026
  if (process.env.NODE_ENV !== "production") {
11782
12027
  const nonSerializableValue = findNonSerializableValue(next);
11783
12028
  if (nonSerializableValue) {
@@ -11798,17 +12043,17 @@ Only serializable value can be synced with Liveblocks.`
11798
12043
  } else if (prev === next) {
11799
12044
  return;
11800
12045
  } else if (isLiveList(value) && Array.isArray(prev) && Array.isArray(next)) {
11801
- patchLiveList(value, prev, next);
12046
+ legacy_patchLiveList(value, prev, next);
11802
12047
  } else if (isLiveObject(value) && isPlainObject(prev) && isPlainObject(next)) {
11803
- patchLiveObject(value, prev, next);
12048
+ legacy_patchLiveObject(value, prev, next);
11804
12049
  } else {
11805
12050
  liveObject.set(key, deepLiveify(next));
11806
12051
  }
11807
12052
  }
11808
- function patchLiveObject(root, prev, next) {
12053
+ function legacy_patchLiveObject(root, prev, next) {
11809
12054
  const updates = {};
11810
12055
  for (const key in next) {
11811
- patchLiveObjectKey(root, key, prev[key], next[key]);
12056
+ legacy_patchLiveObjectKey(root, key, prev[key], next[key]);
11812
12057
  }
11813
12058
  for (const key in prev) {
11814
12059
  if (next[key] === void 0) {
@@ -12202,6 +12447,7 @@ export {
12202
12447
  kInternal,
12203
12448
  keys,
12204
12449
  legacy_patchImmutableObject,
12450
+ legacy_patchLiveObjectKey,
12205
12451
  lsonToJson,
12206
12452
  makeAbortController,
12207
12453
  makeEventSource,
@@ -12213,7 +12459,6 @@ export {
12213
12459
  nn,
12214
12460
  nodeStreamToCompactNodes,
12215
12461
  objectToQuery,
12216
- patchLiveObjectKey,
12217
12462
  patchNotificationSettings,
12218
12463
  raise,
12219
12464
  resolveMentionsInCommentBody,