@liveblocks/server 1.6.0 → 1.6.1

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.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, CompactNode, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
1
+ import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, CompactNode, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, CreateOp, HasOpId, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
2
2
  export { BroadcastEventClientMsg, ClientMsg, ClientWireOp, CreateListOp, CreateMapOp, CreateObjectOp, CreateOp, CreateRegisterOp, DeleteCrdtOp, DeleteObjectKeyOp, FetchStorageClientMsg, FetchYDocClientMsg, HasOpId, IgnoredOp, Op, RoomStateServerMsg, ServerMsg, ServerWireOp, SetParentKeyOp, UpdateObjectOp, UpdatePresenceClientMsg, UpdateStorageClientMsg, UpdateYDocClientMsg } from '@liveblocks/core';
3
3
  import * as decoders from 'decoders';
4
4
  import { Decoder } from 'decoders';
@@ -1121,7 +1121,18 @@ declare class Logger {
1121
1121
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
1122
1122
  */
1123
1123
 
1124
- type ApplyOpResult = OpAccepted | OpIgnored;
1124
+ /**
1125
+ * The three possible outcomes of applying a client op. They differ along
1126
+ * when the op (first) changed storage state, who hears about it, and what
1127
+ * gets sent back to the originating client:
1128
+ *
1129
+ * | | state change | fan out to others | reply to sender |
1130
+ * |-------------|--------------|-------------------|------------------|
1131
+ * | OpAccepted | now | yes | ack echo (+ fix) |
1132
+ * | OpRectified | in the past | no | ack echo + fix |
1133
+ * | OpIgnored | never | no | bare (H)Ack |
1134
+ */
1135
+ type ApplyOpResult = OpAccepted | OpIgnored | OpRectified;
1125
1136
  type OpAccepted = {
1126
1137
  action: "accepted";
1127
1138
  op: ClientWireOp;
@@ -1131,6 +1142,24 @@ type OpIgnored = {
1131
1142
  action: "ignored";
1132
1143
  ignoredOpId?: string;
1133
1144
  };
1145
+ type OpRectified = {
1146
+ action: "rectified";
1147
+ /**
1148
+ * Echo of the client's op, with the stored, authoritative parentKey. Sent
1149
+ * back to the originating client as the acknowledgement, instead of the
1150
+ * bare (H)Ack. Used for re-sent CREATE ops whose node the server already
1151
+ * stored: the echo carries the authoritative position, so the client can
1152
+ * correct any optimistic local position it may have predicted while the op
1153
+ * was pending. Never fanned out to others: they already received the op
1154
+ * when it was originally accepted.
1155
+ */
1156
+ ackOp: CreateOp & HasOpId;
1157
+ /**
1158
+ * A corrective op to send back to the originating client, stating that
1159
+ * same authoritative position (see ackOp).
1160
+ */
1161
+ fix: FixOp;
1162
+ };
1134
1163
  declare class Storage {
1135
1164
  readonly driver: IStorageDriver;
1136
1165
  constructor(driver: IStorageDriver);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, CompactNode, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
1
+ import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, CompactNode, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, CreateOp, HasOpId, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
2
2
  export { BroadcastEventClientMsg, ClientMsg, ClientWireOp, CreateListOp, CreateMapOp, CreateObjectOp, CreateOp, CreateRegisterOp, DeleteCrdtOp, DeleteObjectKeyOp, FetchStorageClientMsg, FetchYDocClientMsg, HasOpId, IgnoredOp, Op, RoomStateServerMsg, ServerMsg, ServerWireOp, SetParentKeyOp, UpdateObjectOp, UpdatePresenceClientMsg, UpdateStorageClientMsg, UpdateYDocClientMsg } from '@liveblocks/core';
3
3
  import * as decoders from 'decoders';
4
4
  import { Decoder } from 'decoders';
@@ -1121,7 +1121,18 @@ declare class Logger {
1121
1121
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
1122
1122
  */
1123
1123
 
1124
- type ApplyOpResult = OpAccepted | OpIgnored;
1124
+ /**
1125
+ * The three possible outcomes of applying a client op. They differ along
1126
+ * when the op (first) changed storage state, who hears about it, and what
1127
+ * gets sent back to the originating client:
1128
+ *
1129
+ * | | state change | fan out to others | reply to sender |
1130
+ * |-------------|--------------|-------------------|------------------|
1131
+ * | OpAccepted | now | yes | ack echo (+ fix) |
1132
+ * | OpRectified | in the past | no | ack echo + fix |
1133
+ * | OpIgnored | never | no | bare (H)Ack |
1134
+ */
1135
+ type ApplyOpResult = OpAccepted | OpIgnored | OpRectified;
1125
1136
  type OpAccepted = {
1126
1137
  action: "accepted";
1127
1138
  op: ClientWireOp;
@@ -1131,6 +1142,24 @@ type OpIgnored = {
1131
1142
  action: "ignored";
1132
1143
  ignoredOpId?: string;
1133
1144
  };
1145
+ type OpRectified = {
1146
+ action: "rectified";
1147
+ /**
1148
+ * Echo of the client's op, with the stored, authoritative parentKey. Sent
1149
+ * back to the originating client as the acknowledgement, instead of the
1150
+ * bare (H)Ack. Used for re-sent CREATE ops whose node the server already
1151
+ * stored: the echo carries the authoritative position, so the client can
1152
+ * correct any optimistic local position it may have predicted while the op
1153
+ * was pending. Never fanned out to others: they already received the op
1154
+ * when it was originally accepted.
1155
+ */
1156
+ ackOp: CreateOp & HasOpId;
1157
+ /**
1158
+ * A corrective op to send back to the originating client, stating that
1159
+ * same authoritative position (see ackOp).
1160
+ */
1161
+ fix: FixOp;
1162
+ };
1134
1163
  declare class Storage {
1135
1164
  readonly driver: IStorageDriver;
1136
1165
  constructor(driver: IStorageDriver);
package/dist/index.js CHANGED
@@ -1563,6 +1563,13 @@ function accept(op2, fix) {
1563
1563
  function ignore(ignoredOp) {
1564
1564
  return { action: "ignored", ignoredOpId: ignoredOp.opId };
1565
1565
  }
1566
+ function rectify(op2, parentKey) {
1567
+ return {
1568
+ action: "rectified",
1569
+ ackOp: { ...op2, parentKey },
1570
+ fix: { type: OpCode2.SET_PARENT_KEY, id: op2.id, parentKey }
1571
+ };
1572
+ }
1566
1573
  function nodeFromCreateChildOp(op2) {
1567
1574
  switch (op2.type) {
1568
1575
  case OpCode2.CREATE_LIST:
@@ -1652,6 +1659,12 @@ var Storage = class {
1652
1659
  }
1653
1660
  applyCreateOp(op2) {
1654
1661
  if (this.driver.has_node(op2.id)) {
1662
+ if (op2.intent === "push") {
1663
+ const stored = this.driver.get_node(op2.id);
1664
+ if (stored?.parentId !== void 0 && this.driver.get_node(stored.parentId)?.type === CrdtType5.LIST) {
1665
+ return rectify(op2, stored.parentKey);
1666
+ }
1667
+ }
1655
1668
  return ignore(op2);
1656
1669
  }
1657
1670
  const node = nodeFromCreateChildOp(op2);
@@ -3109,17 +3122,21 @@ var Room = class {
3109
3122
  const opsToForward = result.flatMap(
3110
3123
  (r) => r.action === "accepted" ? [r.op] : []
3111
3124
  );
3112
- const opsToSendBack = result.flatMap((r) => {
3113
- switch (r.action) {
3114
- case "ignored":
3115
- return r.ignoredOpId !== void 0 ? [ackIgnoredOp(r.ignoredOpId)] : [];
3116
- case "accepted":
3117
- return r.fix !== void 0 ? [r.fix] : [];
3118
- // istanbul ignore next
3119
- default:
3120
- return assertNever3(r, "Unhandled case");
3125
+ const opsToSendBack = result.flatMap(
3126
+ (r) => {
3127
+ switch (r.action) {
3128
+ case "ignored":
3129
+ return r.ignoredOpId !== void 0 ? [ackIgnoredOp(r.ignoredOpId)] : [];
3130
+ case "rectified":
3131
+ return [r.ackOp, r.fix];
3132
+ case "accepted":
3133
+ return r.fix !== void 0 ? [r.fix] : [];
3134
+ // istanbul ignore next
3135
+ default:
3136
+ return assertNever3(r, "Unhandled case");
3137
+ }
3121
3138
  }
3122
- });
3139
+ );
3123
3140
  if (opsToForward.length > 0) {
3124
3141
  scheduleFanOut({
3125
3142
  type: ServerMsgCode2.UPDATE_STORAGE,