edytor 0.1.0-next.21 → 0.1.0-next.23
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/README.md +1 -1
- package/dist/cloudflare/DocumentRoom.d.ts +302 -10
- package/dist/cloudflare/DocumentRoom.js +965 -88
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/routeDocumentSocket.js +30 -6
- package/dist/collaboration/awarenessSelection.d.ts +7 -1
- package/dist/collaboration/awarenessSelection.js +7 -1
- package/dist/collaboration/index.d.ts +2 -2
- package/dist/collaboration/index.js +2 -2
- package/dist/collaboration/providers.d.ts +4 -0
- package/dist/collaboration/providers.js +4 -0
- package/dist/components/Edytor.svelte +14 -2
- package/dist/components/Edytor.svelte.d.ts +8 -1
- package/dist/crdt/admission.d.ts +6 -1
- package/dist/crdt/admission.js +9 -2
- package/dist/crdt/data.d.ts +3 -1
- package/dist/crdt/data.js +42 -32
- package/dist/crdt/document.d.ts +31 -8
- package/dist/crdt/document.js +53 -18
- package/dist/crdt/edytor-doc.d.ts +31 -3
- package/dist/crdt/edytor-doc.js +131 -14
- package/dist/crdt/engine.d.ts +6 -0
- package/dist/crdt/engine.js +7 -0
- package/dist/crdt/flow.d.ts +2 -0
- package/dist/crdt/flow.js +2 -2
- package/dist/crdt/index.d.ts +20 -9
- package/dist/crdt/index.js +3 -3
- package/dist/crdt/migration/migrate.js +9 -3
- package/dist/crdt/placement/model.d.ts +9 -1
- package/dist/crdt/placement/model.js +29 -10
- package/dist/crdt/placement/rank.d.ts +20 -0
- package/dist/crdt/placement/rank.js +79 -0
- package/dist/crdt/protocols/envelope.d.ts +20 -1
- package/dist/crdt/protocols/envelope.js +14 -2
- package/dist/crdt/providers/container.d.ts +15 -0
- package/dist/crdt/providers/container.js +17 -3
- package/dist/crdt/providers/index.d.ts +21 -1
- package/dist/crdt/providers/index.js +86 -3
- package/dist/crdt/providers/indexeddb.js +60 -40
- package/dist/crdt/providers/room.d.ts +18 -2
- package/dist/crdt/providers/room.js +29 -5
- package/dist/crdt/providers/websocket.d.ts +9 -1
- package/dist/crdt/providers/websocket.js +21 -6
- package/dist/crdt/storage.d.ts +21 -0
- package/dist/crdt/storage.js +37 -0
- package/dist/crdt/structs.d.ts +17 -0
- package/dist/crdt/structs.js +41 -0
- package/dist/crdt/text/model.d.ts +9 -1
- package/dist/crdt/text/model.js +24 -14
- package/dist/crdt/text/rows.d.ts +94 -0
- package/dist/crdt/text/rows.js +193 -0
- package/dist/crdt/text/runs.d.ts +30 -10
- package/dist/crdt/text/runs.js +1235 -264
- package/dist/edytor.svelte.d.ts +3 -1
- package/dist/edytor.svelte.js +9 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/image/ImageEmpty.svelte +19 -6
- package/dist/plugins/image/ImagePlugin.svelte +9 -3
- package/dist/plugins/image/ImagePlugin.svelte.d.ts +2 -2
- package/dist/plugins/image/image.d.ts +16 -0
- package/dist/plugins/image/image.js +19 -0
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/plugins.d.ts +11 -0
- package/dist/session/commands.d.ts +1 -0
- package/dist/session/commands.js +4 -1
- package/package.json +1 -1
package/dist/crdt/text/model.js
CHANGED
|
@@ -195,7 +195,7 @@ export const scanText = (home, text) => {
|
|
|
195
195
|
}
|
|
196
196
|
return { home, text, bounds, len: at, key: bounds.map((b) => b.key).join(',') };
|
|
197
197
|
};
|
|
198
|
-
const byId = (a, b) => {
|
|
198
|
+
export const byId = (a, b) => {
|
|
199
199
|
const [ac, ak] = a.split(':').map(Number);
|
|
200
200
|
const [bc, bk] = b.split(':').map(Number);
|
|
201
201
|
return ac - bc || ak - bk;
|
|
@@ -214,21 +214,30 @@ export const delimiters = (blocks, rows) => {
|
|
|
214
214
|
}
|
|
215
215
|
return out;
|
|
216
216
|
};
|
|
217
|
-
/**
|
|
217
|
+
/**
|
|
218
|
+
* The streams of one text, in text order: the home's head (unless delimited
|
|
219
|
+
* elsewhere), then one per delimiting boundary — one sweep over the
|
|
220
|
+
* boundaries (each non-delimiting one is an inert boundary of the stream it
|
|
221
|
+
* sits in).
|
|
222
|
+
*/
|
|
218
223
|
export const placeText = (row, delim) => {
|
|
219
|
-
const cuts = row.bounds.filter((b) => delim.get(b.s) === b.key);
|
|
220
|
-
const heads = [
|
|
221
|
-
{ block: delim.has(row.home) ? null : row.home, start: 0 },
|
|
222
|
-
...cuts.map((b) => ({ block: b.s, start: b.at + 1 }))
|
|
223
|
-
];
|
|
224
224
|
const out = [];
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
225
|
+
let block = delim.has(row.home) ? null : row.home;
|
|
226
|
+
let start = 0;
|
|
227
|
+
let inert = [];
|
|
228
|
+
const close = (end) => {
|
|
229
|
+
if (block !== null)
|
|
230
|
+
out.push({ block, home: row.home, text: row.text, start, end, inert });
|
|
231
|
+
};
|
|
232
|
+
for (const b of row.bounds) {
|
|
233
|
+
if (delim.get(b.s) !== b.key) {
|
|
234
|
+
inert.push(b.at);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
close(b.at);
|
|
238
|
+
[block, start, inert] = [b.s, b.at + 1, []];
|
|
239
|
+
}
|
|
240
|
+
close(row.len);
|
|
232
241
|
return out;
|
|
233
242
|
};
|
|
234
243
|
/** A stream's content pieces: the range minus its inert boundaries (at least one piece). */
|
|
@@ -382,6 +391,7 @@ export const bindText = (Y) => {
|
|
|
382
391
|
top: (m) => top.get(m),
|
|
383
392
|
streamOf: (b) => streams.get(b),
|
|
384
393
|
streamsIn: (home) => inText.get(home) ?? [],
|
|
394
|
+
streamAt: (home, i) => (inText.get(home) ?? []).find((x) => x.start <= i && i <= x.end),
|
|
385
395
|
display: (b) => displayOf(b, blocks, own)
|
|
386
396
|
};
|
|
387
397
|
return own;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The maintained form of one backing text's boundary table (P1) — the
|
|
3
|
+
* index's row: the text's live boundary items in text order and the live
|
|
4
|
+
* units between them, held in a Fenwick tree so an edit that writes or
|
|
5
|
+
* removes no boundary moves every later boundary in O(log B) instead of a
|
|
6
|
+
* rescan.
|
|
7
|
+
*
|
|
8
|
+
* `h[j]` is the units before boundary `j` since the one before it, plus
|
|
9
|
+
* boundary `j` itself (`h[B]`: the units after the last boundary), so the
|
|
10
|
+
* live index of boundary `j` is `H(j) - 1` (`H` the prefix sum) and the
|
|
11
|
+
* text's live length is `H(B)`. A row's boundary SET only changes through a
|
|
12
|
+
* rescan ({@link rowOf}); an insert or delete of other units changes one
|
|
13
|
+
* `h` entry ({@link shiftGap}), found by walking the item list from the
|
|
14
|
+
* edited item to its nearest boundary ({@link gapOfItem}).
|
|
15
|
+
*
|
|
16
|
+
* The streams of a row (`text/model.ts` `placeText`) are its segments
|
|
17
|
+
* between consecutive delimiting boundaries (`cuts`): segment `k` starts
|
|
18
|
+
* after cut `k - 1` (the text start for `k = 0`) and ends at cut `k` (the
|
|
19
|
+
* text end past the last). Segment 0 is the home block's stream unless the
|
|
20
|
+
* home is delimited elsewhere (`head: null` — that stretch is nobody's);
|
|
21
|
+
* segment `k > 0` is the stream of the block its opening cut names.
|
|
22
|
+
*/
|
|
23
|
+
import type { EngineNode } from '../engine-api.js';
|
|
24
|
+
import { type BlockId, type Bound, type Stream, type TextRow } from './model.js';
|
|
25
|
+
export type LiveRow = {
|
|
26
|
+
home: BlockId;
|
|
27
|
+
text: EngineNode;
|
|
28
|
+
/** The live boundaries in text order (`at` is NOT maintained: read {@link boundAt}). */
|
|
29
|
+
bounds: readonly Bound[];
|
|
30
|
+
/** Fenwick tree over `h` (1-based, size `bounds.length + 1`). */
|
|
31
|
+
tree: Float64Array;
|
|
32
|
+
/** The bound keys joined — the boundary set's identity. */
|
|
33
|
+
key: string;
|
|
34
|
+
/** Each bound's index by its key. */
|
|
35
|
+
keyIndex: Map<string, number>;
|
|
36
|
+
/** Indexes into `bounds` of the delimiting boundaries, ascending ({@link placeRow}). */
|
|
37
|
+
cuts: number[];
|
|
38
|
+
/** The block of segment 0 (`null`: the home is delimited elsewhere). */
|
|
39
|
+
head: BlockId | null;
|
|
40
|
+
};
|
|
41
|
+
/** The live index of boundary `j`. */
|
|
42
|
+
export declare const boundAt: (row: LiveRow, j: number) => number;
|
|
43
|
+
/** The text's live length. */
|
|
44
|
+
export declare const rowLength: (row: LiveRow) => number;
|
|
45
|
+
/** A row from a scan (its segments are set by {@link placeRow}). */
|
|
46
|
+
export declare const rowOf: (scan: TextRow) => LiveRow;
|
|
47
|
+
/** A sequence item as the walk reads it (`text/model.ts` `SeqItem`, with its left neighbour). */
|
|
48
|
+
export type RowItem = {
|
|
49
|
+
id: {
|
|
50
|
+
client: number;
|
|
51
|
+
clock: number;
|
|
52
|
+
};
|
|
53
|
+
length: number;
|
|
54
|
+
deleted: boolean;
|
|
55
|
+
countable?: boolean;
|
|
56
|
+
left?: RowItem | null;
|
|
57
|
+
right?: RowItem | null;
|
|
58
|
+
content?: {
|
|
59
|
+
arr?: unknown[];
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* The gap (`h` entry) item `it` lies in — between the nearest live
|
|
64
|
+
* boundaries on its left and right, found by walking the item list both
|
|
65
|
+
* ways at once (the cost is the distance to the nearer one, never the
|
|
66
|
+
* text). It reads only the list, never the engine's position caches, so a
|
|
67
|
+
* write the caches have not seen yet places it all the same. `-1` when the
|
|
68
|
+
* walk finds a boundary the row does not hold (the caller rescans).
|
|
69
|
+
*/
|
|
70
|
+
export declare const gapOfItem: (row: LiveRow, it: RowItem) => number;
|
|
71
|
+
/**
|
|
72
|
+
* Add `delta` units (an insert, or a delete when negative) to gap `gap`.
|
|
73
|
+
* `false` when a delete takes more units than the gap holds (a stale row:
|
|
74
|
+
* the caller rescans).
|
|
75
|
+
*/
|
|
76
|
+
export declare const shiftGap: (row: LiveRow, gap: number, delta: number) => boolean;
|
|
77
|
+
/** The segment holding gap `gap` (the units before boundary `gap`). */
|
|
78
|
+
export declare const segmentOfGap: (row: LiveRow, gap: number) => number;
|
|
79
|
+
/** The segment whose `[start, end]` holds live index `x` (the first, at a seam). */
|
|
80
|
+
export declare const segmentAt: (row: LiveRow, x: number) => number;
|
|
81
|
+
/** The segment the cut keyed `cut` opens (`null`: segment 0); `-1` when it is no cut of the row. */
|
|
82
|
+
export declare const segmentOfCut: (row: LiveRow, cut: string | null) => number;
|
|
83
|
+
/** The block of segment `k` (`null`: nobody's). */
|
|
84
|
+
export declare const headOf: (row: LiveRow, k: number) => BlockId | null;
|
|
85
|
+
/** Segment `k` as a {@link Stream} of `block`. */
|
|
86
|
+
export declare const streamOfSegment: (row: LiveRow, k: number, block: BlockId) => Stream;
|
|
87
|
+
/**
|
|
88
|
+
* Set the row's segments from the delimiting boundaries (`delim`): the
|
|
89
|
+
* home's head (unless delimited elsewhere), then one per cut — the same
|
|
90
|
+
* streams `placeText` returns for the row's scan.
|
|
91
|
+
*/
|
|
92
|
+
export declare const placeRow: (row: LiveRow, delim: ReadonlyMap<BlockId, string>) => void;
|
|
93
|
+
/** The row agrees with a fresh scan of its text (the index's self-check). */
|
|
94
|
+
export declare const sameRow: (row: LiveRow, scan: TextRow) => boolean;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { isBoundary } from './model.js';
|
|
2
|
+
const add = (row, i, d) => {
|
|
3
|
+
const t = row.tree;
|
|
4
|
+
for (let x = i + 1; x < t.length; x += x & -x)
|
|
5
|
+
t[x] += d;
|
|
6
|
+
};
|
|
7
|
+
/** `H(i)`: the sum of `h[0..i]`. */
|
|
8
|
+
const prefix = (row, i) => {
|
|
9
|
+
const t = row.tree;
|
|
10
|
+
let s = 0;
|
|
11
|
+
for (let x = i + 1; x > 0; x -= x & -x)
|
|
12
|
+
s += t[x];
|
|
13
|
+
return s;
|
|
14
|
+
};
|
|
15
|
+
/** The smallest `j` with `H(j) >= target` (`bounds.length + 1` when none). */
|
|
16
|
+
const lowerBound = (row, target) => {
|
|
17
|
+
const t = row.tree;
|
|
18
|
+
const n = t.length - 1;
|
|
19
|
+
let pos = 0;
|
|
20
|
+
let rem = target;
|
|
21
|
+
let step = 1;
|
|
22
|
+
while (step * 2 <= n)
|
|
23
|
+
step *= 2;
|
|
24
|
+
for (; step > 0; step >>= 1) {
|
|
25
|
+
const next = pos + step;
|
|
26
|
+
if (next <= n && t[next] < rem) {
|
|
27
|
+
pos = next;
|
|
28
|
+
rem -= t[next];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return pos;
|
|
32
|
+
};
|
|
33
|
+
/** `h[j]` itself. */
|
|
34
|
+
const valueAt = (row, j) => prefix(row, j) - (j > 0 ? prefix(row, j - 1) : 0);
|
|
35
|
+
/** The live index of boundary `j`. */
|
|
36
|
+
export const boundAt = (row, j) => prefix(row, j) - 1;
|
|
37
|
+
/** The text's live length. */
|
|
38
|
+
export const rowLength = (row) => prefix(row, row.bounds.length);
|
|
39
|
+
/** A row from a scan (its segments are set by {@link placeRow}). */
|
|
40
|
+
export const rowOf = (scan) => {
|
|
41
|
+
const n = scan.bounds.length;
|
|
42
|
+
const keyIndex = new Map();
|
|
43
|
+
const t = new Float64Array(n + 2);
|
|
44
|
+
let prev = -1;
|
|
45
|
+
for (let j = 0; j <= n; j++) {
|
|
46
|
+
const at = j < n ? scan.bounds[j].at : scan.len;
|
|
47
|
+
if (j < n)
|
|
48
|
+
keyIndex.set(scan.bounds[j].key, j);
|
|
49
|
+
t[j + 1] += j < n ? at - prev : at - prev - 1;
|
|
50
|
+
prev = at;
|
|
51
|
+
// Linear Fenwick build: push the node's sum to its parent.
|
|
52
|
+
const parent = j + 1 + ((j + 1) & -(j + 1));
|
|
53
|
+
if (parent <= n + 1)
|
|
54
|
+
t[parent] += t[j + 1];
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
home: scan.home,
|
|
58
|
+
text: scan.text,
|
|
59
|
+
bounds: scan.bounds,
|
|
60
|
+
tree: t,
|
|
61
|
+
key: scan.key,
|
|
62
|
+
keyIndex,
|
|
63
|
+
cuts: [],
|
|
64
|
+
head: scan.home
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* The index of the live boundary `it` holds last (`first`: first), `-1` when
|
|
69
|
+
* it holds none, `-2` when it holds one the row does not know (stale row).
|
|
70
|
+
*/
|
|
71
|
+
const boundIn = (row, it, first) => {
|
|
72
|
+
if (it.deleted || it.countable === false)
|
|
73
|
+
return -1;
|
|
74
|
+
const arr = it.content?.arr;
|
|
75
|
+
if (arr === undefined)
|
|
76
|
+
return -1;
|
|
77
|
+
for (let n = 0; n < it.length; n++) {
|
|
78
|
+
const k = first ? n : it.length - 1 - n;
|
|
79
|
+
if (!isBoundary(arr[k]))
|
|
80
|
+
continue;
|
|
81
|
+
return row.keyIndex.get(`${it.id.client}:${it.id.clock + k}`) ?? -2;
|
|
82
|
+
}
|
|
83
|
+
return -1;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The gap (`h` entry) item `it` lies in — between the nearest live
|
|
87
|
+
* boundaries on its left and right, found by walking the item list both
|
|
88
|
+
* ways at once (the cost is the distance to the nearer one, never the
|
|
89
|
+
* text). It reads only the list, never the engine's position caches, so a
|
|
90
|
+
* write the caches have not seen yet places it all the same. `-1` when the
|
|
91
|
+
* walk finds a boundary the row does not hold (the caller rescans).
|
|
92
|
+
*/
|
|
93
|
+
export const gapOfItem = (row, it) => {
|
|
94
|
+
if (row.bounds.length === 0)
|
|
95
|
+
return 0;
|
|
96
|
+
let l = it.left ?? null;
|
|
97
|
+
let r = it.right ?? null;
|
|
98
|
+
while (l !== null || r !== null) {
|
|
99
|
+
if (l !== null) {
|
|
100
|
+
const j = boundIn(row, l, false);
|
|
101
|
+
if (j === -2)
|
|
102
|
+
return -1;
|
|
103
|
+
if (j >= 0)
|
|
104
|
+
return j + 1;
|
|
105
|
+
l = l.left ?? null;
|
|
106
|
+
}
|
|
107
|
+
if (r !== null) {
|
|
108
|
+
const j = boundIn(row, r, true);
|
|
109
|
+
if (j === -2)
|
|
110
|
+
return -1;
|
|
111
|
+
if (j >= 0)
|
|
112
|
+
return j;
|
|
113
|
+
r = r.right ?? null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return -1;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Add `delta` units (an insert, or a delete when negative) to gap `gap`.
|
|
120
|
+
* `false` when a delete takes more units than the gap holds (a stale row:
|
|
121
|
+
* the caller rescans).
|
|
122
|
+
*/
|
|
123
|
+
export const shiftGap = (row, gap, delta) => {
|
|
124
|
+
if (delta < 0 && valueAt(row, gap) + delta < (gap < row.bounds.length ? 1 : 0))
|
|
125
|
+
return false;
|
|
126
|
+
add(row, gap, delta);
|
|
127
|
+
return true;
|
|
128
|
+
};
|
|
129
|
+
/** The segment holding gap `gap` (the units before boundary `gap`). */
|
|
130
|
+
export const segmentOfGap = (row, gap) => {
|
|
131
|
+
const cuts = row.cuts;
|
|
132
|
+
let lo = 0;
|
|
133
|
+
let hi = cuts.length;
|
|
134
|
+
while (lo < hi) {
|
|
135
|
+
const mid = (lo + hi) >> 1;
|
|
136
|
+
if (cuts[mid] < gap)
|
|
137
|
+
lo = mid + 1;
|
|
138
|
+
else
|
|
139
|
+
hi = mid;
|
|
140
|
+
}
|
|
141
|
+
return lo;
|
|
142
|
+
};
|
|
143
|
+
/** The segment whose `[start, end]` holds live index `x` (the first, at a seam). */
|
|
144
|
+
export const segmentAt = (row, x) => segmentOfGap(row, Math.min(lowerBound(row, x + 1), row.bounds.length));
|
|
145
|
+
/** The segment the cut keyed `cut` opens (`null`: segment 0); `-1` when it is no cut of the row. */
|
|
146
|
+
export const segmentOfCut = (row, cut) => {
|
|
147
|
+
if (cut === null)
|
|
148
|
+
return 0;
|
|
149
|
+
const j = row.keyIndex.get(cut);
|
|
150
|
+
if (j === undefined)
|
|
151
|
+
return -1;
|
|
152
|
+
const k = segmentOfGap(row, j);
|
|
153
|
+
return row.cuts[k] === j ? k + 1 : -1;
|
|
154
|
+
};
|
|
155
|
+
/** The block of segment `k` (`null`: nobody's). */
|
|
156
|
+
export const headOf = (row, k) => k === 0 ? row.head : row.bounds[row.cuts[k - 1]].s;
|
|
157
|
+
/** Segment `k` as a {@link Stream} of `block`. */
|
|
158
|
+
export const streamOfSegment = (row, k, block) => {
|
|
159
|
+
const lo = k === 0 ? -1 : row.cuts[k - 1];
|
|
160
|
+
const hi = k < row.cuts.length ? row.cuts[k] : row.bounds.length;
|
|
161
|
+
const start = lo < 0 ? 0 : boundAt(row, lo) + 1;
|
|
162
|
+
const end = hi === row.bounds.length ? rowLength(row) : boundAt(row, hi);
|
|
163
|
+
const inert = [];
|
|
164
|
+
for (let j = lo + 1; j < hi; j++)
|
|
165
|
+
inert.push(boundAt(row, j));
|
|
166
|
+
return { block, home: row.home, text: row.text, start, end, inert };
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Set the row's segments from the delimiting boundaries (`delim`): the
|
|
170
|
+
* home's head (unless delimited elsewhere), then one per cut — the same
|
|
171
|
+
* streams `placeText` returns for the row's scan.
|
|
172
|
+
*/
|
|
173
|
+
export const placeRow = (row, delim) => {
|
|
174
|
+
const cuts = [];
|
|
175
|
+
for (let j = 0; j < row.bounds.length; j++) {
|
|
176
|
+
const b = row.bounds[j];
|
|
177
|
+
if (delim.get(b.s) === b.key)
|
|
178
|
+
cuts.push(j);
|
|
179
|
+
}
|
|
180
|
+
row.cuts = cuts;
|
|
181
|
+
row.head = delim.has(row.home) ? null : row.home;
|
|
182
|
+
};
|
|
183
|
+
/** The row agrees with a fresh scan of its text (the index's self-check). */
|
|
184
|
+
export const sameRow = (row, scan) => {
|
|
185
|
+
if (row.key !== scan.key || row.bounds.length !== scan.bounds.length)
|
|
186
|
+
return false;
|
|
187
|
+
if (rowLength(row) !== scan.len)
|
|
188
|
+
return false;
|
|
189
|
+
for (let j = 0; j < scan.bounds.length; j++)
|
|
190
|
+
if (boundAt(row, j) !== scan.bounds[j].at)
|
|
191
|
+
return false;
|
|
192
|
+
return true;
|
|
193
|
+
};
|
package/dist/crdt/text/runs.d.ts
CHANGED
|
@@ -22,15 +22,24 @@
|
|
|
22
22
|
* mark, `type`/`data`, or the registry entry itself); one routine folds a
|
|
23
23
|
* set of such pairs into the indexes.
|
|
24
24
|
* - A transaction is folded ONCE at commit, from `transaction.changed`.
|
|
25
|
-
* - A read
|
|
26
|
-
*
|
|
27
|
-
* (
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
25
|
+
* - A read first folds the PENDING part of every transaction whose writes
|
|
26
|
+
* are in the document and whose commit fold has not run: the open one, and
|
|
27
|
+
* those a transaction's cleanup started (their bodies ran, their observers
|
|
28
|
+
* have not) — the structs their insert/delete sets gained since the last
|
|
29
|
+
* fold (the watermark is those sets' lengths). The commit fold takes only
|
|
30
|
+
* what no read folded.
|
|
31
|
+
* - Each backing text has a maintained row (`text/rows.ts`, P1): its
|
|
32
|
+
* boundaries and the live units between them. An edit that writes or
|
|
33
|
+
* removes no boundary moves the row by its units in the gap it lies in
|
|
34
|
+
* (found by walking the item list to the nearest boundary), never a
|
|
35
|
+
* rescan; a boundary written or removed rescans that text, re-decides the
|
|
36
|
+
* delimiters of the blocks its boundaries name, and re-places only the
|
|
37
|
+
* rows those cut. Every cached block records which texts its display
|
|
38
|
+
* walked and which blocks it walked or read a claim on (a skipped claim
|
|
39
|
+
* included); an edit invalidates the display owner of the stream it lies
|
|
40
|
+
* in, a placement the blocks whose stream's delimiting boundaries changed
|
|
41
|
+
* (or every reader of the text when the fold cannot say: a format marker,
|
|
42
|
+
* a change inside an atom).
|
|
34
43
|
*
|
|
35
44
|
* Publication is COMMIT-BOUND (R5): a mid-transaction read refreshes the
|
|
36
45
|
* cache for read-your-writes, but the change report observes committed
|
|
@@ -40,7 +49,9 @@
|
|
|
40
49
|
* The change report `{added, removed, moved, meta, content, order}` is the
|
|
41
50
|
* fold against the last published index: the blocks the commit's folds
|
|
42
51
|
* touched or invalidated are compared with what was published for them;
|
|
43
|
-
*
|
|
52
|
+
* the child lists the commit patched are compared with the published ones,
|
|
53
|
+
* advancing the published tree in place (after a whole rebuild, the
|
|
54
|
+
* reachable tree is compared).
|
|
44
55
|
*/
|
|
45
56
|
import type { EngineApi, EngineDoc } from '../engine-api.js';
|
|
46
57
|
import type { BlockId, ContentItem, ModelView, ProjectedBlock } from '../placement/model.js';
|
|
@@ -218,6 +229,15 @@ export type DisplayRoles = {
|
|
|
218
229
|
/** Every layout kind the roles declare, present in the document or not. */
|
|
219
230
|
layoutKinds: () => Iterable<string>;
|
|
220
231
|
};
|
|
232
|
+
/**
|
|
233
|
+
* Test lanes turn this on (`globalThis.__EDYTOR_INDEX_CHECKS__`, set before
|
|
234
|
+
* the index loads): after every fold the index checks each fact it keeps
|
|
235
|
+
* incrementally against a rebuild from the replicated state and throws on
|
|
236
|
+
* the first difference. Off in production.
|
|
237
|
+
*/
|
|
238
|
+
export declare const indexChecks: {
|
|
239
|
+
on: boolean;
|
|
240
|
+
};
|
|
221
241
|
export declare const bindRuns: (Y: EngineApi) => {
|
|
222
242
|
attach: (doc: EngineDoc) => RunView;
|
|
223
243
|
};
|