@abraca/dabra 2.26.0 → 2.28.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.
package/dist/index.d.ts CHANGED
@@ -5280,13 +5280,16 @@ interface DocumentManagerConfig {
5280
5280
  */
5281
5281
  rootDocId?: string;
5282
5282
  /**
5283
- * PROTOTYPE (Path-1 doctree scaling): opt into TreeManager's in-memory
5284
- * parent→children index. When `false` (default), every tree walk
5285
- * re-scans the whole `doc-tree` Y.Map (O(n) per call, O() recursive
5286
- * traversal) the historical behaviour, byte-for-byte. When `true`,
5287
- * walks resolve against a lazily-rebuilt adjacency index (O(k) per
5288
- * lookup, O(result) traversal) and `tree.childrenOfPage()` becomes
5289
- * usable. Behind a flag so it ships dark until benchmarked.
5283
+ * TreeManager's in-memory parent→children index. When `true` (the
5284
+ * default), tree WALKS resolve against a lazily-rebuilt adjacency index
5285
+ * (O(k) per lookup, O(result) traversal), kept fresh by an `observeDeep`
5286
+ * dirty bit and rebuilt at most once per mutation batch; `childrenOfPage()`
5287
+ * becomes usable with stable (order,id) sibling ordering. Set `false` to
5288
+ * force the legacy whole-map scan (O(n) per call, O() recursive) — only
5289
+ * needed to reproduce the exact historical order-only sibling ordering.
5290
+ * Benchmarked at ~700× on deep walks. Index affects WALK reads only;
5291
+ * `readEntries`/`get` keep raw parentId either way. Lazy: no index or
5292
+ * observer is bound until the first walk read.
5290
5293
  */
5291
5294
  treeIndex?: boolean;
5292
5295
  }
@@ -5325,8 +5328,8 @@ declare class DocumentManager {
5325
5328
  get serverInfo(): ServerInfo | null;
5326
5329
  get rootDocId(): string | null;
5327
5330
  /**
5328
- * Whether the TreeManager in-memory index is enabled (Path-1 prototype).
5329
- * Off by default — see {@link DocumentManagerConfig.treeIndex}.
5331
+ * Whether the TreeManager in-memory index is enabled.
5332
+ * On by default — see {@link DocumentManagerConfig.treeIndex}.
5330
5333
  */
5331
5334
  get treeIndexEnabled(): boolean;
5332
5335
  get rootDocument(): Y.Doc | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/dabra",
3
- "version": "2.26.0",
3
+ "version": "2.28.0",
4
4
  "description": "abracadabra provider",
5
5
  "keywords": [
6
6
  "abracadabra",
@@ -41,7 +41,7 @@
41
41
  "yjs": "^13.6.8"
42
42
  },
43
43
  "devDependencies": {
44
- "@abraca/schema": "2.26.0"
44
+ "@abraca/schema": "2.28.0"
45
45
  },
46
46
  "scripts": {
47
47
  "test": "node --no-warnings --conditions=source --experimental-transform-types --test 'tests/*.test.ts'"
@@ -46,8 +46,7 @@ function xmlTextToMarkdown(xmlText: Y.XmlText): string {
46
46
 
47
47
  function elementTextContent(el: Y.XmlElement | Y.XmlFragment): string {
48
48
  const parts: string[] = [];
49
- for (let i = 0; i < el.length; i++) {
50
- const child = el.get(i);
49
+ for (const child of el.toArray()) {
51
50
  if (child instanceof Y.XmlText) {
52
51
  parts.push(xmlTextToMarkdown(child));
53
52
  } else if (child instanceof Y.XmlElement) {
@@ -242,8 +241,9 @@ function serializeList(
242
241
  indent: string,
243
242
  ): string {
244
243
  const lines: string[] = [];
245
- for (let i = 0; i < el.length; i++) {
246
- const item = el.get(i);
244
+ const items = el.toArray();
245
+ for (let i = 0; i < items.length; i++) {
246
+ const item = items[i];
247
247
  if (
248
248
  item instanceof Y.XmlElement &&
249
249
  item.nodeName === "listItem"
@@ -258,8 +258,7 @@ function serializeList(
258
258
 
259
259
  function serializeTaskList(el: Y.XmlElement, indent: string): string {
260
260
  const lines: string[] = [];
261
- for (let i = 0; i < el.length; i++) {
262
- const item = el.get(i);
261
+ for (const item of el.toArray()) {
263
262
  if (
264
263
  item instanceof Y.XmlElement &&
265
264
  item.nodeName === "taskItem"
@@ -277,8 +276,7 @@ function serializeTaskList(el: Y.XmlElement, indent: string): string {
277
276
  function serializeTable(el: Y.XmlElement): string {
278
277
  const rows: string[][] = [];
279
278
 
280
- for (let i = 0; i < el.length; i++) {
281
- const row = el.get(i);
279
+ for (const row of el.toArray()) {
282
280
  if (
283
281
  !(row instanceof Y.XmlElement) ||
284
282
  row.nodeName !== "tableRow"
@@ -286,8 +284,7 @@ function serializeTable(el: Y.XmlElement): string {
286
284
  continue;
287
285
 
288
286
  const cells: string[] = [];
289
- for (let j = 0; j < row.length; j++) {
290
- const cell = row.get(j);
287
+ for (const cell of row.toArray()) {
291
288
  if (cell instanceof Y.XmlElement) {
292
289
  cells.push(elementTextContent(cell));
293
290
  }
@@ -346,8 +343,7 @@ function serializeChildren(
346
343
  indent: string,
347
344
  ): string {
348
345
  const parts: string[] = [];
349
- for (let i = 0; i < el.length; i++) {
350
- const child = el.get(i);
346
+ for (const child of el.toArray()) {
351
347
  if (child instanceof Y.XmlElement) {
352
348
  const serialized = serializeElement(child, indent);
353
349
  if (serialized) parts.push(serialized);
@@ -373,8 +369,9 @@ export function yjsToMarkdown(
373
369
  let title = "Untitled";
374
370
  const bodyParts: string[] = [];
375
371
 
376
- for (let i = 0; i < fragment.length; i++) {
377
- const child = fragment.get(i);
372
+ // Iterate via toArray() (O(n)). Indexed fragment.get(i) is an O(i)
373
+ // linked-list walk, which made this top-level loop O() on large docs.
374
+ for (const child of fragment.toArray()) {
378
375
  if (!(child instanceof Y.XmlElement)) continue;
379
376
 
380
377
  if (child.nodeName === "documentHeader") {
@@ -1805,8 +1802,8 @@ export interface DocumentBlock {
1805
1802
 
1806
1803
  export function readBlocksFromFragment(fragment: Y.XmlFragment): DocumentBlock[] {
1807
1804
  const result: DocumentBlock[] = [];
1808
- for (let i = 0; i < fragment.length; i++) {
1809
- const child = fragment.get(i);
1805
+ // toArray() is O(n); indexed fragment.get(i) would make this O(n²).
1806
+ for (const child of fragment.toArray()) {
1810
1807
  if (!(child instanceof Y.XmlElement)) continue;
1811
1808
  const name = child.nodeName;
1812
1809
  if (name === "documentHeader" || name === "documentMeta") continue;
@@ -75,13 +75,16 @@ export interface DocumentManagerConfig {
75
75
  */
76
76
  rootDocId?: string;
77
77
  /**
78
- * PROTOTYPE (Path-1 doctree scaling): opt into TreeManager's in-memory
79
- * parent→children index. When `false` (default), every tree walk
80
- * re-scans the whole `doc-tree` Y.Map (O(n) per call, O() recursive
81
- * traversal) the historical behaviour, byte-for-byte. When `true`,
82
- * walks resolve against a lazily-rebuilt adjacency index (O(k) per
83
- * lookup, O(result) traversal) and `tree.childrenOfPage()` becomes
84
- * usable. Behind a flag so it ships dark until benchmarked.
78
+ * TreeManager's in-memory parent→children index. When `true` (the
79
+ * default), tree WALKS resolve against a lazily-rebuilt adjacency index
80
+ * (O(k) per lookup, O(result) traversal), kept fresh by an `observeDeep`
81
+ * dirty bit and rebuilt at most once per mutation batch; `childrenOfPage()`
82
+ * becomes usable with stable (order,id) sibling ordering. Set `false` to
83
+ * force the legacy whole-map scan (O(n) per call, O() recursive) — only
84
+ * needed to reproduce the exact historical order-only sibling ordering.
85
+ * Benchmarked at ~700× on deep walks. Index affects WALK reads only;
86
+ * `readEntries`/`get` keep raw parentId either way. Lazy: no index or
87
+ * observer is bound until the first walk read.
85
88
  */
86
89
  treeIndex?: boolean;
87
90
  }
@@ -206,11 +209,11 @@ export class DocumentManager {
206
209
  }
207
210
 
208
211
  /**
209
- * Whether the TreeManager in-memory index is enabled (Path-1 prototype).
210
- * Off by default — see {@link DocumentManagerConfig.treeIndex}.
212
+ * Whether the TreeManager in-memory index is enabled.
213
+ * On by default — see {@link DocumentManagerConfig.treeIndex}.
211
214
  */
212
215
  get treeIndexEnabled(): boolean {
213
- return this._config.treeIndex ?? false;
216
+ return this._config.treeIndex ?? true;
214
217
  }
215
218
 
216
219
  get rootDocument(): Y.Doc | null {