edytor 5.0.0 → 6.0.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.
Files changed (75) hide show
  1. package/README.md +2 -2
  2. package/dist/cloudflare/room/context.d.ts +62 -74
  3. package/dist/crdt/doc/history.js +2 -3
  4. package/dist/crdt/doc/ops.d.ts +81 -0
  5. package/dist/crdt/doc/ops.js +1898 -0
  6. package/dist/crdt/edytor-doc.d.ts +156 -156
  7. package/dist/crdt/edytor-doc.js +14 -42
  8. package/dist/crdt/flow.d.ts +4 -46
  9. package/dist/crdt/flow.js +2 -262
  10. package/dist/crdt/index.d.ts +62 -74
  11. package/dist/crdt/operations.d.ts +1 -1
  12. package/dist/crdt/placement/resolve.d.ts +1 -1
  13. package/dist/crdt/protocols/awareness.js +46 -81
  14. package/dist/crdt/providers/index.d.ts +10 -22
  15. package/dist/crdt/providers/indexeddb.d.ts +6 -10
  16. package/dist/crdt/providers/indexeddb.js +10 -16
  17. package/dist/crdt/providers/room.d.ts +11 -0
  18. package/dist/crdt/providers/room.js +26 -12
  19. package/dist/crdt/providers/websocket.d.ts +6 -12
  20. package/dist/crdt/providers/websocket.js +8 -15
  21. package/dist/crdt/rangeDelete.d.ts +2 -47
  22. package/dist/crdt/rangeDelete.js +1 -169
  23. package/dist/crdt/text/index/build.d.ts +125 -0
  24. package/dist/crdt/text/index/build.js +2895 -0
  25. package/dist/crdt/text/index/shared.d.ts +2 -6
  26. package/dist/crdt/text/index/shared.js +2 -5
  27. package/dist/crdt/text/model.js +2 -3
  28. package/dist/crdt/text/runs.d.ts +9 -12
  29. package/dist/crdt/text/runs.js +2 -165
  30. package/dist/events/domTextOffset.d.ts +2 -0
  31. package/dist/events/domTextOffset.js +8 -0
  32. package/dist/export/forms.d.ts +50 -50
  33. package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
  34. package/dist/plugins/richtext/richTextOperations.js +26 -32
  35. package/dist/selection/selection.utils.js +5 -8
  36. package/dist/session/handles.js +3 -7
  37. package/dist/surface/observer.svelte.js +15 -30
  38. package/dist/surface/projector.svelte.js +2 -2
  39. package/dist/utils.d.ts +5 -0
  40. package/dist/utils.js +7 -0
  41. package/package.json +1 -1
  42. package/dist/crdt/doc/capability.d.ts +0 -56
  43. package/dist/crdt/doc/capability.js +0 -207
  44. package/dist/crdt/doc/content.d.ts +0 -16
  45. package/dist/crdt/doc/content.js +0 -77
  46. package/dist/crdt/doc/delete.d.ts +0 -15
  47. package/dist/crdt/doc/delete.js +0 -84
  48. package/dist/crdt/doc/layout.d.ts +0 -9
  49. package/dist/crdt/doc/layout.js +0 -178
  50. package/dist/crdt/doc/meta.d.ts +0 -19
  51. package/dist/crdt/doc/meta.js +0 -198
  52. package/dist/crdt/doc/moves.d.ts +0 -21
  53. package/dist/crdt/doc/moves.js +0 -274
  54. package/dist/crdt/doc/split-merge.d.ts +0 -10
  55. package/dist/crdt/doc/split-merge.js +0 -179
  56. package/dist/crdt/doc/steps.d.ts +0 -65
  57. package/dist/crdt/doc/steps.js +0 -311
  58. package/dist/crdt/text/index/cache.d.ts +0 -16
  59. package/dist/crdt/text/index/cache.js +0 -175
  60. package/dist/crdt/text/index/claims.d.ts +0 -18
  61. package/dist/crdt/text/index/claims.js +0 -203
  62. package/dist/crdt/text/index/fold.d.ts +0 -21
  63. package/dist/crdt/text/index/fold.js +0 -656
  64. package/dist/crdt/text/index/layout.d.ts +0 -22
  65. package/dist/crdt/text/index/layout.js +0 -258
  66. package/dist/crdt/text/index/placement.d.ts +0 -47
  67. package/dist/crdt/text/index/placement.js +0 -738
  68. package/dist/crdt/text/index/records.d.ts +0 -13
  69. package/dist/crdt/text/index/records.js +0 -139
  70. package/dist/crdt/text/index/report.d.ts +0 -18
  71. package/dist/crdt/text/index/report.js +0 -337
  72. package/dist/crdt/text/index/state.d.ts +0 -197
  73. package/dist/crdt/text/index/state.js +0 -116
  74. package/dist/crdt/text/index/streams.d.ts +0 -25
  75. package/dist/crdt/text/index/streams.js +0 -248
@@ -56,7 +56,7 @@ import { messagePermissionDenied, messageAccess, messageReadOnly, READ_ONLY_DENI
56
56
  import { bindSync } from '../protocols/sync.js';
57
57
  import { readCommentsMessage, writeCommentsSubscribe } from '../protocols/comments.js';
58
58
  import { frame } from '../protocols/envelope.js';
59
- import { assertRoomId, beginDestroy, bindRoomProtocol, tabs, chunkFrame, CLOSE, createChunkReader, emitFailed, initLifecycle, isRefusal, LOCAL_PRESENCE_LOSS, markSynced, messageAuth, messageChunk, messageComments, messageSaved, messageSync, MAX_FRAME_BYTES, SyncRefusedError } from './room.js';
59
+ import { assertRoomId, beginDestroy, bindRoomProtocol, tabs, chunkFrame, CLOSE, createChunkReader, emitFailed, fault, initLifecycle, isRefusal, unknownType, LOCAL_PRESENCE_LOSS, markSynced, messageAuth, messageChunk, messageComments, messageSaved, messageSync, MAX_FRAME_BYTES, SyncRefusedError } from './room.js';
60
60
  import { asEngineDoc } from '../structs.js';
61
61
  import { ATTRIBUTION_ROOT } from '../schema.js';
62
62
  import { ATTRIBUTION_ORIGIN, writerOf } from '../attribution/attribution.js';
@@ -135,10 +135,7 @@ export const bindWebsocketProvider = (Y) => {
135
135
  authType !== messageAccess) {
136
136
  // An auth type we do not speak inside a valid v14 envelope is
137
137
  // protocol skew — reported, never dropped silently.
138
- provider.emit('message-error', [
139
- new Error(`Unknown auth message type ${authType}`),
140
- provider
141
- ]);
138
+ unknownType(provider, 'auth', authType);
142
139
  }
143
140
  }
144
141
  },
@@ -459,7 +456,7 @@ export const bindWebsocketProvider = (Y) => {
459
456
  room.readMessage(provider, new Uint8Array(event.data), true, (buf) => sendOn(provider, websocket, buf));
460
457
  }
461
458
  catch (error) {
462
- provider.emit('message-error', [error, provider]);
459
+ fault(provider, error);
463
460
  }
464
461
  };
465
462
  websocket.onerror = (event) => {
@@ -526,8 +523,8 @@ export const bindWebsocketProvider = (Y) => {
526
523
  /** Closes the current dial if it has not opened in time. */
527
524
  _connectTimer;
528
525
  _checkInterval;
529
- _updateHandler;
530
- _awarenessUpdateHandler;
526
+ /** @internal Removes the document and presence listeners (`room.listen`). */
527
+ _unlisten;
531
528
  constructor(serverUrl, roomname, doc, { connect = true, awareness = new Awareness(doc), params = {}, WebSocketPolyfill = WebSocket, resyncInterval = 0, maxBackoffTime = 2500, connectTimeout = defaultConnectTimeout, disableBc = false, maxFrameBytes = MAX_FRAME_BYTES } = {}) {
532
529
  assertRoomId(roomname);
533
530
  super();
@@ -548,14 +545,13 @@ export const bindWebsocketProvider = (Y) => {
548
545
  if (resyncInterval > 0) {
549
546
  this._resync = setInterval(() => send(this, room.step1(this)), resyncInterval);
550
547
  }
551
- this._bcSubscriber = room.bcSubscriber(this);
552
548
  providers.add(this);
553
549
  // Local doc updates go to the room; another tab's go to the server
554
550
  // only (`broadcastUpdate` quarantines a read-only document). A
555
551
  // socket the room made read-only gets this document's own edits
556
552
  // alone (each is denied): what a tab or a store delivered, and the
557
553
  // actor records, would draw denials nobody made.
558
- this._updateHandler = (update, origin, _doc, transaction) => {
554
+ const onUpdate = (update, origin, _doc, transaction) => {
559
555
  if (origin === this)
560
556
  return;
561
557
  const heard = origin === this._fromTab;
@@ -573,9 +569,7 @@ export const bindWebsocketProvider = (Y) => {
573
569
  };
574
570
  this._writes = new OwnWrites(doc, (unsaved) => this.emit('saved', [{ saved: unsaved === 0, unsaved }, this]));
575
571
  this._writes.trackDocument();
576
- this.doc.on('update', this._updateHandler);
577
- this._awarenessUpdateHandler = room.awarenessUpdateHandler(this);
578
- awareness.on('update', this._awarenessUpdateHandler);
572
+ this._unlisten = room.listen(this, onUpdate);
579
573
  this._checkInterval = setInterval(() => {
580
574
  if (!this.wsconnected)
581
575
  return;
@@ -651,8 +645,7 @@ export const bindWebsocketProvider = (Y) => {
651
645
  clearInterval(this._checkInterval);
652
646
  providers.delete(this);
653
647
  this.disconnect();
654
- this.awareness.off('update', this._awarenessUpdateHandler);
655
- this.doc.off('update', this._updateHandler);
648
+ this._unlisten();
656
649
  super.destroy();
657
650
  }
658
651
  disconnect() {
@@ -1,48 +1,8 @@
1
1
  /**
2
- * Range deletion between two positions — one prepared document operation
3
- * whose merge is the document's `canMerge`. The rules are the
4
- * `del.range.*` rows of `docs/editor-delete-contract.md`:
5
- *
6
- * - the head keeps `[0, start)`, the tail keeps `[end, len)`, and every
7
- * block strictly between them in document order dies;
8
- * - the head dies iff its prefix is empty (never for a replacement,
9
- * `del.range.replace`, nor when nothing else would be left to hold the
10
- * caret, `del.range.whole-doc`: the head is kept, emptied, with its id,
11
- * type and data, so peers deleting the same range keep the same block)
12
- * and the tail then keeps its id; the tail dies iff
13
- * its suffix is empty; otherwise the tail's suffix merges into the head
14
- * when `canMerge(tail, head)` allows it (`del.range.island-seal`) — an end
15
- * at the tail's start included: the seam between them is deleted;
16
- * - what follows the range end survives: the dying tail's children and the
17
- * later siblings inside every container the range dies through take the
18
- * topmost such container's slot, unless that would cross an island seal —
19
- * then those containers stay (`del.range.outside-survives`);
20
- * - a container (a list: the document's `isContainer`) never dies because
21
- * the range starts before it: it keeps what follows the range end, like
22
- * the keys (`del.merge.container`); it dies only when it loses every
23
- * child and holds no text of its own — the document's `emptiable`, the
24
- * keys' rule (`del.range.empty-container`). An island that renders
25
- * no content dies when it loses every child too, except a `lines` island
26
- * (a code block) the range lies in: its head is then kept, emptied
27
- * (`del.range.island-kept`);
28
- * - what a range rescues takes the kind it shows in its new slot (the
29
- * document's `settle`: a paragraph rescued into a list is its item);
30
- * - blocks the view hides (a closed toggle's body) are not in the range:
31
- * they go only with a block that goes, so a surviving head keeps its
32
- * hidden children, unless it is kept only to hold the caret; a dying
33
- * closed tail's body takes its place, shown (`del.range.hidden-body`);
34
- * - a layout the range leaves with one column dissolves, in the same plan
35
- * (`layout.dissolving`);
36
- * - a range with an end in a table's cell keeps every cell and row: each
37
- * cell it crosses loses the text it covers, nothing merges across a
38
- * cell's edge, the blocks outside the table between the ends go, a
39
- * table wholly between them goes whole, and the caret lands at the start
40
- * (`table.range`);
41
- * - the plan's `at` is where the caret lands (`del.range.caret`).
2
+ * The types range deletion (`deleteRange`, `replaceRange`: the `del.range.*`
3
+ * rows, prepared in `doc/ops.ts`) reads.
42
4
  */
43
5
  import type { BlockId } from './placement/model.js';
44
- import type { Prepared } from './edytor-doc.js';
45
- import type { OpsContext } from './doc/steps.js';
46
6
  /** A position in one document version: a block and a display offset. */
47
7
  export type DocPosition = {
48
8
  block: BlockId;
@@ -56,8 +16,3 @@ export type DocPosition = {
56
16
  export type RangeView = {
57
17
  hidden?: (id: BlockId, removed?: ReadonlySet<BlockId>) => boolean;
58
18
  };
59
- /** `deleteRange` and `replaceRange` (the head kept), prepared. */
60
- export declare const rangeDeleteOps: (c: OpsContext) => {
61
- deleteRange: (from: DocPosition, to: DocPosition, view?: RangeView) => Prepared;
62
- replaceRange: (from: DocPosition, to: DocPosition, view?: RangeView) => Prepared;
63
- };
@@ -1,169 +1 @@
1
- import { ref } from './doc/plan.js';
2
- /** `deleteRange` and `replaceRange` (the head kept), prepared. */
3
- export const rangeDeleteOps = (c) => {
4
- // `headSpent`: a whole-doc retry keeps the head, but what it hid goes as if it went.
5
- const prepare = (keepHead, headSpent = false) => (from, to, view = {}) => {
6
- // A position names shown text: a live block that renders its own content.
7
- const holds = (id) => c.contentTarget(id) && c.rendersContent(id);
8
- const pos = (p) => {
9
- const block = ref(p.block);
10
- const len = holds(block) ? c.displayLength(block) : -1;
11
- return { block, offset: Math.max(0, Math.min(p.offset, len)) };
12
- };
13
- let [s, e] = [pos(from), pos(to)];
14
- if (!holds(s.block) || !holds(e.block))
15
- return c.REFUSED;
16
- const { ids, at } = c.view().order;
17
- if (at.get(s.block) > at.get(e.block) || (s.block === e.block && s.offset > e.offset))
18
- [s, e] = [e, s];
19
- const S = s.block;
20
- const E = e.block;
21
- if (S === E) {
22
- const length = e.offset - s.offset;
23
- const writes = length > 0 ? [{ op: 'deleteText', id: S, offset: s.offset, length }] : [];
24
- return { ...c.plan([S], writes), at: s };
25
- }
26
- const parent = (id) => c.ancestorsOf(id)[0] ?? null;
27
- const chain = c.ancestorsOf(E);
28
- if (c.isTableCell(S) || c.isTableCell(E))
29
- return inTables(s, e, view);
30
- // What the view hides once `removed` go (nothing: as it is now).
31
- const hiddenOnce = (removed) => {
32
- const set = new Set(removed);
33
- return (id) => !!view.hidden?.(id, set);
34
- };
35
- const headDies = !keepHead && s.offset === 0;
36
- // What the view hides is not in the range: it goes only with a block that goes,
37
- // a dying head's body included.
38
- const hidden = hiddenOnce(headDies || headSpent ? [S] : []);
39
- const between = ids.slice(at.get(S) + 1, at.get(E)).filter((id) => !hidden(id));
40
- const lenE = c.displayLength(E);
41
- const tailDies = e.offset === lenE;
42
- const merges = !headDies && !tailDies && c.canMerge(E, S);
43
- const tailGone = tailDies || merges;
44
- // E's ancestors the range starts before die unless the rescue would cross an island.
45
- // A container (a list) is not one: it dies only when the range empties it, and
46
- // keeps its later items like the keys do (`del.merge.container`).
47
- const partial = chain.filter((a) => (a === (headDies ? S : null) || between.includes(a)) && !c.isContainer(a));
48
- const top = partial.at(-1);
49
- const sealed = top !== undefined && chain.slice(0, chain.indexOf(top) + 1).some(c.isIsland);
50
- const spared = new Set(sealed ? chain : []);
51
- const doomed = new Set(between.filter((id) => !spared.has(id) && !chain.includes(id)));
52
- if (!sealed)
53
- partial.forEach((a) => doomed.add(a));
54
- if (headDies && !spared.has(S))
55
- doomed.add(S);
56
- if (tailDies)
57
- doomed.add(E);
58
- // Rescue what follows the range end right after the topmost dying container's slot
59
- // (after, not at it: a concurrent delete of S revives a merged E above them).
60
- const home = sealed || top === undefined ? (tailGone ? E : undefined) : top;
61
- const rescued = home === undefined ? [] : tailGone ? c.childrenIds(E) : [E];
62
- for (let cur = E; home !== undefined && cur !== home; cur = parent(cur)) {
63
- const sibs = c.childrenIds(parent(cur));
64
- rescued.push(...sibs.slice(sibs.indexOf(cur) + 1));
65
- }
66
- const dest = home === undefined || rescued.length === 0 ? null : c.positionOf(home);
67
- // A container (or an island that renders nothing) losing every child dies too.
68
- const gone = (id) => doomed.has(id) || (merges && id === E);
69
- const dies = (id) => c.emptiable(id) || (c.isIsland(id) && !c.rendersContent(id));
70
- const emptied = (id) => {
71
- if (id === null || gone(id) || !dies(id) || id === dest?.parent)
72
- return;
73
- if (!c.childrenIds(id).every(gone))
74
- return;
75
- doomed.add(id);
76
- emptied(parent(id));
77
- };
78
- [...doomed, ...(merges ? [E] : [])].forEach((id) => emptied(parent(id)));
79
- // A range inside one `lines` island never removes it: the head stays, emptied
80
- // (a code block's Mod+A, `del.range.island-kept`).
81
- const island = c.ancestorsOf(S).find(c.isIsland);
82
- const linesIsland = island !== undefined && c.isLines(island);
83
- if (!keepHead && linesIsland && doomed.has(island) && chain.includes(island))
84
- return prepare(true, headDies)(from, to, view);
85
- const lenS = c.displayLength(S);
86
- const writes = [];
87
- if (!doomed.has(S) && lenS > s.offset)
88
- writes.push({ op: 'deleteText', id: S, offset: s.offset, length: lenS - s.offset });
89
- if (!gone(E) && e.offset > 0)
90
- writes.push({ op: 'deleteText', id: E, offset: 0, length: e.offset });
91
- if (dest !== null) {
92
- writes.push(...c.move(rescued, dest.parent, dest.index + 1));
93
- // Each shows the kind of its new slot (an island's line, a list's item leave theirs).
94
- for (const id of rescued)
95
- writes.push(...c.settle(parent(id), [id], dest.parent));
96
- }
97
- if (merges) {
98
- // The whole tail merges, then its cut prefix goes: E's own text is never written.
99
- writes.push({ op: 'mergeBlocks', from: E, into: S, at: s.offset, length: lenE });
100
- if (e.offset > 0)
101
- writes.push({ op: 'deleteText', id: S, offset: s.offset, length: e.offset });
102
- }
103
- const kept = [...rescued, ...(merges ? [E] : [])];
104
- for (const id of doomed)
105
- if (!doomed.has(parent(id)))
106
- writes.push(c.remove(id, kept));
107
- // A layout the range leaves with one column dissolves (`layout.dissolving`).
108
- writes.push(...c.dissolving(new Set([...doomed, ...(merges ? [E] : [])]), rescued, writes));
109
- const caret = (() => {
110
- if (!doomed.has(S))
111
- return s;
112
- if (!gone(E))
113
- return { block: E, offset: 0 };
114
- const survives = (id) => id === null || rescued.includes(id) || (!gone(id) && survives(parent(id)));
115
- // Shown once the plan runs: a dying closed tail's rescued body is.
116
- const hiddenAfter = hiddenOnce([...doomed, ...(merges ? [E] : [])]);
117
- const shown = (id) => survives(id) && holds(id) && !hiddenAfter(id);
118
- // Before the tail: blocks a sealed rescue spared between S and E count too.
119
- const prev = ids.slice(0, at.get(E)).findLast(shown);
120
- if (prev !== undefined)
121
- return { block: prev, offset: c.displayLength(prev) };
122
- const next = ids.slice(at.get(E) + 1).find(shown);
123
- return next === undefined ? null : { block: next, offset: 0 };
124
- })();
125
- // Nothing else left to hold the caret: the head stays, emptied (whole-doc).
126
- if (caret === null)
127
- return prepare(true, headDies)(from, to, view);
128
- return { ...c.plan([caret.block], writes), at: caret };
129
- };
130
- /**
131
- * `table.range`: a range with an end in a cell. Every cell it crosses
132
- * loses the text it covers and stays, with its row; the ends keep what
133
- * lies outside the range, merging nothing; any other block between the
134
- * ends goes (a table wholly between them whole), but the ancestors of
135
- * the end, which stay and lose their text (the range covers it: they
136
- * come after the start in reading order); the caret lands at the start.
137
- */
138
- const inTables = (s, e, view) => {
139
- const { ids, at } = c.view().order;
140
- const [S, E] = [s.block, e.block];
141
- const kept = new Set(c.ancestorsOf(E));
142
- const between = ids.slice(at.get(S) + 1, at.get(E)).filter((id) => !view.hidden?.(id));
143
- const writes = [];
144
- const lenS = c.displayLength(S);
145
- if (lenS > s.offset)
146
- writes.push({ op: 'deleteText', id: S, offset: s.offset, length: lenS - s.offset });
147
- const [tableS, tableE] = [c.tableOf(S), c.tableOf(E)];
148
- const doomed = new Set();
149
- for (const id of between) {
150
- const table = c.tableOf(id);
151
- if (kept.has(id) || (table !== null && (table === tableS || table === tableE))) {
152
- const len = c.isTableCell(id) || kept.has(id) ? c.displayLength(id) : 0;
153
- if (len > 0)
154
- writes.push({ op: 'deleteText', id, offset: 0, length: len });
155
- }
156
- else
157
- doomed.add(id);
158
- }
159
- if (e.offset > 0)
160
- writes.push({ op: 'deleteText', id: E, offset: 0, length: e.offset });
161
- const parentOf = (id) => c.ancestorsOf(id)[0] ?? null;
162
- const roots = [...doomed].filter((id) => !doomed.has(parentOf(id)));
163
- for (const id of roots)
164
- writes.push(c.remove(id, []));
165
- writes.push(...c.dissolving(new Set(roots), [], writes));
166
- return { ...c.plan([S], writes), at: s };
167
- };
168
- return { deleteRange: prepare(false), replaceRange: prepare(true) };
169
- };
1
+ export {};
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The per-document index (`indexOf`) as ONE closure: its state, then its
3
+ * parts — the claim graph, the stream table and each block's claims, the
4
+ * layout and table rules, placements and the children index (with THE
5
+ * shown-kind rule, `typeOf`), block records, the run cache, the fold and the
6
+ * change report — each reading the state and the parts before it as closure
7
+ * variables, then the view (`RunView`) over them. `../runs.ts` declares the
8
+ * index's types and contracts. The self-checks (`indexChecks`) read the
9
+ * maintained facts through {@link IndexFacts} and rebuild their own.
10
+ */
11
+ import type { EngineDoc, EngineNode, EngineTransaction } from '../../engine-api.js';
12
+ import { type BlockId, type BlockRec, type ChildSlot, type DisplayOwnership, type ResolvedPlacement } from '../../placement/model.js';
13
+ import { type IdSetLike } from '../../structs.js';
14
+ import { type LiveRow, type RowItem } from '../rows.js';
15
+ import type { ContentRun, RunView } from '../runs.js';
16
+ import { type Cached } from './shared.js';
17
+ import { type Claim, type Owner, type Stream } from '../model.js';
18
+ import { type Edge, type SlotRef } from '../../placement/resolve.js';
19
+ export type TextEdit = {
20
+ item: RowItem;
21
+ len: number;
22
+ del: boolean;
23
+ };
24
+ export type TextEdits = {
25
+ edits: TextEdit[];
26
+ scan: boolean;
27
+ opaque: boolean;
28
+ };
29
+ /** One fold's effects: invalidated blocks, rescanned texts, rebuilt facets. */
30
+ export type FoldCtx = {
31
+ invalidated: Set<BlockId>;
32
+ /** Per home block: its text's edits, whether it rescans, whether every reader re-reads. */
33
+ texts: Map<BlockId, {
34
+ edits: TextEdit[];
35
+ scan: boolean;
36
+ opaque: boolean;
37
+ seen: Set<TextEdits>;
38
+ }>;
39
+ /** The blocks whose entry or nonce changed: their delimiters are re-decided. */
40
+ named: Set<BlockId>;
41
+ table: boolean;
42
+ /** The winning parents, before and after, of the blocks the fold touched. */
43
+ parents: Set<BlockId>;
44
+ };
45
+ /** A slot in a children index: its display parent, rank and the island it displays out of. */
46
+ export type Slot = {
47
+ parent: BlockId | null;
48
+ rank: string;
49
+ reset?: BlockId;
50
+ };
51
+ /** Each row's segments at its last placement: segment 0's block, then each cut's key and block. */
52
+ export type Placed = {
53
+ head: BlockId | null;
54
+ keys: string[];
55
+ blocks: (BlockId | null)[];
56
+ };
57
+ /** The tree the change report last published. */
58
+ export type Published = {
59
+ nodes: Map<BlockId, {
60
+ parent: BlockId | null;
61
+ index: number;
62
+ type: string;
63
+ data: unknown;
64
+ runs: readonly ContentRun[];
65
+ key?: string;
66
+ }>;
67
+ order: Map<BlockId | null, readonly BlockId[]>;
68
+ /** The children index version it was taken at. */
69
+ kids: number;
70
+ };
71
+ /** A transaction as the fold reads it. */
72
+ export type Tx = {
73
+ insertSet?: IdSetLike;
74
+ deleteSet?: IdSetLike;
75
+ changed?: EngineTransaction['changed'];
76
+ /** The visibility changes hold records made, in order (fork patch YP20). */
77
+ holds?: number[];
78
+ /** The units whose mover changed, in order (fork patch YP22). */
79
+ moves?: number[];
80
+ };
81
+ /** The index's self-checks (`indexChecks`), run after each fold and each report. */
82
+ export type SelfChecks = {
83
+ check: () => void;
84
+ checkPublished: (pub: Published) => void;
85
+ };
86
+ /**
87
+ * What the self-checks read of one index: the facts it maintains
88
+ * incrementally (compared with their rebuild from the replicated state) and
89
+ * the reads they compare through.
90
+ */
91
+ export type IndexFacts = {
92
+ doc: EngineDoc;
93
+ registry: EngineNode;
94
+ blocks: ReadonlyMap<BlockId, BlockRec>;
95
+ displaysMap: ReadonlyMap<BlockId, ReadonlySet<BlockId>>;
96
+ rows: ReadonlyMap<BlockId, LiveRow>;
97
+ streamIx: ReadonlyMap<BlockId, unknown>;
98
+ delim: ReadonlyMap<BlockId, string>;
99
+ placementsMap: ReadonlyMap<BlockId, ResolvedPlacement>;
100
+ slotsByBlock: ReadonlyMap<BlockId, SlotRef[]>;
101
+ edges: ReadonlyMap<BlockId, Edge | null>;
102
+ rejected: ReadonlySet<BlockId>;
103
+ kids0: Map<BlockId | null, ChildSlot[]>;
104
+ kidsMap: Map<BlockId | null, ChildSlot[]>;
105
+ slots: ReadonlyMap<BlockId, Slot>;
106
+ dissolved: ReadonlySet<BlockId>;
107
+ following: ReadonlySet<BlockId>;
108
+ cache: ReadonlyMap<BlockId, Cached>;
109
+ dirty: ReadonlySet<BlockId>;
110
+ ownShim: DisplayOwnership;
111
+ own0: DisplayOwnership;
112
+ ownerOf: (b: BlockId) => Owner;
113
+ streamOf: (b: BlockId) => Stream | undefined;
114
+ sameClaims: (x: readonly Claim[], y: readonly Claim[]) => boolean;
115
+ dissolve: (kids: Map<BlockId | null, ChildSlot[]>) => Set<BlockId>;
116
+ noteFollowing: (id: BlockId) => void;
117
+ ensurePlacements: () => void;
118
+ computeFresh: (b: BlockId) => {
119
+ fresh: readonly ContentRun[];
120
+ };
121
+ unfolded: () => boolean;
122
+ reachable: (prev?: Published['nodes']) => Published;
123
+ };
124
+ /** The doc's index — built on first use, torn down when the doc is destroyed. */
125
+ export declare const indexOf: (doc: EngineDoc) => RunView;