braid-text 0.3.16 → 0.3.17

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.
@@ -199,11 +199,31 @@ function simpleton_client(url, {
199
199
  client_state = apply_patches(client_state, patches)
200
200
  }
201
201
 
202
+ // ── Advance version ─────────────────────────────────────────
203
+ // IMPORTANT: This must happen synchronously (before any await)
204
+ // to prevent the changed() accumulation loop from interleaving
205
+ // and capturing a stale client_version during a yield point.
206
+ client_version = update.version
207
+
208
+ // ── Notify listener ─────────────────────────────────────────
209
+ // IMPORTANT: No changed() / flush is called here.
210
+ // The JS does NOT send edits after receiving a server
211
+ // update. The PUT response handler's async accumulation
212
+ // loop handles flushing accumulated edits.
213
+ if (on_state) on_state(client_state)
214
+
202
215
  // ── Digest verification ─────────────────────────────────────
203
216
  // If the server sent a repr-digest, verify our state
204
217
  // matches. On mismatch, THROW — this halts the
205
218
  // subscription handler. The document is corrupted and
206
219
  // continuing would compound the problem.
220
+ //
221
+ // This is placed after advancing client_version and calling
222
+ // on_state so that the await does not create a yield point
223
+ // between applying patches and advancing client_version.
224
+ // That yield point previously allowed the changed() PUT loop
225
+ // to interleave, capturing a stale client_version and causing
226
+ // edit loss.
207
227
  if (update.extra_headers &&
208
228
  update.extra_headers["repr-digest"] &&
209
229
  update.extra_headers["repr-digest"].startsWith('sha-256=') &&
@@ -213,16 +233,6 @@ function simpleton_client(url, {
213
233
  console.log('state: ' + client_state)
214
234
  throw new Error('repr-digest mismatch')
215
235
  }
216
-
217
- // ── Advance version ─────────────────────────────────────────
218
- client_version = update.version
219
-
220
- // ── Notify listener ─────────────────────────────────────────
221
- // IMPORTANT: No changed() / flush is called here.
222
- // The JS does NOT send edits after receiving a server
223
- // update. The PUT response handler's async accumulation
224
- // loop handles flushing accumulated edits.
225
- if (on_state) on_state(client_state)
226
236
  }
227
237
 
228
238
  // ── Public interface ────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",
package/server.js CHANGED
@@ -1023,16 +1023,10 @@ function create_braid_text() {
1023
1023
 
1024
1024
  if (client.my_timeout) {
1025
1025
  if (peer && client.peer === peer) {
1026
- if (!v_eq(client.my_last_sent_version, parents)) {
1027
- // note: we don't add to client.my_unused_version_count,
1028
- // because we're already in a timeout;
1029
- // we'll just extend it here..
1030
- set_timeout()
1031
- } else {
1032
- // hm.. it appears we got a correctly parented version,
1033
- // which suggests that maybe we can stop the timeout early
1034
- set_timeout(0)
1035
- }
1026
+ // note: we don't add to client.my_unused_version_count,
1027
+ // because we're already in a timeout;
1028
+ // we'll just extend it here..
1029
+ set_timeout()
1036
1030
  }
1037
1031
  continue
1038
1032
  }
@@ -1049,6 +1043,19 @@ function create_braid_text() {
1049
1043
 
1050
1044
  x.parents = [version]
1051
1045
  if (!v_eq(x.version, x.parents)) {
1046
+ // Note: this rebase will never trigger in the
1047
+ // presence of the timeout logic above, because
1048
+ // any version that would cause x.version to
1049
+ // differ from x.parents would have already
1050
+ // updated my_last_sent_version when forwarding
1051
+ // other peers' changes to this client, causing
1052
+ // a mismatch and triggering a timeout instead.
1053
+ //
1054
+ // However, this is the standard rebase path and
1055
+ // is left here for implementations that omit the
1056
+ // timeout optimization — without it, this branch
1057
+ // is needed to rebase the client's version when
1058
+ // concurrent edits have been merged.
1052
1059
  if (braid_text.verbose) console.log("rebasing..")
1053
1060
  x.patches = get_xf_patches(resource.doc, OpLog_remote_to_local(resource.doc, x.parents))
1054
1061
  } else {