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,9 +1,10 @@
1
1
  /**
2
2
  * The maintained form of one backing text's boundary table — 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
3
+ * index's row: the text's live boundary and claim items in text order and
4
+ * the live units between them, held in a Fenwick tree so an edit that writes or
5
5
  * removes no boundary moves every later boundary in O(log B) instead of a
6
- * rescan.
6
+ * rescan. (A claim item is a bound like a boundary that never delimits: below,
7
+ * "boundary" names both.)
7
8
  *
8
9
  * `h[j]` is the units before boundary `j` since the one before it, plus
9
10
  * boundary `j` itself (`h[B]`: the units after the last boundary), so the
@@ -21,11 +22,11 @@
21
22
  * segment `k > 0` is the stream of the block its opening cut names.
22
23
  */
23
24
  import type { EngineNode } from '../engine-api.js';
24
- import { type BlockId, type Bound, type Stream, type TextRow } from './model.js';
25
+ import { type BlockId, type Bound, type Claim, type Stream, type TextRow } from './model.js';
25
26
  export type LiveRow = {
26
27
  home: BlockId;
27
28
  text: EngineNode;
28
- /** The live boundaries in text order (`at` is NOT maintained: read {@link boundAt}). */
29
+ /** The live boundaries and claim items in text order (`at` is NOT maintained: read {@link boundAt}). */
29
30
  bounds: readonly Bound[];
30
31
  /** Fenwick tree over `h` (1-based, size `bounds.length + 1`). */
31
32
  tree: Float64Array;
@@ -35,6 +36,8 @@ export type LiveRow = {
35
36
  keyIndex: Map<string, number>;
36
37
  /** Indexes into `bounds` of the delimiting boundaries, ascending ({@link placeRow}). */
37
38
  cuts: number[];
39
+ /** The block each cut opens the stream of (`null`: a removed block's, nobody's). */
40
+ heads: (BlockId | null)[];
38
41
  /** The block of segment 0 (`null`: the home is delimited elsewhere). */
39
42
  head: BlockId | null;
40
43
  };
@@ -82,11 +85,14 @@ export declare const segmentAt: (row: LiveRow, x: number) => number;
82
85
  export declare const segmentOfCut: (row: LiveRow, cut: string | null) => number;
83
86
  /** The block of segment `k` (`null`: nobody's). */
84
87
  export declare const headOf: (row: LiveRow, k: number) => BlockId | null;
88
+ /** The claims of segment `k`: its claim items, in text order. */
89
+ export declare const segmentClaims: (row: LiveRow, k: number) => Claim[];
85
90
  /** Segment `k` as a {@link Stream} of `block`. */
86
91
  export declare const streamOfSegment: (row: LiveRow, k: number, block: BlockId) => Stream;
87
92
  /**
88
93
  * Set the row's segments from the delimiting boundaries (`delim`): the
89
94
  * home's head (unless delimited elsewhere), then one per cut — the same
90
- * streams `placeText` returns for the row's scan.
95
+ * streams `placeText` returns for the row's scan (a cut of a block `nobody`
96
+ * names opens nobody's stretch).
91
97
  */
92
- export declare const placeRow: (row: LiveRow, delim: ReadonlyMap<BlockId, string>) => void;
98
+ export declare const placeRow: (row: LiveRow, delim: ReadonlyMap<BlockId, string>, nobody: (s: BlockId) => boolean) => void;
@@ -1,4 +1,4 @@
1
- import { isBoundary } from './model.js';
1
+ import { isRowMark } from './model.js';
2
2
  const add = (row, i, d) => {
3
3
  const t = row.tree;
4
4
  for (let x = i + 1; x < t.length; x += x & -x)
@@ -61,6 +61,7 @@ export const rowOf = (scan) => {
61
61
  key: scan.key,
62
62
  keyIndex,
63
63
  cuts: [],
64
+ heads: [],
64
65
  head: scan.home
65
66
  };
66
67
  };
@@ -76,7 +77,7 @@ const boundIn = (row, it, first) => {
76
77
  return -1;
77
78
  for (let n = 0; n < it.length; n++) {
78
79
  const k = first ? n : it.length - 1 - n;
79
- if (!isBoundary(arr[k]))
80
+ if (!isRowMark(arr[k]))
80
81
  continue;
81
82
  return row.keyIndex.get(`${it.id.client}:${it.id.clock + k}`) ?? -2;
82
83
  }
@@ -153,7 +154,18 @@ export const segmentOfCut = (row, cut) => {
153
154
  return row.cuts[k] === j ? k + 1 : -1;
154
155
  };
155
156
  /** 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
+ export const headOf = (row, k) => k === 0 ? row.head : row.heads[k - 1];
158
+ /** The claims of segment `k`: its claim items, in text order. */
159
+ export const segmentClaims = (row, k) => {
160
+ const out = [];
161
+ const hi = k < row.cuts.length ? row.cuts[k] : row.bounds.length;
162
+ for (let j = k === 0 ? 0 : row.cuts[k - 1] + 1; j < hi; j++) {
163
+ const b = row.bounds[j];
164
+ if (b.m !== undefined)
165
+ out.push({ m: b.m, stamp: b.stamp });
166
+ }
167
+ return out;
168
+ };
157
169
  /** Segment `k` as a {@link Stream} of `block`. */
158
170
  export const streamOfSegment = (row, k, block) => {
159
171
  const lo = k === 0 ? -1 : row.cuts[k - 1];
@@ -168,15 +180,20 @@ export const streamOfSegment = (row, k, block) => {
168
180
  /**
169
181
  * Set the row's segments from the delimiting boundaries (`delim`): the
170
182
  * home's head (unless delimited elsewhere), then one per cut — the same
171
- * streams `placeText` returns for the row's scan.
183
+ * streams `placeText` returns for the row's scan (a cut of a block `nobody`
184
+ * names opens nobody's stretch).
172
185
  */
173
- export const placeRow = (row, delim) => {
186
+ export const placeRow = (row, delim, nobody) => {
174
187
  const cuts = [];
188
+ const heads = [];
175
189
  for (let j = 0; j < row.bounds.length; j++) {
176
190
  const b = row.bounds[j];
177
- if (delim.get(b.s) === b.key)
178
- cuts.push(j);
191
+ if (b.m !== undefined || delim.get(b.s) !== b.key)
192
+ continue;
193
+ cuts.push(j);
194
+ heads.push(nobody(b.s) ? null : b.s);
179
195
  }
180
196
  row.cuts = cuts;
197
+ row.heads = heads;
181
198
  row.head = delim.has(row.home) ? null : row.home;
182
199
  };
@@ -18,9 +18,9 @@
18
18
  * The fold (see `docs/crdt-v14-richtext-adr.md` for the run contract):
19
19
  *
20
20
  * - One classifier maps a changed `(type, parentSub)` pair to the block and
21
- * the facet it entered through (`content`, `claims`, `at`, `n`, a delete
22
- * mark, `type`/`data`, or the registry entry itself); one routine folds a
23
- * set of such pairs into the indexes.
21
+ * the facet it entered through (`content`, `at`, `n`, a delete mark,
22
+ * `type`/`data`, or the registry entry itself); one routine folds a set of
23
+ * such pairs into the indexes.
24
24
  * - A transaction is folded ONCE at commit, from `transaction.changed`.
25
25
  * - A read first folds the PENDING part of every transaction whose writes
26
26
  * are in the document and whose commit fold has not run: the open one, and
@@ -29,12 +29,12 @@
29
29
  * fold (the watermark is those sets' lengths). The commit fold takes only
30
30
  * what no read folded.
31
31
  * - Each backing text has a maintained row (`text/rows.ts`): 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
32
+ * boundaries and claim items and the live units between them. An edit
33
+ * that writes or removes neither moves the row by its units in the gap it
34
+ * lies in (found by walking the item list to the nearest boundary), never a
35
+ * rescan; a boundary or claim item written or removed rescans that text,
36
+ * re-decides the delimiters of the blocks its boundaries name, re-places
37
+ * only the rows those cut, and re-reads the claims of their blocks. Every cached block records which texts its display
38
38
  * walked and which blocks it walked or read a claim on (a skipped claim
39
39
  * included); an edit invalidates the display owner of the stream it lies
40
40
  * in, a placement the blocks whose stream's delimiting boundaries changed
@@ -55,8 +55,8 @@
55
55
  *
56
56
  * The index is built from parts under `index/`, each over the shared state
57
57
  * (`index/state.ts`) and the parts before it: the claim graph
58
- * (`claims.ts`), the stream table (`streams.ts`), anchored merge claims
59
- * (`anchored.ts`), the layout rules (`layout.ts`), placements and the
58
+ * (`claims.ts`), the stream table and each block's claims (`streams.ts`),
59
+ * the layout rules (`layout.ts`), placements and the
60
60
  * children index (`placement.ts`), block records (`records.ts`), the run
61
61
  * cache (`cache.ts`), the fold (`fold.ts`), the change report
62
62
  * (`report.ts`); the self-checks a test lane installs are `indexChecks`. This module declares the
@@ -64,7 +64,7 @@
64
64
  */
65
65
  import type { EngineApi, EngineDoc } from '../engine-api.js';
66
66
  import type { BlockId, ContentItem, ModelView, ProjectedBlock } from '../placement/model.js';
67
- export { CONTENT_ATTR, ENTRY_FACET, indexChecks } from './index/shared.js';
67
+ export { CONTENT_ATTR, ENTRY_FACET, SEQ_FACET, indexChecks } from './index/shared.js';
68
68
  /**
69
69
  * One visible run of a block — the maintained form of `ContentItem`.
70
70
  * `marks`/`data` are interned: equal payloads share one frozen instance,
@@ -139,6 +139,8 @@ export type RunViewDebug = {
139
139
  * last `reset()`: each struct once, whatever the number of reads.
140
140
  */
141
141
  readonly foldedStructs: number;
142
+ /** Blocks whose placement the index decided since last `reset()` (a global pass decides every one). */
143
+ readonly placements: number;
142
144
  reset: () => void;
143
145
  };
144
146
  /**
@@ -201,9 +203,12 @@ export type RunView = {
201
203
  * Track the folds from now on (the write funnel's frame): `end()` folds
202
204
  * what is pending and returns the facets every fold since `track()` saw
203
205
  * per block, and whether the transaction wrote anything meanwhile.
206
+ * `quiet(write)` runs a write whose facets are not the frame's (it counts
207
+ * as written).
204
208
  */
205
209
  track: () => {
206
210
  end: () => Folded;
211
+ quiet: (write: () => void) => void;
207
212
  };
208
213
  /**
209
214
  * Subscribe to the change report — one per commit that changed the
@@ -3,14 +3,13 @@ import { cloneJsonSafe } from '../../utils/json.js';
3
3
  import { indexState } from './index/state.js';
4
4
  import { indexClaims } from './index/claims.js';
5
5
  import { indexStreams } from './index/streams.js';
6
- import { indexAnchored } from './index/anchored.js';
7
6
  import { indexLayout } from './index/layout.js';
8
7
  import { indexPlacement } from './index/placement.js';
9
8
  import { indexRecords } from './index/records.js';
10
9
  import { indexCache } from './index/cache.js';
11
10
  import { indexFold } from './index/fold.js';
12
11
  import { indexReporter } from './index/report.js';
13
- export { CONTENT_ATTR, ENTRY_FACET, indexChecks } from './index/shared.js';
12
+ export { CONTENT_ATTR, ENTRY_FACET, SEQ_FACET, indexChecks } from './index/shared.js';
14
13
  /** One index per engine doc, shared by every binding (the doc's lifetime). */
15
14
  const indexes = new WeakMap();
16
15
  /** The doc's index — built on first use, torn down when the doc is destroyed. */
@@ -27,14 +26,13 @@ const buildView = (doc) => {
27
26
  const ix0 = indexState(doc);
28
27
  const ix1 = Object.assign(ix0, indexClaims(ix0));
29
28
  const ix2 = Object.assign(ix1, indexStreams(ix1));
30
- const ix3 = Object.assign(ix2, indexAnchored(ix2));
31
- const ix4 = Object.assign(ix3, indexLayout(ix3));
29
+ const ix4 = Object.assign(ix2, indexLayout(ix2));
32
30
  const ix5 = Object.assign(ix4, indexPlacement(ix4));
33
31
  const ix6 = Object.assign(ix5, indexRecords(ix5));
34
32
  const ix7 = Object.assign(ix6, indexCache(ix6));
35
33
  const ix8 = Object.assign(ix7, indexFold(ix7));
36
34
  const ix = Object.assign(ix8, indexReporter(ix8));
37
- const { registry, blocks, rows, homeOfText, shells, placementsMap, frames, displays, ownShim, ensurePlacements, typeOf, order, rebuildTable, scanRow, applyRetargets, updateBlockRec, syncIncarnations, intern, debug, runs, settle, syncPending, syncAll, openTx, onCommit, committed, itemsOf, projectBlock, publish, onUpdate, onReport, hasSubscribers } = ix;
35
+ const { registry, blocks, rows, homeOfText, shells, placementsMap, frames, displays, ownShim, ensurePlacements, typeOf, order, rebuildTable, scanRow, applyReclaims, updateBlockRec, syncIncarnations, intern, debug, runs, settle, syncPending, syncAll, openTx, onCommit, committed, itemsOf, projectBlock, publish, onUpdate, onReport, hasSubscribers } = ix;
38
36
  const ctx = {
39
37
  blocks,
40
38
  own: ownShim,
@@ -53,7 +51,11 @@ const buildView = (doc) => {
53
51
  // The publication boundary shares THIS interner, so a payload
54
52
  // emitted by `project()`/`contentItems()` is `===` the runs' one.
55
53
  intern,
56
- displays
54
+ displays,
55
+ accepted: (id) => {
56
+ ensurePlacements();
57
+ return ix.rejected.has(id) ? null : (ix.edges.get(id) ?? null);
58
+ }
57
59
  };
58
60
  // ── initial scan: index every existing block ────────────────────
59
61
  registry.forEachAttr((v, id) => {
@@ -69,7 +71,7 @@ const buildView = (doc) => {
69
71
  }
70
72
  });
71
73
  rebuildTable(new Set());
72
- applyRetargets(null);
74
+ applyReclaims(null);
73
75
  settle(shells, null);
74
76
  // Every commit folds once, before the document's `afterTransaction` listeners.
75
77
  doc.on('commit', onCommit);
@@ -141,6 +143,21 @@ const buildView = (doc) => {
141
143
  syncAll();
142
144
  frames.delete(f);
143
145
  return f;
146
+ },
147
+ quiet: (write) => {
148
+ syncAll();
149
+ const g = { touched: new Map(), wrote: false };
150
+ frames.delete(f);
151
+ frames.add(g);
152
+ try {
153
+ write();
154
+ syncAll();
155
+ }
156
+ finally {
157
+ frames.delete(g);
158
+ frames.add(f);
159
+ f.wrote ||= g.wrote;
160
+ }
144
161
  }
145
162
  };
146
163
  },
@@ -1,27 +1,40 @@
1
1
  /**
2
- * The stream table, pure: a backing text's boundaries, each block's
3
- * delimiting boundary, a text's streams, and a block's display as pieces
4
- * of streams (its stream, then each effective claim's display).
2
+ * The stream table, pure: a backing text's boundaries and claim items, each
3
+ * block's delimiting boundary, a text's streams, and a block's display as
4
+ * pieces of streams (its stream, then each claim's display).
5
5
  */
6
6
  import type { EngineNode } from '../engine-api.js';
7
- import type { BlockId, Ownership, Seg, Stream, TextBlockRec, TextRow } from './model.js';
8
- /** Scan `text` (live space) for its boundary items. */
7
+ import type { BlockId, Claim, Ownership, Seg, Stream, TextBlockRec, TextRow } from './model.js';
8
+ /** Scan `text` (live space) for its boundary and claim items. */
9
9
  export declare const scanText: (home: BlockId, text: EngineNode) => TextRow;
10
10
  export declare const byId: (a: string, b: string) => number;
11
- /** Each block's delimiting boundary: the lowest-id live boundary whose nonce matches its record. */
12
- export declare const delimiters: (blocks: ReadonlyMap<BlockId, Pick<TextBlockRec, "n">>, rows: Iterable<TextRow>) => Map<BlockId, string>;
11
+ /**
12
+ * Whether `s` names a removed block of `registry` (`room.purge.what`): its
13
+ * entry was deleted — only the purge deletes one — and holds no value. The
14
+ * purge removes a block that streams in another block's text alone, and its
15
+ * boundary keeps delimiting the stream it emptied, which nobody shows.
16
+ */
17
+ export declare const removedKeys: (registry: EngineNode) => (s: BlockId) => boolean;
18
+ /**
19
+ * Each block's delimiting boundary: the lowest-id live boundary whose nonce
20
+ * matches its record; for a removed block (`removed`, no record), the
21
+ * lowest-id live boundary naming it.
22
+ */
23
+ export declare const delimiters: (blocks: ReadonlyMap<BlockId, Pick<TextBlockRec, "n">>, rows: Iterable<TextRow>, removed?: (s: BlockId) => boolean) => Map<BlockId, string>;
13
24
  /**
14
25
  * The streams of one text, in text order: the home's head (unless delimited
15
26
  * elsewhere), then one per delimiting boundary — one sweep over the
16
- * boundaries (each non-delimiting one is an inert boundary of the stream it
17
- * sits in).
27
+ * boundaries (each non-delimiting one, and each claim item, is an inert unit
28
+ * of the stream it sits in). A boundary of a block `nobody` names (a
29
+ * removed one) opens a stretch that is no block's stream.
18
30
  */
19
- export declare const placeText: (row: TextRow, delim: ReadonlyMap<BlockId, string>) => Stream[];
20
- /** A stream's content pieces: the range minus its inert boundaries (at least one piece). */
31
+ export declare const placeText: (row: TextRow, delim: ReadonlyMap<BlockId, string>, nobody?: (s: BlockId) => boolean) => Stream[];
32
+ /** The claims of stream `s` of `row`: its claim items, in text order. */
33
+ export declare const claimsIn: (row: TextRow, s: Stream) => Claim[];
34
+ /** A stream's content pieces: the range minus its inert units (at least one piece). */
21
35
  export declare const pieces: (s: Stream) => [number, number][];
22
36
  /**
23
- * `display(b)`: `b`'s stream, then each effective claim's display, in claim
24
- * order. `track(x, home)` reports every walked block and the home of its
37
+ * `display(b)`: `b`'s stream, then each claim's display, in claim order. `track(x, home)` reports every walked block and the home of its
25
38
  * stream (the index's dependency capture). `null` when `b` is hidden, unless
26
39
  * `hidden` asks for the pieces a hidden block would show (an anchor minted in
27
40
  * a merged-away or deleted block binds its items all the same).
@@ -1,5 +1,7 @@
1
- import { isBoundary, nodeStart } from './items.js';
2
- /** Scan `text` (live space) for its boundary items. */
1
+ import { isIncarnationId } from '../incarnations.js';
2
+ import { attrItems } from '../structs.js';
3
+ import { isBoundary, isClaimItem, nodeStart } from './items.js';
4
+ /** Scan `text` (live space) for its boundary and claim items. */
3
5
  export const scanText = (home, text) => {
4
6
  const bounds = [];
5
7
  let at = 0;
@@ -10,8 +12,11 @@ export const scanText = (home, text) => {
10
12
  if (arr !== undefined) {
11
13
  for (let j = 0; j < it.length; j++) {
12
14
  const v = arr[j];
15
+ const [c, k] = [it.id.client, it.id.clock + j];
13
16
  if (isBoundary(v))
14
- bounds.push({ s: v.s, n: v.n, at: at + j, key: `${it.id.client}:${it.id.clock + j}` });
17
+ bounds.push({ s: v.s, n: v.n, at: at + j, key: `${c}:${k}` });
18
+ else if (isClaimItem(v))
19
+ bounds.push({ m: v.m, stamp: { c, k }, at: at + j, key: `${c}:${k}` });
15
20
  }
16
21
  }
17
22
  at += it.length;
@@ -23,12 +28,26 @@ export const byId = (a, b) => {
23
28
  const [bc, bk] = b.split(':').map(Number);
24
29
  return ac - bc || ak - bk;
25
30
  };
26
- /** Each block's delimiting boundary: the lowest-id live boundary whose nonce matches its record. */
27
- export const delimiters = (blocks, rows) => {
31
+ /**
32
+ * Whether `s` names a removed block of `registry` (`room.purge.what`): its
33
+ * entry was deleted — only the purge deletes one — and holds no value. The
34
+ * purge removes a block that streams in another block's text alone, and its
35
+ * boundary keeps delimiting the stream it emptied, which nobody shows.
36
+ */
37
+ export const removedKeys = (registry) => (s) => attrItems(registry).get(s)?.deleted === true && !isIncarnationId(s);
38
+ /**
39
+ * Each block's delimiting boundary: the lowest-id live boundary whose nonce
40
+ * matches its record; for a removed block (`removed`, no record), the
41
+ * lowest-id live boundary naming it.
42
+ */
43
+ export const delimiters = (blocks, rows, removed = () => false) => {
28
44
  const out = new Map();
29
45
  for (const row of rows) {
30
46
  for (const b of row.bounds) {
31
- if (blocks.get(b.s)?.n !== b.n)
47
+ if (b.m !== undefined)
48
+ continue;
49
+ const rec = blocks.get(b.s);
50
+ if (rec === undefined ? !removed(b.s) : rec.n !== b.n)
32
51
  continue;
33
52
  const cur = out.get(b.s);
34
53
  if (cur === undefined || byId(b.key, cur) < 0)
@@ -40,10 +59,11 @@ export const delimiters = (blocks, rows) => {
40
59
  /**
41
60
  * The streams of one text, in text order: the home's head (unless delimited
42
61
  * elsewhere), then one per delimiting boundary — one sweep over the
43
- * boundaries (each non-delimiting one is an inert boundary of the stream it
44
- * sits in).
62
+ * boundaries (each non-delimiting one, and each claim item, is an inert unit
63
+ * of the stream it sits in). A boundary of a block `nobody` names (a
64
+ * removed one) opens a stretch that is no block's stream.
45
65
  */
46
- export const placeText = (row, delim) => {
66
+ export const placeText = (row, delim, nobody = () => false) => {
47
67
  const out = [];
48
68
  let block = delim.has(row.home) ? null : row.home;
49
69
  let start = 0;
@@ -53,17 +73,25 @@ export const placeText = (row, delim) => {
53
73
  out.push({ block, home: row.home, text: row.text, start, end, inert });
54
74
  };
55
75
  for (const b of row.bounds) {
56
- if (delim.get(b.s) !== b.key) {
76
+ if (b.m !== undefined || delim.get(b.s) !== b.key) {
57
77
  inert.push(b.at);
58
78
  continue;
59
79
  }
60
80
  close(b.at);
61
- [block, start, inert] = [b.s, b.at + 1, []];
81
+ [block, start, inert] = [nobody(b.s) ? null : b.s, b.at + 1, []];
62
82
  }
63
83
  close(row.len);
64
84
  return out;
65
85
  };
66
- /** A stream's content pieces: the range minus its inert boundaries (at least one piece). */
86
+ /** The claims of stream `s` of `row`: its claim items, in text order. */
87
+ export const claimsIn = (row, s) => {
88
+ const out = [];
89
+ for (const b of row.bounds)
90
+ if (b.m !== undefined && b.at >= s.start && b.at < s.end)
91
+ out.push({ m: b.m, stamp: b.stamp });
92
+ return out;
93
+ };
94
+ /** A stream's content pieces: the range minus its inert units (at least one piece). */
67
95
  export const pieces = (s) => {
68
96
  const out = [];
69
97
  let a = s.start;
@@ -75,8 +103,7 @@ export const pieces = (s) => {
75
103
  return out;
76
104
  };
77
105
  /**
78
- * `display(b)`: `b`'s stream, then each effective claim's display, in claim
79
- * order. `track(x, home)` reports every walked block and the home of its
106
+ * `display(b)`: `b`'s stream, then each claim's display, in claim order. `track(x, home)` reports every walked block and the home of its
80
107
  * stream (the index's dependency capture). `null` when `b` is hidden, unless
81
108
  * `hidden` asks for the pieces a hidden block would show (an anchor minted in
82
109
  * a merged-away or deleted block binds its items all the same).