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
@@ -1,175 +0,0 @@
1
- /**
2
- * The run cache: each block's visible runs as a frozen snapshot, recomputed
3
- * when a fold invalidates it, sharing unchanged runs and interned payloads.
4
- */
5
- import { cloneJsonSafe } from '../../../utils/json.js';
6
- import { canonKey, deepFreeze, displayOf, readSegs } from '../model.js';
7
- import { runEquals } from './shared.js';
8
- /** The run cache of one doc's index. */
9
- export const indexCache = (ix) => {
10
- const { blocks, textConsumers, listConsumers, cache, dirty, foldStats, frames, ownShim } = ix;
11
- const rangeStats = { items: 0, markers: 0 };
12
- // ── run cache ────────────────────────────────────────────────────
13
- const internMap = new Map();
14
- const debug = {
15
- recomputes: 0,
16
- recomputed: new Set(),
17
- get itemsWalked() {
18
- return rangeStats.items;
19
- },
20
- get markersWalked() {
21
- return rangeStats.markers;
22
- },
23
- get frames() {
24
- return frames.size;
25
- },
26
- get folds() {
27
- return foldStats.folds;
28
- },
29
- get foldedPairs() {
30
- return foldStats.pairs;
31
- },
32
- get foldedStructs() {
33
- return foldStats.structs;
34
- },
35
- get placements() {
36
- return foldStats.placed;
37
- },
38
- reset() {
39
- debug.recomputes = 0;
40
- debug.recomputed.clear();
41
- foldStats.folds = 0;
42
- foldStats.pairs = 0;
43
- foldStats.structs = 0;
44
- foldStats.placed = 0;
45
- rangeStats.items = 0;
46
- rangeStats.markers = 0;
47
- }
48
- };
49
- // ── interning / freezing ─────────────────────────────────────────
50
- // `cloneJsonSafe` is total even against hostile replicated payloads,
51
- // and interning the normalized form keys it by its own canonical shape.
52
- // The shared instance is built FROM that key (sorted keys): it must not
53
- // remember the key order its first reader happened to fold, or
54
- // `toJSON` would depend on when the view was read.
55
- const intern = (v) => {
56
- const key = canonKey(cloneJsonSafe(v));
57
- let f = internMap.get(key);
58
- if (f === undefined)
59
- internMap.set(key, (f = deepFreeze(JSON.parse(key))));
60
- return f;
61
- };
62
- // ── dep bookkeeping ──────────────────────────────────────────────
63
- const drop = (index, keys, b) => {
64
- for (const k of keys) {
65
- const set = index.get(k);
66
- set?.delete(b);
67
- if (set?.size === 0)
68
- index.delete(k);
69
- }
70
- };
71
- const add = (index, keys, b) => {
72
- for (const k of keys) {
73
- let set = index.get(k);
74
- if (set === undefined)
75
- index.set(k, (set = new Set()));
76
- set.add(b);
77
- }
78
- };
79
- const applyDeps = (b, deps) => {
80
- const old = cache.get(b)?.deps;
81
- if (old) {
82
- drop(textConsumers, old.texts, b);
83
- drop(listConsumers, old.lists, b);
84
- }
85
- add(textConsumers, deps.texts, b);
86
- add(listConsumers, deps.lists, b);
87
- };
88
- // ── snapshot construction ────────────────────────────────────────
89
- const freezeFresh = (r) => Object.freeze(r);
90
- /** The normalized fresh runs of `b` (interned payloads, equal marks merged) and their deps. */
91
- const computeFresh = (b) => {
92
- const deps = { texts: new Set(), lists: new Set([b]) };
93
- const segs = displayOf(b, blocks, ownShim, (x, home) => {
94
- deps.lists.add(x);
95
- if (home !== undefined)
96
- deps.texts.add(home);
97
- // Every claim on a walked list is read, followed or not: a claim
98
- // skipped for a dead target or a higher claimer becomes effective
99
- // when that target revives or its winning claim goes away.
100
- for (const c of blocks.get(x)?.claims ?? [])
101
- deps.lists.add(c.m);
102
- }) ?? [];
103
- const fresh = [];
104
- for (const item of readSegs(segs, rangeStats)) {
105
- if (item.kind === 'text') {
106
- const last = fresh[fresh.length - 1];
107
- const marks = item.marks === undefined ? undefined : intern(item.marks);
108
- if (last && last.kind === 'text' && last.marks === marks) {
109
- last.text += item.text;
110
- }
111
- else {
112
- fresh.push({
113
- kind: 'text',
114
- text: item.text,
115
- ...(marks === undefined ? {} : { marks })
116
- });
117
- }
118
- }
119
- else {
120
- fresh.push({
121
- kind: 'inline',
122
- id: item.id,
123
- type: item.type,
124
- ...(item.data === undefined ? {} : { data: intern(item.data) })
125
- });
126
- }
127
- }
128
- return { fresh, deps };
129
- };
130
- /**
131
- * Structural sharing: reuse unchanged run objects from `old` — maximal
132
- * equal prefix + equal suffix; `old` itself when every run is identical.
133
- */
134
- const reconcile = (old, fresh) => {
135
- if (!old)
136
- return Object.freeze(fresh.map(freezeFresh));
137
- let s = 0;
138
- const n = Math.min(old.length, fresh.length);
139
- while (s < n && runEquals(old[s], fresh[s]))
140
- s++;
141
- if (s === old.length && s === fresh.length)
142
- return old;
143
- let eo = old.length - 1;
144
- let ef = fresh.length - 1;
145
- while (eo >= s && ef >= s && runEquals(old[eo], fresh[ef])) {
146
- eo--;
147
- ef--;
148
- }
149
- const out = new Array(fresh.length);
150
- for (let i = 0; i < s; i++)
151
- out[i] = old[i];
152
- for (let i = s; i <= ef; i++)
153
- out[i] = freezeFresh(fresh[i]);
154
- for (let i = eo + 1; i < old.length; i++)
155
- out[fresh.length - (old.length - i)] = old[i];
156
- return Object.freeze(out);
157
- };
158
- // ── recompute ────────────────────────────────────────────────────
159
- const computeRuns = (b) => {
160
- const old = cache.get(b);
161
- const { fresh, deps } = computeFresh(b);
162
- const runs = reconcile(old?.runs, fresh);
163
- applyDeps(b, deps);
164
- cache.set(b, { runs, deps });
165
- dirty.delete(b);
166
- debug.recomputes++;
167
- debug.recomputed.add(b);
168
- };
169
- return {
170
- intern,
171
- computeFresh,
172
- computeRuns,
173
- debug
174
- };
175
- };
@@ -1,18 +0,0 @@
1
- /**
2
- * The claim graph (maintained): each block's owner along the max-stamp
3
- * claims (`top`), the blocks each owner displays, and what a structural
4
- * change of a block invalidates.
5
- */
6
- import type { BlockId } from '../../placement/model.js';
7
- import { type Claim, type Owner } from '../model.js';
8
- import type { FoldCtx, IndexState } from './state.js';
9
- /** The claim graph of one doc's index, over its state. */
10
- export declare const indexClaims: (ix: IndexState) => {
11
- ensureOwners: () => void;
12
- ownerOf: (b: BlockId) => Owner;
13
- top: (m: BlockId) => BlockId | undefined;
14
- displays: (owner: BlockId) => readonly BlockId[];
15
- noteClaims: (id: BlockId, before: readonly Claim[], after: readonly Claim[]) => void;
16
- invalidateBlock: (id: BlockId, ctx: FoldCtx, claimsBefore?: Claim[]) => void;
17
- };
18
- export type IndexClaims = ReturnType<typeof indexClaims>;
@@ -1,203 +0,0 @@
1
- import { DEAD, cmpStamp } from '../model.js';
2
- import { addTo, dropFrom } from './shared.js';
3
- /** The claim graph of one doc's index, over its state. */
4
- export const indexClaims = (ix) => {
5
- const { blocks, listConsumers, displaysMap, ownerSeeds, ownerChanged } = ix;
6
- // ── the claim graph (maintained) ─────────────────────────────
7
- // `top(m)` is the max-stamp claim on `m` held by a live block; each
8
- // block's owner follows `top` up (`claimGraph`). A structural change
9
- // of a block (its claims, its delete mark, its record) re-decides the
10
- // `top` of the blocks it claims (before and after) and the owners of
11
- // those blocks and of every block whose `top` chain reaches them —
12
- // never the whole graph.
13
- const owners = new Map();
14
- const topOf = new Map();
15
- /** Claimer → the blocks it tops. */
16
- const topInv = new Map();
17
- /** Claimed block → the blocks whose claims list names it (live or not). */
18
- const claimersOf = new Map();
19
- /** The max-stamp claim on `m` held by a live block. */
20
- const topClaim = (m) => {
21
- let best;
22
- for (const holder of claimersOf.get(m) ?? []) {
23
- const rec = blocks.get(holder);
24
- if (rec === undefined || rec.deleted)
25
- continue;
26
- for (const c of rec.claims)
27
- if (c.m === m && (best === undefined || cmpStamp(c.stamp, best.stamp) > 0))
28
- best = { claimer: holder, stamp: c.stamp };
29
- }
30
- return best;
31
- };
32
- const setTop = (m, t) => {
33
- const old = topOf.get(m);
34
- if (old?.claimer === t?.claimer && (old === undefined || cmpStamp(old.stamp, t.stamp) === 0))
35
- return false;
36
- if (old !== undefined)
37
- dropFrom(topInv, old.claimer, m);
38
- if (t === undefined)
39
- topOf.delete(m);
40
- else {
41
- topOf.set(m, t);
42
- addTo(topInv, t.claimer, m);
43
- }
44
- return true;
45
- };
46
- /** `owner(b)` along `top` (`claimGraph`'s walk), memoized into `owners`. */
47
- const walkOwner = (b) => {
48
- const path = [];
49
- let cur = b;
50
- let result;
51
- for (;;) {
52
- const known = owners.get(cur);
53
- if (known !== undefined) {
54
- result = known;
55
- break;
56
- }
57
- const rec = blocks.get(cur);
58
- if (!rec || rec.deleted) {
59
- result = DEAD;
60
- break;
61
- }
62
- const at = path.indexOf(cur);
63
- if (at >= 0) {
64
- let best = topOf.get(path[at]);
65
- for (const member of path.slice(at)) {
66
- const t = topOf.get(member);
67
- if (cmpStamp(t.stamp, best.stamp) > 0)
68
- best = t;
69
- }
70
- result = best.claimer;
71
- break;
72
- }
73
- const t = topOf.get(cur);
74
- if (t === undefined) {
75
- result = cur;
76
- break;
77
- }
78
- path.push(cur);
79
- cur = t.claimer;
80
- }
81
- if (blocks.has(cur))
82
- owners.set(cur, result);
83
- for (const x of path)
84
- owners.set(x, result);
85
- };
86
- const setDisplay = (b, from, to) => {
87
- if (typeof from === 'string')
88
- dropFrom(displaysMap, from, b);
89
- if (typeof to === 'string')
90
- addTo(displaysMap, to, b);
91
- };
92
- const ensureOwners = () => {
93
- if (ownerSeeds.size === 0)
94
- return;
95
- // The tops a seed's claims (now, and those it dropped: noted in `claimersOf` changes) decide.
96
- const affected = new Set();
97
- const stack = [];
98
- const seeds = [...ownerSeeds];
99
- for (const x of ownerSeeds) {
100
- stack.push(x);
101
- for (const m of claimTargets.get(x) ?? [])
102
- if (setTop(m, topClaim(m)))
103
- stack.push(m);
104
- for (const c of blocks.get(x)?.claims ?? [])
105
- if (setTop(c.m, topClaim(c.m)))
106
- stack.push(c.m);
107
- }
108
- ownerSeeds.clear();
109
- claimTargets.clear();
110
- for (let x = stack.pop(); x !== undefined; x = stack.pop()) {
111
- if (affected.has(x))
112
- continue;
113
- affected.add(x);
114
- for (const m of topInv.get(x) ?? [])
115
- stack.push(m);
116
- }
117
- const before = new Map();
118
- for (const x of affected) {
119
- before.set(x, owners.get(x));
120
- owners.delete(x);
121
- }
122
- for (const x of affected)
123
- if (blocks.has(x))
124
- walkOwner(x);
125
- for (const [x, was] of before) {
126
- const now = owners.get(x);
127
- if (now === was)
128
- continue;
129
- setDisplay(x, was, now);
130
- ownerChanged.add(x);
131
- }
132
- // A holder whose claims changed re-slots what shows through them: their
133
- // order among its claims is the key of the blocks shown under it
134
- // (`claimedRank`) — under its owner when it is itself merged away.
135
- for (const x of seeds) {
136
- if (!blocks.has(x))
137
- continue;
138
- ownerChanged.add(x);
139
- const o = owners.get(x);
140
- if (typeof o === 'string' && o !== x)
141
- ownerChanged.add(o);
142
- }
143
- };
144
- /** The claims each seed held before its record changed (their tops are re-decided too). */
145
- const claimTargets = new Map();
146
- const ownerOf = (b) => {
147
- ensureOwners();
148
- return owners.get(b) ?? DEAD;
149
- };
150
- /** The live claimer of the max-stamp claim on `m` (`DisplayOwnership.top`). */
151
- const top = (m) => {
152
- ensureOwners();
153
- return topOf.get(m)?.claimer;
154
- };
155
- /** The blocks `owner` displays (the delete step's marks). */
156
- const displays = (owner) => {
157
- ensureOwners();
158
- return [...(displaysMap.get(owner) ?? [])];
159
- };
160
- /** Union-ever claim targets per holder (never shrinks: a dropped claim still invalidates). */
161
- const effects = new Map();
162
- const noteEffects = (id, claims) => {
163
- let fx = effects.get(id);
164
- if (fx === undefined)
165
- effects.set(id, (fx = new Set()));
166
- for (const c of claims)
167
- fx.add(c.m);
168
- };
169
- /** `id`'s claims went from `before` to `after`: its claim facts follow. */
170
- const noteClaims = (id, before, after) => {
171
- noteEffects(id, after);
172
- const was = new Set(before.map((c) => c.m));
173
- const now = new Set(after.map((c) => c.m));
174
- for (const m of was)
175
- if (!now.has(m)) {
176
- dropFrom(claimersOf, m, id);
177
- addTo(claimTargets, id, m);
178
- }
179
- for (const m of now)
180
- if (!was.has(m))
181
- addTo(claimersOf, m, id);
182
- ownerSeeds.add(id);
183
- };
184
- /** A structural change of `id`: its readers, and the blocks it claims, re-read. */
185
- const invalidateBlock = (id, ctx, claimsBefore = []) => {
186
- ctx.invalidated.add(id);
187
- for (const c of listConsumers.get(id) ?? [])
188
- ctx.invalidated.add(c);
189
- for (const m of new Set([...(effects.get(id) ?? []), ...claimsBefore.map((c) => c.m)])) {
190
- ctx.invalidated.add(m);
191
- for (const c of listConsumers.get(m) ?? [])
192
- ctx.invalidated.add(c);
193
- }
194
- };
195
- return {
196
- ensureOwners,
197
- ownerOf,
198
- top,
199
- displays,
200
- noteClaims,
201
- invalidateBlock
202
- };
203
- };
@@ -1,21 +0,0 @@
1
- import type { BlockId } from '../../placement/model.js';
2
- import type { ContentRun } from '../runs.js';
3
- import type { IndexCache } from './cache.js';
4
- import type { IndexClaims } from './claims.js';
5
- import type { IndexPlacement } from './placement.js';
6
- import type { IndexRecords } from './records.js';
7
- import type { FoldCtx, IndexState, Tx } from './state.js';
8
- import type { IndexStreams } from './streams.js';
9
- import type { IndexLayout } from './layout.js';
10
- /** The fold of one doc's index, over every part it folds into. */
11
- export declare const indexFold: (ix: IndexState & IndexClaims & IndexStreams & IndexLayout & IndexPlacement & IndexRecords & IndexCache) => {
12
- runs: (b: BlockId) => readonly ContentRun[];
13
- settle: (affected: Iterable<BlockId>, ctx: FoldCtx | null) => void;
14
- syncPending: (tr: Tx | null | undefined) => boolean;
15
- openTx: () => Tx | null | undefined;
16
- syncAll: () => void;
17
- unfolded: () => boolean;
18
- onCommit: (tr: Tx) => void;
19
- committed: WeakSet<Tx>;
20
- };
21
- export type IndexFold = ReturnType<typeof indexFold>;