@liveblocks/core 3.16.0-flow3 → 3.16.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.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-flow3";
9
+ var PKG_VERSION = "3.16.0";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -3219,10 +3219,26 @@ var ServerMsgCode = Object.freeze({
3219
3219
  COMMENT_REACTION_ADDED: 405,
3220
3220
  COMMENT_REACTION_REMOVED: 406,
3221
3221
  COMMENT_METADATA_UPDATED: 409,
3222
+ // For Feeds
3223
+ FEEDS_LIST: 500,
3224
+ FEEDS_ADDED: 501,
3225
+ FEEDS_UPDATED: 502,
3226
+ FEED_DELETED: 503,
3227
+ FEED_MESSAGES_LIST: 504,
3228
+ FEED_MESSAGES_ADDED: 505,
3229
+ FEED_MESSAGES_UPDATED: 506,
3230
+ FEED_MESSAGES_DELETED: 507,
3231
+ FEED_REQUEST_FAILED: 508,
3222
3232
  // Error codes
3223
3233
  REJECT_STORAGE_OP: 299
3224
3234
  // Sent if a mutation was not allowed on the server (i.e. due to permissions, limit exceeded, etc)
3225
3235
  });
3236
+ var FeedRequestErrorCode = {
3237
+ INTERNAL: "INTERNAL",
3238
+ FEED_ALREADY_EXISTS: "FEED_ALREADY_EXISTS",
3239
+ FEED_NOT_FOUND: "FEED_NOT_FOUND",
3240
+ FEED_MESSAGE_NOT_FOUND: "FEED_MESSAGE_NOT_FOUND"
3241
+ };
3226
3242
 
3227
3243
  // src/types/IWebSocket.ts
3228
3244
  var WebsocketCloseCodes = /* @__PURE__ */ ((WebsocketCloseCodes2) => {
@@ -5186,6 +5202,7 @@ var Permission = /* @__PURE__ */ ((Permission2) => {
5186
5202
  Permission2["PresenceWrite"] = "room:presence:write";
5187
5203
  Permission2["CommentsWrite"] = "comments:write";
5188
5204
  Permission2["CommentsRead"] = "comments:read";
5205
+ Permission2["FeedsWrite"] = "feeds:write";
5189
5206
  return Permission2;
5190
5207
  })(Permission || {});
5191
5208
  function canWriteStorage(scopes) {
@@ -6302,6 +6319,7 @@ var AbstractCrdt = class {
6302
6319
  }
6303
6320
  /**
6304
6321
  * @internal
6322
+ *
6305
6323
  * Return an snapshot of this Live tree for use in DevTools.
6306
6324
  */
6307
6325
  toTreeNode(key) {
@@ -6311,14 +6329,6 @@ var AbstractCrdt = class {
6311
6329
  }
6312
6330
  return this.#cachedTreeNode;
6313
6331
  }
6314
- /**
6315
- * @private
6316
- * Returns true if the cached immutable snapshot exists and is
6317
- * reference-equal to the given value. Does not trigger a recompute.
6318
- */
6319
- immutableIs(value) {
6320
- return this.#cachedImmutable !== void 0 && this.#cachedImmutable === value;
6321
- }
6322
6332
  /**
6323
6333
  * Return an immutable snapshot of this Live node and its children.
6324
6334
  */
@@ -7866,649 +7876,248 @@ var LiveMap = class _LiveMap extends AbstractCrdt {
7866
7876
  }
7867
7877
  };
7868
7878
 
7869
- // src/lib/Json.ts
7870
- function isJsonScalar(data) {
7871
- return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
7872
- }
7873
- function isJsonArray(data) {
7874
- return Array.isArray(data);
7875
- }
7876
- function isJsonObject(data) {
7877
- return !isJsonScalar(data) && !isJsonArray(data);
7878
- }
7879
-
7880
- // src/immutable.ts
7881
- function lsonObjectToJson(obj) {
7882
- const result = {};
7883
- for (const key in obj) {
7884
- const val = obj[key];
7885
- if (val !== void 0) {
7886
- result[key] = lsonToJson(val);
7887
- }
7888
- }
7889
- return result;
7890
- }
7891
- function liveObjectToJson(liveObject) {
7892
- return lsonObjectToJson(liveObject.toObject());
7893
- }
7894
- function liveMapToJson(map) {
7895
- const result = {};
7896
- for (const [key, value] of map.entries()) {
7897
- result[key] = lsonToJson(value);
7898
- }
7899
- return result;
7900
- }
7901
- function lsonListToJson(value) {
7902
- return value.map(lsonToJson);
7903
- }
7904
- function liveListToJson(value) {
7905
- return lsonListToJson(value.toArray());
7906
- }
7907
- function lsonToJson(value) {
7908
- if (value instanceof LiveObject) {
7909
- return liveObjectToJson(value);
7910
- } else if (value instanceof LiveList) {
7911
- return liveListToJson(value);
7912
- } else if (value instanceof LiveMap) {
7913
- return liveMapToJson(value);
7914
- } else if (value instanceof LiveRegister) {
7915
- return value.data;
7916
- }
7917
- if (Array.isArray(value)) {
7918
- return lsonListToJson(value);
7919
- } else if (isPlainObject(value)) {
7920
- return lsonObjectToJson(value);
7921
- }
7922
- return value;
7923
- }
7924
- function deepLiveify(value, config) {
7925
- if (Array.isArray(value)) {
7926
- return new LiveList(value.map((v) => deepLiveify(v, config)));
7927
- } else if (isPlainObject(value)) {
7928
- const init = {};
7929
- const locals = {};
7930
- for (const key in value) {
7931
- const val = value[key];
7932
- if (val === void 0) {
7933
- continue;
7934
- }
7935
- const subConfig = isPlainObject(config) ? config[key] : config;
7936
- if (subConfig === false) {
7937
- locals[key] = val;
7938
- } else if (subConfig === "atomic") {
7939
- init[key] = val;
7879
+ // src/crdts/LiveObject.ts
7880
+ var MAX_LIVE_OBJECT_SIZE = 128 * 1024;
7881
+ var LiveObject = class _LiveObject extends AbstractCrdt {
7882
+ #map;
7883
+ /**
7884
+ * Tracks unacknowledged local changes per property to preserve optimistic
7885
+ * updates. Maps property keys to their pending operation IDs.
7886
+ *
7887
+ * INVARIANT: Only locally-generated opIds are ever stored here. Remote opIds
7888
+ * are only compared against (to detect ACKs), never stored.
7889
+ *
7890
+ * When a local change is made, the opId is stored here. When a remote op
7891
+ * arrives for the same key:
7892
+ * - If no entry exists → apply remote op
7893
+ * - If opId matches → it's an ACK, clear the entry
7894
+ * - If opId differs → ignore remote op to preserve optimistic update
7895
+ */
7896
+ #unackedOpsByKey;
7897
+ /**
7898
+ * Enable or disable detection of too large LiveObjects.
7899
+ * When enabled, throws an error if LiveObject static data exceeds 128KB, which
7900
+ * is the maximum value the server will be able to accept.
7901
+ * By default, this behavior is disabled to avoid the runtime performance
7902
+ * overhead on every LiveObject.set() or LiveObject.update() call.
7903
+ *
7904
+ * @experimental
7905
+ */
7906
+ static detectLargeObjects = false;
7907
+ static #buildRootAndParentToChildren(nodes) {
7908
+ const parentToChildren = /* @__PURE__ */ new Map();
7909
+ let root = null;
7910
+ for (const node of nodes) {
7911
+ if (isRootStorageNode(node)) {
7912
+ root = node[1];
7940
7913
  } else {
7941
- init[key] = deepLiveify(val, subConfig);
7914
+ const crdt = node[1];
7915
+ const children = parentToChildren.get(crdt.parentId);
7916
+ if (children !== void 0) {
7917
+ children.push(node);
7918
+ } else {
7919
+ parentToChildren.set(crdt.parentId, [node]);
7920
+ }
7942
7921
  }
7943
7922
  }
7944
- const lo = new LiveObject(init);
7945
- for (const key in locals) {
7946
- lo.setLocal(key, locals[key]);
7923
+ if (root === null) {
7924
+ throw new Error("Root can't be null");
7947
7925
  }
7948
- return lo;
7949
- } else {
7950
- return value;
7926
+ return [root, parentToChildren];
7951
7927
  }
7952
- }
7953
- function reconcile(live, json, config) {
7954
- if (isLiveObject(live) && isPlainObject(json)) {
7955
- return reconcileLiveObject(live, json, config);
7956
- } else if (isLiveList(live) && Array.isArray(json)) {
7957
- return reconcileLiveList(live, json, config);
7958
- } else if (isLiveMap(live) && isPlainObject(json)) {
7959
- return reconcileLiveMap(live, config);
7960
- } else {
7961
- return deepLiveify(json, config);
7928
+ /** @private Do not use this API directly */
7929
+ static _fromItems(nodes, pool) {
7930
+ const [root, parentToChildren] = _LiveObject.#buildRootAndParentToChildren(nodes);
7931
+ return _LiveObject._deserialize(
7932
+ ["root", root],
7933
+ parentToChildren,
7934
+ pool
7935
+ );
7962
7936
  }
7963
- }
7964
- function reconcileLiveMap(_liveMap, _config) {
7965
- throw new Error("Reconciling a LiveMap is not supported yet");
7966
- }
7967
- function reconcileLiveObject(liveObj, jsonObj, config) {
7968
- const currentKeys = liveObj.keys();
7969
- for (const key in jsonObj) {
7970
- currentKeys.delete(key);
7971
- const newVal = jsonObj[key];
7972
- if (newVal === void 0) {
7973
- liveObj.delete(key);
7974
- continue;
7975
- }
7976
- const subConfig = isPlainObject(config) ? config[key] : config;
7977
- if (subConfig === false) {
7978
- liveObj.setLocal(key, newVal);
7979
- } else if (subConfig === "atomic") {
7980
- const curVal = liveObj.get(key);
7981
- if (curVal !== newVal) {
7982
- liveObj.set(key, newVal);
7983
- }
7984
- } else {
7985
- const curVal = liveObj.get(key);
7986
- if (curVal === void 0) {
7987
- liveObj.set(key, deepLiveify(newVal, subConfig));
7988
- } else if (isLiveStructure(curVal)) {
7989
- const next = reconcile(curVal, newVal, subConfig);
7990
- if (next !== curVal) {
7991
- liveObj.set(key, next);
7992
- }
7993
- } else if (curVal !== newVal) {
7994
- liveObj.set(key, deepLiveify(newVal, subConfig));
7937
+ constructor(obj = {}) {
7938
+ super();
7939
+ this.#unackedOpsByKey = /* @__PURE__ */ new Map();
7940
+ const o = compactObject(obj);
7941
+ for (const key of Object.keys(o)) {
7942
+ const value = o[key];
7943
+ if (isLiveNode(value)) {
7944
+ value._setParentLink(this, key);
7995
7945
  }
7996
7946
  }
7947
+ this.#map = new Map(Object.entries(o));
7997
7948
  }
7998
- for (const key of currentKeys) {
7999
- liveObj.delete(key);
8000
- }
8001
- return liveObj;
8002
- }
8003
- function reconcileLiveList(liveList, jsonArr, config) {
8004
- const curLen = liveList.length;
8005
- const newLen = jsonArr.length;
8006
- for (let i = 0; i < Math.min(curLen, newLen); i++) {
8007
- const curVal = liveList.get(i);
8008
- const newVal = jsonArr[i];
8009
- if (isLiveStructure(curVal)) {
8010
- const next = reconcile(curVal, newVal, config);
8011
- if (next !== curVal) {
8012
- liveList.set(i, next);
7949
+ /** @internal */
7950
+ _toOps(parentId, parentKey) {
7951
+ if (this._id === void 0) {
7952
+ throw new Error("Cannot serialize item is not attached");
7953
+ }
7954
+ const ops = [];
7955
+ const op = {
7956
+ type: OpCode.CREATE_OBJECT,
7957
+ id: this._id,
7958
+ parentId,
7959
+ parentKey,
7960
+ data: {}
7961
+ };
7962
+ ops.push(op);
7963
+ for (const [key, value] of this.#map) {
7964
+ if (isLiveNode(value)) {
7965
+ for (const childOp of value._toOps(this._id, key)) {
7966
+ ops.push(childOp);
7967
+ }
7968
+ } else {
7969
+ op.data[key] = value;
8013
7970
  }
8014
- } else if (curVal !== newVal) {
8015
- liveList.set(i, deepLiveify(newVal, config));
8016
7971
  }
7972
+ return ops;
8017
7973
  }
8018
- for (let i = curLen; i < newLen; i++) {
8019
- liveList.push(deepLiveify(jsonArr[i], config));
8020
- }
8021
- for (let i = curLen - 1; i >= newLen; i--) {
8022
- liveList.delete(i);
7974
+ /** @internal */
7975
+ static _deserialize([id, item], parentToChildren, pool) {
7976
+ const liveObj = new _LiveObject(item.data);
7977
+ liveObj._attach(id, pool);
7978
+ return this._deserializeChildren(liveObj, parentToChildren, pool);
8023
7979
  }
8024
- return liveList;
8025
- }
8026
- function legacy_patchLiveList(liveList, prev, next) {
8027
- let i = 0;
8028
- let prevEnd = prev.length - 1;
8029
- let nextEnd = next.length - 1;
8030
- let prevNode = prev[0];
8031
- let nextNode = next[0];
8032
- outer: {
8033
- while (prevNode === nextNode) {
8034
- ++i;
8035
- if (i > prevEnd || i > nextEnd) {
8036
- break outer;
8037
- }
8038
- prevNode = prev[i];
8039
- nextNode = next[i];
7980
+ /** @internal */
7981
+ static _deserializeChildren(liveObj, parentToChildren, pool) {
7982
+ const children = parentToChildren.get(nn(liveObj._id));
7983
+ if (children === void 0) {
7984
+ return liveObj;
8040
7985
  }
8041
- prevNode = prev[prevEnd];
8042
- nextNode = next[nextEnd];
8043
- while (prevNode === nextNode) {
8044
- prevEnd--;
8045
- nextEnd--;
8046
- if (i > prevEnd || i > nextEnd) {
8047
- break outer;
7986
+ for (const node of children) {
7987
+ const child = deserializeToLson(node, parentToChildren, pool);
7988
+ const crdt = node[1];
7989
+ if (isLiveStructure(child)) {
7990
+ child._setParentLink(liveObj, crdt.parentKey);
8048
7991
  }
8049
- prevNode = prev[prevEnd];
8050
- nextNode = next[nextEnd];
7992
+ liveObj.#map.set(crdt.parentKey, child);
7993
+ liveObj.invalidate();
8051
7994
  }
7995
+ return liveObj;
8052
7996
  }
8053
- if (i > prevEnd) {
8054
- if (i <= nextEnd) {
8055
- while (i <= nextEnd) {
8056
- liveList.insert(deepLiveify(next[i]), i);
8057
- i++;
7997
+ /** @internal */
7998
+ _attach(id, pool) {
7999
+ super._attach(id, pool);
8000
+ for (const [_key, value] of this.#map) {
8001
+ if (isLiveNode(value)) {
8002
+ value._attach(pool.generateId(), pool);
8058
8003
  }
8059
8004
  }
8060
- } else if (i > nextEnd) {
8061
- let localI = i;
8062
- while (localI <= prevEnd) {
8063
- liveList.delete(i);
8064
- localI++;
8005
+ }
8006
+ /** @internal */
8007
+ _attachChild(op, source) {
8008
+ if (this._pool === void 0) {
8009
+ throw new Error("Can't attach child if managed pool is not present");
8065
8010
  }
8066
- } else {
8067
- while (i <= prevEnd && i <= nextEnd) {
8068
- prevNode = prev[i];
8069
- nextNode = next[i];
8070
- const liveListNode = liveList.get(i);
8071
- if (isLiveObject(liveListNode) && isPlainObject(prevNode) && isPlainObject(nextNode)) {
8072
- legacy_patchLiveObject(liveListNode, prevNode, nextNode);
8073
- } else {
8074
- liveList.set(i, deepLiveify(nextNode));
8011
+ const { id, opId, parentKey: key } = op;
8012
+ const child = creationOpToLson(op);
8013
+ if (this._pool.getNode(id) !== void 0) {
8014
+ if (this.#unackedOpsByKey.get(key) === opId) {
8015
+ this.#unackedOpsByKey.delete(key);
8075
8016
  }
8076
- i++;
8077
- }
8078
- while (i <= nextEnd) {
8079
- liveList.insert(deepLiveify(next[i]), i);
8080
- i++;
8017
+ return { modified: false };
8081
8018
  }
8082
- let localI = i;
8083
- while (localI <= prevEnd) {
8084
- liveList.delete(i);
8085
- localI++;
8019
+ if (source === 0 /* LOCAL */) {
8020
+ this.#unackedOpsByKey.set(key, nn(opId));
8021
+ } else if (this.#unackedOpsByKey.get(key) === void 0) {
8022
+ } else if (this.#unackedOpsByKey.get(key) === opId) {
8023
+ this.#unackedOpsByKey.delete(key);
8024
+ return { modified: false };
8025
+ } else {
8026
+ return { modified: false };
8086
8027
  }
8087
- }
8088
- }
8089
- function legacy_patchLiveObjectKey(liveObject, key, prev, next) {
8090
- if (process.env.NODE_ENV !== "production") {
8091
- const nonSerializableValue = findNonSerializableValue(next);
8092
- if (nonSerializableValue) {
8093
- error2(
8094
- `New state path: '${nonSerializableValue.path}' value: '${String(
8095
- nonSerializableValue.value
8096
- )}' is not serializable.
8097
- Only serializable value can be synced with Liveblocks.`
8098
- );
8099
- return;
8100
- }
8101
- }
8102
- const value = liveObject.get(key);
8103
- if (next === void 0) {
8104
- liveObject.delete(key);
8105
- } else if (value === void 0) {
8106
- liveObject.set(key, deepLiveify(next));
8107
- } else if (prev === next) {
8108
- return;
8109
- } else if (isLiveList(value) && Array.isArray(prev) && Array.isArray(next)) {
8110
- legacy_patchLiveList(value, prev, next);
8111
- } else if (isLiveObject(value) && isPlainObject(prev) && isPlainObject(next)) {
8112
- legacy_patchLiveObject(value, prev, next);
8113
- } else {
8114
- liveObject.set(key, deepLiveify(next));
8115
- }
8116
- }
8117
- function legacy_patchLiveObject(root, prev, next) {
8118
- const updates = {};
8119
- for (const key in next) {
8120
- legacy_patchLiveObjectKey(root, key, prev[key], next[key]);
8121
- }
8122
- for (const key in prev) {
8123
- if (next[key] === void 0) {
8124
- root.delete(key);
8125
- }
8126
- }
8127
- if (Object.keys(updates).length > 0) {
8128
- root.update(updates);
8129
- }
8130
- }
8131
- function getParentsPath(node) {
8132
- const path = [];
8133
- while (node.parent.type === "HasParent") {
8134
- if (isLiveList(node.parent.node)) {
8135
- path.push(node.parent.node._indexOfPosition(node.parent.key));
8028
+ const thisId = nn(this._id);
8029
+ const previousValue = this.#map.get(key);
8030
+ let reverse;
8031
+ if (isLiveNode(previousValue)) {
8032
+ reverse = previousValue._toOps(thisId, key);
8033
+ previousValue._detach();
8034
+ } else if (previousValue === void 0) {
8035
+ reverse = [{ type: OpCode.DELETE_OBJECT_KEY, id: thisId, key }];
8136
8036
  } else {
8137
- path.push(node.parent.key);
8138
- }
8139
- node = node.parent.node;
8140
- }
8141
- return path;
8142
- }
8143
- function legacy_patchImmutableObject(state, updates) {
8144
- return updates.reduce(
8145
- (state2, update) => legacy_patchImmutableObjectWithUpdate(state2, update),
8146
- state
8147
- );
8148
- }
8149
- function legacy_patchImmutableObjectWithUpdate(state, update) {
8150
- const path = getParentsPath(update.node);
8151
- return legacy_patchImmutableNode(state, path, update);
8152
- }
8153
- function legacy_patchImmutableNode(state, path, update) {
8154
- const pathItem = path.pop();
8155
- if (pathItem === void 0) {
8156
- switch (update.type) {
8157
- case "LiveObject": {
8158
- if (!isJsonObject(state)) {
8159
- throw new Error(
8160
- "Internal: received update on LiveObject but state was not an object"
8161
- );
8162
- }
8163
- const newState = Object.assign({}, state);
8164
- for (const key in update.updates) {
8165
- if (update.updates[key]?.type === "update") {
8166
- const val = update.node.get(key);
8167
- if (val !== void 0) {
8168
- newState[key] = lsonToJson(val);
8169
- }
8170
- } else if (update.updates[key]?.type === "delete") {
8171
- delete newState[key];
8172
- }
8037
+ reverse = [
8038
+ {
8039
+ type: OpCode.UPDATE_OBJECT,
8040
+ id: thisId,
8041
+ data: { [key]: previousValue }
8173
8042
  }
8174
- return newState;
8043
+ ];
8044
+ }
8045
+ this.#map.set(key, child);
8046
+ this.invalidate();
8047
+ if (isLiveStructure(child)) {
8048
+ child._setParentLink(this, key);
8049
+ child._attach(id, this._pool);
8050
+ }
8051
+ return {
8052
+ reverse,
8053
+ modified: {
8054
+ node: this,
8055
+ type: "LiveObject",
8056
+ updates: { [key]: { type: "update" } }
8175
8057
  }
8176
- case "LiveList": {
8177
- if (!Array.isArray(state)) {
8178
- throw new Error(
8179
- "Internal: received update on LiveList but state was not an array"
8180
- );
8181
- }
8182
- let newState = state.map((x) => x);
8183
- for (const listUpdate of update.updates) {
8184
- if (listUpdate.type === "set") {
8185
- newState = newState.map(
8186
- (item, index) => index === listUpdate.index ? lsonToJson(listUpdate.item) : item
8187
- );
8188
- } else if (listUpdate.type === "insert") {
8189
- if (listUpdate.index === newState.length) {
8190
- newState.push(lsonToJson(listUpdate.item));
8191
- } else {
8192
- newState = [
8193
- ...newState.slice(0, listUpdate.index),
8194
- lsonToJson(listUpdate.item),
8195
- ...newState.slice(listUpdate.index)
8196
- ];
8197
- }
8198
- } else if (listUpdate.type === "delete") {
8199
- newState.splice(listUpdate.index, 1);
8200
- } else if (listUpdate.type === "move") {
8201
- if (listUpdate.previousIndex > listUpdate.index) {
8202
- newState = [
8203
- ...newState.slice(0, listUpdate.index),
8204
- lsonToJson(listUpdate.item),
8205
- ...newState.slice(listUpdate.index, listUpdate.previousIndex),
8206
- ...newState.slice(listUpdate.previousIndex + 1)
8207
- ];
8208
- } else {
8209
- newState = [
8210
- ...newState.slice(0, listUpdate.previousIndex),
8211
- ...newState.slice(
8212
- listUpdate.previousIndex + 1,
8213
- listUpdate.index + 1
8214
- ),
8215
- lsonToJson(listUpdate.item),
8216
- ...newState.slice(listUpdate.index + 1)
8217
- ];
8218
- }
8219
- }
8058
+ };
8059
+ }
8060
+ /** @internal */
8061
+ _detachChild(child) {
8062
+ if (child) {
8063
+ const id = nn(this._id);
8064
+ const parentKey = nn(child._parentKey);
8065
+ const reverse = child._toOps(id, parentKey);
8066
+ for (const [key, value] of this.#map) {
8067
+ if (value === child) {
8068
+ this.#map.delete(key);
8069
+ this.invalidate();
8220
8070
  }
8221
- return newState;
8222
8071
  }
8223
- case "LiveMap": {
8224
- if (!isJsonObject(state)) {
8225
- throw new Error(
8226
- "Internal: received update on LiveMap but state was not an object"
8227
- );
8228
- }
8229
- const newState = Object.assign({}, state);
8230
- for (const key in update.updates) {
8231
- if (update.updates[key]?.type === "update") {
8232
- const value = update.node.get(key);
8233
- if (value !== void 0) {
8234
- newState[key] = lsonToJson(value);
8235
- }
8236
- } else if (update.updates[key]?.type === "delete") {
8237
- delete newState[key];
8238
- }
8072
+ child._detach();
8073
+ const storageUpdate = {
8074
+ node: this,
8075
+ type: "LiveObject",
8076
+ updates: {
8077
+ [parentKey]: { type: "delete" }
8239
8078
  }
8240
- return newState;
8079
+ };
8080
+ return { modified: storageUpdate, reverse };
8081
+ }
8082
+ return { modified: false };
8083
+ }
8084
+ /** @internal */
8085
+ _detach() {
8086
+ super._detach();
8087
+ for (const value of this.#map.values()) {
8088
+ if (isLiveNode(value)) {
8089
+ value._detach();
8241
8090
  }
8242
8091
  }
8243
8092
  }
8244
- if (Array.isArray(state)) {
8245
- const newArray = [...state];
8246
- newArray[pathItem] = legacy_patchImmutableNode(
8247
- state[pathItem],
8248
- path,
8249
- update
8250
- );
8251
- return newArray;
8252
- } else if (isJsonObject(state)) {
8253
- const node = state[pathItem];
8254
- if (node === void 0) {
8255
- return state;
8093
+ /** @internal */
8094
+ _apply(op, isLocal) {
8095
+ if (op.type === OpCode.UPDATE_OBJECT) {
8096
+ return this.#applyUpdate(op, isLocal);
8097
+ } else if (op.type === OpCode.DELETE_OBJECT_KEY) {
8098
+ return this.#applyDeleteObjectKey(op, isLocal);
8099
+ }
8100
+ return super._apply(op, isLocal);
8101
+ }
8102
+ /** @internal */
8103
+ _serialize() {
8104
+ const data = {};
8105
+ for (const [key, value] of this.#map) {
8106
+ if (!isLiveNode(value)) {
8107
+ data[key] = value;
8108
+ }
8109
+ }
8110
+ if (this.parent.type === "HasParent" && this.parent.node._id) {
8111
+ return {
8112
+ type: CrdtType.OBJECT,
8113
+ parentId: this.parent.node._id,
8114
+ parentKey: this.parent.key,
8115
+ data
8116
+ };
8256
8117
  } else {
8257
- const stateAsObj = state;
8258
8118
  return {
8259
- ...stateAsObj,
8260
- [pathItem]: legacy_patchImmutableNode(node, path, update)
8261
- };
8262
- }
8263
- } else {
8264
- return state;
8265
- }
8266
- }
8267
-
8268
- // src/crdts/LiveObject.ts
8269
- var MAX_LIVE_OBJECT_SIZE = 128 * 1024;
8270
- var LiveObject = class _LiveObject extends AbstractCrdt {
8271
- #synced;
8272
- #local = /* @__PURE__ */ new Map();
8273
- /**
8274
- * Tracks unacknowledged local changes per property to preserve optimistic
8275
- * updates. Maps property keys to their pending operation IDs.
8276
- *
8277
- * INVARIANT: Only locally-generated opIds are ever stored here. Remote opIds
8278
- * are only compared against (to detect ACKs), never stored.
8279
- *
8280
- * When a local change is made, the opId is stored here. When a remote op
8281
- * arrives for the same key:
8282
- * - If no entry exists → apply remote op
8283
- * - If opId matches → it's an ACK, clear the entry
8284
- * - If opId differs → ignore remote op to preserve optimistic update
8285
- */
8286
- #unackedOpsByKey;
8287
- /**
8288
- * Enable or disable detection of too large LiveObjects.
8289
- * When enabled, throws an error if LiveObject static data exceeds 128KB, which
8290
- * is the maximum value the server will be able to accept.
8291
- * By default, this behavior is disabled to avoid the runtime performance
8292
- * overhead on every LiveObject.set() or LiveObject.update() call.
8293
- *
8294
- * @experimental
8295
- */
8296
- static detectLargeObjects = false;
8297
- static #buildRootAndParentToChildren(nodes) {
8298
- const parentToChildren = /* @__PURE__ */ new Map();
8299
- let root = null;
8300
- for (const node of nodes) {
8301
- if (isRootStorageNode(node)) {
8302
- root = node[1];
8303
- } else {
8304
- const crdt = node[1];
8305
- const children = parentToChildren.get(crdt.parentId);
8306
- if (children !== void 0) {
8307
- children.push(node);
8308
- } else {
8309
- parentToChildren.set(crdt.parentId, [node]);
8310
- }
8311
- }
8312
- }
8313
- if (root === null) {
8314
- throw new Error("Root can't be null");
8315
- }
8316
- return [root, parentToChildren];
8317
- }
8318
- /** @private Do not use this API directly */
8319
- static _fromItems(nodes, pool) {
8320
- const [root, parentToChildren] = _LiveObject.#buildRootAndParentToChildren(nodes);
8321
- return _LiveObject._deserialize(
8322
- ["root", root],
8323
- parentToChildren,
8324
- pool
8325
- );
8326
- }
8327
- constructor(obj = {}) {
8328
- super();
8329
- this.#unackedOpsByKey = /* @__PURE__ */ new Map();
8330
- const o = compactObject(obj);
8331
- for (const key of Object.keys(o)) {
8332
- const value = o[key];
8333
- if (isLiveNode(value)) {
8334
- value._setParentLink(this, key);
8335
- }
8336
- }
8337
- this.#synced = new Map(Object.entries(o));
8338
- }
8339
- /** @internal */
8340
- _toOps(parentId, parentKey) {
8341
- if (this._id === void 0) {
8342
- throw new Error("Cannot serialize item is not attached");
8343
- }
8344
- const ops = [];
8345
- const op = {
8346
- type: OpCode.CREATE_OBJECT,
8347
- id: this._id,
8348
- parentId,
8349
- parentKey,
8350
- data: {}
8351
- };
8352
- ops.push(op);
8353
- for (const [key, value] of this.#synced) {
8354
- if (isLiveNode(value)) {
8355
- for (const childOp of value._toOps(this._id, key)) {
8356
- ops.push(childOp);
8357
- }
8358
- } else {
8359
- op.data[key] = value;
8360
- }
8361
- }
8362
- return ops;
8363
- }
8364
- /** @internal */
8365
- static _deserialize([id, item], parentToChildren, pool) {
8366
- const liveObj = new _LiveObject(item.data);
8367
- liveObj._attach(id, pool);
8368
- return this._deserializeChildren(liveObj, parentToChildren, pool);
8369
- }
8370
- /** @internal */
8371
- static _deserializeChildren(liveObj, parentToChildren, pool) {
8372
- const children = parentToChildren.get(nn(liveObj._id));
8373
- if (children === void 0) {
8374
- return liveObj;
8375
- }
8376
- for (const node of children) {
8377
- const child = deserializeToLson(node, parentToChildren, pool);
8378
- const crdt = node[1];
8379
- if (isLiveStructure(child)) {
8380
- child._setParentLink(liveObj, crdt.parentKey);
8381
- }
8382
- liveObj.#synced.set(crdt.parentKey, child);
8383
- liveObj.invalidate();
8384
- }
8385
- return liveObj;
8386
- }
8387
- /** @internal */
8388
- _attach(id, pool) {
8389
- super._attach(id, pool);
8390
- for (const [_key, value] of this.#synced) {
8391
- if (isLiveNode(value)) {
8392
- value._attach(pool.generateId(), pool);
8393
- }
8394
- }
8395
- }
8396
- /** @internal */
8397
- _attachChild(op, source) {
8398
- if (this._pool === void 0) {
8399
- throw new Error("Can't attach child if managed pool is not present");
8400
- }
8401
- const { id, opId, parentKey: key } = op;
8402
- const child = creationOpToLson(op);
8403
- if (this._pool.getNode(id) !== void 0) {
8404
- if (this.#unackedOpsByKey.get(key) === opId) {
8405
- this.#unackedOpsByKey.delete(key);
8406
- }
8407
- return { modified: false };
8408
- }
8409
- if (source === 0 /* LOCAL */) {
8410
- this.#unackedOpsByKey.set(key, nn(opId));
8411
- } else if (this.#unackedOpsByKey.get(key) === void 0) {
8412
- } else if (this.#unackedOpsByKey.get(key) === opId) {
8413
- this.#unackedOpsByKey.delete(key);
8414
- return { modified: false };
8415
- } else {
8416
- return { modified: false };
8417
- }
8418
- const thisId = nn(this._id);
8419
- const previousValue = this.#synced.get(key);
8420
- let reverse;
8421
- if (isLiveNode(previousValue)) {
8422
- reverse = previousValue._toOps(thisId, key);
8423
- previousValue._detach();
8424
- } else if (previousValue === void 0) {
8425
- reverse = [{ type: OpCode.DELETE_OBJECT_KEY, id: thisId, key }];
8426
- } else {
8427
- reverse = [
8428
- {
8429
- type: OpCode.UPDATE_OBJECT,
8430
- id: thisId,
8431
- data: { [key]: previousValue }
8432
- }
8433
- ];
8434
- }
8435
- this.#local.delete(key);
8436
- this.#synced.set(key, child);
8437
- this.invalidate();
8438
- if (isLiveStructure(child)) {
8439
- child._setParentLink(this, key);
8440
- child._attach(id, this._pool);
8441
- }
8442
- return {
8443
- reverse,
8444
- modified: {
8445
- node: this,
8446
- type: "LiveObject",
8447
- updates: { [key]: { type: "update" } }
8448
- }
8449
- };
8450
- }
8451
- /** @internal */
8452
- _detachChild(child) {
8453
- if (child) {
8454
- const id = nn(this._id);
8455
- const parentKey = nn(child._parentKey);
8456
- const reverse = child._toOps(id, parentKey);
8457
- for (const [key, value] of this.#synced) {
8458
- if (value === child) {
8459
- this.#synced.delete(key);
8460
- this.invalidate();
8461
- }
8462
- }
8463
- child._detach();
8464
- const storageUpdate = {
8465
- node: this,
8466
- type: "LiveObject",
8467
- updates: {
8468
- [parentKey]: { type: "delete" }
8469
- }
8470
- };
8471
- return { modified: storageUpdate, reverse };
8472
- }
8473
- return { modified: false };
8474
- }
8475
- /** @internal */
8476
- _detach() {
8477
- super._detach();
8478
- for (const value of this.#synced.values()) {
8479
- if (isLiveNode(value)) {
8480
- value._detach();
8481
- }
8482
- }
8483
- }
8484
- /** @internal */
8485
- _apply(op, isLocal) {
8486
- if (op.type === OpCode.UPDATE_OBJECT) {
8487
- return this.#applyUpdate(op, isLocal);
8488
- } else if (op.type === OpCode.DELETE_OBJECT_KEY) {
8489
- return this.#applyDeleteObjectKey(op, isLocal);
8490
- }
8491
- return super._apply(op, isLocal);
8492
- }
8493
- /** @internal */
8494
- _serialize() {
8495
- const data = {};
8496
- for (const [key, value] of this.#synced) {
8497
- if (!isLiveNode(value)) {
8498
- data[key] = value;
8499
- }
8500
- }
8501
- if (this.parent.type === "HasParent" && this.parent.node._id) {
8502
- return {
8503
- type: CrdtType.OBJECT,
8504
- parentId: this.parent.node._id,
8505
- parentKey: this.parent.key,
8506
- data
8507
- };
8508
- } else {
8509
- return {
8510
- type: CrdtType.OBJECT,
8511
- data
8119
+ type: CrdtType.OBJECT,
8120
+ data
8512
8121
  };
8513
8122
  }
8514
8123
  }
@@ -8522,7 +8131,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8522
8131
  data: {}
8523
8132
  };
8524
8133
  for (const key in op.data) {
8525
- const oldValue = this.#synced.get(key);
8134
+ const oldValue = this.#map.get(key);
8526
8135
  if (isLiveNode(oldValue)) {
8527
8136
  for (const childOp of oldValue._toOps(id, key)) {
8528
8137
  reverse.push(childOp);
@@ -8550,14 +8159,13 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8550
8159
  } else {
8551
8160
  continue;
8552
8161
  }
8553
- const oldValue = this.#synced.get(key);
8162
+ const oldValue = this.#map.get(key);
8554
8163
  if (isLiveNode(oldValue)) {
8555
8164
  oldValue._detach();
8556
8165
  }
8557
8166
  isModified = true;
8558
8167
  updateDelta[key] = { type: "update" };
8559
- this.#local.delete(key);
8560
- this.#synced.set(key, value);
8168
+ this.#map.set(key, value);
8561
8169
  this.invalidate();
8562
8170
  }
8563
8171
  if (Object.keys(reverseUpdate.data).length !== 0) {
@@ -8574,7 +8182,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8574
8182
  }
8575
8183
  #applyDeleteObjectKey(op, isLocal) {
8576
8184
  const key = op.key;
8577
- const oldValue = this.#synced.get(key);
8185
+ const oldValue = this.#map.get(key);
8578
8186
  if (oldValue === void 0) {
8579
8187
  return { modified: false };
8580
8188
  }
@@ -8595,8 +8203,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8595
8203
  }
8596
8204
  ];
8597
8205
  }
8598
- this.#local.delete(key);
8599
- this.#synced.delete(key);
8206
+ this.#map.delete(key);
8600
8207
  this.invalidate();
8601
8208
  return {
8602
8209
  modified: {
@@ -8609,23 +8216,11 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8609
8216
  reverse
8610
8217
  };
8611
8218
  }
8612
- /** @private */
8613
- keys() {
8614
- const result = new Set(this.#synced.keys());
8615
- for (const key of this.#local.keys()) {
8616
- result.add(key);
8617
- }
8618
- return result;
8619
- }
8620
8219
  /**
8621
8220
  * Transform the LiveObject into a javascript object
8622
8221
  */
8623
8222
  toObject() {
8624
- const result = Object.fromEntries(this.#synced);
8625
- for (const [key, value] of this.#local) {
8626
- result[key] = value;
8627
- }
8628
- return result;
8223
+ return Object.fromEntries(this.#map);
8629
8224
  }
8630
8225
  /**
8631
8226
  * Adds or updates a property with a specified key and a value.
@@ -8633,109 +8228,49 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8633
8228
  * @param value The value of the property to add
8634
8229
  */
8635
8230
  set(key, value) {
8636
- this.update({ [key]: value });
8637
- }
8638
- /**
8639
- * @experimental
8640
- *
8641
- * Sets a local-only property that is not synchronized over the wire.
8642
- * The value will be visible via get(), toObject(), and toImmutable() on
8643
- * this client only. Other clients and the server will see `undefined`
8644
- * for this key.
8645
- *
8646
- * Caveat: this method will not add changes to the undo/redo stack.
8647
- */
8648
- setLocal(key, value) {
8649
8231
  this._pool?.assertStorageIsWritable();
8650
- const deleteResult = this.#prepareDelete(key);
8651
- this.#local.set(key, value);
8652
- this.invalidate();
8653
- if (this._pool !== void 0 && this._id !== void 0) {
8654
- const ops = deleteResult?.[0] ?? [];
8655
- const reverse = deleteResult?.[1] ?? [];
8656
- const storageUpdates = deleteResult?.[2] ?? /* @__PURE__ */ new Map();
8657
- const existing = storageUpdates.get(this._id);
8658
- storageUpdates.set(this._id, {
8659
- node: this,
8660
- type: "LiveObject",
8661
- updates: {
8662
- ...existing?.updates,
8663
- [key]: { type: "update" }
8664
- }
8665
- });
8666
- this._pool.dispatch(ops, reverse, storageUpdates);
8667
- }
8232
+ this.update({ [key]: value });
8668
8233
  }
8669
8234
  /**
8670
8235
  * Returns a specified property from the LiveObject.
8671
8236
  * @param key The key of the property to get
8672
8237
  */
8673
8238
  get(key) {
8674
- return this.#local.has(key) ? this.#local.get(key) : this.#synced.get(key);
8239
+ return this.#map.get(key);
8675
8240
  }
8676
8241
  /**
8677
- * Removes a synced key, returning the ops, reverse ops, and storage updates
8678
- * needed to notify the pool. Returns null if the key doesn't exist in
8679
- * #synced or pool/id are unavailable. Does NOT dispatch.
8242
+ * Deletes a key from the LiveObject
8243
+ * @param key The key of the property to delete
8680
8244
  */
8681
- #prepareDelete(key) {
8245
+ delete(key) {
8682
8246
  this._pool?.assertStorageIsWritable();
8683
- const k = key;
8684
- if (this.#local.has(k) && !this.#synced.has(k)) {
8685
- const oldValue2 = this.#local.get(k);
8686
- this.#local.delete(k);
8687
- this.invalidate();
8688
- if (this._pool !== void 0 && this._id !== void 0) {
8689
- const storageUpdates2 = /* @__PURE__ */ new Map();
8690
- storageUpdates2.set(this._id, {
8691
- node: this,
8692
- type: "LiveObject",
8693
- updates: {
8694
- [k]: {
8695
- type: "delete",
8696
- deletedItem: oldValue2
8697
- }
8698
- }
8699
- });
8700
- return [[], [], storageUpdates2];
8701
- }
8702
- return null;
8703
- }
8704
- this.#local.delete(k);
8705
- const oldValue = this.#synced.get(k);
8247
+ const keyAsString = key;
8248
+ const oldValue = this.#map.get(keyAsString);
8706
8249
  if (oldValue === void 0) {
8707
- return null;
8250
+ return;
8708
8251
  }
8709
8252
  if (this._pool === void 0 || this._id === void 0) {
8710
8253
  if (isLiveNode(oldValue)) {
8711
8254
  oldValue._detach();
8712
8255
  }
8713
- this.#synced.delete(k);
8256
+ this.#map.delete(keyAsString);
8714
8257
  this.invalidate();
8715
- return null;
8258
+ return;
8716
8259
  }
8717
- const ops = [
8718
- {
8719
- type: OpCode.DELETE_OBJECT_KEY,
8720
- key: k,
8721
- id: this._id,
8722
- opId: this._pool.generateOpId()
8723
- }
8724
- ];
8725
8260
  let reverse;
8726
8261
  if (isLiveNode(oldValue)) {
8727
8262
  oldValue._detach();
8728
- reverse = oldValue._toOps(this._id, k);
8263
+ reverse = oldValue._toOps(this._id, keyAsString);
8729
8264
  } else {
8730
8265
  reverse = [
8731
8266
  {
8732
8267
  type: OpCode.UPDATE_OBJECT,
8733
- data: { [k]: oldValue },
8268
+ data: { [keyAsString]: oldValue },
8734
8269
  id: this._id
8735
8270
  }
8736
8271
  ];
8737
8272
  }
8738
- this.#synced.delete(k);
8273
+ this.#map.delete(keyAsString);
8739
8274
  this.invalidate();
8740
8275
  const storageUpdates = /* @__PURE__ */ new Map();
8741
8276
  storageUpdates.set(this._id, {
@@ -8745,18 +8280,18 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8745
8280
  [key]: { type: "delete", deletedItem: oldValue }
8746
8281
  }
8747
8282
  });
8748
- return [ops, reverse, storageUpdates];
8749
- }
8750
- /**
8751
- * Deletes a key from the LiveObject
8752
- * @param key The key of the property to delete
8753
- */
8754
- delete(key) {
8755
- const result = this.#prepareDelete(key);
8756
- if (result) {
8757
- const [ops, reverse, storageUpdates] = result;
8758
- this._pool?.dispatch(ops, reverse, storageUpdates);
8759
- }
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
+ );
8760
8295
  }
8761
8296
  /**
8762
8297
  * Adds or updates multiple properties at once with an object.
@@ -8766,7 +8301,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8766
8301
  this._pool?.assertStorageIsWritable();
8767
8302
  if (_LiveObject.detectLargeObjects) {
8768
8303
  const data = {};
8769
- for (const [key, value] of this.#synced) {
8304
+ for (const [key, value] of this.#map) {
8770
8305
  if (!isLiveNode(value)) {
8771
8306
  data[key] = value;
8772
8307
  }
@@ -8795,15 +8330,14 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8795
8330
  if (newValue === void 0) {
8796
8331
  continue;
8797
8332
  }
8798
- const oldValue = this.#synced.get(key);
8333
+ const oldValue = this.#map.get(key);
8799
8334
  if (isLiveNode(oldValue)) {
8800
8335
  oldValue._detach();
8801
8336
  }
8802
8337
  if (isLiveNode(newValue)) {
8803
8338
  newValue._setParentLink(this, key);
8804
8339
  }
8805
- this.#local.delete(key);
8806
- this.#synced.set(key, newValue);
8340
+ this.#map.set(key, newValue);
8807
8341
  this.invalidate();
8808
8342
  }
8809
8343
  return;
@@ -8823,7 +8357,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8823
8357
  if (newValue === void 0) {
8824
8358
  continue;
8825
8359
  }
8826
- const oldValue = this.#synced.get(key);
8360
+ const oldValue = this.#map.get(key);
8827
8361
  if (isLiveNode(oldValue)) {
8828
8362
  for (const childOp of oldValue._toOps(this._id, key)) {
8829
8363
  reverseOps.push(childOp);
@@ -8855,8 +8389,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8855
8389
  updatedProps[key] = newValue;
8856
8390
  this.#unackedOpsByKey.set(key, opId);
8857
8391
  }
8858
- this.#local.delete(key);
8859
- this.#synced.set(key, newValue);
8392
+ this.#map.set(key, newValue);
8860
8393
  this.invalidate();
8861
8394
  updateDelta[key] = { type: "update" };
8862
8395
  }
@@ -8879,34 +8412,6 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8879
8412
  });
8880
8413
  this._pool.dispatch(ops, reverseOps, storageUpdates);
8881
8414
  }
8882
- /**
8883
- * Creates a new LiveObject from a plain JSON object, recursively converting
8884
- * nested objects to LiveObjects and arrays to LiveLists. An optional
8885
- * SyncConfig controls per-key behavior (local-only, atomic, or deep).
8886
- *
8887
- * @private
8888
- */
8889
- static from(obj, config) {
8890
- if (!isPlainObject(obj)) throw new Error("Expected a JSON object");
8891
- const liveObj = new _LiveObject({});
8892
- liveObj.reconcile(obj, config);
8893
- return liveObj;
8894
- }
8895
- /**
8896
- * Reconciles a LiveObject tree to match the given JSON object and sync
8897
- * config. Only mutates keys that actually changed. Recursively reconciles
8898
- * the entire Live tree below it.
8899
- *
8900
- * @private
8901
- */
8902
- reconcile(jsonObj, config) {
8903
- if (this.immutableIs(jsonObj)) return;
8904
- if (!isPlainObject(jsonObj))
8905
- throw new Error(
8906
- "Reconciling the document root expects a plain object value"
8907
- );
8908
- reconcileLiveObject(this, jsonObj, config);
8909
- }
8910
8415
  toImmutable() {
8911
8416
  return super.toImmutable();
8912
8417
  }
@@ -8921,7 +8426,7 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8921
8426
  type: "LiveObject",
8922
8427
  id: nodeId,
8923
8428
  key,
8924
- payload: Array.from(this.#synced.entries()).map(
8429
+ payload: Array.from(this.#map.entries()).map(
8925
8430
  ([key2, value]) => isLiveNode(value) ? value.toTreeNode(key2) : { type: "Json", id: `${nodeId}:${key2}`, key: key2, payload: value }
8926
8431
  )
8927
8432
  };
@@ -8929,27 +8434,20 @@ var LiveObject = class _LiveObject extends AbstractCrdt {
8929
8434
  /** @internal */
8930
8435
  _toImmutable() {
8931
8436
  const result = {};
8932
- for (const [key, val] of this.#synced) {
8437
+ for (const [key, val] of this.#map) {
8933
8438
  result[key] = isLiveStructure(val) ? val.toImmutable() : val;
8934
8439
  }
8935
- for (const [key, val] of this.#local) {
8936
- result[key] = val;
8937
- }
8938
8440
  return process.env.NODE_ENV === "production" ? result : Object.freeze(result);
8939
8441
  }
8940
8442
  clone() {
8941
- const cloned = new _LiveObject(
8443
+ return new _LiveObject(
8942
8444
  Object.fromEntries(
8943
- Array.from(this.#synced).map(([key, value]) => [
8445
+ Array.from(this.#map).map(([key, value]) => [
8944
8446
  key,
8945
8447
  isLiveStructure(value) ? value.clone() : deepClone(value)
8946
8448
  ])
8947
8449
  )
8948
8450
  );
8949
- for (const [key, value] of this.#local) {
8950
- cloned.#local.set(key, deepClone(value));
8951
- }
8952
- return cloned;
8953
8451
  }
8954
8452
  };
8955
8453
 
@@ -9249,6 +8747,17 @@ var Deque = class {
9249
8747
  }
9250
8748
  };
9251
8749
 
8750
+ // src/lib/Json.ts
8751
+ function isJsonScalar(data) {
8752
+ return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
8753
+ }
8754
+ function isJsonArray(data) {
8755
+ return Array.isArray(data);
8756
+ }
8757
+ function isJsonObject(data) {
8758
+ return !isJsonScalar(data) && !isJsonArray(data);
8759
+ }
8760
+
9252
8761
  // src/lib/stopwatch.ts
9253
8762
  function makeStopWatch() {
9254
8763
  let startTime = 0;
@@ -9282,7 +8791,16 @@ var ClientMsgCode = Object.freeze({
9282
8791
  UPDATE_STORAGE: 201,
9283
8792
  // For Yjs support
9284
8793
  FETCH_YDOC: 300,
9285
- UPDATE_YDOC: 301
8794
+ UPDATE_YDOC: 301,
8795
+ // For Feeds
8796
+ FETCH_FEEDS: 510,
8797
+ FETCH_FEED_MESSAGES: 511,
8798
+ ADD_FEED: 512,
8799
+ UPDATE_FEED: 513,
8800
+ DELETE_FEED: 514,
8801
+ ADD_FEED_MESSAGE: 515,
8802
+ UPDATE_FEED_MESSAGE: 516,
8803
+ DELETE_FEED_MESSAGE: 517
9286
8804
  });
9287
8805
 
9288
8806
  // src/refs/ManagedOthers.ts
@@ -9518,12 +9036,15 @@ function defaultMessageFromContext(context) {
9518
9036
  return "Could not update notification settings";
9519
9037
  case "LARGE_MESSAGE_ERROR":
9520
9038
  return "Could not send large message";
9039
+ case "FEED_REQUEST_ERROR":
9040
+ return context.reason ?? "Feed request failed";
9521
9041
  default:
9522
9042
  return assertNever(context, "Unhandled case");
9523
9043
  }
9524
9044
  }
9525
9045
 
9526
9046
  // src/room.ts
9047
+ var FEEDS_TIMEOUT = 5e3;
9527
9048
  function makeIdFactory(connectionId) {
9528
9049
  let count = 0;
9529
9050
  return () => `${connectionId}:${count++}`;
@@ -9734,13 +9255,9 @@ function createRoom(options, config) {
9734
9255
  }
9735
9256
  context.activeBatch.reverseOps.pushLeft(reverse);
9736
9257
  } else {
9737
- if (reverse.length > 0) {
9738
- addToUndoStack(reverse);
9739
- }
9740
- if (ops.length > 0) {
9741
- context.redoStack.length = 0;
9742
- dispatchOps(ops);
9743
- }
9258
+ addToUndoStack(reverse);
9259
+ context.redoStack.length = 0;
9260
+ dispatchOps(ops);
9744
9261
  notify({ storageUpdates });
9745
9262
  }
9746
9263
  }
@@ -9762,6 +9279,7 @@ function createRoom(options, config) {
9762
9279
  storageStatus: makeEventSource(),
9763
9280
  ydoc: makeEventSource(),
9764
9281
  comments: makeEventSource(),
9282
+ feeds: makeEventSource(),
9765
9283
  roomWillDestroy: makeEventSource()
9766
9284
  };
9767
9285
  async function createTextMention(mentionId, mention) {
@@ -9849,20 +9367,19 @@ function createRoom(options, config) {
9849
9367
  );
9850
9368
  }
9851
9369
  const canWrite = self.get()?.canWrite ?? true;
9852
- const root = context.root;
9853
- withoutHistory(() => {
9854
- for (const key in context.initialStorage) {
9855
- if (root.get(key) === void 0) {
9856
- if (canWrite) {
9857
- root.set(key, cloneLson(context.initialStorage[key]));
9858
- } else {
9859
- warn(
9860
- `Attempted to populate missing storage key '${key}', but current user has no write access`
9861
- );
9862
- }
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
+ );
9863
9379
  }
9864
9380
  }
9865
- });
9381
+ }
9382
+ context.undoStack.length = stackSizeBefore;
9866
9383
  }
9867
9384
  function _addToRealUndoStack(frames) {
9868
9385
  if (context.undoStack.length >= 50) {
@@ -10183,6 +9700,9 @@ function createRoom(options, config) {
10183
9700
  notify(result.updates);
10184
9701
  sendMessages(messages);
10185
9702
  }
9703
+ function isFeedRequestFailedMsg(msg) {
9704
+ return msg.type === ServerMsgCode.FEED_REQUEST_FAILED;
9705
+ }
10186
9706
  function handleServerMessage(event) {
10187
9707
  if (typeof event.data !== "string") {
10188
9708
  return;
@@ -10296,6 +9816,94 @@ function createRoom(options, config) {
10296
9816
  eventHub.comments.notify(message);
10297
9817
  break;
10298
9818
  }
9819
+ case ServerMsgCode.FEEDS_LIST: {
9820
+ const feedsListMsg = message;
9821
+ const pending = pendingFeedsRequests.get(feedsListMsg.requestId);
9822
+ if (pending) {
9823
+ pending.resolve({
9824
+ feeds: feedsListMsg.feeds,
9825
+ nextCursor: feedsListMsg.nextCursor
9826
+ });
9827
+ pendingFeedsRequests.delete(feedsListMsg.requestId);
9828
+ }
9829
+ eventHub.feeds.notify(feedsListMsg);
9830
+ break;
9831
+ }
9832
+ case ServerMsgCode.FEEDS_ADDED: {
9833
+ const feedsAddedMsg = message;
9834
+ eventHub.feeds.notify(feedsAddedMsg);
9835
+ tryResolvePendingFeedMutationsFromFeedsEvent(feedsAddedMsg);
9836
+ break;
9837
+ }
9838
+ case ServerMsgCode.FEEDS_UPDATED: {
9839
+ const feedsUpdatedMsg = message;
9840
+ eventHub.feeds.notify(feedsUpdatedMsg);
9841
+ tryResolvePendingFeedMutationsFromFeedsEvent(feedsUpdatedMsg);
9842
+ break;
9843
+ }
9844
+ case ServerMsgCode.FEED_DELETED: {
9845
+ eventHub.feeds.notify(message);
9846
+ tryResolvePendingFeedMutationsFromFeedsEvent(message);
9847
+ break;
9848
+ }
9849
+ case ServerMsgCode.FEED_MESSAGES_LIST: {
9850
+ const feedMsgsListMsg = message;
9851
+ const pending = pendingFeedMessagesRequests.get(
9852
+ feedMsgsListMsg.requestId
9853
+ );
9854
+ if (pending) {
9855
+ pending.resolve({
9856
+ messages: feedMsgsListMsg.messages,
9857
+ nextCursor: feedMsgsListMsg.nextCursor
9858
+ });
9859
+ pendingFeedMessagesRequests.delete(feedMsgsListMsg.requestId);
9860
+ }
9861
+ eventHub.feeds.notify(feedMsgsListMsg);
9862
+ break;
9863
+ }
9864
+ case ServerMsgCode.FEED_MESSAGES_ADDED: {
9865
+ const feedMsgsAddedMsg = message;
9866
+ eventHub.feeds.notify(feedMsgsAddedMsg);
9867
+ tryResolvePendingFeedMutationsFromFeedsEvent(feedMsgsAddedMsg);
9868
+ break;
9869
+ }
9870
+ case ServerMsgCode.FEED_MESSAGES_UPDATED: {
9871
+ const feedMsgsUpdatedMsg = message;
9872
+ eventHub.feeds.notify(feedMsgsUpdatedMsg);
9873
+ tryResolvePendingFeedMutationsFromFeedsEvent(feedMsgsUpdatedMsg);
9874
+ break;
9875
+ }
9876
+ case ServerMsgCode.FEED_MESSAGES_DELETED: {
9877
+ eventHub.feeds.notify(message);
9878
+ tryResolvePendingFeedMutationsFromFeedsEvent(message);
9879
+ break;
9880
+ }
9881
+ case ServerMsgCode.FEED_REQUEST_FAILED: {
9882
+ if (!isFeedRequestFailedMsg(message)) {
9883
+ break;
9884
+ }
9885
+ const { requestId, code, reason } = message;
9886
+ const err = new LiveblocksError(reason ?? "Feed request failed", {
9887
+ type: "FEED_REQUEST_ERROR",
9888
+ roomId,
9889
+ requestId,
9890
+ code,
9891
+ reason
9892
+ });
9893
+ if (pendingFeedMutations.has(requestId)) {
9894
+ settleFeedMutation(requestId, "error", err);
9895
+ } else if (pendingFeedsRequests.has(requestId)) {
9896
+ const pending = pendingFeedsRequests.get(requestId);
9897
+ pendingFeedsRequests.delete(requestId);
9898
+ pending?.reject(err);
9899
+ } else if (pendingFeedMessagesRequests.has(requestId)) {
9900
+ const pending = pendingFeedMessagesRequests.get(requestId);
9901
+ pendingFeedMessagesRequests.delete(requestId);
9902
+ pending?.reject(err);
9903
+ }
9904
+ eventHub.feeds.notify(message);
9905
+ break;
9906
+ }
10299
9907
  case ServerMsgCode.STORAGE_STATE_V7:
10300
9908
  // No longer used in V8
10301
9909
  default:
@@ -10399,6 +10007,141 @@ function createRoom(options, config) {
10399
10007
  }
10400
10008
  let _getStorage$ = null;
10401
10009
  let _resolveStoragePromise = null;
10010
+ const pendingFeedsRequests = /* @__PURE__ */ new Map();
10011
+ const pendingFeedMessagesRequests = /* @__PURE__ */ new Map();
10012
+ const pendingFeedMutations = /* @__PURE__ */ new Map();
10013
+ const pendingAddMessageFifoByFeed = /* @__PURE__ */ new Map();
10014
+ function settleFeedMutation(requestId, outcome, error3) {
10015
+ const pending = pendingFeedMutations.get(requestId);
10016
+ if (pending === void 0) {
10017
+ return;
10018
+ }
10019
+ clearTimeout(pending.timeoutId);
10020
+ pendingFeedMutations.delete(requestId);
10021
+ if (pending.kind === "add-message" && !pending.expectedClientMessageId) {
10022
+ const q = pendingAddMessageFifoByFeed.get(pending.feedId);
10023
+ if (q !== void 0) {
10024
+ const idx = q.indexOf(requestId);
10025
+ if (idx >= 0) {
10026
+ q.splice(idx, 1);
10027
+ }
10028
+ if (q.length === 0) {
10029
+ pendingAddMessageFifoByFeed.delete(pending.feedId);
10030
+ }
10031
+ }
10032
+ }
10033
+ if (outcome === "ok") {
10034
+ pending.resolve();
10035
+ } else {
10036
+ pending.reject(error3 ?? new Error("Feed mutation failed"));
10037
+ }
10038
+ }
10039
+ function registerFeedMutation(requestId, kind, feedId, options2) {
10040
+ const { promise, resolve, reject } = Promise_withResolvers();
10041
+ const timeoutId = setTimeout(() => {
10042
+ if (pendingFeedMutations.has(requestId)) {
10043
+ settleFeedMutation(
10044
+ requestId,
10045
+ "error",
10046
+ new Error("Feed mutation timeout")
10047
+ );
10048
+ }
10049
+ }, FEEDS_TIMEOUT);
10050
+ pendingFeedMutations.set(requestId, {
10051
+ resolve,
10052
+ reject,
10053
+ timeoutId,
10054
+ kind,
10055
+ feedId,
10056
+ messageId: options2?.messageId,
10057
+ expectedClientMessageId: options2?.expectedClientMessageId
10058
+ });
10059
+ if (kind === "add-message" && options2?.expectedClientMessageId === void 0) {
10060
+ const q = pendingAddMessageFifoByFeed.get(feedId) ?? [];
10061
+ q.push(requestId);
10062
+ pendingAddMessageFifoByFeed.set(feedId, q);
10063
+ }
10064
+ return promise;
10065
+ }
10066
+ function tryResolvePendingFeedMutationsFromFeedsEvent(message) {
10067
+ switch (message.type) {
10068
+ case ServerMsgCode.FEEDS_ADDED: {
10069
+ for (const feed of message.feeds) {
10070
+ for (const [requestId, pending] of [...pendingFeedMutations]) {
10071
+ if (pending.kind === "add-feed" && pending.feedId === feed.feedId) {
10072
+ settleFeedMutation(requestId, "ok");
10073
+ break;
10074
+ }
10075
+ }
10076
+ }
10077
+ break;
10078
+ }
10079
+ case ServerMsgCode.FEEDS_UPDATED: {
10080
+ for (const feed of message.feeds) {
10081
+ for (const [requestId, pending] of [...pendingFeedMutations]) {
10082
+ if (pending.kind === "update-feed" && pending.feedId === feed.feedId) {
10083
+ settleFeedMutation(requestId, "ok");
10084
+ }
10085
+ }
10086
+ }
10087
+ break;
10088
+ }
10089
+ case ServerMsgCode.FEED_DELETED: {
10090
+ for (const [requestId, pending] of [...pendingFeedMutations]) {
10091
+ if (pending.kind === "delete-feed" && pending.feedId === message.feedId) {
10092
+ settleFeedMutation(requestId, "ok");
10093
+ break;
10094
+ }
10095
+ }
10096
+ break;
10097
+ }
10098
+ case ServerMsgCode.FEED_MESSAGES_ADDED: {
10099
+ for (const m of message.messages) {
10100
+ let matched = false;
10101
+ for (const [requestId, pending] of [...pendingFeedMutations]) {
10102
+ if (pending.kind === "add-message" && pending.feedId === message.feedId && pending.expectedClientMessageId === m.id) {
10103
+ settleFeedMutation(requestId, "ok");
10104
+ matched = true;
10105
+ break;
10106
+ }
10107
+ }
10108
+ if (!matched) {
10109
+ const q = pendingAddMessageFifoByFeed.get(message.feedId);
10110
+ const headId = q?.[0];
10111
+ if (headId !== void 0) {
10112
+ const pending = pendingFeedMutations.get(headId);
10113
+ if (pending?.kind === "add-message" && pending.expectedClientMessageId === void 0) {
10114
+ settleFeedMutation(headId, "ok");
10115
+ }
10116
+ }
10117
+ }
10118
+ }
10119
+ break;
10120
+ }
10121
+ case ServerMsgCode.FEED_MESSAGES_UPDATED: {
10122
+ for (const m of message.messages) {
10123
+ for (const [requestId, pending] of [...pendingFeedMutations]) {
10124
+ if (pending.kind === "update-message" && pending.feedId === message.feedId && pending.messageId === m.id) {
10125
+ settleFeedMutation(requestId, "ok");
10126
+ }
10127
+ }
10128
+ }
10129
+ break;
10130
+ }
10131
+ case ServerMsgCode.FEED_MESSAGES_DELETED: {
10132
+ for (const mid of message.messageIds) {
10133
+ for (const [requestId, pending] of [...pendingFeedMutations]) {
10134
+ if (pending.kind === "delete-message" && pending.feedId === message.feedId && pending.messageId === mid) {
10135
+ settleFeedMutation(requestId, "ok");
10136
+ }
10137
+ }
10138
+ }
10139
+ break;
10140
+ }
10141
+ default:
10142
+ break;
10143
+ }
10144
+ }
10402
10145
  function processInitialStorage(nodes) {
10403
10146
  const unacknowledgedOps = new Map(context.unacknowledgedOps);
10404
10147
  createOrUpdateRootFromMessage(nodes);
@@ -10470,6 +10213,138 @@ function createRoom(options, config) {
10470
10213
  }
10471
10214
  flushNowOrSoon();
10472
10215
  }
10216
+ async function fetchFeeds(options2) {
10217
+ const requestId = nanoid();
10218
+ const { promise, resolve, reject } = Promise_withResolvers();
10219
+ pendingFeedsRequests.set(requestId, { resolve, reject });
10220
+ const message = {
10221
+ type: ClientMsgCode.FETCH_FEEDS,
10222
+ requestId,
10223
+ cursor: options2?.cursor,
10224
+ since: options2?.since,
10225
+ limit: options2?.limit,
10226
+ metadata: options2?.metadata
10227
+ };
10228
+ context.buffer.messages.push(message);
10229
+ flushNowOrSoon();
10230
+ setTimeout(() => {
10231
+ if (pendingFeedsRequests.has(requestId)) {
10232
+ pendingFeedsRequests.delete(requestId);
10233
+ reject(new Error("Feeds fetch timeout"));
10234
+ }
10235
+ }, FEEDS_TIMEOUT);
10236
+ return promise;
10237
+ }
10238
+ async function fetchFeedMessages(feedId, options2) {
10239
+ const requestId = nanoid();
10240
+ const { promise, resolve, reject } = Promise_withResolvers();
10241
+ pendingFeedMessagesRequests.set(requestId, { resolve, reject });
10242
+ const message = {
10243
+ type: ClientMsgCode.FETCH_FEED_MESSAGES,
10244
+ requestId,
10245
+ feedId,
10246
+ cursor: options2?.cursor,
10247
+ since: options2?.since,
10248
+ limit: options2?.limit
10249
+ };
10250
+ context.buffer.messages.push(message);
10251
+ flushNowOrSoon();
10252
+ setTimeout(() => {
10253
+ if (pendingFeedMessagesRequests.has(requestId)) {
10254
+ pendingFeedMessagesRequests.delete(requestId);
10255
+ reject(new Error("Feed messages fetch timeout"));
10256
+ }
10257
+ }, FEEDS_TIMEOUT);
10258
+ return promise;
10259
+ }
10260
+ function addFeed(feedId, options2) {
10261
+ const requestId = nanoid();
10262
+ const promise = registerFeedMutation(requestId, "add-feed", feedId);
10263
+ const message = {
10264
+ type: ClientMsgCode.ADD_FEED,
10265
+ requestId,
10266
+ feedId,
10267
+ metadata: options2?.metadata,
10268
+ createdAt: options2?.createdAt
10269
+ };
10270
+ context.buffer.messages.push(message);
10271
+ flushNowOrSoon();
10272
+ return promise;
10273
+ }
10274
+ function updateFeed(feedId, metadata) {
10275
+ const requestId = nanoid();
10276
+ const promise = registerFeedMutation(requestId, "update-feed", feedId);
10277
+ const message = {
10278
+ type: ClientMsgCode.UPDATE_FEED,
10279
+ requestId,
10280
+ feedId,
10281
+ metadata
10282
+ };
10283
+ context.buffer.messages.push(message);
10284
+ flushNowOrSoon();
10285
+ return promise;
10286
+ }
10287
+ function deleteFeed(feedId) {
10288
+ const requestId = nanoid();
10289
+ const promise = registerFeedMutation(requestId, "delete-feed", feedId);
10290
+ const message = {
10291
+ type: ClientMsgCode.DELETE_FEED,
10292
+ requestId,
10293
+ feedId
10294
+ };
10295
+ context.buffer.messages.push(message);
10296
+ flushNowOrSoon();
10297
+ return promise;
10298
+ }
10299
+ function addFeedMessage(feedId, data, options2) {
10300
+ const requestId = nanoid();
10301
+ const promise = registerFeedMutation(requestId, "add-message", feedId, {
10302
+ expectedClientMessageId: options2?.id
10303
+ });
10304
+ const message = {
10305
+ type: ClientMsgCode.ADD_FEED_MESSAGE,
10306
+ requestId,
10307
+ feedId,
10308
+ data,
10309
+ id: options2?.id,
10310
+ createdAt: options2?.createdAt
10311
+ };
10312
+ context.buffer.messages.push(message);
10313
+ flushNowOrSoon();
10314
+ return promise;
10315
+ }
10316
+ function updateFeedMessage(feedId, messageId, data, options2) {
10317
+ const requestId = nanoid();
10318
+ const promise = registerFeedMutation(requestId, "update-message", feedId, {
10319
+ messageId
10320
+ });
10321
+ const message = {
10322
+ type: ClientMsgCode.UPDATE_FEED_MESSAGE,
10323
+ requestId,
10324
+ feedId,
10325
+ messageId,
10326
+ data,
10327
+ updatedAt: options2?.updatedAt
10328
+ };
10329
+ context.buffer.messages.push(message);
10330
+ flushNowOrSoon();
10331
+ return promise;
10332
+ }
10333
+ function deleteFeedMessage(feedId, messageId) {
10334
+ const requestId = nanoid();
10335
+ const promise = registerFeedMutation(requestId, "delete-message", feedId, {
10336
+ messageId
10337
+ });
10338
+ const message = {
10339
+ type: ClientMsgCode.DELETE_FEED_MESSAGE,
10340
+ requestId,
10341
+ feedId,
10342
+ messageId
10343
+ };
10344
+ context.buffer.messages.push(message);
10345
+ flushNowOrSoon();
10346
+ return promise;
10347
+ }
10473
10348
  function undo() {
10474
10349
  if (context.activeBatch) {
10475
10350
  throw new Error("undo is not allowed during a batch");
@@ -10555,16 +10430,6 @@ function createRoom(options, config) {
10555
10430
  _addToRealUndoStack(Array.from(frames));
10556
10431
  }
10557
10432
  }
10558
- function withoutHistory(fn) {
10559
- const undoBefore = context.undoStack.length;
10560
- const redoBefore = context.redoStack.length;
10561
- try {
10562
- return fn();
10563
- } finally {
10564
- context.undoStack.length = undoBefore;
10565
- context.redoStack.length = redoBefore;
10566
- }
10567
- }
10568
10433
  const syncSourceForStorage = config.createSyncSource();
10569
10434
  function getStorageStatus() {
10570
10435
  if (context.root === void 0) {
@@ -10622,6 +10487,7 @@ function createRoom(options, config) {
10622
10487
  storageStatus: eventHub.storageStatus.observable,
10623
10488
  ydoc: eventHub.ydoc.observable,
10624
10489
  comments: eventHub.comments.observable,
10490
+ feeds: eventHub.feeds.observable,
10625
10491
  roomWillDestroy: eventHub.roomWillDestroy.observable
10626
10492
  };
10627
10493
  async function getThreadsSince(options2) {
@@ -10831,13 +10697,19 @@ function createRoom(options, config) {
10831
10697
  id: roomId,
10832
10698
  subscribe: makeClassicSubscribeFn(
10833
10699
  roomId,
10834
- events,
10700
+ eventHub,
10835
10701
  config.errorEventSource
10836
10702
  ),
10837
10703
  connect: () => managedSocket.connect(),
10838
10704
  reconnect: () => managedSocket.reconnect(),
10839
10705
  disconnect: () => managedSocket.disconnect(),
10840
10706
  destroy: () => {
10707
+ pendingFeedsRequests.forEach(
10708
+ (request) => request.reject(new Error("Room destroyed"))
10709
+ );
10710
+ pendingFeedMessagesRequests.forEach(
10711
+ (request) => request.reject(new Error("Room destroyed"))
10712
+ );
10841
10713
  const { roomWillDestroy, ...eventsExceptDestroy } = eventHub;
10842
10714
  for (const source of Object.values(eventsExceptDestroy)) {
10843
10715
  source.dispose();
@@ -10863,12 +10735,17 @@ function createRoom(options, config) {
10863
10735
  canRedo,
10864
10736
  clear,
10865
10737
  pause: pauseHistory,
10866
- resume: resumeHistory,
10867
- [kInternal]: {
10868
- withoutHistory
10869
- }
10738
+ resume: resumeHistory
10870
10739
  },
10871
10740
  fetchYDoc,
10741
+ fetchFeeds,
10742
+ fetchFeedMessages,
10743
+ addFeed,
10744
+ updateFeed,
10745
+ deleteFeed,
10746
+ addFeedMessage,
10747
+ updateFeedMessage,
10748
+ deleteFeedMessage,
10872
10749
  getStorage,
10873
10750
  getStorageSnapshot,
10874
10751
  getStorageStatus,
@@ -11776,6 +11653,309 @@ function toPlainLson(lson) {
11776
11653
  }
11777
11654
  }
11778
11655
 
11656
+ // src/immutable.ts
11657
+ function lsonObjectToJson(obj) {
11658
+ const result = {};
11659
+ for (const key in obj) {
11660
+ const val = obj[key];
11661
+ if (val !== void 0) {
11662
+ result[key] = lsonToJson(val);
11663
+ }
11664
+ }
11665
+ return result;
11666
+ }
11667
+ function liveObjectToJson(liveObject) {
11668
+ return lsonObjectToJson(liveObject.toObject());
11669
+ }
11670
+ function liveMapToJson(map) {
11671
+ const result = {};
11672
+ for (const [key, value] of map.entries()) {
11673
+ result[key] = lsonToJson(value);
11674
+ }
11675
+ return result;
11676
+ }
11677
+ function lsonListToJson(value) {
11678
+ return value.map(lsonToJson);
11679
+ }
11680
+ function liveListToJson(value) {
11681
+ return lsonListToJson(value.toArray());
11682
+ }
11683
+ function lsonToJson(value) {
11684
+ if (value instanceof LiveObject) {
11685
+ return liveObjectToJson(value);
11686
+ } else if (value instanceof LiveList) {
11687
+ return liveListToJson(value);
11688
+ } else if (value instanceof LiveMap) {
11689
+ return liveMapToJson(value);
11690
+ } else if (value instanceof LiveRegister) {
11691
+ return value.data;
11692
+ }
11693
+ if (Array.isArray(value)) {
11694
+ return lsonListToJson(value);
11695
+ } else if (isPlainObject(value)) {
11696
+ return lsonObjectToJson(value);
11697
+ }
11698
+ return value;
11699
+ }
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) {
11718
+ let i = 0;
11719
+ let prevEnd = prev.length - 1;
11720
+ let nextEnd = next.length - 1;
11721
+ let prevNode = prev[0];
11722
+ let nextNode = next[0];
11723
+ outer: {
11724
+ while (prevNode === nextNode) {
11725
+ ++i;
11726
+ if (i > prevEnd || i > nextEnd) {
11727
+ break outer;
11728
+ }
11729
+ prevNode = prev[i];
11730
+ nextNode = next[i];
11731
+ }
11732
+ prevNode = prev[prevEnd];
11733
+ nextNode = next[nextEnd];
11734
+ while (prevNode === nextNode) {
11735
+ prevEnd--;
11736
+ nextEnd--;
11737
+ if (i > prevEnd || i > nextEnd) {
11738
+ break outer;
11739
+ }
11740
+ prevNode = prev[prevEnd];
11741
+ nextNode = next[nextEnd];
11742
+ }
11743
+ }
11744
+ if (i > prevEnd) {
11745
+ if (i <= nextEnd) {
11746
+ while (i <= nextEnd) {
11747
+ liveList.insert(deepLiveify(next[i]), i);
11748
+ i++;
11749
+ }
11750
+ }
11751
+ } else if (i > nextEnd) {
11752
+ let localI = i;
11753
+ while (localI <= prevEnd) {
11754
+ liveList.delete(i);
11755
+ localI++;
11756
+ }
11757
+ } else {
11758
+ while (i <= prevEnd && i <= nextEnd) {
11759
+ prevNode = prev[i];
11760
+ nextNode = next[i];
11761
+ const liveListNode = liveList.get(i);
11762
+ if (isLiveObject(liveListNode) && isPlainObject(prevNode) && isPlainObject(nextNode)) {
11763
+ patchLiveObject(liveListNode, prevNode, nextNode);
11764
+ } else {
11765
+ liveList.set(i, deepLiveify(nextNode));
11766
+ }
11767
+ i++;
11768
+ }
11769
+ while (i <= nextEnd) {
11770
+ liveList.insert(deepLiveify(next[i]), i);
11771
+ i++;
11772
+ }
11773
+ let localI = i;
11774
+ while (localI <= prevEnd) {
11775
+ liveList.delete(i);
11776
+ localI++;
11777
+ }
11778
+ }
11779
+ }
11780
+ function patchLiveObjectKey(liveObject, key, prev, next) {
11781
+ if (process.env.NODE_ENV !== "production") {
11782
+ const nonSerializableValue = findNonSerializableValue(next);
11783
+ if (nonSerializableValue) {
11784
+ error2(
11785
+ `New state path: '${nonSerializableValue.path}' value: '${String(
11786
+ nonSerializableValue.value
11787
+ )}' is not serializable.
11788
+ Only serializable value can be synced with Liveblocks.`
11789
+ );
11790
+ return;
11791
+ }
11792
+ }
11793
+ const value = liveObject.get(key);
11794
+ if (next === void 0) {
11795
+ liveObject.delete(key);
11796
+ } else if (value === void 0) {
11797
+ liveObject.set(key, deepLiveify(next));
11798
+ } else if (prev === next) {
11799
+ return;
11800
+ } else if (isLiveList(value) && Array.isArray(prev) && Array.isArray(next)) {
11801
+ patchLiveList(value, prev, next);
11802
+ } else if (isLiveObject(value) && isPlainObject(prev) && isPlainObject(next)) {
11803
+ patchLiveObject(value, prev, next);
11804
+ } else {
11805
+ liveObject.set(key, deepLiveify(next));
11806
+ }
11807
+ }
11808
+ function patchLiveObject(root, prev, next) {
11809
+ const updates = {};
11810
+ for (const key in next) {
11811
+ patchLiveObjectKey(root, key, prev[key], next[key]);
11812
+ }
11813
+ for (const key in prev) {
11814
+ if (next[key] === void 0) {
11815
+ root.delete(key);
11816
+ }
11817
+ }
11818
+ if (Object.keys(updates).length > 0) {
11819
+ root.update(updates);
11820
+ }
11821
+ }
11822
+ function getParentsPath(node) {
11823
+ const path = [];
11824
+ while (node.parent.type === "HasParent") {
11825
+ if (isLiveList(node.parent.node)) {
11826
+ path.push(node.parent.node._indexOfPosition(node.parent.key));
11827
+ } else {
11828
+ path.push(node.parent.key);
11829
+ }
11830
+ node = node.parent.node;
11831
+ }
11832
+ return path;
11833
+ }
11834
+ function legacy_patchImmutableObject(state, updates) {
11835
+ return updates.reduce(
11836
+ (state2, update) => legacy_patchImmutableObjectWithUpdate(state2, update),
11837
+ state
11838
+ );
11839
+ }
11840
+ function legacy_patchImmutableObjectWithUpdate(state, update) {
11841
+ const path = getParentsPath(update.node);
11842
+ return legacy_patchImmutableNode(state, path, update);
11843
+ }
11844
+ function legacy_patchImmutableNode(state, path, update) {
11845
+ const pathItem = path.pop();
11846
+ if (pathItem === void 0) {
11847
+ switch (update.type) {
11848
+ case "LiveObject": {
11849
+ if (!isJsonObject(state)) {
11850
+ throw new Error(
11851
+ "Internal: received update on LiveObject but state was not an object"
11852
+ );
11853
+ }
11854
+ const newState = Object.assign({}, state);
11855
+ for (const key in update.updates) {
11856
+ if (update.updates[key]?.type === "update") {
11857
+ const val = update.node.get(key);
11858
+ if (val !== void 0) {
11859
+ newState[key] = lsonToJson(val);
11860
+ }
11861
+ } else if (update.updates[key]?.type === "delete") {
11862
+ delete newState[key];
11863
+ }
11864
+ }
11865
+ return newState;
11866
+ }
11867
+ case "LiveList": {
11868
+ if (!Array.isArray(state)) {
11869
+ throw new Error(
11870
+ "Internal: received update on LiveList but state was not an array"
11871
+ );
11872
+ }
11873
+ let newState = state.map((x) => x);
11874
+ for (const listUpdate of update.updates) {
11875
+ if (listUpdate.type === "set") {
11876
+ newState = newState.map(
11877
+ (item, index) => index === listUpdate.index ? lsonToJson(listUpdate.item) : item
11878
+ );
11879
+ } else if (listUpdate.type === "insert") {
11880
+ if (listUpdate.index === newState.length) {
11881
+ newState.push(lsonToJson(listUpdate.item));
11882
+ } else {
11883
+ newState = [
11884
+ ...newState.slice(0, listUpdate.index),
11885
+ lsonToJson(listUpdate.item),
11886
+ ...newState.slice(listUpdate.index)
11887
+ ];
11888
+ }
11889
+ } else if (listUpdate.type === "delete") {
11890
+ newState.splice(listUpdate.index, 1);
11891
+ } else if (listUpdate.type === "move") {
11892
+ if (listUpdate.previousIndex > listUpdate.index) {
11893
+ newState = [
11894
+ ...newState.slice(0, listUpdate.index),
11895
+ lsonToJson(listUpdate.item),
11896
+ ...newState.slice(listUpdate.index, listUpdate.previousIndex),
11897
+ ...newState.slice(listUpdate.previousIndex + 1)
11898
+ ];
11899
+ } else {
11900
+ newState = [
11901
+ ...newState.slice(0, listUpdate.previousIndex),
11902
+ ...newState.slice(
11903
+ listUpdate.previousIndex + 1,
11904
+ listUpdate.index + 1
11905
+ ),
11906
+ lsonToJson(listUpdate.item),
11907
+ ...newState.slice(listUpdate.index + 1)
11908
+ ];
11909
+ }
11910
+ }
11911
+ }
11912
+ return newState;
11913
+ }
11914
+ case "LiveMap": {
11915
+ if (!isJsonObject(state)) {
11916
+ throw new Error(
11917
+ "Internal: received update on LiveMap but state was not an object"
11918
+ );
11919
+ }
11920
+ const newState = Object.assign({}, state);
11921
+ for (const key in update.updates) {
11922
+ if (update.updates[key]?.type === "update") {
11923
+ const value = update.node.get(key);
11924
+ if (value !== void 0) {
11925
+ newState[key] = lsonToJson(value);
11926
+ }
11927
+ } else if (update.updates[key]?.type === "delete") {
11928
+ delete newState[key];
11929
+ }
11930
+ }
11931
+ return newState;
11932
+ }
11933
+ }
11934
+ }
11935
+ if (Array.isArray(state)) {
11936
+ const newArray = [...state];
11937
+ newArray[pathItem] = legacy_patchImmutableNode(
11938
+ state[pathItem],
11939
+ path,
11940
+ update
11941
+ );
11942
+ return newArray;
11943
+ } else if (isJsonObject(state)) {
11944
+ const node = state[pathItem];
11945
+ if (node === void 0) {
11946
+ return state;
11947
+ } else {
11948
+ const stateAsObj = state;
11949
+ return {
11950
+ ...stateAsObj,
11951
+ [pathItem]: legacy_patchImmutableNode(node, path, update)
11952
+ };
11953
+ }
11954
+ } else {
11955
+ return state;
11956
+ }
11957
+ }
11958
+
11779
11959
  // src/lib/abortController.ts
11780
11960
  function makeAbortController(externalSignal) {
11781
11961
  const ctl = new AbortController();
@@ -11947,6 +12127,7 @@ export {
11947
12127
  DefaultMap,
11948
12128
  Deque,
11949
12129
  DerivedSignal,
12130
+ FeedRequestErrorCode,
11950
12131
  HttpError,
11951
12132
  LiveList,
11952
12133
  LiveMap,
@@ -12021,7 +12202,6 @@ export {
12021
12202
  kInternal,
12022
12203
  keys,
12023
12204
  legacy_patchImmutableObject,
12024
- legacy_patchLiveObjectKey,
12025
12205
  lsonToJson,
12026
12206
  makeAbortController,
12027
12207
  makeEventSource,
@@ -12033,6 +12213,7 @@ export {
12033
12213
  nn,
12034
12214
  nodeStreamToCompactNodes,
12035
12215
  objectToQuery,
12216
+ patchLiveObjectKey,
12036
12217
  patchNotificationSettings,
12037
12218
  raise,
12038
12219
  resolveMentionsInCommentBody,