edytor 5.0.0 → 7.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 (137) hide show
  1. package/README.md +3 -3
  2. package/dist/cloudflare/DocumentRoom.d.ts +77 -2
  3. package/dist/cloudflare/DocumentRoom.js +37 -1
  4. package/dist/cloudflare/index.d.ts +1 -1
  5. package/dist/cloudflare/index.js +1 -1
  6. package/dist/cloudflare/room/access.d.ts +6 -1
  7. package/dist/cloudflare/room/access.js +30 -4
  8. package/dist/cloudflare/room/admission.d.ts +11 -0
  9. package/dist/cloudflare/room/admission.js +43 -10
  10. package/dist/cloudflare/room/context.d.ts +73 -76
  11. package/dist/cloudflare/room/context.js +17 -6
  12. package/dist/cloudflare/room/forged.d.ts +1 -1
  13. package/dist/cloudflare/room/forged.js +11 -9
  14. package/dist/cloudflare/room/frames.d.ts +2 -0
  15. package/dist/cloudflare/room/frames.js +2 -0
  16. package/dist/cloudflare/room/presence.js +1 -4
  17. package/dist/cloudflare/room/reseed.d.ts +93 -0
  18. package/dist/cloudflare/room/reseed.js +500 -0
  19. package/dist/cloudflare/room/scheduler.d.ts +1 -1
  20. package/dist/cloudflare/room/scheduler.js +9 -1
  21. package/dist/cloudflare/room/shared.d.ts +10 -0
  22. package/dist/cloudflare/room/shared.js +10 -0
  23. package/dist/cloudflare/room/storage.d.ts +7 -0
  24. package/dist/cloudflare/room/storage.js +33 -1
  25. package/dist/components/Edytor.svelte +18 -3
  26. package/dist/components/Edytor.svelte.d.ts +10 -1
  27. package/dist/components/EdytorRoot.svelte +17 -0
  28. package/dist/components/EdytorRoot.svelte.d.ts +4 -0
  29. package/dist/components/Suggestion.svelte +1 -1
  30. package/dist/crdt/attribution/block.d.ts +3 -0
  31. package/dist/crdt/attribution/block.js +3 -2
  32. package/dist/crdt/doc/history.js +2 -3
  33. package/dist/crdt/doc/ops.d.ts +81 -0
  34. package/dist/crdt/doc/ops.js +1898 -0
  35. package/dist/crdt/doc/suggest.d.ts +6 -2
  36. package/dist/crdt/doc/suggest.js +6 -5
  37. package/dist/crdt/edytor-doc.d.ts +157 -157
  38. package/dist/crdt/edytor-doc.js +15 -43
  39. package/dist/crdt/engine-room.d.ts +4 -0
  40. package/dist/crdt/engine-room.js +6 -0
  41. package/dist/crdt/flow.d.ts +4 -46
  42. package/dist/crdt/flow.js +2 -262
  43. package/dist/crdt/index.d.ts +64 -75
  44. package/dist/crdt/operations.d.ts +1 -1
  45. package/dist/crdt/placement/resolve.d.ts +1 -1
  46. package/dist/crdt/protocol.d.ts +2 -2
  47. package/dist/crdt/protocol.js +2 -2
  48. package/dist/crdt/protocols/auth.d.ts +8 -1
  49. package/dist/crdt/protocols/auth.js +14 -1
  50. package/dist/crdt/protocols/awareness.js +46 -81
  51. package/dist/crdt/providers/index.d.ts +11 -22
  52. package/dist/crdt/providers/index.js +22 -3
  53. package/dist/crdt/providers/indexeddb.d.ts +6 -10
  54. package/dist/crdt/providers/indexeddb.js +10 -16
  55. package/dist/crdt/providers/room.d.ts +26 -0
  56. package/dist/crdt/providers/room.js +41 -12
  57. package/dist/crdt/providers/websocket.d.ts +10 -14
  58. package/dist/crdt/providers/websocket.js +43 -23
  59. package/dist/crdt/rangeDelete.d.ts +2 -47
  60. package/dist/crdt/rangeDelete.js +1 -169
  61. package/dist/crdt/reseed.d.ts +127 -0
  62. package/dist/crdt/reseed.js +533 -0
  63. package/dist/crdt/restore.d.ts +13 -1
  64. package/dist/crdt/restore.js +8 -3
  65. package/dist/crdt/schema.d.ts +6 -0
  66. package/dist/crdt/schema.js +6 -0
  67. package/dist/crdt/structs.d.ts +8 -11
  68. package/dist/crdt/structs.js +25 -44
  69. package/dist/crdt/text/index/build.d.ts +119 -0
  70. package/dist/crdt/text/index/build.js +2801 -0
  71. package/dist/crdt/text/index/shared.d.ts +2 -6
  72. package/dist/crdt/text/index/shared.js +2 -5
  73. package/dist/crdt/text/model.js +2 -3
  74. package/dist/crdt/text/runs.d.ts +9 -12
  75. package/dist/crdt/text/runs.js +2 -165
  76. package/dist/crdt/vendor/yjs/UPSTREAM.md +101 -5
  77. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  78. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +10 -10
  79. package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +1 -0
  80. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  81. package/dist/crdt/vendor/yjs/src/structs/Item.js +24 -13
  82. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +12 -10
  83. package/dist/crdt/vendor/yjs/src/utils/encoding.js +42 -1
  84. package/dist/events/domTextOffset.d.ts +2 -0
  85. package/dist/events/domTextOffset.js +8 -0
  86. package/dist/export/forms.d.ts +50 -50
  87. package/dist/index.d.ts +1 -1
  88. package/dist/index.js +1 -1
  89. package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
  90. package/dist/plugins/richtext/richTextOperations.js +26 -32
  91. package/dist/selection/selection.svelte.d.ts +1 -1
  92. package/dist/selection/selection.svelte.js +87 -106
  93. package/dist/selection/selection.utils.js +5 -8
  94. package/dist/session/commands.js +49 -51
  95. package/dist/session/handles.js +3 -7
  96. package/dist/surface/attributes.d.ts +3 -3
  97. package/dist/surface/attributes.js +2 -2
  98. package/dist/surface/observer.svelte.d.ts +7 -7
  99. package/dist/surface/observer.svelte.js +225 -336
  100. package/dist/surface/projector.svelte.js +2 -2
  101. package/dist/utils.d.ts +5 -0
  102. package/dist/utils.js +7 -0
  103. package/package.json +1 -1
  104. package/dist/crdt/doc/capability.d.ts +0 -56
  105. package/dist/crdt/doc/capability.js +0 -207
  106. package/dist/crdt/doc/content.d.ts +0 -16
  107. package/dist/crdt/doc/content.js +0 -77
  108. package/dist/crdt/doc/delete.d.ts +0 -15
  109. package/dist/crdt/doc/delete.js +0 -84
  110. package/dist/crdt/doc/layout.d.ts +0 -9
  111. package/dist/crdt/doc/layout.js +0 -178
  112. package/dist/crdt/doc/meta.d.ts +0 -19
  113. package/dist/crdt/doc/meta.js +0 -198
  114. package/dist/crdt/doc/moves.d.ts +0 -21
  115. package/dist/crdt/doc/moves.js +0 -274
  116. package/dist/crdt/doc/split-merge.d.ts +0 -10
  117. package/dist/crdt/doc/split-merge.js +0 -179
  118. package/dist/crdt/doc/steps.d.ts +0 -65
  119. package/dist/crdt/doc/steps.js +0 -311
  120. package/dist/crdt/text/index/cache.d.ts +0 -16
  121. package/dist/crdt/text/index/cache.js +0 -175
  122. package/dist/crdt/text/index/claims.d.ts +0 -18
  123. package/dist/crdt/text/index/claims.js +0 -203
  124. package/dist/crdt/text/index/fold.d.ts +0 -21
  125. package/dist/crdt/text/index/fold.js +0 -656
  126. package/dist/crdt/text/index/layout.d.ts +0 -22
  127. package/dist/crdt/text/index/layout.js +0 -258
  128. package/dist/crdt/text/index/placement.d.ts +0 -47
  129. package/dist/crdt/text/index/placement.js +0 -738
  130. package/dist/crdt/text/index/records.d.ts +0 -13
  131. package/dist/crdt/text/index/records.js +0 -139
  132. package/dist/crdt/text/index/report.d.ts +0 -18
  133. package/dist/crdt/text/index/report.js +0 -337
  134. package/dist/crdt/text/index/state.d.ts +0 -197
  135. package/dist/crdt/text/index/state.js +0 -116
  136. package/dist/crdt/text/index/streams.d.ts +0 -25
  137. package/dist/crdt/text/index/streams.js +0 -248
@@ -1,656 +0,0 @@
1
- /**
2
- * THE fold: one pass over a transaction's changed `(type, parentSub)` pairs
3
- * into the index, once at commit and, for read-your-writes, over the
4
- * pending part of every transaction whose commit fold has not run.
5
- */
6
- import { readData } from '../../data.js';
7
- import { Y } from '../../engine.js';
8
- import { LIVE_WRITERS, baseIdOf, incarnationId } from '../../incarnations.js';
9
- import { ownerOfNode, slotOwner } from '../../placement/resolve.js';
10
- import { isBlockSlot, isDataSlot, isRecord, unitOf } from '../../placement/slots.js';
11
- import { CONTENT, CONTENT_NODE, NONCE, hasDeleteMark, isNodeLike } from '../../schema.js';
12
- import { changedUnder, clientsOf, engineOps, itemOf, queuedTransactions, sequenceLength, structAt, walkIdSet } from '../../structs.js';
13
- import { DEAD, isRowMark } from '../model.js';
14
- import { gapOfItem, headOf, rowLength, segmentOfGap, shiftGap } from '../rows.js';
15
- import { CONTENT_ATTR, DATA_SEQ_FACET, EMPTY_RUNS, ENTRY_FACET, SEQ_FACET, facetOf, checksOf, indexChecks, sameShape, typeAttr } from './shared.js';
16
- /** The fold of one doc's index, over every part it folds into. */
17
- export const indexFold = (ix) => {
18
- const { doc, registry, dataRoot, blocks, shells, textConsumers, ownerSeeds, rows, reclaim, placementSeeds, stateSeeds, incarnations, cache, dirty, foldStats, frames, candidates, ownerOf, invalidateBlock, streamOf, streamsIn, setRow, scanRow, refreshDelims, placeHome, applyReclaims, noteKind, homeFor, putSlot, edgeParent, kept, updateBlockRec, ensureRec, syncIncarnations, computeRuns, tableFacts } = ix;
19
- const ops = engineOps(Y);
20
- const NO_EDITS = Object.freeze({
21
- edits: [],
22
- scan: false,
23
- opaque: false
24
- });
25
- const holdsBoundary = (s, from, to) => {
26
- const arr = s.content?.arr;
27
- if (arr === undefined)
28
- return false;
29
- for (let k = from - s.id.clock; k < to - s.id.clock; k++)
30
- if (isRowMark(arr[k]))
31
- return true;
32
- return false;
33
- };
34
- /** The struct holding id `client:clock` (unsplit), `null` when the store holds none. */
35
- const structOf = (client, clock) => structAt(Y, clientsOf(doc).get(client) ?? [], clock);
36
- /**
37
- * The edits the id sets `ins`/`del`, the hold log `log` and the move log
38
- * `moves` (what the fold has not folded yet) hold: per text, its edits
39
- * (a log entry is a unit a hold record hid or showed, fork patch YP20:
40
- * `client, clock, ±len`, in order); per sequence, what changed in it
41
- * (`seq`): the slots written and removed, the records written and
42
- * removed and the units whose mover changed name the blocks to re-place.
43
- * Reading the sequences writes the slot map (the slots written and
44
- * removed, `putSlot`) and lists the ids whose home to read again
45
- * (`rehome`).
46
- */
47
- const makeEditIndex = (ins, del, log = [], moves = []) => {
48
- let seqs;
49
- const rehome = new Set();
50
- const seqOf = (node) => {
51
- let e = seqs.get(node);
52
- if (e === undefined)
53
- seqs.set(node, (e = { blocks: new Set(), data: false }));
54
- return e;
55
- };
56
- /** Note unit `c:k` (`value`) of the sequence of `node`: a block slot names its block, a data slot its data. */
57
- const noteUnit = (e, owner, value, c, k, inserted) => {
58
- if (isBlockSlot(value)) {
59
- e.blocks.add(slotOwner(registry, kept, value.b, c));
60
- if (inserted === null || (inserted && owner === undefined))
61
- return;
62
- putSlot(value.b, { c, k }, inserted ? { parent: owner, c, k } : null);
63
- rehome.add(value.b);
64
- }
65
- else if (isDataSlot(value))
66
- e.data = true;
67
- };
68
- const visitSeq = (s, from, to, inserted) => {
69
- const parent = s.parent;
70
- if (s.parentSub !== null || !isNodeLike(parent) || parent.name === CONTENT_NODE)
71
- return;
72
- const e = seqOf(parent);
73
- const owner = ownerOfNode(registry, kept, parent);
74
- const it = s;
75
- if (it.content === undefined)
76
- return;
77
- if (isRecord(it)) {
78
- const ids = it.content.ids;
79
- for (let i = 0; i < ids.length; i += 3)
80
- for (let j = 0; j < ids[i + 2]; j++) {
81
- const u = unitOf(doc, { c: ids[i], k: ids[i + 1] + j });
82
- if (u !== null)
83
- noteUnit(e, owner, u.value, ids[i], ids[i + 1] + j, null);
84
- }
85
- }
86
- else if (it.content.arr !== undefined)
87
- for (let k = from; k < to; k++)
88
- noteUnit(e, owner, it.content.arr[k - s.id.clock], s.id.client, k, inserted);
89
- };
90
- const buildSeqs = () => {
91
- if (seqs !== undefined)
92
- return seqs;
93
- seqs = new Map();
94
- if (ins === undefined || del === undefined)
95
- return seqs;
96
- walkIdSet(Y, doc, ins, (s, from, to) => visitSeq(s, from, to, true));
97
- walkIdSet(Y, doc, del, (s, from, to) => visitSeq(s, from, to, false));
98
- for (let i = 0; i < moves.length; i += 3) {
99
- const s = structOf(moves[i], moves[i + 1]);
100
- if (s === null)
101
- continue;
102
- visitSeq(s, moves[i + 1], moves[i + 1] + moves[i + 2], null);
103
- // Where the unit shows now: that sequence changed too.
104
- const mover = s.mover;
105
- if (mover)
106
- seqOf(mover.parent);
107
- }
108
- return seqs;
109
- };
110
- let built;
111
- const build = () => {
112
- if (built !== undefined)
113
- return built;
114
- if (ins === undefined || del === undefined)
115
- return (built = null);
116
- const out = new Map();
117
- const edit = (s, from, to, deleted) => {
118
- const parent = s.parent;
119
- if (s.parentSub !== null || !isNodeLike(parent))
120
- return;
121
- let e = out.get(parent);
122
- if (e === undefined)
123
- out.set(parent, (e = { edits: [], scan: false, opaque: false }));
124
- // A format marker moves no unit; its effect reaches past the edit.
125
- if (s.countable !== true)
126
- return void (e.opaque = true);
127
- if (holdsBoundary(s, from, to))
128
- return void (e.scan = true);
129
- e.edits.push({ item: s, len: to - from, del: deleted });
130
- };
131
- const visit = (idSet, deleted) => walkIdSet(Y, doc, idSet, (s, from, to) => {
132
- // Written and removed (or held) within the fold: nothing moved.
133
- if (!(deleted ? ins.has(s.id.client, from) : s.deleted))
134
- edit(s, from, to, deleted);
135
- });
136
- // In order, after the inserts and before the deletes (each gap then
137
- // holds what an edit takes); a unit written within the fold shows or
138
- // not as its insert says.
139
- const held = () => {
140
- for (let i = 0; i < log.length; i += 3) {
141
- const s = structOf(log[i], log[i + 1]);
142
- if (s === null)
143
- return false;
144
- const n = log[i + 2];
145
- if (!ins.has(log[i], log[i + 1]))
146
- edit(s, log[i + 1], log[i + 1] + Math.abs(n), n < 0);
147
- }
148
- return true;
149
- };
150
- built = visit(ins, false) && held() && visit(del, true) ? out : null;
151
- return built;
152
- };
153
- return {
154
- /** `node`'s edits (`null`: unknown — rescan, every consumer recomputes). */
155
- text: (node) => {
156
- const map = build();
157
- return map === null ? null : (map.get(node) ?? NO_EDITS);
158
- },
159
- /** What changed in `node`'s sequence (`undefined`: nothing). */
160
- seq: (node) => buildSeqs().get(node),
161
- /** The sequences the fold changed. */
162
- seqNodes: () => buildSeqs().keys(),
163
- /** The ids whose home to read again (their slots changed). */
164
- rehome
165
- };
166
- };
167
- const runs = (b) => {
168
- // Read-your-writes: fold the open transaction's pending writes first.
169
- syncAll();
170
- if (dirty.has(b) || !cache.has(b))
171
- computeRuns(b);
172
- return cache.get(b)?.runs ?? EMPTY_RUNS;
173
- };
174
- // ── the fold ─────────────────────────────────────────────────────
175
- /** The owner of the sequence `id`'s slot shows in (its display edge's parent), as the engine stands. */
176
- const parentOf = (id) => edgeParent(id);
177
- /** Note edits of `id`'s own text (`null`: unknown — rescan, every reader re-reads). */
178
- const noteText = (ctx, id, e) => {
179
- let t = ctx.texts.get(id);
180
- if (t === undefined)
181
- ctx.texts.set(id, (t = { edits: [], scan: false, opaque: false, seen: new Set() }));
182
- if (e === null) {
183
- t.scan = t.opaque = true;
184
- return;
185
- }
186
- if (t.seen.has(e))
187
- return;
188
- t.seen.add(e);
189
- t.scan ||= e.scan;
190
- t.opaque ||= e.opaque;
191
- for (const x of e.edits)
192
- t.edits.push(x);
193
- };
194
- /** Fold one block's changed facets (`edits`: its own text's, one per changed content node). */
195
- const foldBlock = (id, facets, edits, ctx, editsOf) => {
196
- const kinds = new Set();
197
- for (const f of facets)
198
- kinds.add(f === ENTRY_FACET ? 'entry' : facetOf(f));
199
- // The display parents a shell may flip by (`settle`): read while there are shells.
200
- const parentBefore = shells.size > 0 ? parentOf(id) : undefined;
201
- if (typeof parentBefore === 'string')
202
- ctx.parents.add(parentBefore);
203
- if (kinds.has('entry') || kinds.has('structure')) {
204
- const [claimsBefore, typeBefore] = [blocks.get(id)?.claims, blocks.get(id)?.type];
205
- updateBlockRec(id);
206
- // A retype that lands with a structure facet is still a retype.
207
- if (typeBefore !== undefined && blocks.get(id)?.type !== typeBefore)
208
- ix.retyped = true;
209
- // A new entry, a nonce or an own text can move streams.
210
- if (kinds.has('entry') || facets.has(NONCE) || facets.has(CONTENT_ATTR)) {
211
- ctx.table = true;
212
- ctx.named.add(id);
213
- noteText(ctx, id, null);
214
- }
215
- // The key's losing incarnations follow its entry and its liveness.
216
- for (const v of syncIncarnations(id)) {
217
- ctx.table = true;
218
- ctx.named.add(v);
219
- noteText(ctx, v, null);
220
- invalidateBlock(v, ctx);
221
- }
222
- invalidateBlock(id, ctx, claimsBefore);
223
- }
224
- else {
225
- ensureRec(id);
226
- const rec = blocks.get(id);
227
- // A data slot in the block's sequence: its arrays' items changed (the seq pass seeded the blocks).
228
- if (kinds.has('seq') && rec && editsOf.seq(rec.node)?.data)
229
- kinds.add('meta');
230
- if (kinds.has('meta') && rec) {
231
- const was = rec.type;
232
- const facts = tableFacts(id, rec.data);
233
- rec.type = typeAttr(rec.node);
234
- rec.data = readData(rec.node);
235
- if (rec.type !== was) {
236
- ix.retyped = true;
237
- noteKind(id);
238
- }
239
- // A table's columns or a cell's column re-place the table's cells (`table.columns`).
240
- if (tableFacts(id, rec.data) !== facts)
241
- stateSeeds.add(id);
242
- // A retype that changes the kind's display shape re-parents
243
- // (or re-kinds) its children.
244
- if (ix.roles !== null && !sameShape(ix.roles, was, rec.type))
245
- stateSeeds.add(id);
246
- }
247
- }
248
- const parentAfter = shells.size > 0 ? parentOf(id) : undefined;
249
- if (typeof parentAfter === 'string')
250
- ctx.parents.add(parentAfter);
251
- if (kinds.has('content'))
252
- for (const e of edits)
253
- noteText(ctx, id, e);
254
- };
255
- /**
256
- * Move each edited text's row by its edits (or rescan it), re-decide the
257
- * delimiters a boundary change names, re-place the rows they cut, and
258
- * invalidate the readers: the display owner of each edited stream,
259
- * the blocks whose stream changed, every reader of an opaque text.
260
- */
261
- const foldTexts = (ctx) => {
262
- const homes = new Set();
263
- const gapsOf = new Map();
264
- const gapsIn = (row, edits) => {
265
- const gaps = [];
266
- for (const e of edits) {
267
- const gap = gapOfItem(row, e.item);
268
- if (gap < 0)
269
- return null;
270
- gaps.push(gap);
271
- }
272
- return gaps;
273
- };
274
- for (const [home, t] of ctx.texts) {
275
- const text = blocks.get(home)?.content;
276
- const row = rows.get(home);
277
- let gaps = [];
278
- let fresh = t.scan || row === undefined || text === undefined || row.text !== text;
279
- if (!fresh) {
280
- for (const e of t.edits) {
281
- const gap = gapOfItem(row, e.item);
282
- if (gap < 0 || !shiftGap(row, gap, e.del ? -e.len : e.len)) {
283
- fresh = true;
284
- break;
285
- }
286
- gaps.push(gap);
287
- }
288
- // The engine's own count of live units: a row that disagrees is stale.
289
- if (!fresh && rowLength(row) !== sequenceLength(text))
290
- fresh = true;
291
- }
292
- if (fresh) {
293
- const next = scanRow(home);
294
- const same = next !== undefined && row !== undefined && next.key === row.key;
295
- if (same)
296
- [next.cuts, next.heads, next.head] = [row.cuts, row.heads, row.head];
297
- setRow(home, next, ctx.named);
298
- if (!same)
299
- homes.add(home);
300
- gaps = next === undefined ? [] : gapsIn(next, t.edits);
301
- }
302
- gapsOf.set(home, t.opaque ? null : gaps);
303
- }
304
- refreshDelims(ctx.named, homes);
305
- if (homes.size > 0)
306
- ctx.table = true;
307
- for (const home of homes)
308
- placeHome(home, ctx.invalidated);
309
- for (const [home, gaps] of gapsOf) {
310
- if (gaps === null) {
311
- for (const c of textConsumers.get(home) ?? [])
312
- ctx.invalidated.add(c);
313
- continue;
314
- }
315
- // Only the stream each edit lies in changes its display.
316
- const row = rows.get(home);
317
- if (row === undefined)
318
- continue;
319
- for (const gap of gaps) {
320
- const b = headOf(row, segmentOfGap(row, gap));
321
- const owner = b === null ? DEAD : ownerOf(b);
322
- if (typeof owner === 'string')
323
- ctx.invalidated.add(owner);
324
- }
325
- }
326
- };
327
- /** The blocks whose stream lies in a text the fold edited. */
328
- const editedStreams = (ctx) => [...ctx.texts.keys()].flatMap((home) => streamsIn(home).map((st) => st.block));
329
- /** A live unit in `id`'s stream (boundaries excepted). */
330
- const streamHolds = (id) => {
331
- const st = streamOf(id);
332
- return st !== undefined && st.end - st.start > st.inert.length;
333
- };
334
- /** A live unit in `id`'s stream or in the stream of a losing incarnation it shows. */
335
- const holdsUnit = (id) => streamHolds(id) || (incarnations.get(id)?.some(streamHolds) ?? false);
336
- /**
337
- * Settle the withdrawn blocks (`hist.undo.withdraw`): a shell is deleted
338
- * iff it holds nothing — no live unit in its stream and no child (a
339
- * block whose winning candidate names it) that is itself not deleted.
340
- * The least fixpoint, computed over every shell whenever an `affected`
341
- * one may flip (one whose stream still holds a unit keeps its answer),
342
- * so every replica settles the same whatever the fold order. A flip is
343
- * a structural change of the shell.
344
- */
345
- const settle = (affected, ctx) => {
346
- if (shells.size === 0)
347
- return;
348
- let quiet = true;
349
- for (const x of affected) {
350
- const id = baseIdOf(x);
351
- if (!shells.has(id))
352
- continue;
353
- if (blocks.get(id)?.deleted !== false || !holdsUnit(id))
354
- quiet = false;
355
- }
356
- if (quiet)
357
- return;
358
- const alive = new Set();
359
- const up = [];
360
- const hold = (id) => {
361
- if (alive.has(id))
362
- return;
363
- alive.add(id);
364
- up.push(id);
365
- };
366
- for (const id of shells)
367
- if (holdsUnit(id))
368
- hold(id);
369
- for (const [id, rec] of blocks) {
370
- if (shells.has(id) || rec.deleted)
371
- continue;
372
- const p = parentOf(id);
373
- if (typeof p === 'string' && shells.has(p))
374
- hold(p);
375
- }
376
- for (let id = up.pop(); id !== undefined; id = up.pop()) {
377
- const p = parentOf(id);
378
- if (typeof p === 'string' && shells.has(p))
379
- hold(p);
380
- }
381
- for (const id of shells) {
382
- const rec = blocks.get(id);
383
- if (rec.deleted === !alive.has(id))
384
- continue;
385
- rec.deleted = !alive.has(id);
386
- ownerSeeds.add(id);
387
- // Its losing incarnations follow it.
388
- for (const v of incarnations.get(id) ?? []) {
389
- const inc = blocks.get(v);
390
- if (inc === undefined)
391
- continue;
392
- const deleted = hasDeleteMark(inc.node) || rec.deleted;
393
- if (inc.deleted === deleted)
394
- continue;
395
- inc.deleted = deleted;
396
- ownerSeeds.add(v);
397
- if (ctx !== null)
398
- invalidateBlock(v, ctx);
399
- }
400
- if (ctx === null)
401
- continue;
402
- invalidateBlock(id, ctx);
403
- }
404
- };
405
- const registryNode = registry;
406
- /**
407
- * The block a changed `(type, parentSub)` pair belongs to, the facet it
408
- * entered through (the block attr the chain hangs under; the `content`
409
- * attr itself is {@link CONTENT_ATTR}), and whether it is a sequence
410
- * edit of the block's own text. `null` outside the registry subtree.
411
- */
412
- const locate = (type, sub) => {
413
- if (type === registryNode)
414
- return typeof sub === 'string' ? [sub, ENTRY_FACET, false] : null;
415
- let below = null;
416
- for (let cur = type;;) {
417
- const it = cur._item;
418
- const parent = it?.parent;
419
- if (parent === registryNode) {
420
- let id = it.parentSub;
421
- if (typeof id !== 'string')
422
- return null;
423
- // A losing incarnation's subtree: its derived id; a seed's shows nothing.
424
- if (it.deleted && kept(it)) {
425
- const item = itemOf(cur);
426
- if (item.id.client < LIVE_WRITERS)
427
- return null;
428
- id = incarnationId(id, item);
429
- }
430
- // The block's own sequence (its slots and records: `SEQ_FACET`), or one of its attrs.
431
- if (below === null)
432
- return [id, sub === CONTENT ? CONTENT_ATTR : sub === null ? SEQ_FACET : sub, false];
433
- const facet = below._item?.parentSub ?? CONTENT;
434
- return [id, facet, sub === null && below === type && facet === CONTENT];
435
- }
436
- if (!isNodeLike(parent))
437
- return null;
438
- below = cur;
439
- cur = parent;
440
- }
441
- };
442
- /** THE fold: one pass over changed `(type, parentSub)` pairs, each block folded once. */
443
- const fold = (changed, editsOf) => {
444
- const touched = new Map();
445
- const edits = new Map();
446
- let derived = changed.has(dataRoot);
447
- foldStats.folds++;
448
- for (const [type, subs] of changed) {
449
- foldStats.pairs += subs.size;
450
- for (const sub of subs) {
451
- const hit = locate(type, sub);
452
- if (hit === null)
453
- continue;
454
- const [id, facet, seq] = hit;
455
- let facets = touched.get(id);
456
- if (facets === undefined)
457
- touched.set(id, (facets = new Set()));
458
- facets.add(facet);
459
- if (facet !== CONTENT)
460
- continue;
461
- // A sequence edit of the block's own text, or a change inside one of its atoms.
462
- let list = edits.get(id);
463
- if (list === undefined)
464
- edits.set(id, (list = []));
465
- list.push(seq ? editsOf.text(type) : null);
466
- }
467
- }
468
- const ctx = {
469
- invalidated: new Set(),
470
- texts: new Map(),
471
- named: new Set(),
472
- table: false,
473
- parents: new Set()
474
- };
475
- // The sequences (the root's, the blocks'): their slots index the blocks' homes, their changes seed the placements.
476
- for (const node of editsOf.seqNodes()) {
477
- const ch = editsOf.seq(node);
478
- ix.seqEpoch++;
479
- for (const b of ch.blocks)
480
- placementSeeds.add(b);
481
- if (ch.blocks.size > 0 || ch.data)
482
- derived = true;
483
- const owner = ownerOfNode(registry, kept, node);
484
- if (typeof owner === 'string') {
485
- let facets = touched.get(owner);
486
- if (facets === undefined)
487
- touched.set(owner, (facets = new Set()));
488
- facets.add(SEQ_FACET);
489
- if (ch.data)
490
- facets.add(DATA_SEQ_FACET);
491
- }
492
- }
493
- // A slot's block, and its losing incarnations, re-read their home (which slot is whose).
494
- for (const b of editsOf.rehome)
495
- for (const id of [b, ...(incarnations.get(b) ?? [])]) {
496
- const rec = blocks.get(id);
497
- if (rec === undefined)
498
- continue;
499
- const home = homeFor(id);
500
- if (home?.c !== rec.home?.c || home?.k !== rec.home?.k)
501
- placementSeeds.add(id);
502
- rec.home = home;
503
- }
504
- for (const [id, facets] of touched) {
505
- foldBlock(id, facets, edits.get(id) ?? [], ctx, editsOf);
506
- derived ||= [...facets].some((f) => f === ENTRY_FACET || facetOf(f) !== 'ignore');
507
- }
508
- foldTexts(ctx);
509
- if (reclaim.size > 0)
510
- applyReclaims(ctx);
511
- if (shells.size > 0)
512
- settle(ctx.table ? shells : [...touched.keys(), ...ctx.parents, ...editedStreams(ctx)], ctx);
513
- for (const b of ctx.invalidated)
514
- dirty.add(b);
515
- if (indexChecks.on)
516
- checksOf(ix).check();
517
- if (derived)
518
- ix.version++;
519
- if (ix.reporting) {
520
- for (const id of touched.keys())
521
- candidates.add(id);
522
- for (const id of ctx.invalidated)
523
- candidates.add(id);
524
- }
525
- for (const f of frames) {
526
- for (const [id, facets] of touched) {
527
- const into = f.touched.get(id);
528
- if (into === undefined)
529
- f.touched.set(id, new Set(facets));
530
- else
531
- for (const x of facets)
532
- into.add(x);
533
- }
534
- }
535
- return touched;
536
- };
537
- /** Each transaction's folded part: copies of its id sets, the hold and move logs' folded lengths, and the watermark. */
538
- const cursors = new WeakMap();
539
- /** The transactions whose commit fold ran. */
540
- const committed = new WeakSet();
541
- const lengthOf = (set) => {
542
- let n = 0;
543
- set.clients.forEach((ranges) => {
544
- for (const r of ranges.getIds())
545
- n += r.len;
546
- });
547
- return n;
548
- };
549
- /** How much a transaction wrote so far: its id sets' lengths and its hold and move logs'. */
550
- const watermark = (tr) => lengthOf(tr.insertSet) +
551
- lengthOf(tr.deleteSet) +
552
- (tr.holds?.length ?? 0) +
553
- (tr.moves?.length ?? 0);
554
- /**
555
- * Fold the pending part of `tr` — the structs its insert/delete sets
556
- * gained since the last fold — before a read inside it (read-your-
557
- * writes; `transaction.changed` does not grow on a second edit to an
558
- * already-changed type).
559
- */
560
- const syncPending = (tr) => {
561
- if (tr?.insertSet === undefined || tr.deleteSet === undefined)
562
- return false;
563
- let cursor = cursors.get(tr);
564
- if (cursor === undefined)
565
- cursors.set(tr, (cursor = { ins: ops.idSet(), del: ops.idSet(), holds: 0, moves: 0, mark: 0 }));
566
- const mark = watermark(tr);
567
- if (mark === cursor.mark)
568
- return false;
569
- const ins = ops.diff(tr.insertSet, cursor.ins);
570
- const del = ops.diff(tr.deleteSet, cursor.del);
571
- const log = (tr.holds ?? []).slice(cursor.holds);
572
- const moves = (tr.moves ?? []).slice(cursor.moves);
573
- ops.insertInto(cursor.ins, ins);
574
- ops.insertInto(cursor.del, del);
575
- cursor.holds += log.length;
576
- cursor.moves += moves.length;
577
- cursor.mark = mark;
578
- for (const f of frames)
579
- f.wrote = true;
580
- const changed = new Map();
581
- const note = (s) => {
582
- foldStats.structs++;
583
- if (!isNodeLike(s.parent))
584
- return;
585
- let subs = changed.get(s.parent);
586
- if (subs === undefined)
587
- changed.set(s.parent, (subs = new Set()));
588
- subs.add(s.parentSub);
589
- };
590
- walkIdSet(Y, doc, ins, note);
591
- walkIdSet(Y, doc, del, note);
592
- for (const l of [log, moves])
593
- for (let i = 0; i < l.length; i += 3) {
594
- const s = structOf(l[i], l[i + 1]);
595
- if (s === null)
596
- continue;
597
- note(s);
598
- // A unit a record moved: where it shows now changed too (a held unit has no mover).
599
- const mover = s.mover;
600
- if (mover)
601
- note(mover);
602
- }
603
- fold(changed, makeEditIndex(ins, del, log, moves));
604
- return true;
605
- };
606
- const openTx = () => doc._transaction;
607
- /**
608
- * Fold every transaction whose writes are in the document and whose
609
- * commit fold has not run, in their order: the open one, and those a
610
- * transaction's cleanup started (an observer's or an `update`
611
- * listener's write runs its body at once, its observers later) — a
612
- * read in between must not mix their items with an index that never
613
- * saw them.
614
- */
615
- const syncAll = () => {
616
- for (const tr of queuedTransactions(doc))
617
- if (!committed.has(tr))
618
- syncPending(tr);
619
- };
620
- /** Some queued transaction holds writes no fold has seen. */
621
- const unfolded = () => queuedTransactions(doc).some((tr) => !committed.has(tr) &&
622
- tr.insertSet !== undefined &&
623
- tr.deleteSet !== undefined &&
624
- watermark(tr) !== (cursors.get(tr)?.mark ?? 0));
625
- /**
626
- * The commit of a transaction that changed the registry or a node under
627
- * it: fold `transaction.changed` once (the report publishes from `update`).
628
- */
629
- const onCommit = (tr) => {
630
- if (!changedUnder(tr, [registry]))
631
- return;
632
- let [ins, del] = [tr.insertSet, tr.deleteSet];
633
- // The writes a read folded already moved the rows: only the rest
634
- // are edits now (the facets fold again, idempotent).
635
- const cursor = cursors.get(tr);
636
- if (cursor !== undefined && ins !== undefined && del !== undefined) {
637
- ins = ops.diff(ins, cursor.ins);
638
- del = ops.diff(del, cursor.del);
639
- }
640
- const log = (tr.holds ?? []).slice(cursor?.holds ?? 0);
641
- const moves = (tr.moves ?? []).slice(cursor?.moves ?? 0);
642
- cursors.delete(tr);
643
- committed.add(tr);
644
- fold(tr.changed ?? new Map(), makeEditIndex(ins, del, log, moves));
645
- };
646
- return {
647
- runs,
648
- settle,
649
- syncPending,
650
- openTx,
651
- syncAll,
652
- unfolded,
653
- onCommit,
654
- committed
655
- };
656
- };
@@ -1,22 +0,0 @@
1
- /**
2
- * The layout rules (`layout.*`) and the table rules (`table.*`) over a
3
- * children index, and the role table's answers for a block's stored kind
4
- * they read.
5
- */
6
- import type { BlockId, ChildSlot } from '../../placement/model.js';
7
- import type { KindRole } from '../runs.js';
8
- import type { IndexState } from './state.js';
9
- /** The layout rules of one doc's index, over its state. */
10
- export declare const indexLayout: (ix: IndexState) => {
11
- kindOf: (b: BlockId) => KindRole | undefined;
12
- lineKind: (b: BlockId) => string | undefined;
13
- itemKind: (b: BlockId) => string | undefined;
14
- rowKind: (b: BlockId) => string | undefined;
15
- rowOfTable: (table: string) => [string, string] | undefined;
16
- cellKind: (b: BlockId) => string | undefined;
17
- cellRank: (row: BlockId, id: BlockId) => string | undefined;
18
- tableFacts: (b: BlockId, data: unknown) => string;
19
- dissolve: (kids: Map<BlockId | null, ChildSlot[]>) => Set<BlockId>;
20
- redissolve: (changed: Map<BlockId, [BlockId | null | undefined, BlockId | null | undefined]>, kinds: Iterable<BlockId>) => Set<BlockId>;
21
- };
22
- export type IndexLayout = ReturnType<typeof indexLayout>;