@dabble/patches 0.8.6 → 0.8.7

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.
@@ -31,7 +31,7 @@ declare class LWWInMemoryStore implements LWWClientStore {
31
31
  listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
32
32
  /**
33
33
  * Saves the current document state to storage.
34
- * Clears all committed fields, pending ops, and sending change.
34
+ * Clears committed fields (subsumed by the snapshot) but preserves pending ops.
35
35
  */
36
36
  saveDoc(docId: string, docState: PatchesState): Promise<void>;
37
37
  /**
@@ -71,7 +71,13 @@ declare class LWWInMemoryStore implements LWWClientStore {
71
71
  */
72
72
  saveSendingChange(docId: string, change: Change): Promise<void>;
73
73
  /**
74
- * Clear sendingChange after server ack, move ops to committed.
74
+ * Move sending ops to committed, then clear the sending slot.
75
+ * committedRev is NOT updated here — applyServerChanges owns that using the
76
+ * server's actual rev. Updating it here would bump the rev above the server's
77
+ * real value for noop changes (where the server doesn't create a new rev).
78
+ *
79
+ * Call this BEFORE applyServerChanges so that server corrections (which run
80
+ * after) overwrite any stale ops for fields the server won via LWW.
75
81
  */
76
82
  confirmSendingChange(docId: string): Promise<void>;
77
83
  /**
@@ -51,14 +51,15 @@ class LWWInMemoryStore {
51
51
  }
52
52
  /**
53
53
  * Saves the current document state to storage.
54
- * Clears all committed fields, pending ops, and sending change.
54
+ * Clears committed fields (subsumed by the snapshot) but preserves pending ops.
55
55
  */
56
56
  async saveDoc(docId, docState) {
57
+ const existing = this.docs.get(docId);
57
58
  this.docs.set(docId, {
58
59
  snapshot: { state: docState.state, rev: docState.rev },
59
60
  committedFields: /* @__PURE__ */ new Map(),
60
- pendingOps: /* @__PURE__ */ new Map(),
61
- sendingChange: null,
61
+ pendingOps: existing?.pendingOps ?? /* @__PURE__ */ new Map(),
62
+ sendingChange: existing?.sendingChange ?? null,
62
63
  committedRev: docState.rev
63
64
  });
64
65
  }
@@ -150,7 +151,13 @@ class LWWInMemoryStore {
150
151
  buf.pendingOps.clear();
151
152
  }
152
153
  /**
153
- * Clear sendingChange after server ack, move ops to committed.
154
+ * Move sending ops to committed, then clear the sending slot.
155
+ * committedRev is NOT updated here — applyServerChanges owns that using the
156
+ * server's actual rev. Updating it here would bump the rev above the server's
157
+ * real value for noop changes (where the server doesn't create a new rev).
158
+ *
159
+ * Call this BEFORE applyServerChanges so that server corrections (which run
160
+ * after) overwrite any stale ops for fields the server won via LWW.
154
161
  */
155
162
  async confirmSendingChange(docId) {
156
163
  const buf = this.docs.get(docId);
@@ -158,10 +165,6 @@ class LWWInMemoryStore {
158
165
  for (const op of buf.sendingChange.ops) {
159
166
  buf.committedFields.set(op.path, op.value);
160
167
  }
161
- const changeRev = buf.sendingChange.rev;
162
- if (changeRev !== void 0 && changeRev > buf.committedRev) {
163
- buf.committedRev = changeRev;
164
- }
165
168
  buf.sendingChange = null;
166
169
  }
167
170
  /**
@@ -73,7 +73,7 @@ declare class LWWIndexedDBStore implements LWWClientStore {
73
73
  getDoc(docId: string): Promise<PatchesSnapshot | undefined>;
74
74
  /**
75
75
  * Saves the current document state to storage.
76
- * Clears all committed fields and pending ops.
76
+ * Clears committed fields (subsumed by the snapshot) but preserves pending ops.
77
77
  */
78
78
  saveDoc(docId: string, docState: PatchesState): Promise<void>;
79
79
  /**
@@ -101,7 +101,13 @@ declare class LWWIndexedDBStore implements LWWClientStore {
101
101
  */
102
102
  saveSendingChange(docId: string, change: Change): Promise<void>;
103
103
  /**
104
- * Clear sendingChange after server ack, move ops to committed.
104
+ * Move sending ops to committed, then clear the sending slot.
105
+ * committedRev is NOT updated here — applyServerChanges owns that using the
106
+ * server's actual rev. Updating it here would bump the rev above the server's
107
+ * real value for noop changes (where the server doesn't create a new rev).
108
+ *
109
+ * Call this BEFORE applyServerChanges so that server corrections (which run
110
+ * after) overwrite any stale ops for fields the server won via LWW.
105
111
  */
106
112
  confirmSendingChange(docId: string): Promise<void>;
107
113
  /**
@@ -122,17 +122,15 @@ class LWWIndexedDBStore {
122
122
  };
123
123
  }
124
124
  async saveDoc(docId, docState) {
125
- const [tx, snapshots, committedOps, pendingOps, sendingChanges, docsStore] = await this.db.transaction(
126
- ["snapshots", "committedOps", "pendingOps", "sendingChanges", "docs"],
125
+ const [tx, snapshots, committedOps, docsStore] = await this.db.transaction(
126
+ ["snapshots", "committedOps", "docs"],
127
127
  "readwrite"
128
128
  );
129
129
  const { rev, state } = docState;
130
130
  await Promise.all([
131
131
  docsStore.put({ docId, committedRev: rev, algorithm: "lww" }),
132
132
  snapshots.put({ docId, state, rev }),
133
- this.deleteFieldsForDoc(committedOps, docId),
134
- this.deleteFieldsForDoc(pendingOps, docId),
135
- sendingChanges.delete(docId)
133
+ this.deleteFieldsForDoc(committedOps, docId)
136
134
  ]);
137
135
  await tx.complete();
138
136
  }
@@ -233,8 +231,8 @@ class LWWIndexedDBStore {
233
231
  await tx.complete();
234
232
  }
235
233
  async confirmSendingChange(docId) {
236
- const [tx, sendingChanges, committedOps, docsStore] = await this.db.transaction(
237
- ["sendingChanges", "committedOps", "docs"],
234
+ const [tx, sendingChanges, committedOps] = await this.db.transaction(
235
+ ["sendingChanges", "committedOps"],
238
236
  "readwrite"
239
237
  );
240
238
  const sending = await sendingChanges.get(docId);
@@ -243,13 +241,6 @@ class LWWIndexedDBStore {
243
241
  return;
244
242
  }
245
243
  await Promise.all(sending.change.ops.map((op) => committedOps.put({ ...op, docId })));
246
- const changeRev = sending.change.rev;
247
- if (changeRev !== void 0) {
248
- const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
249
- if (changeRev > docMeta.committedRev) {
250
- await docsStore.put({ ...docMeta, committedRev: changeRev });
251
- }
252
- }
253
244
  await sendingChanges.delete(docId);
254
245
  await tx.complete();
255
246
  }
@@ -31,10 +31,11 @@ class OTInMemoryStore {
31
31
  }
32
32
  // ─── Writes ────────────────────────────────────────────────────────────
33
33
  async saveDoc(docId, snapshot) {
34
+ const existing = this.docs.get(docId);
34
35
  this.docs.set(docId, {
35
36
  snapshot,
36
37
  committed: [],
37
- pending: []
38
+ pending: existing?.pending ?? []
38
39
  });
39
40
  }
40
41
  async savePendingChanges(docId, changes) {
@@ -119,8 +119,7 @@ class OTIndexedDBStore {
119
119
  await Promise.all([
120
120
  docsStore.put({ docId, committedRev: rev, algorithm: "ot" }),
121
121
  snapshots.put({ docId, state, rev }),
122
- committedChanges.delete([docId, 0], [docId, Infinity]),
123
- pendingChanges.delete([docId, 0], [docId, Infinity])
122
+ committedChanges.delete([docId, 0], [docId, Infinity])
124
123
  ]);
125
124
  await tx.complete();
126
125
  }
@@ -318,8 +318,8 @@ class PatchesSync extends (_a = ReadonlyStoreClass, _syncDoc_dec = [serialGate],
318
318
  openDoc.import({ ...snapshot, changes: [] });
319
319
  }
320
320
  } else {
321
- await this._applyServerChangesToDoc(docId, committed);
322
321
  await algorithm.confirmSent(docId, changeBatch);
322
+ await this._applyServerChangesToDoc(docId, committed);
323
323
  }
324
324
  pending = await algorithm.getPendingToSend(docId) ?? [];
325
325
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dabble/patches",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Immutable JSON Patch implementation based on RFC 6902 supporting operational transformation and last-writer-wins",
5
5
  "author": "Jacob Wright <jacwright@gmail.com>",
6
6
  "bugs": {