@openlfcp/shared-objects 0.2.0-beta.3 → 0.2.0-beta.4

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.
@@ -192,6 +192,10 @@ export function deriveTree(doc, validation = validateSection(doc)) {
192
192
  * operation on its Text, a Task's title or status, or a move of the node
193
193
  * (a write to its `placement` register — node.move or node.resolve_placement,
194
194
  * §8). Two changes are concurrent when neither is in the other's dependencies.
195
+ * Only a deletion the lifecycle register still holds counts: one a later write
196
+ * to the same register depends on is superseded (deleting the node again clears
197
+ * the attention a concurrent edit raised), while two concurrent deletions both
198
+ * count (baseline.9).
195
199
  */
196
200
  function retainedEdits(doc, hidden, parentOf, sectionId) {
197
201
  const root = doc;
@@ -225,6 +229,8 @@ function retainedEdits(doc, hidden, parentOf, sectionId) {
225
229
  }
226
230
  const deps = new Map();
227
231
  const deletes = [];
232
+ // Every lifecycle write (any value), to tell whether a deletion is still held.
233
+ const lifecycleWrites = [];
228
234
  const edits = [];
229
235
  for (const bytes of A.getAllChanges(doc)) {
230
236
  const change = A.decodeChange(bytes);
@@ -252,9 +258,11 @@ function retainedEdits(doc, hidden, parentOf, sectionId) {
252
258
  if (owner !== undefined && key === "lifecycle")
253
259
  lastLifecycle.set(owner, value);
254
260
  }
255
- for (const [owner, value] of lastLifecycle)
261
+ for (const [owner, value] of lastLifecycle) {
262
+ lifecycleWrites.push({ hash: change.hash, node: owner });
256
263
  if (value === "deleted")
257
264
  deletes.push({ hash: change.hash, node: owner });
265
+ }
258
266
  for (const node of touched)
259
267
  edits.push({ hash: change.hash, node });
260
268
  }
@@ -281,8 +289,15 @@ function retainedEdits(doc, hidden, parentOf, sectionId) {
281
289
  return out;
282
290
  };
283
291
  const concurrent = (a, b) => a !== b && !ancestorsOf(a).has(b) && !ancestorsOf(b).has(a);
292
+ // §9 (baseline.9): a deletion counts only while the lifecycle register still
293
+ // holds it. A later write to the same register — one that has the deletion in
294
+ // its dependencies — supersedes it, so deleting a node again clears the
295
+ // attention a concurrent edit raised. Two concurrent deletions supersede
296
+ // neither, so both still count (SS86); the writes come from each change's
297
+ // operations, which also keeps a deletion merged after a concurrent one.
298
+ const heldDeletes = deletes.filter((d) => !lifecycleWrites.some((w) => w.node === d.node && w.hash !== d.hash && ancestorsOf(w.hash).has(d.hash)));
284
299
  const deletesOf = new Map();
285
- for (const d of deletes)
300
+ for (const d of heldDeletes)
286
301
  deletesOf.set(d.node, [...(deletesOf.get(d.node) ?? []), d.hash]);
287
302
  const attention = new Set();
288
303
  for (const edit of edits) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlfcp/shared-objects",
3
- "version": "0.2.0-beta.3",
3
+ "version": "0.2.0-beta.4",
4
4
  "description": "SHARED-OBJECTS-PROFILE-01 (org.openlfcp.shared-objects.v1) implementation.",
5
5
  "keywords": [
6
6
  "openlfcp",
@@ -53,8 +53,8 @@
53
53
  "dependencies": {
54
54
  "@automerge/automerge": "3.5.0",
55
55
  "fflate": "0.8.3",
56
- "@openlfcp/core": "^0.2.0-beta.3",
57
- "@openlfcp/crypto": "^0.2.0-beta.3"
56
+ "@openlfcp/core": "^0.2.0-beta.4",
57
+ "@openlfcp/crypto": "^0.2.0-beta.4"
58
58
  },
59
59
  "devDependencies": {
60
60
  "fast-check": "4.10.2"