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.
- package/README.md +3 -3
- package/dist/cloudflare/DocumentRoom.d.ts +77 -2
- package/dist/cloudflare/DocumentRoom.js +37 -1
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/room/access.d.ts +6 -1
- package/dist/cloudflare/room/access.js +30 -4
- package/dist/cloudflare/room/admission.d.ts +11 -0
- package/dist/cloudflare/room/admission.js +43 -10
- package/dist/cloudflare/room/context.d.ts +73 -76
- package/dist/cloudflare/room/context.js +17 -6
- package/dist/cloudflare/room/forged.d.ts +1 -1
- package/dist/cloudflare/room/forged.js +11 -9
- package/dist/cloudflare/room/frames.d.ts +2 -0
- package/dist/cloudflare/room/frames.js +2 -0
- package/dist/cloudflare/room/presence.js +1 -4
- package/dist/cloudflare/room/reseed.d.ts +93 -0
- package/dist/cloudflare/room/reseed.js +500 -0
- package/dist/cloudflare/room/scheduler.d.ts +1 -1
- package/dist/cloudflare/room/scheduler.js +9 -1
- package/dist/cloudflare/room/shared.d.ts +10 -0
- package/dist/cloudflare/room/shared.js +10 -0
- package/dist/cloudflare/room/storage.d.ts +7 -0
- package/dist/cloudflare/room/storage.js +33 -1
- package/dist/components/Edytor.svelte +18 -3
- package/dist/components/Edytor.svelte.d.ts +10 -1
- package/dist/components/EdytorRoot.svelte +17 -0
- package/dist/components/EdytorRoot.svelte.d.ts +4 -0
- package/dist/components/Suggestion.svelte +1 -1
- package/dist/crdt/attribution/block.d.ts +3 -0
- package/dist/crdt/attribution/block.js +3 -2
- package/dist/crdt/doc/history.js +2 -3
- package/dist/crdt/doc/ops.d.ts +81 -0
- package/dist/crdt/doc/ops.js +1898 -0
- package/dist/crdt/doc/suggest.d.ts +6 -2
- package/dist/crdt/doc/suggest.js +6 -5
- package/dist/crdt/edytor-doc.d.ts +157 -157
- package/dist/crdt/edytor-doc.js +15 -43
- package/dist/crdt/engine-room.d.ts +4 -0
- package/dist/crdt/engine-room.js +6 -0
- package/dist/crdt/flow.d.ts +4 -46
- package/dist/crdt/flow.js +2 -262
- package/dist/crdt/index.d.ts +64 -75
- package/dist/crdt/operations.d.ts +1 -1
- package/dist/crdt/placement/resolve.d.ts +1 -1
- package/dist/crdt/protocol.d.ts +2 -2
- package/dist/crdt/protocol.js +2 -2
- package/dist/crdt/protocols/auth.d.ts +8 -1
- package/dist/crdt/protocols/auth.js +14 -1
- package/dist/crdt/protocols/awareness.js +46 -81
- package/dist/crdt/providers/index.d.ts +11 -22
- package/dist/crdt/providers/index.js +22 -3
- package/dist/crdt/providers/indexeddb.d.ts +6 -10
- package/dist/crdt/providers/indexeddb.js +10 -16
- package/dist/crdt/providers/room.d.ts +26 -0
- package/dist/crdt/providers/room.js +41 -12
- package/dist/crdt/providers/websocket.d.ts +10 -14
- package/dist/crdt/providers/websocket.js +43 -23
- package/dist/crdt/rangeDelete.d.ts +2 -47
- package/dist/crdt/rangeDelete.js +1 -169
- package/dist/crdt/reseed.d.ts +127 -0
- package/dist/crdt/reseed.js +533 -0
- package/dist/crdt/restore.d.ts +13 -1
- package/dist/crdt/restore.js +8 -3
- package/dist/crdt/schema.d.ts +6 -0
- package/dist/crdt/schema.js +6 -0
- package/dist/crdt/structs.d.ts +8 -11
- package/dist/crdt/structs.js +25 -44
- package/dist/crdt/text/index/build.d.ts +119 -0
- package/dist/crdt/text/index/build.js +2801 -0
- package/dist/crdt/text/index/shared.d.ts +2 -6
- package/dist/crdt/text/index/shared.js +2 -5
- package/dist/crdt/text/model.js +2 -3
- package/dist/crdt/text/runs.d.ts +9 -12
- package/dist/crdt/text/runs.js +2 -165
- package/dist/crdt/vendor/yjs/UPSTREAM.md +101 -5
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +10 -10
- package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +1 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/Item.js +24 -13
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +12 -10
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +42 -1
- package/dist/events/domTextOffset.d.ts +2 -0
- package/dist/events/domTextOffset.js +8 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
- package/dist/plugins/richtext/richTextOperations.js +26 -32
- package/dist/selection/selection.svelte.d.ts +1 -1
- package/dist/selection/selection.svelte.js +87 -106
- package/dist/selection/selection.utils.js +5 -8
- package/dist/session/commands.js +49 -51
- package/dist/session/handles.js +3 -7
- package/dist/surface/attributes.d.ts +3 -3
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/observer.svelte.d.ts +7 -7
- package/dist/surface/observer.svelte.js +225 -336
- package/dist/surface/projector.svelte.js +2 -2
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
- package/dist/crdt/doc/capability.d.ts +0 -56
- package/dist/crdt/doc/capability.js +0 -207
- package/dist/crdt/doc/content.d.ts +0 -16
- package/dist/crdt/doc/content.js +0 -77
- package/dist/crdt/doc/delete.d.ts +0 -15
- package/dist/crdt/doc/delete.js +0 -84
- package/dist/crdt/doc/layout.d.ts +0 -9
- package/dist/crdt/doc/layout.js +0 -178
- package/dist/crdt/doc/meta.d.ts +0 -19
- package/dist/crdt/doc/meta.js +0 -198
- package/dist/crdt/doc/moves.d.ts +0 -21
- package/dist/crdt/doc/moves.js +0 -274
- package/dist/crdt/doc/split-merge.d.ts +0 -10
- package/dist/crdt/doc/split-merge.js +0 -179
- package/dist/crdt/doc/steps.d.ts +0 -65
- package/dist/crdt/doc/steps.js +0 -311
- package/dist/crdt/text/index/cache.d.ts +0 -16
- package/dist/crdt/text/index/cache.js +0 -175
- package/dist/crdt/text/index/claims.d.ts +0 -18
- package/dist/crdt/text/index/claims.js +0 -203
- package/dist/crdt/text/index/fold.d.ts +0 -21
- package/dist/crdt/text/index/fold.js +0 -656
- package/dist/crdt/text/index/layout.d.ts +0 -22
- package/dist/crdt/text/index/layout.js +0 -258
- package/dist/crdt/text/index/placement.d.ts +0 -47
- package/dist/crdt/text/index/placement.js +0 -738
- package/dist/crdt/text/index/records.d.ts +0 -13
- package/dist/crdt/text/index/records.js +0 -139
- package/dist/crdt/text/index/report.d.ts +0 -18
- package/dist/crdt/text/index/report.js +0 -337
- package/dist/crdt/text/index/state.d.ts +0 -197
- package/dist/crdt/text/index/state.js +0 -116
- package/dist/crdt/text/index/streams.d.ts +0 -25
- package/dist/crdt/text/index/streams.js +0 -248
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { BlockId } from '../../placement/model.js';
|
|
2
|
-
import type { IndexClaims } from './claims.js';
|
|
3
|
-
import type { IndexPlacement } from './placement.js';
|
|
4
|
-
import type { IndexState } from './state.js';
|
|
5
|
-
import type { IndexStreams } from './streams.js';
|
|
6
|
-
/** The block records of one doc's index. */
|
|
7
|
-
export declare const indexRecords: (ix: IndexState & IndexClaims & IndexStreams & IndexPlacement) => {
|
|
8
|
-
kept: (item: unknown) => boolean;
|
|
9
|
-
updateBlockRec: (id: BlockId) => void;
|
|
10
|
-
ensureRec: (id: BlockId) => void;
|
|
11
|
-
syncIncarnations: (id: BlockId) => string[];
|
|
12
|
-
};
|
|
13
|
-
export type IndexRecords = ReturnType<typeof indexRecords>;
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Block records: each registry entry's record (and the losing
|
|
3
|
-
* incarnations a key shows), rebuilt on a structural change, noting what
|
|
4
|
-
* the maintained facts must re-decide.
|
|
5
|
-
*/
|
|
6
|
-
import { readData } from '../../data.js';
|
|
7
|
-
import { Y } from '../../engine.js';
|
|
8
|
-
import { baseIdOf, incarnationNode, incarnationsOf, isIncarnationId } from '../../incarnations.js';
|
|
9
|
-
import { engineOps } from '../../structs.js';
|
|
10
|
-
import { CONTENT, NONCE, hasDeleteMark, hasWithdrawMark, isNodeLike } from '../../schema.js';
|
|
11
|
-
import { sameShape, typeAttr } from './shared.js';
|
|
12
|
-
/** The block records of one doc's index. */
|
|
13
|
-
export const indexRecords = (ix) => {
|
|
14
|
-
const { registry, blocks, shells, reclaim, rawOrder, placementSeeds, stateSeeds, incarnations, noteClaims, claimsOf, noteKind, homeFor, reslot } = ix;
|
|
15
|
-
// ── records ──────────────────────────────────────────────────────
|
|
16
|
-
/** Fork patch YP14's predicate: the registry values the engine keeps. */
|
|
17
|
-
const kept = engineOps(Y).isKeptReplaced;
|
|
18
|
-
/** The node of `id`: its registry value, or the losing incarnation a derived id names. */
|
|
19
|
-
const nodeAt = (id) => {
|
|
20
|
-
const v = registry.getAttr(id);
|
|
21
|
-
if (isNodeLike(v) || !isIncarnationId(id))
|
|
22
|
-
return v;
|
|
23
|
-
return incarnationNode(registry, id, kept) ?? undefined;
|
|
24
|
-
};
|
|
25
|
-
const buildRec = (id, node) => {
|
|
26
|
-
const content = node.getAttr(CONTENT);
|
|
27
|
-
const derived = isIncarnationId(id);
|
|
28
|
-
// A losing incarnation lives and dies with its key's block: hidden by its
|
|
29
|
-
// own delete mark or while that block is deleted (a withdrawn one included).
|
|
30
|
-
const keyDeleted = derived ? (blocks.get(baseIdOf(id))?.deleted ?? true) : false;
|
|
31
|
-
// The implicit claims stamp below every written one: an explicit claim
|
|
32
|
-
// (a split moving them to its tail block) outranks them.
|
|
33
|
-
const implicit = derived
|
|
34
|
-
? []
|
|
35
|
-
: incarnationsOf(registry, id, kept).map((x, i) => ({
|
|
36
|
-
m: x.id,
|
|
37
|
-
stamp: { c: -1, k: -1 - i }
|
|
38
|
-
}));
|
|
39
|
-
return {
|
|
40
|
-
id,
|
|
41
|
-
node,
|
|
42
|
-
type: typeAttr(node),
|
|
43
|
-
data: readData(node),
|
|
44
|
-
n: node.getAttr(NONCE),
|
|
45
|
-
deleted: hasDeleteMark(node) || keyDeleted,
|
|
46
|
-
content: isNodeLike(content) ? content : undefined,
|
|
47
|
-
implicit,
|
|
48
|
-
// Read from its stream once the record is in (`claimsOf`), again after the fold's texts.
|
|
49
|
-
claims: [],
|
|
50
|
-
home: homeFor(id)
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
/**
|
|
54
|
-
* Rebuild (or create, or drop) block `id`'s record, and note what the
|
|
55
|
-
* maintained facts must re-decide: its owner and the tops of the
|
|
56
|
-
* blocks it claims, its placement — and, when its entry came or
|
|
57
|
-
* went, the placements of the blocks whose display edge it holds.
|
|
58
|
-
*/
|
|
59
|
-
const updateBlockRec = (id) => {
|
|
60
|
-
const old = blocks.get(id);
|
|
61
|
-
const node = nodeAt(id);
|
|
62
|
-
if (!isNodeLike(node))
|
|
63
|
-
blocks.delete(id);
|
|
64
|
-
else {
|
|
65
|
-
const rec = buildRec(id, node);
|
|
66
|
-
blocks.set(id, rec);
|
|
67
|
-
rec.claims = claimsOf(id);
|
|
68
|
-
reclaim.add(id);
|
|
69
|
-
}
|
|
70
|
-
const rec = blocks.get(id);
|
|
71
|
-
// Another node now stands for `id` (or none): its sequence's slots change owner.
|
|
72
|
-
if (old?.node !== rec?.node) {
|
|
73
|
-
if (old !== undefined)
|
|
74
|
-
reslot(old.node);
|
|
75
|
-
if (rec !== undefined)
|
|
76
|
-
reslot(rec.node);
|
|
77
|
-
}
|
|
78
|
-
noteShell(id);
|
|
79
|
-
noteClaims(id, old?.claims ?? [], rec?.claims ?? []);
|
|
80
|
-
placementSeeds.add(id);
|
|
81
|
-
if ((old === undefined) !== (rec === undefined)) {
|
|
82
|
-
stateSeeds.add(id);
|
|
83
|
-
for (const c of rawOrder.get(id) ?? [])
|
|
84
|
-
placementSeeds.add(c.id);
|
|
85
|
-
}
|
|
86
|
-
if (old?.type !== rec?.type) {
|
|
87
|
-
noteKind(id);
|
|
88
|
-
if (old === undefined ||
|
|
89
|
-
rec === undefined ||
|
|
90
|
-
ix.roles === null ||
|
|
91
|
-
!sameShape(ix.roles, old.type, rec.type))
|
|
92
|
-
stateSeeds.add(id);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
/** A withdrawn block without a delete mark: its `deleted` is settled after each fold. */
|
|
96
|
-
const noteShell = (id) => {
|
|
97
|
-
const rec = blocks.get(id);
|
|
98
|
-
const was = shells.has(id);
|
|
99
|
-
// A losing incarnation is never withdrawn on its own: its key's block decides.
|
|
100
|
-
if (isIncarnationId(id))
|
|
101
|
-
shells.delete(id);
|
|
102
|
-
else if (rec !== undefined && !rec.deleted && hasWithdrawMark(rec.node))
|
|
103
|
-
shells.add(id);
|
|
104
|
-
else
|
|
105
|
-
shells.delete(id);
|
|
106
|
-
// A withdrawn cell shows in a column its table no longer lists (`table.cell`): re-place it.
|
|
107
|
-
if (was !== shells.has(id) && rec !== undefined && ix.cellKinds.has(rec.type))
|
|
108
|
-
stateSeeds.add(id);
|
|
109
|
-
};
|
|
110
|
-
const ensureRec = (id) => {
|
|
111
|
-
if (!blocks.has(id))
|
|
112
|
-
updateBlockRec(id);
|
|
113
|
-
};
|
|
114
|
-
/**
|
|
115
|
-
* Re-read the losing incarnations of key `id` after its record
|
|
116
|
-
* changed: each one shown now or before gets its record rebuilt (its
|
|
117
|
-
* liveness is the key's); returns them, for the fold to rescan.
|
|
118
|
-
*/
|
|
119
|
-
const syncIncarnations = (id) => {
|
|
120
|
-
if (isIncarnationId(id))
|
|
121
|
-
return [];
|
|
122
|
-
const before = incarnations.get(id) ?? [];
|
|
123
|
-
const now = incarnationsOf(registry, id, kept).map((x) => x.id);
|
|
124
|
-
if (now.length === 0)
|
|
125
|
-
incarnations.delete(id);
|
|
126
|
-
else
|
|
127
|
-
incarnations.set(id, now);
|
|
128
|
-
const all = [...new Set([...before, ...now])];
|
|
129
|
-
for (const v of all)
|
|
130
|
-
updateBlockRec(v);
|
|
131
|
-
return all;
|
|
132
|
-
};
|
|
133
|
-
return {
|
|
134
|
-
kept,
|
|
135
|
-
updateBlockRec,
|
|
136
|
-
ensureRec,
|
|
137
|
-
syncIncarnations
|
|
138
|
-
};
|
|
139
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { EngineDoc } from '../../engine-api.js';
|
|
2
|
-
import type { BlockId, ContentItem, ProjectedBlock } from '../../placement/model.js';
|
|
3
|
-
import type { IndexReport } from '../runs.js';
|
|
4
|
-
import type { IndexCache } from './cache.js';
|
|
5
|
-
import type { IndexFold } from './fold.js';
|
|
6
|
-
import type { IndexPlacement } from './placement.js';
|
|
7
|
-
import type { IndexState, Published } from './state.js';
|
|
8
|
-
/** The change report of one doc's index. */
|
|
9
|
-
export declare const indexReporter: (ix: IndexState & IndexPlacement & IndexCache & IndexFold) => {
|
|
10
|
-
itemsOf: (id: BlockId) => ContentItem[];
|
|
11
|
-
projectBlock: (id: BlockId) => ProjectedBlock;
|
|
12
|
-
reachable: (prev?: Published["nodes"]) => Published;
|
|
13
|
-
publish: (origin: unknown, local: boolean) => void;
|
|
14
|
-
onUpdate: (_u: Uint8Array, origin: unknown, _d: EngineDoc, tr: unknown) => void;
|
|
15
|
-
onReport: (cb: (r: IndexReport, origin: unknown, local: boolean) => void) => (() => void);
|
|
16
|
-
hasSubscribers: () => boolean;
|
|
17
|
-
};
|
|
18
|
-
export type IndexReporter = ReturnType<typeof indexReporter>;
|
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The change report: the fold against the last published index, one per
|
|
3
|
-
* commit that changed the visible document, and the projection it carries.
|
|
4
|
-
*/
|
|
5
|
-
import { cloneJsonSafe, sameIds } from '../../../utils/json.js';
|
|
6
|
-
import { readData } from '../../data.js';
|
|
7
|
-
import { callEach } from '../../protocols/observable.js';
|
|
8
|
-
import { EMPTY_IDS, checksOf, dataOf, indexChecks, keyOf } from './shared.js';
|
|
9
|
-
/** The change report of one doc's index. */
|
|
10
|
-
export const indexReporter = (ix) => {
|
|
11
|
-
const { doc, dataRoot, blocks, following, dirtyLists, leftLists, candidates, ensurePlacements, typeOf, runs, syncAll } = ix;
|
|
12
|
-
const docData = () => cloneJsonSafe(readData(dataRoot) ?? {});
|
|
13
|
-
let dataChanged = false;
|
|
14
|
-
// ── projection ───────────────────────────────────────────────────
|
|
15
|
-
/** `id`'s visible content items: its runs, as fresh wrappers over their shared payloads. */
|
|
16
|
-
const itemsOf = (id) => runs(id).map((r) => ({ ...r }));
|
|
17
|
-
const projectBlock = (id) => {
|
|
18
|
-
const rec = blocks.get(id);
|
|
19
|
-
const projected = {
|
|
20
|
-
id,
|
|
21
|
-
type: typeOf(id),
|
|
22
|
-
data: dataOf(rec),
|
|
23
|
-
content: itemsOf(id),
|
|
24
|
-
children: []
|
|
25
|
-
};
|
|
26
|
-
for (const k of ix.kidsMap.get(id) ?? [])
|
|
27
|
-
projected.children.push(projectBlock(k.id));
|
|
28
|
-
return projected;
|
|
29
|
-
};
|
|
30
|
-
// ── the change report: the fold against the last published index ─
|
|
31
|
-
let published = null;
|
|
32
|
-
const reportSubs = new Set();
|
|
33
|
-
/** The reachable tree — nodes carry `prev`'s published payloads where they exist. */
|
|
34
|
-
const reachable = (prev) => {
|
|
35
|
-
ensurePlacements();
|
|
36
|
-
const nodes = new Map();
|
|
37
|
-
const order = new Map();
|
|
38
|
-
const stack = [null];
|
|
39
|
-
for (let parent = stack.pop(); parent !== undefined; parent = stack.pop()) {
|
|
40
|
-
const ks = ix.kidsMap.get(parent) ?? [];
|
|
41
|
-
if (ks.length === 0)
|
|
42
|
-
continue;
|
|
43
|
-
order.set(parent, Object.freeze(ks.map((k) => k.id)));
|
|
44
|
-
for (let index = ks.length - 1; index >= 0; index--)
|
|
45
|
-
stack.push(ks[index].id);
|
|
46
|
-
ks.forEach(({ id }, index) => {
|
|
47
|
-
const old = prev?.get(id);
|
|
48
|
-
const rec = blocks.get(id);
|
|
49
|
-
nodes.set(id, old === undefined
|
|
50
|
-
? { parent, index, type: typeOf(id), data: rec.data, runs: runs(id) }
|
|
51
|
-
: { ...old, parent, index });
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return { nodes, order, kids: ix.kidsVersion };
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* The report's structural part from the child lists patched since the
|
|
58
|
-
* last report, advancing the published tree in place — what
|
|
59
|
-
* `reachable` would give, without walking the lists nothing changed:
|
|
60
|
-
* a published block no longer shown leaves (a root when its parent
|
|
61
|
-
* stays), its still-shown descendants staying; each patched list's
|
|
62
|
-
* new order is news, a block in it at a new parent or index moved, one
|
|
63
|
-
* not published before is added with its subtree.
|
|
64
|
-
*/
|
|
65
|
-
const advance = (pub, r, covered, reparented) => {
|
|
66
|
-
const { nodes, order } = pub;
|
|
67
|
-
const shown = (x) => {
|
|
68
|
-
for (let c = x; c !== null;) {
|
|
69
|
-
const slot = ix.slots.get(c);
|
|
70
|
-
if (slot === undefined)
|
|
71
|
-
return false;
|
|
72
|
-
c = slot.parent;
|
|
73
|
-
}
|
|
74
|
-
return true;
|
|
75
|
-
};
|
|
76
|
-
const lists = [...dirtyLists];
|
|
77
|
-
// Leaving: a published block of a patched list that no longer shows.
|
|
78
|
-
const drop = (id) => {
|
|
79
|
-
const ks = order.get(id);
|
|
80
|
-
if (ks !== undefined) {
|
|
81
|
-
order.delete(id);
|
|
82
|
-
r.order.set(id, EMPTY_IDS);
|
|
83
|
-
for (const k of ks)
|
|
84
|
-
if (!shown(k))
|
|
85
|
-
drop(k);
|
|
86
|
-
}
|
|
87
|
-
nodes.delete(id);
|
|
88
|
-
};
|
|
89
|
-
for (const id of leftLists) {
|
|
90
|
-
const o = nodes.get(id);
|
|
91
|
-
if (o === undefined || shown(id))
|
|
92
|
-
continue;
|
|
93
|
-
if (o.parent === null || shown(o.parent))
|
|
94
|
-
r.removed.add(id);
|
|
95
|
-
drop(id);
|
|
96
|
-
}
|
|
97
|
-
// Each patched list that shows: its order, its moved and new blocks.
|
|
98
|
-
const fresh = new Map();
|
|
99
|
-
for (const p of lists) {
|
|
100
|
-
if (p !== null && !shown(p))
|
|
101
|
-
continue;
|
|
102
|
-
const ks = ix.kidsMap.get(p) ?? [];
|
|
103
|
-
const prev = order.get(p);
|
|
104
|
-
if (ks.length === 0) {
|
|
105
|
-
if (prev !== undefined) {
|
|
106
|
-
order.delete(p);
|
|
107
|
-
r.order.set(p, EMPTY_IDS);
|
|
108
|
-
}
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
const ids = ks.map((k) => k.id);
|
|
112
|
-
if (prev === undefined || !sameIds(prev, ids)) {
|
|
113
|
-
const frozen = Object.freeze(ids);
|
|
114
|
-
order.set(p, frozen);
|
|
115
|
-
r.order.set(p, frozen);
|
|
116
|
-
}
|
|
117
|
-
ks.forEach(({ id }, index) => {
|
|
118
|
-
const n = nodes.get(id);
|
|
119
|
-
if (n === undefined)
|
|
120
|
-
fresh.set(id, { parent: p, index });
|
|
121
|
-
else if (n.parent !== p || n.index !== index) {
|
|
122
|
-
r.moved.add(id);
|
|
123
|
-
if (n.parent !== p)
|
|
124
|
-
reparented.add(id);
|
|
125
|
-
n.parent = p;
|
|
126
|
-
n.index = index;
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
// New roots (no new ancestor) bring their subtree.
|
|
131
|
-
const add = (b, parent, index) => {
|
|
132
|
-
if (!nodes.has(b.id)) {
|
|
133
|
-
covered.add(b.id);
|
|
134
|
-
const rec = blocks.get(b.id);
|
|
135
|
-
nodes.set(b.id, { parent, index, type: typeOf(b.id), data: rec.data, runs: runs(b.id) });
|
|
136
|
-
}
|
|
137
|
-
if (b.children.length > 0 && !order.has(b.id)) {
|
|
138
|
-
const ids = Object.freeze(b.children.map((c) => c.id));
|
|
139
|
-
order.set(b.id, ids);
|
|
140
|
-
r.order.set(b.id, ids);
|
|
141
|
-
}
|
|
142
|
-
b.children.forEach((c, i) => add(c, b.id, i));
|
|
143
|
-
};
|
|
144
|
-
for (const [id, at] of fresh) {
|
|
145
|
-
let under = false;
|
|
146
|
-
for (let c = ix.slots.get(id)?.parent ?? null; c !== null && !under; c = ix.slots.get(c)?.parent ?? null)
|
|
147
|
-
under = fresh.has(c);
|
|
148
|
-
if (under || covered.has(id))
|
|
149
|
-
continue;
|
|
150
|
-
const b = projectBlock(id);
|
|
151
|
-
r.added.set(id, b);
|
|
152
|
-
add(b, at.parent, at.index);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
/** The blocks whose shown kind followed their slot at the last report. */
|
|
156
|
-
let followingBefore = [];
|
|
157
|
-
/** The document data's key at the last report. */
|
|
158
|
-
let dataKey = '';
|
|
159
|
-
/** Build the commit's report and advance the published index to it. */
|
|
160
|
-
const report = () => {
|
|
161
|
-
const before = published;
|
|
162
|
-
// Fold every queued transaction first: a follow-up a cleanup
|
|
163
|
-
// started (a delete-mark repair) has already written, and the
|
|
164
|
-
// first content read below would fold it mid-report, after the
|
|
165
|
-
// child lists were read (found by the purge fuzz, rich lane, seeds 651 and 1271).
|
|
166
|
-
syncAll();
|
|
167
|
-
ensurePlacements();
|
|
168
|
-
const r = {
|
|
169
|
-
added: new Map(),
|
|
170
|
-
removed: new Set(),
|
|
171
|
-
moved: new Set(),
|
|
172
|
-
meta: new Map(),
|
|
173
|
-
content: new Map(),
|
|
174
|
-
order: new Map()
|
|
175
|
-
};
|
|
176
|
-
// The document's data is news when it differs from what was published.
|
|
177
|
-
if (dataChanged) {
|
|
178
|
-
dataChanged = false;
|
|
179
|
-
const data = docData();
|
|
180
|
-
const key = keyOf(data);
|
|
181
|
-
if (key !== dataKey)
|
|
182
|
-
[r.data, dataKey] = [data, key];
|
|
183
|
-
}
|
|
184
|
-
if (ix.kidsVersion === before.kids && candidates.size === 0)
|
|
185
|
-
return r.data ? r : null;
|
|
186
|
-
// Added subtrees carry their new descendants. A descendant that was
|
|
187
|
-
// visible before is reported like any visible block (moved, retyped,
|
|
188
|
-
// edited against its published baseline), so consumers keep it.
|
|
189
|
-
const covered = new Set();
|
|
190
|
-
/** Moved to another display parent: only those can show another kind (an index never does). */
|
|
191
|
-
const reparented = new Set();
|
|
192
|
-
const lists = ix.kidsVersion !== before.kids && !ix.allListsDirty;
|
|
193
|
-
const after = ix.kidsVersion === before.kids || lists ? before : reachable(before.nodes);
|
|
194
|
-
if (lists)
|
|
195
|
-
advance(before, r, covered, reparented);
|
|
196
|
-
else if (after !== before) {
|
|
197
|
-
for (const [parent, ids] of after.order) {
|
|
198
|
-
const prev = before.order.get(parent);
|
|
199
|
-
if (prev === undefined || !sameIds(prev, ids))
|
|
200
|
-
r.order.set(parent, ids);
|
|
201
|
-
}
|
|
202
|
-
for (const parent of before.order.keys()) {
|
|
203
|
-
if (!after.order.has(parent))
|
|
204
|
-
r.order.set(parent, EMPTY_IDS);
|
|
205
|
-
}
|
|
206
|
-
const register = (b) => {
|
|
207
|
-
if (!before.nodes.has(b.id))
|
|
208
|
-
covered.add(b.id);
|
|
209
|
-
b.children.forEach(register);
|
|
210
|
-
};
|
|
211
|
-
for (const [id, n] of after.nodes) {
|
|
212
|
-
if (!before.nodes.has(id)) {
|
|
213
|
-
if (covered.has(id))
|
|
214
|
-
continue;
|
|
215
|
-
const b = projectBlock(id);
|
|
216
|
-
r.added.set(id, b);
|
|
217
|
-
register(b);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
const o = before.nodes.get(id);
|
|
221
|
-
if (o.parent !== n.parent || o.index !== n.index)
|
|
222
|
-
r.moved.add(id);
|
|
223
|
-
if (o.parent !== n.parent)
|
|
224
|
-
reparented.add(id);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Removed subtree ROOTS: a removed id whose before-parent stays
|
|
228
|
-
// visible (or is the root). One under a removed parent leaves with
|
|
229
|
-
// it; one under a surviving child of a removed subtree does not.
|
|
230
|
-
for (const [id, o] of before.nodes) {
|
|
231
|
-
if (after.nodes.has(id))
|
|
232
|
-
continue;
|
|
233
|
-
if (o.parent === null || after.nodes.has(o.parent))
|
|
234
|
-
r.removed.add(id);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
const meta = (id, n) => {
|
|
238
|
-
const rec = blocks.get(id);
|
|
239
|
-
const type = typeOf(id);
|
|
240
|
-
r.meta.set(id, { type, data: dataOf(rec) });
|
|
241
|
-
n.type = type;
|
|
242
|
-
n.data = rec.data;
|
|
243
|
-
};
|
|
244
|
-
for (const id of candidates) {
|
|
245
|
-
const n = after.nodes.get(id);
|
|
246
|
-
if (n === undefined || covered.has(id))
|
|
247
|
-
continue;
|
|
248
|
-
const rec = blocks.get(id);
|
|
249
|
-
if (typeOf(id) !== n.type || keyOf(rec.data) !== keyOf(n.data))
|
|
250
|
-
meta(id, n);
|
|
251
|
-
const next = runs(id);
|
|
252
|
-
if (next !== n.runs) {
|
|
253
|
-
const key = keyOf(next);
|
|
254
|
-
if ((n.key ??= keyOf(n.runs)) !== key)
|
|
255
|
-
r.content.set(id, next);
|
|
256
|
-
n.runs = next;
|
|
257
|
-
n.key = key;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// A block that starts or stops displaying a kind other than its
|
|
261
|
-
// stored one moved, or its kind follows (or followed) its slot:
|
|
262
|
-
// its kind is news. Without a placement change, only a retype
|
|
263
|
-
// changes a shown kind — the retyped block's (a candidate) and the
|
|
264
|
-
// kinds derived from it: a promoted or stray line shows its display
|
|
265
|
-
// parent's default child.
|
|
266
|
-
if (after !== before || lists || ix.retyped) {
|
|
267
|
-
const now = [...following];
|
|
268
|
-
for (const id of [...reparented, ...now, ...followingBefore]) {
|
|
269
|
-
const n = after.nodes.get(id);
|
|
270
|
-
if (n !== undefined && !candidates.has(id) && !covered.has(id) && typeOf(id) !== n.type)
|
|
271
|
-
meta(id, n);
|
|
272
|
-
}
|
|
273
|
-
followingBefore = now;
|
|
274
|
-
}
|
|
275
|
-
// A plain block directly in a list shows as its item (`itemOf`):
|
|
276
|
-
// a block whose shown kind changed re-reads its children's (a worklist:
|
|
277
|
-
// the map visits the entries added meanwhile).
|
|
278
|
-
for (const id of r.meta.keys())
|
|
279
|
-
for (const { id: kid } of ix.kidsMap.get(id) ?? []) {
|
|
280
|
-
const n = after.nodes.get(kid);
|
|
281
|
-
if (n !== undefined && !covered.has(kid) && !r.meta.has(kid) && typeOf(kid) !== n.type)
|
|
282
|
-
meta(kid, n);
|
|
283
|
-
}
|
|
284
|
-
ix.retyped = false;
|
|
285
|
-
candidates.clear();
|
|
286
|
-
published = after;
|
|
287
|
-
after.kids = ix.kidsVersion;
|
|
288
|
-
dirtyLists.clear();
|
|
289
|
-
leftLists.clear();
|
|
290
|
-
ix.allListsDirty = false;
|
|
291
|
-
if (indexChecks.on)
|
|
292
|
-
checksOf(ix).checkPublished(published);
|
|
293
|
-
const empty = r.added.size + r.removed.size + r.moved.size + r.meta.size + r.content.size + r.order.size;
|
|
294
|
-
return empty === 0 && !r.data ? null : r;
|
|
295
|
-
};
|
|
296
|
-
/** Report the commit (or role change) to every subscriber, when it changed the visible document. */
|
|
297
|
-
const publish = (origin, local) => {
|
|
298
|
-
const r = report();
|
|
299
|
-
if (r !== null)
|
|
300
|
-
callEach('[edytor-doc] change', [...reportSubs], r, origin, local);
|
|
301
|
-
};
|
|
302
|
-
const onUpdate = (_u, origin, _d, tr) => {
|
|
303
|
-
if (tr.changed?.has(dataRoot))
|
|
304
|
-
dataChanged = true;
|
|
305
|
-
publish(origin, tr.local === true);
|
|
306
|
-
};
|
|
307
|
-
/** Subscribe to the change report; the first subscription takes the baseline. */
|
|
308
|
-
const onReport = (cb) => {
|
|
309
|
-
if (reportSubs.size === 0) {
|
|
310
|
-
syncAll();
|
|
311
|
-
published = reachable();
|
|
312
|
-
dataKey = keyOf(docData());
|
|
313
|
-
candidates.clear();
|
|
314
|
-
ix.reporting = true;
|
|
315
|
-
doc.on('update', onUpdate);
|
|
316
|
-
}
|
|
317
|
-
reportSubs.add(cb);
|
|
318
|
-
return () => {
|
|
319
|
-
if (!reportSubs.delete(cb) || reportSubs.size > 0)
|
|
320
|
-
return;
|
|
321
|
-
doc.off('update', onUpdate);
|
|
322
|
-
published = null;
|
|
323
|
-
ix.reporting = false;
|
|
324
|
-
candidates.clear();
|
|
325
|
-
};
|
|
326
|
-
};
|
|
327
|
-
const hasSubscribers = () => reportSubs.size > 0;
|
|
328
|
-
return {
|
|
329
|
-
itemsOf,
|
|
330
|
-
projectBlock,
|
|
331
|
-
reachable,
|
|
332
|
-
publish,
|
|
333
|
-
onUpdate,
|
|
334
|
-
onReport,
|
|
335
|
-
hasSubscribers
|
|
336
|
-
};
|
|
337
|
-
};
|