edytor 3.0.0 → 4.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 (143) hide show
  1. package/README.md +2 -2
  2. package/dist/cloudflare/DocumentRoom.d.ts +13 -9
  3. package/dist/cloudflare/DocumentRoom.js +10 -6
  4. package/dist/cloudflare/room/context.d.ts +27 -26
  5. package/dist/cloudflare/room/context.js +2 -2
  6. package/dist/cloudflare/room/purge.d.ts +6 -3
  7. package/dist/cloudflare/room/purge.js +44 -19
  8. package/dist/cloudflare/room/updates.d.ts +3 -4
  9. package/dist/cloudflare/room/updates.js +6 -8
  10. package/dist/crdt/attribution/attribution.d.ts +31 -12
  11. package/dist/crdt/attribution/attribution.js +56 -18
  12. package/dist/crdt/attribution/block.d.ts +3 -3
  13. package/dist/crdt/attribution/block.js +7 -8
  14. package/dist/crdt/attribution/index.d.ts +1 -1
  15. package/dist/crdt/attribution/index.js +1 -1
  16. package/dist/crdt/data.d.ts +62 -31
  17. package/dist/crdt/data.js +211 -103
  18. package/dist/crdt/doc/delete.d.ts +1 -1
  19. package/dist/crdt/doc/delete.js +13 -30
  20. package/dist/crdt/doc/funnel.js +29 -24
  21. package/dist/crdt/doc/gate.d.ts +3 -3
  22. package/dist/crdt/doc/history.d.ts +25 -9
  23. package/dist/crdt/doc/history.js +150 -40
  24. package/dist/crdt/doc/layout.js +11 -22
  25. package/dist/crdt/doc/meta.js +12 -17
  26. package/dist/crdt/doc/moves.d.ts +1 -1
  27. package/dist/crdt/doc/moves.js +52 -32
  28. package/dist/crdt/doc/reads.js +2 -2
  29. package/dist/crdt/doc/seed.js +2 -2
  30. package/dist/crdt/doc/split-merge.js +12 -15
  31. package/dist/crdt/doc/steps.d.ts +25 -13
  32. package/dist/crdt/doc/steps.js +52 -122
  33. package/dist/crdt/doc/types.d.ts +21 -9
  34. package/dist/crdt/document.d.ts +16 -5
  35. package/dist/crdt/document.js +21 -4
  36. package/dist/crdt/edytor-doc.d.ts +62 -64
  37. package/dist/crdt/edytor-doc.js +18 -13
  38. package/dist/crdt/engine-api.d.ts +33 -1
  39. package/dist/crdt/engine.d.ts +0 -1
  40. package/dist/crdt/engine.js +1 -4
  41. package/dist/crdt/flow.d.ts +1 -1
  42. package/dist/crdt/flow.js +25 -27
  43. package/dist/crdt/index.d.ts +26 -25
  44. package/dist/crdt/index.js +1 -1
  45. package/dist/crdt/placement/display.d.ts +5 -30
  46. package/dist/crdt/placement/display.js +39 -144
  47. package/dist/crdt/placement/keys.d.ts +58 -0
  48. package/dist/crdt/placement/keys.js +90 -0
  49. package/dist/crdt/placement/model.d.ts +119 -118
  50. package/dist/crdt/placement/model.js +242 -142
  51. package/dist/crdt/placement/resolve.d.ts +123 -35
  52. package/dist/crdt/placement/resolve.js +187 -153
  53. package/dist/crdt/placement/slots.d.ts +134 -0
  54. package/dist/crdt/placement/slots.js +152 -0
  55. package/dist/crdt/providers/room.d.ts +16 -2
  56. package/dist/crdt/providers/room.js +64 -17
  57. package/dist/crdt/providers/websocket.js +5 -3
  58. package/dist/crdt/purge.d.ts +17 -8
  59. package/dist/crdt/purge.js +236 -126
  60. package/dist/crdt/restore.d.ts +2 -2
  61. package/dist/crdt/restore.js +18 -34
  62. package/dist/crdt/schema.d.ts +22 -21
  63. package/dist/crdt/schema.js +29 -23
  64. package/dist/crdt/storage.d.ts +1 -1
  65. package/dist/crdt/storage.js +1 -1
  66. package/dist/crdt/structs.d.ts +15 -15
  67. package/dist/crdt/structs.js +8 -22
  68. package/dist/crdt/text/index/cache.js +4 -0
  69. package/dist/crdt/text/index/claims.js +12 -0
  70. package/dist/crdt/text/index/fold.d.ts +2 -3
  71. package/dist/crdt/text/index/fold.js +194 -44
  72. package/dist/crdt/text/index/layout.js +5 -8
  73. package/dist/crdt/text/index/placement.d.ts +26 -18
  74. package/dist/crdt/text/index/placement.js +404 -138
  75. package/dist/crdt/text/index/records.d.ts +3 -3
  76. package/dist/crdt/text/index/records.js +24 -31
  77. package/dist/crdt/text/index/shared.d.ts +11 -3
  78. package/dist/crdt/text/index/shared.js +12 -3
  79. package/dist/crdt/text/index/state.d.ts +21 -17
  80. package/dist/crdt/text/index/state.js +15 -17
  81. package/dist/crdt/text/index/streams.d.ts +10 -5
  82. package/dist/crdt/text/index/streams.js +78 -27
  83. package/dist/crdt/text/items.d.ts +10 -3
  84. package/dist/crdt/text/items.js +7 -0
  85. package/dist/crdt/text/model.d.ts +77 -62
  86. package/dist/crdt/text/model.js +84 -169
  87. package/dist/crdt/text/ranges.d.ts +1 -1
  88. package/dist/crdt/text/ranges.js +3 -3
  89. package/dist/crdt/text/rows.d.ts +13 -7
  90. package/dist/crdt/text/rows.js +24 -7
  91. package/dist/crdt/text/runs.d.ts +17 -12
  92. package/dist/crdt/text/runs.js +24 -7
  93. package/dist/crdt/text/streams.d.ts +26 -13
  94. package/dist/crdt/text/streams.js +41 -14
  95. package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
  96. package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
  97. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  98. package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
  99. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
  100. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
  101. package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
  102. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
  103. package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
  104. package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
  105. package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
  106. package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
  107. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
  108. package/dist/crdt/vendor/yjs/global.d.ts +2 -0
  109. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  110. package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
  111. package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
  112. package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
  113. package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
  114. package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
  115. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
  116. package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
  117. package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
  118. package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
  119. package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
  120. package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
  121. package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
  122. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
  123. package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
  124. package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
  125. package/dist/edytor.svelte.js +4 -1
  126. package/dist/events/beforeInputCommands.js +2 -2
  127. package/dist/events/onPageHide.d.ts +3 -0
  128. package/dist/events/onPageHide.js +16 -0
  129. package/dist/export/forms.d.ts +25 -25
  130. package/dist/surface/observer.svelte.js +2 -2
  131. package/dist/surface/projector.svelte.d.ts +15 -3
  132. package/dist/surface/projector.svelte.js +59 -8
  133. package/package.json +2 -2
  134. package/dist/crdt/placement/rank.d.ts +0 -87
  135. package/dist/crdt/placement/rank.js +0 -332
  136. package/dist/crdt/placement/source.d.ts +0 -43
  137. package/dist/crdt/placement/source.js +0 -66
  138. package/dist/crdt/text/delete-index.d.ts +0 -129
  139. package/dist/crdt/text/delete-index.js +0 -365
  140. package/dist/crdt/text/deletes.d.ts +0 -76
  141. package/dist/crdt/text/deletes.js +0 -362
  142. package/dist/crdt/text/index/anchored.d.ts +0 -17
  143. package/dist/crdt/text/index/anchored.js +0 -197
@@ -1,332 +0,0 @@
1
- /**
2
- * Rank keys for the placement model — a Logoot-style dense ordering.
3
- *
4
- * A rank is a non-empty sequence of *segments* `(v, t)`:
5
- *
6
- * - `v` — a signed digit in `[VMIN, VMAX]` (±2^40). There is always room
7
- * between two distinct digits (`rv - lv > 1`); when there is not, the
8
- * generator descends one level instead of renumbering.
9
- * - `t` — the allocator's `doc.clientID` (uint53), the *tiebreak* that makes
10
- * concurrent allocations of the same digit path distinct, or none
11
- * (`Infinity`: sorts above every client) on a segment that only leads to
12
- * deeper ones. The last segment always carries one (schema
13
- * generation 5): a rank's own client sits on its last segment, and a
14
- * copied segment keeps its tie only where the order needs it (between two
15
- * keys that differ only by their ties).
16
- *
17
- * Encoding is variable-length and order-preserving, so the persisted rank
18
- * is a plain string and the comparator is `a < b` (CRDT study 2026-10;
19
- * up to generation 4 every segment was 16 characters, 9 of them a tie):
20
- *
21
- * - `v` → a length character (`'H'` for zero, `'H' + k` for a positive
22
- * digit of `k` base-64 places, `'H' - k` for a negative one, its places
23
- * complemented) and its places — small digits are short;
24
- * - a tie → `'!'`, then `t` as a digit (`'!'` sorts below every length
25
- * character, so a tied segment sorts before the same digit leading on);
26
- * - rank = segments concatenated; a prefix sorts before its extension, as
27
- * string `<` does.
28
- *
29
- * Allocation (`rankBetween(left, right, clientId)`):
30
- *
31
- * - gap at level `i` (`rv - lv > 1`): emit `lv + 1 + floor(rand·(rv-lv-1))`,
32
- * own tie — random-in-gap keeps sequential inserts shallow and concurrent
33
- * same-gap inserts deterministic-distinct.
34
- * - adjacent or equal digits (`rv - lv ≤ 1`): the left digit or the right
35
- * one with the allocator's own tie when that tie sorts between theirs;
36
- * else copy the left segment and descend — untied when that already sorts
37
- * below the right bound (the result is then above left and below right,
38
- * and the next level is free), tied otherwise.
39
- * - left exhausted/open: emit `rSeg.v - 1` (boundary-minus). Sequential
40
- * prepends stay at depth 1; the bound can only underflow after ~2^40
41
- * consecutive boundary pushes — documented unreachable, guarded by a
42
- * descriptive throw.
43
- *
44
- * No floating point, no global renumbering, no wall clocks.
45
- */
46
- /**
47
- * Order-preserving alphabet: charCode order equals numeric order.
48
- * `-`(45) < `0-9`(48-57) < `A-Z`(65-90) < `_`(95) < `a-z`(97-122) — 64 chars,
49
- * strictly ascending charCodes, all safe inside a plain JSON string.
50
- */
51
- const ALPHABET = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
52
- const BASE = 64;
53
- /** Digit range. VMIN = -2^40, VMAX = 2^40 - 1. */
54
- export const RANK_VMIN = -(2 ** 40);
55
- export const RANK_VMAX = 2 ** 40 - 1;
56
- /** The length character of zero; a digit of `k` places is `ZERO ± k`. */
57
- const ZERO = 'H'.charCodeAt(0);
58
- /** Most places a digit or a tie takes (uint53 → 9 base-64 places). */
59
- const MAX_PLACES = 9;
60
- /** Marks a segment's tie: below every length character. */
61
- const TIE = '!';
62
- /** Thrown when rank generation would need a digit below VMIN (unreachable). */
63
- export class RankSpaceExhausted extends Error {
64
- constructor() {
65
- super('rank space exhausted at digit minimum — requires ~2^40 sequential boundary inserts; unreachable in practice');
66
- this.name = 'RankSpaceExhausted';
67
- }
68
- }
69
- /** A rank this module cannot encode, a malformed rank, or bounds out of order (a caller's error). */
70
- const bad = (what) => {
71
- throw new Error(`rank: ${what}`);
72
- };
73
- /** `n` (≥ 0) as exactly `width` base-64 places. */
74
- const places = (n, width) => {
75
- let out = '';
76
- for (let i = 0; i < width; i++) {
77
- out = ALPHABET[n % BASE] + out;
78
- n = Math.floor(n / BASE);
79
- }
80
- if (n > 0)
81
- bad('overflow');
82
- return out;
83
- };
84
- /** How many base-64 places `m` (≥ 1) takes. */
85
- const width = (m) => {
86
- let k = 1;
87
- for (let x = Math.floor(m / BASE); x > 0; x = Math.floor(x / BASE))
88
- k++;
89
- return k;
90
- };
91
- /** A signed integer as a self-delimiting, order-preserving string. */
92
- const encInt = (v) => {
93
- if (!Number.isSafeInteger(v))
94
- bad('digit');
95
- if (v === 0)
96
- return 'H';
97
- const m = Math.abs(v);
98
- const k = width(m);
99
- if (k > MAX_PLACES)
100
- bad('overflow');
101
- if (v > 0)
102
- return String.fromCharCode(ZERO + k) + places(m, k);
103
- // Complemented: a greater magnitude sorts first.
104
- return String.fromCharCode(ZERO - k) + places(BASE ** k - 1 - m, k);
105
- };
106
- /** The integer at `s[i]`, and where it ends. */
107
- const decInt = (s, i) => {
108
- const k = s.charCodeAt(i) - ZERO;
109
- const n = Math.abs(k);
110
- if (Number.isNaN(k) || n > MAX_PLACES || i + 1 + n > s.length)
111
- bad('malformed');
112
- let x = 0;
113
- for (let j = i + 1; j <= i + n; j++) {
114
- const d = ALPHABET.indexOf(s[j]);
115
- if (d < 0)
116
- bad('malformed');
117
- x = x * BASE + d;
118
- }
119
- const v = k === 0 ? 0 : k > 0 ? x : -(BASE ** n - 1 - x);
120
- return [v, i + 1 + n];
121
- };
122
- /** Decode a rank string into its segment array (`t: Infinity` where a segment has no tie). */
123
- export const decodeRank = (rank) => {
124
- if (rank.length === 0)
125
- bad('malformed');
126
- const segs = [];
127
- for (let i = 0; i < rank.length;) {
128
- const [v, j] = decInt(rank, i);
129
- let t = Infinity;
130
- i = j;
131
- if (rank[i] === TIE)
132
- [t, i] = decInt(rank, i + 1);
133
- segs.push({ v, t });
134
- }
135
- if (segs[segs.length - 1].t === Infinity)
136
- bad('malformed');
137
- return segs;
138
- };
139
- /** Encode segments back to the persisted string form. */
140
- export const encodeRank = (segs) => {
141
- let out = '';
142
- for (const { v, t } of segs) {
143
- if (v < RANK_VMIN || v > RANK_VMAX)
144
- bad('overflow');
145
- out += encInt(v);
146
- if (t !== Infinity)
147
- out += TIE + encInt(t);
148
- }
149
- return out;
150
- };
151
- const tieCmp = (a, b) => (a === b ? 0 : a < b ? -1 : 1);
152
- const segCmp = (a, b) => a.v - b.v || tieCmp(a.t, b.t);
153
- /**
154
- * Window for open-end (append-at-tail) allocation: emit at most `lv + APPEND_WINDOW`
155
- * rather than anywhere in `(lv, VMAX]`. Sequential appends advance the tail
156
- * digit by ≤2^16, so the digit space only saturates after ~2^24 tail-appends;
157
- * a uniform-in-`(lv, VMAX]` pick would instead halve the headroom every step
158
- * and cascade into ever-deeper levels after ~40 appends. Concurrent tail
159
- * inserts may collide on `v` — the `t` tiebreak orders them.
160
- */
161
- const APPEND_WINDOW = 2 ** 16;
162
- /**
163
- * Allocate a rank strictly between `left` and `right` (either may be
164
- * `undefined` for the open end of a sibling list). `clientId` is the
165
- * allocating doc's clientID; `rand` supplies the in-gap pick (defaults to
166
- * `Math.random`, tests inject a seeded stream).
167
- *
168
- * Guaranteed: `left < result < right` for any `left < right`, including
169
- * equal-prefix/concurrent-collision inputs — the tiebreak dimension makes a
170
- * strictly-between key always exist (modulo the documented VMIN bound).
171
- */
172
- export const rankBetween = (left, right, clientId, rand = Math.random, compact = true) => {
173
- const L = left === undefined ? [] : decodeRank(left);
174
- const R = right === undefined ? [] : decodeRank(right);
175
- if (left !== undefined && right !== undefined && left >= right)
176
- bad('left >= right');
177
- const rightOpen = right === undefined;
178
- const path = [];
179
- // `locked`: the emitted prefix already sorts below the right bound.
180
- let locked = false;
181
- // `above`: the emitted prefix already sorts above the left bound.
182
- let above = false;
183
- // `floor` = right segments at the digit minimum were copied past the left
184
- // bound's end (a promoted slot's separator): the result equals right so far.
185
- let floor = false;
186
- const own = (v) => encodeRank([...path, { v, t: clientId }]);
187
- for (let i = 0;; i++) {
188
- const lSeg = above ? undefined : L[i];
189
- // Locked: the right bound no longer constrains any deeper level
190
- // (descending past it grew ranks linearly under concentrated edits).
191
- const rSeg = rightOpen || locked ? undefined : R[i];
192
- if (lSeg === undefined) {
193
- // Left bound exhausted (or passed): extending the prefix keeps the
194
- // result above it. Only the right bound constrains the digit.
195
- if (rSeg === undefined) {
196
- if (floor)
197
- throw new RankSpaceExhausted();
198
- return own(0);
199
- }
200
- if (rSeg.v <= RANK_VMIN) {
201
- // No digit below: copy it and descend — below a promoted block
202
- // (`slot + separator + rank`) there is room under its own rank.
203
- floor = true;
204
- path.push(rSeg);
205
- continue;
206
- }
207
- return own(rSeg.v - 1);
208
- }
209
- if (rSeg === undefined) {
210
- // Right bound ended while left continues: valid only when `locked`
211
- // already holds (an earlier copied segment was < the right's). If not,
212
- // right is a proper prefix of left — i.e. right < left, caller error.
213
- if (!rightOpen && !locked)
214
- bad('left >= right');
215
- // No right bound at this level (open, or `locked` below it): emit
216
- // within the bounded append window — dense growth, and one more
217
- // level per exhausted gap, not one per insert (SW12-crdt-2).
218
- const span = Math.min(APPEND_WINDOW, RANK_VMAX - lSeg.v);
219
- if (span <= 0) {
220
- path.push(lSeg); // digit space exhausted at this level — descend
221
- continue;
222
- }
223
- return own(lSeg.v + 1 + Math.floor(rand() * span));
224
- }
225
- if (rSeg.v - lSeg.v > 1) {
226
- // Gap at this level: any v in (lv, rv) is > left and < right.
227
- return own(lSeg.v + 1 + Math.floor(rand() * (rSeg.v - lSeg.v - 1)));
228
- }
229
- // rv - lv ≤ 1. Compact: the left digit with our tie, or the right
230
- // one, when it sorts between; else the left digit untied when that
231
- // already sorts below the right bound. A run's member (`rankAfter`) is
232
- // never compact: a peer's rank of the same digit and another tie would
233
- // sort between two of its members.
234
- const mineL = { v: lSeg.v, t: clientId };
235
- if (compact && segCmp(lSeg, mineL) < 0 && segCmp(mineL, rSeg) < 0)
236
- return own(lSeg.v);
237
- const mineR = { v: rSeg.v, t: clientId };
238
- if (compact && rSeg.v > lSeg.v && segCmp(mineR, rSeg) < 0)
239
- return own(rSeg.v);
240
- // Copy the left digit and descend: untied when compact and that
241
- // already sorts below the right bound (then above the left one too,
242
- // unless its segment was untied as well), else with its tie.
243
- const bare = { v: lSeg.v, t: Infinity };
244
- if (compact && segCmp(bare, rSeg) < 0) {
245
- path.push(bare);
246
- locked = true;
247
- if (lSeg.t !== Infinity)
248
- above = true;
249
- continue;
250
- }
251
- const c = segCmp(lSeg, rSeg);
252
- if (c < 0)
253
- locked = true;
254
- else if (c > 0 && !locked) {
255
- // Never locked and left's segment exceeds right's at the first
256
- // differing level → left > right (invalid bounds — caller error).
257
- bad('left >= right');
258
- }
259
- path.push(lSeg);
260
- }
261
- };
262
- /** The base of the run band: a run member's digit lies just above it. */
263
- export const RANK_RUN = -(2 ** 30);
264
- /**
265
- * A run member's digit lies in `(RANK_RUN, RANK_RUN + RUN_BAND)`: below every
266
- * digit a plain rank extends a prefix with (`0`, an append's), far above the
267
- * digit minimum. A run opens at {@link RUN_START}, the middle of the band, so
268
- * its members can be inserted before and after one another.
269
- */
270
- const RUN_BAND = 2 ** 29;
271
- const RUN_START = RANK_RUN + 2 ** 28;
272
- const inRun = (v) => v > RANK_RUN && v < RANK_RUN + RUN_BAND;
273
- /**
274
- * A rank in `(left, right)` for an insert right after `left` — one rule for
275
- * array items (`crdt/data.ts`) and inserted blocks (`order.insert.run`).
276
- * When `client` made `left`, the rank is in the client's run after it: `left`
277
- * itself as the prefix, then one segment of the run band — or, when `left`
278
- * is already a member of the client's run, that run's prefix and a member
279
- * between `left` and the next one. So what one client inserts after its own
280
- * items stays together whatever a peer inserts in that gap meanwhile: a
281
- * peer's rank there is either no extension of the run's prefix (it sorts
282
- * after the whole run) or one whose next digit is above the band (YATA's
283
- * origin rule, as `Y.Array` keeps an insert after its origin). Elsewhere, or
284
- * when the run rank would not sort below `right`, a plain {@link rankBetween}.
285
- */
286
- export const rankAfter = (left, right, client, rand = Math.random) => {
287
- const plain = () => rankBetween(left, right, client, rand);
288
- if (left === undefined)
289
- return plain();
290
- const segs = decodeRank(left);
291
- const last = segs.at(-1);
292
- const bottom = encodeRank([{ v: RANK_RUN, t: 0 }]);
293
- const top = encodeRank([{ v: RANK_RUN + RUN_BAND, t: 0 }]);
294
- /**
295
- * `prefix` and a rank after `after` (`undefined`: the run opens) in the
296
- * run, inside the band. Opening, it takes the band's middle, or a digit
297
- * below the run's first member; at the run's end it appends as an open
298
- * end does (a window, not half the band); else between the two.
299
- */
300
- const own = (prefix, after) => {
301
- const tail = (r) => r && r.length > prefix.length && r.startsWith(prefix) ? r.slice(prefix.length) : undefined;
302
- let next = tail(right);
303
- if (next !== undefined && next > top)
304
- next = undefined;
305
- let mine;
306
- if (after === undefined) {
307
- if (next === undefined)
308
- mine = encodeRank([{ v: RUN_START, t: client }]);
309
- else if (next > bottom)
310
- mine = rankBetween(bottom, next, client, rand, false);
311
- else
312
- return plain();
313
- }
314
- else if (next === undefined) {
315
- const append = rankBetween(after, undefined, client, rand, false);
316
- mine = append < top ? append : rankBetween(after, top, client, rand, false);
317
- }
318
- else
319
- mine = rankBetween(after, next, client, rand, false);
320
- const rank = prefix + mine;
321
- return right === undefined || rank < right ? rank : plain();
322
- };
323
- // A member of the client's run: the next member, inside the band.
324
- if (last.t === client && inRun(last.v))
325
- return own(encodeRank(segs.slice(0, -1)), encodeRank([last]));
326
- // A rank the client made: its run opens right after it. The prefix keeps
327
- // `left`'s tie: a peer's rank of the same digit and a greater tie
328
- // then sorts after the whole run, never between `left` and it.
329
- if (last.t === client)
330
- return own(left, undefined);
331
- return plain();
332
- };
@@ -1,43 +0,0 @@
1
- /**
2
- * The rank of a block promoted into the slot ranked `slot` (read-time
3
- * promotion, and a promote-delete's planned moves): a valid rank, so an
4
- * insert beside a promoted block ranks against it like any other.
5
- */
6
- export declare const promotedRank: (slot: string, rank: string) => string;
7
- /**
8
- * Which side of a gap `(X, Y)` a block ranked by its source comes from
9
- * ({@link sourceRank}), in the order every serial run puts them: the
10
- * pieces a split of `X` creates, then the blocks leaving `X` for the gap
11
- * right after it (its children, a list's last items), then the blocks
12
- * leaving `Y` for the gap right before it (a list's first items).
13
- */
14
- export declare const SOURCE_SIDE: {
15
- readonly pieces: 0;
16
- readonly after: 1;
17
- readonly before: 2;
18
- };
19
- /**
20
- * The rank of a block ranked by where it came from for the gap
21
- * `(left, right)` right beside its source, not by who moved it.
22
- * Two peers splitting or lifting out of the same block at once rank their
23
- * blocks in the same gap; ranks drawn at random there (and tied by client
24
- * id) sorted one peer's blocks before the other's whatever the text order.
25
- * Here the rank is a base every replica that saw the same gap computes
26
- * alike (the gap's midpoint, a fixed tie), then the `side` it comes from
27
- * ({@link SOURCE_SIDE}: blocks from different sources share the gap, and
28
- * their paths are not comparable), then the ranks down the
29
- * block's `path` in its source (the block it stands at), each closed by
30
- * the lowest segment (a shorter rank then sorts first, as a prefix does),
31
- * then its `part` there (the caller's order among what stands at one
32
- * block), its first segment tied by `clientId`: two peers' blocks at one
33
- * part never share a rank (a later insert between two equal ranks could
34
- * not land between them), and each peer's blocks there stay together. A
35
- * segment tied by `clock`, the client's own next clock, follows that first
36
- * one: one client never mints one rank twice in a gap, even when the block
37
- * it first minted it for was deleted there and a peer's undo brings it
38
- * back, and what one gesture minted (the lines of one paste)
39
- * sorts before what its later gestures mint there, never among it.
40
- * `null` on a degenerate gap (`left >= right`): the caller ranks it as any
41
- * insert.
42
- */
43
- export declare const sourceRank: (left: string | undefined, right: string | undefined, side: number, path: readonly string[], part: readonly number[], clientId: number, clock: number) => string | null;
@@ -1,66 +0,0 @@
1
- /**
2
- * Ranks minted from where a block comes from: the rank of a block promoted
3
- * into a slot (`promotedRank`), and the source rank of a block placed beside
4
- * its source (`sourceRank`), the same on every replica that saw the gap.
5
- */
6
- import { decodeRank, encodeRank, rankBetween, RANK_VMIN } from './rank.js';
7
- /**
8
- * The rank separator of a promoted slot: the lowest segment a rank can hold
9
- * (`rankBetween` only copies it from a promoted bound), so `slot + PROMOTED + rank` sorts after
10
- * `slot` and before every rank the slot's list minted after it — also one
11
- * that extends `slot`, as an insert between two adjacent digits does.
12
- */
13
- const PROMOTED = encodeRank([{ v: RANK_VMIN, t: 0 }]);
14
- /**
15
- * The rank of a block promoted into the slot ranked `slot` (read-time
16
- * promotion, and a promote-delete's planned moves): a valid rank, so an
17
- * insert beside a promoted block ranks against it like any other.
18
- */
19
- export const promotedRank = (slot, rank) => slot + PROMOTED + rank;
20
- /**
21
- * Which side of a gap `(X, Y)` a block ranked by its source comes from
22
- * ({@link sourceRank}), in the order every serial run puts them: the
23
- * pieces a split of `X` creates, then the blocks leaving `X` for the gap
24
- * right after it (its children, a list's last items), then the blocks
25
- * leaving `Y` for the gap right before it (a list's first items).
26
- */
27
- export const SOURCE_SIDE = { pieces: 0, after: 1, before: 2 };
28
- /**
29
- * The rank of a block ranked by where it came from for the gap
30
- * `(left, right)` right beside its source, not by who moved it.
31
- * Two peers splitting or lifting out of the same block at once rank their
32
- * blocks in the same gap; ranks drawn at random there (and tied by client
33
- * id) sorted one peer's blocks before the other's whatever the text order.
34
- * Here the rank is a base every replica that saw the same gap computes
35
- * alike (the gap's midpoint, a fixed tie), then the `side` it comes from
36
- * ({@link SOURCE_SIDE}: blocks from different sources share the gap, and
37
- * their paths are not comparable), then the ranks down the
38
- * block's `path` in its source (the block it stands at), each closed by
39
- * the lowest segment (a shorter rank then sorts first, as a prefix does),
40
- * then its `part` there (the caller's order among what stands at one
41
- * block), its first segment tied by `clientId`: two peers' blocks at one
42
- * part never share a rank (a later insert between two equal ranks could
43
- * not land between them), and each peer's blocks there stay together. A
44
- * segment tied by `clock`, the client's own next clock, follows that first
45
- * one: one client never mints one rank twice in a gap, even when the block
46
- * it first minted it for was deleted there and a peer's undo brings it
47
- * back, and what one gesture minted (the lines of one paste)
48
- * sorts before what its later gestures mint there, never among it.
49
- * `null` on a degenerate gap (`left >= right`): the caller ranks it as any
50
- * insert.
51
- */
52
- export const sourceRank = (left, right, side, path, part, clientId, clock) => {
53
- if (left !== undefined && right !== undefined && left >= right)
54
- return null;
55
- const closed = path.flatMap((r) => [...decodeRank(r), { v: RANK_VMIN, t: SOURCE_TIE }]);
56
- const [first, ...rest] = part;
57
- const parts = [
58
- { v: first, t: clientId },
59
- { v: 0, t: clock },
60
- ...rest.map((v) => ({ v, t: SOURCE_TIE }))
61
- ];
62
- const own = [{ v: side, t: SOURCE_TIE }, ...closed, ...parts];
63
- return rankBetween(left, right, SOURCE_TIE, () => 0.5) + encodeRank(own);
64
- };
65
- /** The tie of a source rank's own segments: the same on every replica. */
66
- const SOURCE_TIE = 0;
@@ -1,129 +0,0 @@
1
- import type { EngineApi, EngineDoc, EngineNode, YUndoManager } from '../engine-api.js';
2
- import { type IdSetLike, type IdSetOf, type StoreStruct } from '../structs.js';
3
- import type { Span } from './deletes.js';
4
- /** Copy `c:k…` of member `oc:ok…`, whose root characters are `rc:rk…` (`n` of them). */
5
- export type Copy = Span & {
6
- rc: number;
7
- rk: number;
8
- oc: number;
9
- ok: number;
10
- };
11
- /** A piece of one member (root or copy) of some characters: ids `c:k…`, root characters from `rk`. */
12
- export type Member = Span & {
13
- rk: number;
14
- };
15
- /** A record (one element of a records list), by engine id. */
16
- export type Rec = {
17
- client: number;
18
- clock: number;
19
- };
20
- export type Unit = StoreStruct & {
21
- parent: EngineNode & {
22
- name?: string;
23
- };
24
- content: {
25
- getContent(): unknown[];
26
- str?: string;
27
- type?: {
28
- name?: string;
29
- _map?: Map<string, Unit>;
30
- };
31
- };
32
- delete(tr: unknown): void;
33
- };
34
- export type IdSet = IdSetOf;
35
- export type Tr = {
36
- local: boolean;
37
- insertSet: IdSet;
38
- deleteSet: IdSet;
39
- changed: Map<unknown, unknown>;
40
- };
41
- export type Step = {
42
- inserts: IdSet;
43
- deletes: IdSet;
44
- };
45
- export type Policy = {
46
- um: () => YUndoManager;
47
- /**
48
- * Root spans this session answers for: characters an undo skipped (held,
49
- * or shown by another copy) and copies it removed as duplicates. Each is
50
- * restored whenever nothing shows it and no mark holds it.
51
- */
52
- watch: Span[];
53
- /** The text units of the step being popped (its transaction). */
54
- tr: unknown;
55
- units: Span[];
56
- };
57
- export type State = {
58
- doc: EngineDoc & {
59
- clientID: number;
60
- _transaction: Tr | null;
61
- };
62
- marks: EngineNode;
63
- restored: EngineNode;
64
- /** The purge horizon's root: a transaction that changes it is a purge. */
65
- horizon: EngineNode;
66
- /** Marks by root client: the root spans each holds (and by mark key). */
67
- held: Map<number, Map<string, {
68
- k: number;
69
- n: number;
70
- mark: Rec;
71
- }[]>>;
72
- holds: Map<string, Span[]>;
73
- /** Copies by copy client, root client and origin client. */
74
- copies: Map<number, Copy[]>;
75
- byRoot: Map<number, Copy[]>;
76
- byOrigin: Map<number, Copy[]>;
77
- copyKeys: Set<string>;
78
- policies: Set<Policy>;
79
- /** This replica's last delete record (its id), the one a delete may fold into. */
80
- lastMark: {
81
- c: number;
82
- k: number;
83
- } | null;
84
- };
85
- export declare const overlaps: (a: {
86
- k: number;
87
- n: number;
88
- }, b: {
89
- k: number;
90
- n: number;
91
- }) => boolean;
92
- export declare const push: <T>(map: Map<number, T[]>, key: number, v: T) => void;
93
- export declare const keyOf: (r: Rec) => string;
94
- /** The most spans a folded delete record holds (a backspace run merges into one). */
95
- export declare const FOLD_SPANS = 8;
96
- /** `spans` sorted, with touching and overlapping spans of one client merged. */
97
- export declare const mergeSpans: (spans: readonly Span[]) => Span[];
98
- /** `m` narrowed to root characters `[lo, hi)`. */
99
- export declare const clip: (m: Member, lo: number, hi: number) => Member;
100
- export declare const encode: (rows: number[][]) => Uint8Array;
101
- export declare const decode: (bytes: Uint8Array, width: number) => number[][];
102
- /** The index's reads and writes over a document's `State`. */
103
- export declare const deleteIndex: (Y: EngineApi) => {
104
- idSet: (spans: readonly Span[]) => IdSet;
105
- each: (tr: Tr, spans: readonly Span[], fn: (it: Unit) => void) => void;
106
- rootSpans: (s: State, sp: Span) => Span[];
107
- members: (s: State, r: Span) => Member[];
108
- liveRoots: (s: State, m: Member) => Span[];
109
- shown: (s: State, roots: readonly Span[]) => Span[];
110
- indexOf: (s: State, c: number, k: number) => number;
111
- leaves: (s: State, r: Span) => Member[];
112
- holders: (s: State, r: Span, skip?: (mark: Rec) => boolean) => Rec[];
113
- minus: (r: Span, done: readonly Span[]) => Span[];
114
- parts: (s: State, r: Span) => Span[];
115
- addCopies: (s: State, copies: readonly Copy[]) => Copy[];
116
- indexRecord: (s: State, rec: Rec, bytes: Uint8Array) => Copy[];
117
- indexMark: (s: State, mark: Rec, bytes: Uint8Array) => boolean;
118
- dropMarks: (s: State, marks: readonly Rec[]) => boolean;
119
- recordsIn: (st: StoreStruct, ids: IdSetLike | null) => [Rec, Uint8Array][];
120
- sync: (s: State, tr: Tr) => {
121
- copies: Copy[];
122
- marks: Rec[];
123
- released: boolean;
124
- };
125
- copiesOf: (r: Rec, bytes: Uint8Array) => Copy[];
126
- forget: (s: State, copies: readonly Copy[]) => void;
127
- collect: (s: State, tr: unknown, copies: readonly Copy[]) => void;
128
- };
129
- export type DeleteIndex = ReturnType<typeof deleteIndex>;