@abraca/dabra 2.6.0 → 2.8.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.
@@ -2328,6 +2328,21 @@ var QueryClient = class extends EventEmitter {
2328
2328
  }
2329
2329
  };
2330
2330
 
2331
+ //#endregion
2332
+ //#region packages/provider/src/OutgoingMessages/QueryAwarenessMessage.ts
2333
+ var QueryAwarenessMessage = class extends OutgoingMessage {
2334
+ constructor(..._args) {
2335
+ super(..._args);
2336
+ this.type = MessageType.QueryAwareness;
2337
+ this.description = "Queries awareness states";
2338
+ }
2339
+ get(args) {
2340
+ writeVarString(this.encoder, args.documentName);
2341
+ writeVarUint(this.encoder, this.type);
2342
+ return this.encoder;
2343
+ }
2344
+ };
2345
+
2331
2346
  //#endregion
2332
2347
  //#region packages/provider/src/OutgoingMessages/SyncStepOneMessage.ts
2333
2348
  var SyncStepOneMessage = class extends OutgoingMessage {
@@ -2610,6 +2625,7 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2610
2625
  clients: [this.document.clientID],
2611
2626
  documentName: this.configuration.name
2612
2627
  });
2628
+ if (this.awareness) this.send(QueryAwarenessMessage, { documentName: this.configuration.name });
2613
2629
  }
2614
2630
  send(message, args) {
2615
2631
  if (!this._isAttached) return;
@@ -15699,6 +15715,19 @@ function makeEntryMap(fields) {
15699
15715
  return m;
15700
15716
  }
15701
15717
  /**
15718
+ * A label is a "placeholder" — i.e. carries no real user title — when it is
15719
+ * empty/whitespace, null/undefined, or the literal `"Untitled"` sentinel
15720
+ * (case-insensitive, so `"untitled"` from a `labelToFilename` round-trip is
15721
+ * caught too). The whole tree-label corruption class boils down to a
15722
+ * placeholder being allowed to overwrite a real title; `patchEntry` refuses
15723
+ * exactly that. Mirrors cou-sh's `isEmptyTreeLabel`.
15724
+ */
15725
+ function isPlaceholderLabel(label) {
15726
+ if (typeof label !== "string") return label == null;
15727
+ const t = label.trim();
15728
+ return t === "" || t.toLowerCase() === "untitled";
15729
+ }
15730
+ /**
15702
15731
  * Patch an EXISTING entry's fields per-key on its nested `Y.Map`, so a
15703
15732
  * concurrent edit to a *different* field by a peer is preserved instead
15704
15733
  * of being clobbered by a whole-entry write — the whole-entry-LWW fix
@@ -15713,21 +15742,44 @@ function makeEntryMap(fields) {
15713
15742
  * Self-transacting: it batches its writes in one `Y.Doc` transaction
15714
15743
  * (a safe reentrant no-op join when already inside one), so callers
15715
15744
  * don't need to pass or own a transaction.
15745
+ *
15746
+ * ── NO-DESTROY LABEL INVARIANT ──────────────────────────────────────────
15747
+ * A `label` patch that is a placeholder (empty/whitespace/"Untitled") is
15748
+ * DROPPED when the entry already holds a real (non-placeholder) label —
15749
+ * regardless of which consumer (cou-sh title-sync, fs-sync rename
15750
+ * detection, MCP, table renderers, a stale snapshot) tried it. This is the
15751
+ * source-of-truth guard against the "card title silently becomes Untitled
15752
+ * / files renamed to untitled.md" corruption: a placeholder must never win
15753
+ * over a real title. Creating a brand-new entry with an empty label (e.g.
15754
+ * a fresh kanban card) is still allowed — the guard only fires when a real
15755
+ * label already exists. Pass `{ allowLabelClear: true }` to override (the
15756
+ * single legitimate "user explicitly cleared it" path).
15716
15757
  */
15717
- function patchEntry(treeMap, id, patch, removeKeys = []) {
15758
+ function patchEntry(treeMap, id, patch, removeKeys = [], opts = {}) {
15718
15759
  const apply = () => {
15719
15760
  const raw = treeMap.get(id);
15761
+ let effectivePatch = patch;
15762
+ if (!opts.allowLabelClear && Object.hasOwn(patch, "label") && isPlaceholderLabel(patch.label)) {
15763
+ let existingLabel;
15764
+ if (raw != null && typeof raw.get === "function") existingLabel = raw.get("label");
15765
+ else if (raw != null && typeof raw.toJSON === "function") existingLabel = raw.toJSON()?.label;
15766
+ else if (raw != null && typeof raw === "object") existingLabel = raw.label;
15767
+ if (!isPlaceholderLabel(existingLabel)) {
15768
+ const { label: _dropped, ...rest } = patch;
15769
+ effectivePatch = rest;
15770
+ }
15771
+ }
15720
15772
  if (raw instanceof yjs.Map) {
15721
- for (const [k, v] of Object.entries(patch)) if (v === void 0) raw.delete(k);
15773
+ for (const [k, v] of Object.entries(effectivePatch)) if (v === void 0) raw.delete(k);
15722
15774
  else raw.set(k, v);
15723
15775
  for (const k of removeKeys) raw.delete(k);
15724
15776
  return;
15725
15777
  }
15726
15778
  const merged = {
15727
15779
  ...raw == null ? {} : toPlain(raw),
15728
- ...patch
15780
+ ...effectivePatch
15729
15781
  };
15730
- for (const [k, v] of Object.entries(patch)) if (v === void 0) delete merged[k];
15782
+ for (const [k, v] of Object.entries(effectivePatch)) if (v === void 0) delete merged[k];
15731
15783
  for (const k of removeKeys) delete merged[k];
15732
15784
  treeMap.set(id, makeEntryMap(merged));
15733
15785
  };
@@ -20320,8 +20372,13 @@ var ContentManager = class {
20320
20372
  }
20321
20373
  /**
20322
20374
  * Read document content as markdown.
20323
- * Returns the title extracted from the TipTap documentHeader, the markdown
20324
- * body, tree metadata, and immediate children.
20375
+ *
20376
+ * Returns the markdown body, tree-derived label/type/meta, and immediate
20377
+ * children. `title` mirrors `label` (the tree entry's display name) — it
20378
+ * is *not* derived from a TipTap `documentHeader`, and the markdown body
20379
+ * does NOT include YAML frontmatter. Callers that want frontmatter-style
20380
+ * round-tripping should serialise `meta`/`type` themselves on top of the
20381
+ * returned markdown.
20325
20382
  */
20326
20383
  async read(docId) {
20327
20384
  const fragment = (await this.dm.getChildProvider(docId)).document.getXmlFragment("default");
@@ -20356,7 +20413,7 @@ var ContentManager = class {
20356
20413
  type,
20357
20414
  meta
20358
20415
  }));
20359
- const markdown = yjsToMarkdown(fragment, label, meta, type);
20416
+ const { markdown } = yjsToMarkdown(fragment);
20360
20417
  return {
20361
20418
  label,
20362
20419
  type,
@@ -20879,6 +20936,7 @@ exports.filenameToLabel = filenameToLabel;
20879
20936
  exports.foldRecords = foldRecords;
20880
20937
  exports.generateMnemonic = generateMnemonic;
20881
20938
  exports.isEncryptedContent = isEncryptedContent;
20939
+ exports.isPlaceholderLabel = isPlaceholderLabel;
20882
20940
  exports.makeEncryptedYMap = makeEncryptedYMap;
20883
20941
  exports.makeEncryptedYText = makeEncryptedYText;
20884
20942
  exports.makeEntryMap = makeEntryMap;