@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.
@@ -2298,6 +2298,21 @@ var QueryClient = class extends EventEmitter {
2298
2298
  }
2299
2299
  };
2300
2300
 
2301
+ //#endregion
2302
+ //#region packages/provider/src/OutgoingMessages/QueryAwarenessMessage.ts
2303
+ var QueryAwarenessMessage = class extends OutgoingMessage {
2304
+ constructor(..._args) {
2305
+ super(..._args);
2306
+ this.type = MessageType.QueryAwareness;
2307
+ this.description = "Queries awareness states";
2308
+ }
2309
+ get(args) {
2310
+ writeVarString(this.encoder, args.documentName);
2311
+ writeVarUint(this.encoder, this.type);
2312
+ return this.encoder;
2313
+ }
2314
+ };
2315
+
2301
2316
  //#endregion
2302
2317
  //#region packages/provider/src/OutgoingMessages/SyncStepOneMessage.ts
2303
2318
  var SyncStepOneMessage = class extends OutgoingMessage {
@@ -2580,6 +2595,7 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2580
2595
  clients: [this.document.clientID],
2581
2596
  documentName: this.configuration.name
2582
2597
  });
2598
+ if (this.awareness) this.send(QueryAwarenessMessage, { documentName: this.configuration.name });
2583
2599
  }
2584
2600
  send(message, args) {
2585
2601
  if (!this._isAttached) return;
@@ -15660,6 +15676,19 @@ function makeEntryMap(fields) {
15660
15676
  return m;
15661
15677
  }
15662
15678
  /**
15679
+ * A label is a "placeholder" — i.e. carries no real user title — when it is
15680
+ * empty/whitespace, null/undefined, or the literal `"Untitled"` sentinel
15681
+ * (case-insensitive, so `"untitled"` from a `labelToFilename` round-trip is
15682
+ * caught too). The whole tree-label corruption class boils down to a
15683
+ * placeholder being allowed to overwrite a real title; `patchEntry` refuses
15684
+ * exactly that. Mirrors cou-sh's `isEmptyTreeLabel`.
15685
+ */
15686
+ function isPlaceholderLabel(label) {
15687
+ if (typeof label !== "string") return label == null;
15688
+ const t = label.trim();
15689
+ return t === "" || t.toLowerCase() === "untitled";
15690
+ }
15691
+ /**
15663
15692
  * Patch an EXISTING entry's fields per-key on its nested `Y.Map`, so a
15664
15693
  * concurrent edit to a *different* field by a peer is preserved instead
15665
15694
  * of being clobbered by a whole-entry write — the whole-entry-LWW fix
@@ -15674,21 +15703,44 @@ function makeEntryMap(fields) {
15674
15703
  * Self-transacting: it batches its writes in one `Y.Doc` transaction
15675
15704
  * (a safe reentrant no-op join when already inside one), so callers
15676
15705
  * don't need to pass or own a transaction.
15706
+ *
15707
+ * ── NO-DESTROY LABEL INVARIANT ──────────────────────────────────────────
15708
+ * A `label` patch that is a placeholder (empty/whitespace/"Untitled") is
15709
+ * DROPPED when the entry already holds a real (non-placeholder) label —
15710
+ * regardless of which consumer (cou-sh title-sync, fs-sync rename
15711
+ * detection, MCP, table renderers, a stale snapshot) tried it. This is the
15712
+ * source-of-truth guard against the "card title silently becomes Untitled
15713
+ * / files renamed to untitled.md" corruption: a placeholder must never win
15714
+ * over a real title. Creating a brand-new entry with an empty label (e.g.
15715
+ * a fresh kanban card) is still allowed — the guard only fires when a real
15716
+ * label already exists. Pass `{ allowLabelClear: true }` to override (the
15717
+ * single legitimate "user explicitly cleared it" path).
15677
15718
  */
15678
- function patchEntry(treeMap, id, patch, removeKeys = []) {
15719
+ function patchEntry(treeMap, id, patch, removeKeys = [], opts = {}) {
15679
15720
  const apply = () => {
15680
15721
  const raw = treeMap.get(id);
15722
+ let effectivePatch = patch;
15723
+ if (!opts.allowLabelClear && Object.hasOwn(patch, "label") && isPlaceholderLabel(patch.label)) {
15724
+ let existingLabel;
15725
+ if (raw != null && typeof raw.get === "function") existingLabel = raw.get("label");
15726
+ else if (raw != null && typeof raw.toJSON === "function") existingLabel = raw.toJSON()?.label;
15727
+ else if (raw != null && typeof raw === "object") existingLabel = raw.label;
15728
+ if (!isPlaceholderLabel(existingLabel)) {
15729
+ const { label: _dropped, ...rest } = patch;
15730
+ effectivePatch = rest;
15731
+ }
15732
+ }
15681
15733
  if (raw instanceof Y.Map) {
15682
- for (const [k, v] of Object.entries(patch)) if (v === void 0) raw.delete(k);
15734
+ for (const [k, v] of Object.entries(effectivePatch)) if (v === void 0) raw.delete(k);
15683
15735
  else raw.set(k, v);
15684
15736
  for (const k of removeKeys) raw.delete(k);
15685
15737
  return;
15686
15738
  }
15687
15739
  const merged = {
15688
15740
  ...raw == null ? {} : toPlain(raw),
15689
- ...patch
15741
+ ...effectivePatch
15690
15742
  };
15691
- for (const [k, v] of Object.entries(patch)) if (v === void 0) delete merged[k];
15743
+ for (const [k, v] of Object.entries(effectivePatch)) if (v === void 0) delete merged[k];
15692
15744
  for (const k of removeKeys) delete merged[k];
15693
15745
  treeMap.set(id, makeEntryMap(merged));
15694
15746
  };
@@ -20255,8 +20307,13 @@ var ContentManager = class {
20255
20307
  }
20256
20308
  /**
20257
20309
  * Read document content as markdown.
20258
- * Returns the title extracted from the TipTap documentHeader, the markdown
20259
- * body, tree metadata, and immediate children.
20310
+ *
20311
+ * Returns the markdown body, tree-derived label/type/meta, and immediate
20312
+ * children. `title` mirrors `label` (the tree entry's display name) — it
20313
+ * is *not* derived from a TipTap `documentHeader`, and the markdown body
20314
+ * does NOT include YAML frontmatter. Callers that want frontmatter-style
20315
+ * round-tripping should serialise `meta`/`type` themselves on top of the
20316
+ * returned markdown.
20260
20317
  */
20261
20318
  async read(docId) {
20262
20319
  const fragment = (await this.dm.getChildProvider(docId)).document.getXmlFragment("default");
@@ -20291,7 +20348,7 @@ var ContentManager = class {
20291
20348
  type,
20292
20349
  meta
20293
20350
  }));
20294
- const markdown = yjsToMarkdown(fragment, label, meta, type);
20351
+ const { markdown } = yjsToMarkdown(fragment);
20295
20352
  return {
20296
20353
  label,
20297
20354
  type,
@@ -20723,5 +20780,5 @@ var DocumentManager = class {
20723
20780
  };
20724
20781
 
20725
20782
  //#endregion
20726
- export { AbracadabraBaseProvider, AbracadabraClient, AbracadabraProvider, AbracadabraWS, AbracadabraWebRTC, AuthMessageType, AwarenessError, BackgroundSyncManager, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ChannelKeyResolver, ChatClient, ConnectionTimeout, ContentManager, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, DevicePairingChannel, DeviceRegistrationService, DocKeyManager, DocumentCache, DocumentManager, E2EAbracadabraProvider, E2EEChannel, E2EOfflineStore, EncryptedChatClient, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, Forbidden, GEO_TYPE_META_SCHEMAS, HocuspocusProvider, HocuspocusProviderWebsocket, IdentityDocProvider, KEY_EXCHANGE_CHANNEL, Kind, LocalStorageDeviceSessionStorage, ManualSignaling, MessageTooBig, MessageType, MetaManager, MetaValidationError, NotificationsClient, OfflineStore, PAGE_TYPES, PeerConnection, QUERY_PREFIX, QueryClient, QueryError, RPC_PREFIX, ResetConnection, RpcClient, RpcError, SERVER_ROOT_ID, SearchIndex, SignalingSocket, SubdocMessage, TYPE_ALIASES, TokenManager, TreeManager, TypedDocTypeMismatchError, Unauthorized, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, wordlist as bip39Wordlist, buildBlockquoteElement, buildBlocksFromMarkdown, buildBulletListElement, buildCodeBlockElement, buildHeadingElement, buildHorizontalRuleElement, buildOrderedListElement, buildParagraphElement, buildTaskListElement, decryptChatContent, decryptField, deriveIdentityDocId, deriveSeedWrappingKey, encryptChatContent, encryptField, filenameToLabel, foldRecords, generateMnemonic, isEncryptedContent, makeEncryptedYMap, makeEncryptedYText, makeEntryMap, mnemonicToEd25519Seed, mnemonicToKeyPair, normalizeRootId, parseFrontmatter, patchEntry, populateYDocFromMarkdown, readAuthMessage, readBlocksFromFragment, recordFromYAny, resolvePageType, toPlain, unwrapSeed, validateMnemonic, waitForSync, withTimeout, wrapSeed, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest, yjsToMarkdown };
20783
+ export { AbracadabraBaseProvider, AbracadabraClient, AbracadabraProvider, AbracadabraWS, AbracadabraWebRTC, AuthMessageType, AwarenessError, BackgroundSyncManager, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ChannelKeyResolver, ChatClient, ConnectionTimeout, ContentManager, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, DevicePairingChannel, DeviceRegistrationService, DocKeyManager, DocumentCache, DocumentManager, E2EAbracadabraProvider, E2EEChannel, E2EOfflineStore, EncryptedChatClient, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, Forbidden, GEO_TYPE_META_SCHEMAS, HocuspocusProvider, HocuspocusProviderWebsocket, IdentityDocProvider, KEY_EXCHANGE_CHANNEL, Kind, LocalStorageDeviceSessionStorage, ManualSignaling, MessageTooBig, MessageType, MetaManager, MetaValidationError, NotificationsClient, OfflineStore, PAGE_TYPES, PeerConnection, QUERY_PREFIX, QueryClient, QueryError, RPC_PREFIX, ResetConnection, RpcClient, RpcError, SERVER_ROOT_ID, SearchIndex, SignalingSocket, SubdocMessage, TYPE_ALIASES, TokenManager, TreeManager, TypedDocTypeMismatchError, Unauthorized, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, wordlist as bip39Wordlist, buildBlockquoteElement, buildBlocksFromMarkdown, buildBulletListElement, buildCodeBlockElement, buildHeadingElement, buildHorizontalRuleElement, buildOrderedListElement, buildParagraphElement, buildTaskListElement, decryptChatContent, decryptField, deriveIdentityDocId, deriveSeedWrappingKey, encryptChatContent, encryptField, filenameToLabel, foldRecords, generateMnemonic, isEncryptedContent, isPlaceholderLabel, makeEncryptedYMap, makeEncryptedYText, makeEntryMap, mnemonicToEd25519Seed, mnemonicToKeyPair, normalizeRootId, parseFrontmatter, patchEntry, populateYDocFromMarkdown, readAuthMessage, readBlocksFromFragment, recordFromYAny, resolvePageType, toPlain, unwrapSeed, validateMnemonic, waitForSync, withTimeout, wrapSeed, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest, yjsToMarkdown };
20727
20784
  //# sourceMappingURL=abracadabra-provider.esm.js.map