@liveblocks/core 3.16.0 → 3.17.0
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 +413 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -4
- package/dist/index.d.ts +105 -4
- package/dist/index.js +341 -96
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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.
|
|
9
|
+
var PKG_VERSION = "3.17.0";
|
|
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(
|
|
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
|
-
#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
8182
|
+
for (const [key, value] of this.#synced) {
|
|
8067
8183
|
if (value === child) {
|
|
8068
|
-
this.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
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.#
|
|
8399
|
+
return this.#local.has(key) ? this.#local.get(key) : this.#synced.get(key);
|
|
8240
8400
|
}
|
|
8241
8401
|
/**
|
|
8242
|
-
*
|
|
8243
|
-
*
|
|
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
|
-
|
|
8406
|
+
#prepareDelete(key) {
|
|
8246
8407
|
this._pool?.assertStorageIsWritable();
|
|
8247
|
-
const
|
|
8248
|
-
|
|
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.#
|
|
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,
|
|
8453
|
+
reverse = oldValue._toOps(this._id, k);
|
|
8264
8454
|
} else {
|
|
8265
8455
|
reverse = [
|
|
8266
8456
|
{
|
|
8267
8457
|
type: OpCode.UPDATE_OBJECT,
|
|
8268
|
-
data: { [
|
|
8458
|
+
data: { [k]: oldValue },
|
|
8269
8459
|
id: this._id
|
|
8270
8460
|
}
|
|
8271
8461
|
];
|
|
8272
8462
|
}
|
|
8273
|
-
this.#
|
|
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
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
8672
|
+
const cloned = new _LiveObject(
|
|
8444
8673
|
Object.fromEntries(
|
|
8445
|
-
Array.from(this.#
|
|
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
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
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
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
if (
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
12046
|
+
legacy_patchLiveList(value, prev, next);
|
|
11802
12047
|
} else if (isLiveObject(value) && isPlainObject(prev) && isPlainObject(next)) {
|
|
11803
|
-
|
|
12048
|
+
legacy_patchLiveObject(value, prev, next);
|
|
11804
12049
|
} else {
|
|
11805
12050
|
liveObject.set(key, deepLiveify(next));
|
|
11806
12051
|
}
|
|
11807
12052
|
}
|
|
11808
|
-
function
|
|
12053
|
+
function legacy_patchLiveObject(root, prev, next) {
|
|
11809
12054
|
const updates = {};
|
|
11810
12055
|
for (const key in next) {
|
|
11811
|
-
|
|
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,
|