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
@@ -8,7 +8,7 @@ import type { AttributionActor } from '../attribution/index.js';
8
8
  import type { BlockId, BlockSpec, InlineSpec, ProjectedBlock, SplitTail } from '../placement/model.js';
9
9
  import type { Anchor } from '../text/model.js';
10
10
  import type { ContentRun } from '../text/runs.js';
11
- import type { DataPatch, LeafWrite } from '../data.js';
11
+ import type { DataPatch, DataWrite } from '../data.js';
12
12
  import type { DocPosition } from '../rangeDelete.js';
13
13
  /** Structural role of a block type (plugin-side policy, resolved per op). */
14
14
  export type BlockRole = {
@@ -180,22 +180,34 @@ export type TextRange = {
180
180
  /**
181
181
  * One planned write, named by the document operation that
182
182
  * performs it — the name a hook matches. Steps carry everything
183
- * their write needs (ranks, marks, offsets), decided at prepare time.
183
+ * their write needs (places, marks, offsets), decided at prepare time; a
184
+ * place is a display index among a parent's children, read against the
185
+ * document as the step is written (the steps before it applied).
184
186
  */
185
187
  export type PlanStep =
186
- /** `index`: the destination slot at prepare time, as hooks see it. */
188
+ /** `index`: the destination slot, as hooks see it. */
189
+ /** `split`: the split-born block of this plan whose boundary these blocks' slots take as their sort key (a paste's lines at its split). */
187
190
  {
188
191
  op: 'insertBlocks';
189
192
  parent: BlockId | null;
190
193
  index: number;
191
194
  specs: BlockSpec[];
192
- ranks: string[];
193
- } | {
195
+ split?: BlockId;
196
+ }
197
+ /**
198
+ * `side`: the move record's class among the items of its origin —
199
+ * absent or `0`, the blocks stand after what precedes them; `1`, before
200
+ * what follows them (blocks leaving a block for the gap before it).
201
+ */
202
+ /** `side`: the record's class at its origin (`1`: before); `at`, `part`: the block it stands at and its order there (`moveTo`). */
203
+ | {
194
204
  op: 'moveBlocks';
195
205
  ids: BlockId[];
196
206
  parent: BlockId | null;
197
207
  index: number;
198
- ranks: string[];
208
+ side?: 0 | 1;
209
+ part?: number;
210
+ at?: BlockId;
199
211
  }
200
212
  /** `marks`: the blocks that get this writer's mark; `removes`: those that leave the document. */
201
213
  | {
@@ -212,7 +224,7 @@ export type PlanStep =
212
224
  newId: BlockId;
213
225
  tail: SplitTail;
214
226
  parent: BlockId | null;
215
- rank: string;
227
+ index: number;
216
228
  }
217
229
  /** `at`/`length`: where `from`'s display lands in `into`'s. */
218
230
  | {
@@ -229,7 +241,7 @@ export type PlanStep =
229
241
  /**
230
242
  * A data patch (`crdt/data.ts`): of block `id`, of its atom `inlineId`
231
243
  * (at display `offset`), or of the document (no `id`). `ops` as asked;
232
- * `leaves` the attr writes they plan.
244
+ * `writes` the attr and slot writes they plan.
233
245
  */
234
246
  | {
235
247
  op: 'patchData';
@@ -237,7 +249,7 @@ export type PlanStep =
237
249
  inlineId?: string;
238
250
  offset?: number;
239
251
  ops: DataPatch[];
240
- leaves: LeafWrite[];
252
+ writes: DataWrite[];
241
253
  } | {
242
254
  op: 'insertText';
243
255
  id: BlockId;
@@ -50,10 +50,9 @@
50
50
  * bootstrap-shaped is written or broadcast, so writes made raw against a
51
51
  * pending doc produce unversioned state that `sync()` then refuses (see
52
52
  * below). `local`/`hydrated` — the full surface is legal; the only
53
- * difference is WHO decided the content. (One exception to "nothing
54
- * broadcast": the actor dictionary attach publishes this replica's
55
- * `c/<clientID>`/`u/<actorId>` records on ANY constructed document —
56
- * identity metadata, not content, and outside every admission gate.)
53
+ * difference is WHO decided the content. (The actor dictionary's
54
+ * `c/<clientID>`/`u/<actorId>` records go with the replica's first write,
55
+ * whatever it is: a document that is only read publishes nothing.)
57
56
  *
58
57
  * ── Admission boundary ────────────────────────────────────────────
59
58
  *
@@ -324,7 +323,7 @@ export declare class EdytorDocument {
324
323
  * replicated actor dictionary (`u/` profiles, `c/` replica bindings).
325
324
  * Ordinary edits perform no attribution
326
325
  * writes — authorship lands inside the owning op's transaction as the
327
- * block-level `b/<id>` record/`l` stamp. See
326
+ * block-level record (`blockattr` `<id>`)/`l` stamp. See
328
327
  * `src/lib/crdt/attribution/attribution.ts` for the full contract.
329
328
  * Access after teardown throws {@link DocumentDestroyedError} — same
330
329
  * lifecycle contract as {@link history}.
@@ -488,6 +487,18 @@ export declare class EdytorDocument {
488
487
  * on pending).
489
488
  */
490
489
  clearHistory: () => void;
490
+ /**
491
+ * End this session's history: the page it was edited in left for good.
492
+ * Every step is dropped, and what this replica deleted is released: the
493
+ * text its deletes hid is deleted for good now, and the room's purge
494
+ * removes the blocks it deleted at its next run, without waiting for the
495
+ * purge horizon (one small write, sent if the connection still can). A
496
+ * view calls it at a `pagehide` the back/forward cache keeps nothing of;
497
+ * a page kept there, or one that lost its connection, keeps its history.
498
+ * Undo starts empty afterwards. No-op while history has not attached, and
499
+ * on a destroyed document.
500
+ */
501
+ endHistory: () => void;
491
502
  /**
492
503
  * Local document transaction — headless edits, one undo step per call
493
504
  * (`origin`: another origin, which the document's history tracks only
@@ -92,7 +92,7 @@ export class EdytorDocument {
92
92
  * replicated actor dictionary (`u/` profiles, `c/` replica bindings).
93
93
  * Ordinary edits perform no attribution
94
94
  * writes — authorship lands inside the owning op's transaction as the
95
- * block-level `b/<id>` record/`l` stamp. See
95
+ * block-level record (`blockattr` `<id>`)/`l` stamp. See
96
96
  * `src/lib/crdt/attribution/attribution.ts` for the full contract.
97
97
  * Access after teardown throws {@link DocumentDestroyedError} — same
98
98
  * lifecycle contract as {@link history}.
@@ -213,9 +213,10 @@ export class EdytorDocument {
213
213
  this.awareness.setLocalStateField('user', user);
214
214
  // Attribution service — attaches after the facade (which gates
215
215
  // foreign docs) and after the actor is fixed. Publishes this
216
- // replica's actor dictionary entries (`c/`+`u/`) once; ordinary
217
- // edits perform no attribution work and per-block records are
218
- // written inside the owning op's transaction by the facade.
216
+ // replica's actor dictionary entries (`c/`+`u/`) inside its first
217
+ // write's transaction; ordinary edits perform no attribution work
218
+ // and per-block records are written inside the owning op's
219
+ // transaction by the facade.
219
220
  this.#attributionCtl = init.attribution.attach(asEngineDoc(this.doc), {
220
221
  actor: this.actor
221
222
  });
@@ -501,6 +502,22 @@ export class EdytorDocument {
501
502
  alive(this, 'clearHistory');
502
503
  this.#_history?.clear();
503
504
  };
505
+ /**
506
+ * End this session's history: the page it was edited in left for good.
507
+ * Every step is dropped, and what this replica deleted is released: the
508
+ * text its deletes hid is deleted for good now, and the room's purge
509
+ * removes the blocks it deleted at its next run, without waiting for the
510
+ * purge horizon (one small write, sent if the connection still can). A
511
+ * view calls it at a `pagehide` the back/forward cache keeps nothing of;
512
+ * a page kept there, or one that lost its connection, keeps its history.
513
+ * Undo starts empty afterwards. No-op while history has not attached, and
514
+ * on a destroyed document.
515
+ */
516
+ endHistory = () => {
517
+ if (this._destroyed)
518
+ return;
519
+ this.#_history?.end();
520
+ };
504
521
  /**
505
522
  * Local document transaction — headless edits, one undo step per call
506
523
  * (`origin`: another origin, which the document's history tracks only
@@ -38,6 +38,16 @@ type Layers = {
38
38
  * The per-doc facade factory over the engine `Y` (the editor's documents
39
39
  * use it directly; {@link bindDocModel} is the public binding around it).
40
40
  */
41
+ /**
42
+ * Give `doc` edytor's rule — a registry value a concurrent creation replaced
43
+ * keeps its subtree (fork patch YP14, `keepingReplaced`) — or refuse it
44
+ * loudly when it has integrated updates without it: the engine decides what
45
+ * a replaced value keeps when it integrates, so a document that took updates
46
+ * under the engine's default could lose a subtree its peers keep, forever.
47
+ * Create it with `createDoc()` (or `createDocument`/`loadDocument`), never
48
+ * `new Y.Doc()`.
49
+ */
50
+ export declare const adoptRules: <D extends object>(doc: D) => D;
41
51
  export declare const docFacade: <L extends Layers>(Y: EngineApi, layers: L) => (doc: EngineDoc, config?: EdytorDocConfig) => {
42
52
  /** Every op prepared: pure, a plan of named steps + its effect, or `refused`. */
43
53
  prepare: {
@@ -104,7 +114,7 @@ export declare const docFacade: <L extends Layers>(Y: EngineApi, layers: L) => (
104
114
  keep?: boolean;
105
115
  after?: readonly import("./index.js").BlockSpec[];
106
116
  } | undefined) => OpResult;
107
- placeBeside: (ids: readonly string[], target: string, side: "right" | "left", kind?: string | undefined) => OpResult;
117
+ placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => OpResult;
108
118
  wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => OpResult;
109
119
  splitBlock: (id: string, offset: number, newId: string, tail?: SplitTail | undefined) => OpResult;
110
120
  mergeBlocks: (fromId: string, intoId: string) => OpResult;
@@ -265,7 +275,7 @@ export declare const docFacade: <L extends Layers>(Y: EngineApi, layers: L) => (
265
275
  export declare const bindDocModel: (Y: EngineApi) => {
266
276
  /** The schema manifest constants table. */
267
277
  SCHEMA: {
268
- readonly version: 6;
278
+ readonly version: 10;
269
279
  readonly name: "edytor-doc";
270
280
  readonly roots: {
271
281
  readonly registry: "blocks";
@@ -276,7 +286,7 @@ export declare const bindDocModel: (Y: EngineApi) => {
276
286
  readonly schema: "schema";
277
287
  };
278
288
  };
279
- SCHEMA_VERSION: 6;
289
+ SCHEMA_VERSION: 10;
280
290
  SCHEMA_NAME: "edytor-doc";
281
291
  META_KEY: "meta";
282
292
  /** Doc-level API (no facade needed). */
@@ -349,29 +359,30 @@ export declare const bindDocModel: (Y: EngineApi) => {
349
359
  blockNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
350
360
  liveNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
351
361
  isLive: (doc: EngineDoc, id: BlockId) => boolean;
352
- candidatesOf: (node: EngineNode) => import("./placement/model.js").PlacementCand[];
353
362
  view: (doc: EngineDoc) => ModelView;
354
- resolvePlacements: (blocks: Map<BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: BlockId) => import("./text/model.js").Owner) => Map<BlockId, import("./placement/model.js").ResolvedPlacement>;
355
363
  childrenIndex: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<BlockId | null, import("./placement/model.js").ChildSlot[]>;
356
364
  positionInView: (v: ModelView, id: BlockId) => import("./index.js").Destination | null;
357
365
  buildInline: (atom: InlineSpec) => EngineNode;
358
366
  isSelfOrDescendant: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: BlockId, maybeAncestor: BlockId) => boolean;
359
- ranksAt: (siblings: readonly {
360
- rank: string;
361
- }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
362
- writePlacement: (doc: EngineDoc, node: EngineNode, p: BlockId | null, r: string) => void;
367
+ placeBlocks: (doc: EngineDoc, v: ModelView, parent: BlockId | null, left: BlockId | null, right: BlockId | null, slots: readonly import("./placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("./placement/slots.js").Unit | null) => void;
368
+ moveBlocksTo: (doc: EngineDoc, v: ModelView, ids: readonly BlockId[], parent: BlockId | null, index: number, side?: number, part?: number, at?: BlockId | null) => void;
369
+ insertSpecsAt: (doc: EngineDoc, v: ModelView, parent: BlockId | null, index: number, specs: readonly import("./index.js").BlockSpec[], split?: import("./placement/slots.js").Unit | null) => void;
363
370
  withdrawOnUndo: (doc: EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
364
371
  inserts: {
365
372
  has(client: number, clock: number): boolean;
366
373
  };
367
374
  }) => boolean;
368
- materializeSpec: (doc: EngineDoc, sp: import("./index.js").BlockSpec, parent: BlockId | null, rank: string) => void;
369
375
  collides: (doc: EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
370
- writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, place: import("./placement/model.js").PlacementValue) => void;
376
+ writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, parent: BlockId | null, index: number) => import("./placement/slots.js").Unit;
371
377
  ownText: (doc: EngineDoc, id: BlockId) => {
372
378
  from: unknown;
373
379
  to: number;
374
380
  };
381
+ recordsOf: (doc: EngineDoc, node: EngineNode, v?: ModelView) => {
382
+ item: import("./placement/slots.js").SeqItem;
383
+ stamp: import("./placement/slots.js").Stamp;
384
+ wins: boolean;
385
+ }[];
375
386
  insertBlocks: (doc: EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
376
387
  insertBlock: (doc: EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
377
388
  restoreBlocks: (doc: EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
@@ -403,8 +414,8 @@ export declare const bindDocModel: (Y: EngineApi) => {
403
414
  at: number;
404
415
  node: EngineNode;
405
416
  } | null;
406
- splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => import("./text/model.js").MergeClaim[];
407
- claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
417
+ splitAt: (doc: EngineDoc, blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => void;
418
+ claimInto: (own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
408
419
  ownText: (doc: EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
409
420
  from: unknown;
410
421
  to: number;
@@ -424,7 +435,7 @@ export declare const bindDocModel: (Y: EngineApi) => {
424
435
  keep?: boolean;
425
436
  after?: readonly import("./index.js").BlockSpec[];
426
437
  } | undefined) => OpResult;
427
- placeBeside: (ids: readonly string[], target: string, side: "right" | "left", kind?: string | undefined) => OpResult;
438
+ placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => OpResult;
428
439
  wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => OpResult;
429
440
  splitBlock: (id: string, offset: number, newId: string, tail?: SplitTail | undefined) => OpResult;
430
441
  mergeBlocks: (fromId: string, intoId: string) => OpResult;
@@ -584,14 +595,6 @@ export declare const bindDocModel: (Y: EngineApi) => {
584
595
  * not have integrated anything yet.
585
596
  */
586
597
  newDoc: (opts?: ConstructorParameters<EngineApi["Doc"]>[0]) => YDoc;
587
- /**
588
- * Keep, on `doc`, the text a replica may have to copy again: its
589
- * `gcFilter` then spares a deleted copy's content, as every facade's
590
- * history does. A doc that only relays updates (the room) calls it
591
- * before applying anything, so what it collects and encodes matches
592
- * what an editing replica keeps.
593
- */
594
- keepCopies: (doc: EngineDoc) => void;
595
598
  /** The bound engine layers (same instances the facades use). */
596
599
  model: {
597
600
  REGISTRY_KEY: string;
@@ -599,29 +602,30 @@ export declare const bindDocModel: (Y: EngineApi) => {
599
602
  blockNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
600
603
  liveNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
601
604
  isLive: (doc: EngineDoc, id: BlockId) => boolean;
602
- candidatesOf: (node: EngineNode) => import("./placement/model.js").PlacementCand[];
603
605
  view: (doc: EngineDoc) => ModelView;
604
- resolvePlacements: (blocks: Map<BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: BlockId) => import("./text/model.js").Owner) => Map<BlockId, import("./placement/model.js").ResolvedPlacement>;
605
606
  childrenIndex: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<BlockId | null, import("./placement/model.js").ChildSlot[]>;
606
607
  positionInView: (v: ModelView, id: BlockId) => import("./index.js").Destination | null;
607
608
  buildInline: (atom: InlineSpec) => EngineNode;
608
609
  isSelfOrDescendant: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: BlockId, maybeAncestor: BlockId) => boolean;
609
- ranksAt: (siblings: readonly {
610
- rank: string;
611
- }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
612
- writePlacement: (doc: EngineDoc, node: EngineNode, p: BlockId | null, r: string) => void;
610
+ placeBlocks: (doc: EngineDoc, v: ModelView, parent: BlockId | null, left: BlockId | null, right: BlockId | null, slots: readonly import("./placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("./placement/slots.js").Unit | null) => void;
611
+ moveBlocksTo: (doc: EngineDoc, v: ModelView, ids: readonly BlockId[], parent: BlockId | null, index: number, side?: number, part?: number, at?: BlockId | null) => void;
612
+ insertSpecsAt: (doc: EngineDoc, v: ModelView, parent: BlockId | null, index: number, specs: readonly import("./index.js").BlockSpec[], split?: import("./placement/slots.js").Unit | null) => void;
613
613
  withdrawOnUndo: (doc: EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
614
614
  inserts: {
615
615
  has(client: number, clock: number): boolean;
616
616
  };
617
617
  }) => boolean;
618
- materializeSpec: (doc: EngineDoc, sp: import("./index.js").BlockSpec, parent: BlockId | null, rank: string) => void;
619
618
  collides: (doc: EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
620
- writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, place: import("./placement/model.js").PlacementValue) => void;
619
+ writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, parent: BlockId | null, index: number) => import("./placement/slots.js").Unit;
621
620
  ownText: (doc: EngineDoc, id: BlockId) => {
622
621
  from: unknown;
623
622
  to: number;
624
623
  };
624
+ recordsOf: (doc: EngineDoc, node: EngineNode, v?: ModelView) => {
625
+ item: import("./placement/slots.js").SeqItem;
626
+ stamp: import("./placement/slots.js").Stamp;
627
+ wins: boolean;
628
+ }[];
625
629
  insertBlocks: (doc: EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
626
630
  insertBlock: (doc: EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
627
631
  restoreBlocks: (doc: EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
@@ -653,8 +657,8 @@ export declare const bindDocModel: (Y: EngineApi) => {
653
657
  at: number;
654
658
  node: EngineNode;
655
659
  } | null;
656
- splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => import("./text/model.js").MergeClaim[];
657
- claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
660
+ splitAt: (doc: EngineDoc, blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => void;
661
+ claimInto: (own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
658
662
  ownText: (doc: EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
659
663
  from: unknown;
660
664
  to: number;
@@ -671,7 +675,7 @@ export declare const bindDocModel: (Y: EngineApi) => {
671
675
  export declare const bindEdytorDoc: (Y: EngineApi) => {
672
676
  /** The schema manifest constants table. */
673
677
  SCHEMA: {
674
- readonly version: 6;
678
+ readonly version: 10;
675
679
  readonly name: "edytor-doc";
676
680
  readonly roots: {
677
681
  readonly registry: "blocks";
@@ -682,7 +686,7 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
682
686
  readonly schema: "schema";
683
687
  };
684
688
  };
685
- SCHEMA_VERSION: 6;
689
+ SCHEMA_VERSION: 10;
686
690
  SCHEMA_NAME: "edytor-doc";
687
691
  META_KEY: "meta";
688
692
  /** Doc-level API (no facade needed). */
@@ -755,29 +759,30 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
755
759
  blockNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
756
760
  liveNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
757
761
  isLive: (doc: EngineDoc, id: BlockId) => boolean;
758
- candidatesOf: (node: EngineNode) => import("./placement/model.js").PlacementCand[];
759
762
  view: (doc: EngineDoc) => ModelView;
760
- resolvePlacements: (blocks: Map<BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: BlockId) => import("./text/model.js").Owner) => Map<BlockId, import("./placement/model.js").ResolvedPlacement>;
761
763
  childrenIndex: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<BlockId | null, import("./placement/model.js").ChildSlot[]>;
762
764
  positionInView: (v: ModelView, id: BlockId) => import("./index.js").Destination | null;
763
765
  buildInline: (atom: InlineSpec) => EngineNode;
764
766
  isSelfOrDescendant: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: BlockId, maybeAncestor: BlockId) => boolean;
765
- ranksAt: (siblings: readonly {
766
- rank: string;
767
- }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
768
- writePlacement: (doc: EngineDoc, node: EngineNode, p: BlockId | null, r: string) => void;
767
+ placeBlocks: (doc: EngineDoc, v: ModelView, parent: BlockId | null, left: BlockId | null, right: BlockId | null, slots: readonly import("./placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("./placement/slots.js").Unit | null) => void;
768
+ moveBlocksTo: (doc: EngineDoc, v: ModelView, ids: readonly BlockId[], parent: BlockId | null, index: number, side?: number, part?: number, at?: BlockId | null) => void;
769
+ insertSpecsAt: (doc: EngineDoc, v: ModelView, parent: BlockId | null, index: number, specs: readonly import("./index.js").BlockSpec[], split?: import("./placement/slots.js").Unit | null) => void;
769
770
  withdrawOnUndo: (doc: EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
770
771
  inserts: {
771
772
  has(client: number, clock: number): boolean;
772
773
  };
773
774
  }) => boolean;
774
- materializeSpec: (doc: EngineDoc, sp: import("./index.js").BlockSpec, parent: BlockId | null, rank: string) => void;
775
775
  collides: (doc: EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
776
- writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, place: import("./placement/model.js").PlacementValue) => void;
776
+ writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, parent: BlockId | null, index: number) => import("./placement/slots.js").Unit;
777
777
  ownText: (doc: EngineDoc, id: BlockId) => {
778
778
  from: unknown;
779
779
  to: number;
780
780
  };
781
+ recordsOf: (doc: EngineDoc, node: EngineNode, v?: ModelView) => {
782
+ item: import("./placement/slots.js").SeqItem;
783
+ stamp: import("./placement/slots.js").Stamp;
784
+ wins: boolean;
785
+ }[];
781
786
  insertBlocks: (doc: EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
782
787
  insertBlock: (doc: EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
783
788
  restoreBlocks: (doc: EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
@@ -809,8 +814,8 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
809
814
  at: number;
810
815
  node: EngineNode;
811
816
  } | null;
812
- splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => import("./text/model.js").MergeClaim[];
813
- claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
817
+ splitAt: (doc: EngineDoc, blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => void;
818
+ claimInto: (own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
814
819
  ownText: (doc: EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
815
820
  from: unknown;
816
821
  to: number;
@@ -830,7 +835,7 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
830
835
  keep?: boolean;
831
836
  after?: readonly import("./index.js").BlockSpec[];
832
837
  } | undefined) => OpResult;
833
- placeBeside: (ids: readonly string[], target: string, side: "right" | "left", kind?: string | undefined) => OpResult;
838
+ placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => OpResult;
834
839
  wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => OpResult;
835
840
  splitBlock: (id: string, offset: number, newId: string, tail?: SplitTail | undefined) => OpResult;
836
841
  mergeBlocks: (fromId: string, intoId: string) => OpResult;
@@ -990,14 +995,6 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
990
995
  * not have integrated anything yet.
991
996
  */
992
997
  newDoc: (opts?: ConstructorParameters<EngineApi["Doc"]>[0]) => YDoc;
993
- /**
994
- * Keep, on `doc`, the text a replica may have to copy again: its
995
- * `gcFilter` then spares a deleted copy's content, as every facade's
996
- * history does. A doc that only relays updates (the room) calls it
997
- * before applying anything, so what it collects and encodes matches
998
- * what an editing replica keeps.
999
- */
1000
- keepCopies: (doc: EngineDoc) => void;
1001
998
  /**
1002
999
  * Purge from `doc` what was deleted before `horizon` (`room.purge.what`): real deletes, inside the caller's transaction,
1003
1000
  * and the horizon record every history reads (`hist.purge.horizon`).
@@ -1006,7 +1003,7 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
1006
1003
  purge: (doc: EngineDoc, facade: {
1007
1004
  model: import("./placement/model.js").PlacementModel;
1008
1005
  text: import("./text/model.js").TextEngine;
1009
- }, horizon: import("./purge.js").Horizon) => import("./purge.js").PurgeReport;
1006
+ }, horizon: import("./purge.js").Horizon | null) => import("./purge.js").PurgeReport;
1010
1007
  /** The purge horizon `doc` holds, or `null`. */
1011
1008
  horizonOf: (doc: EngineDoc) => import("./purge.js").Horizon | null;
1012
1009
  /**
@@ -1020,29 +1017,30 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
1020
1017
  blockNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
1021
1018
  liveNodeOf: (doc: EngineDoc, id: BlockId) => EngineNode | null;
1022
1019
  isLive: (doc: EngineDoc, id: BlockId) => boolean;
1023
- candidatesOf: (node: EngineNode) => import("./placement/model.js").PlacementCand[];
1024
1020
  view: (doc: EngineDoc) => ModelView;
1025
- resolvePlacements: (blocks: Map<BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: BlockId) => import("./text/model.js").Owner) => Map<BlockId, import("./placement/model.js").ResolvedPlacement>;
1026
1021
  childrenIndex: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<BlockId | null, import("./placement/model.js").ChildSlot[]>;
1027
1022
  positionInView: (v: ModelView, id: BlockId) => import("./index.js").Destination | null;
1028
1023
  buildInline: (atom: InlineSpec) => EngineNode;
1029
1024
  isSelfOrDescendant: (placements: Map<BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: BlockId, maybeAncestor: BlockId) => boolean;
1030
- ranksAt: (siblings: readonly {
1031
- rank: string;
1032
- }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
1033
- writePlacement: (doc: EngineDoc, node: EngineNode, p: BlockId | null, r: string) => void;
1025
+ placeBlocks: (doc: EngineDoc, v: ModelView, parent: BlockId | null, left: BlockId | null, right: BlockId | null, slots: readonly import("./placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("./placement/slots.js").Unit | null) => void;
1026
+ moveBlocksTo: (doc: EngineDoc, v: ModelView, ids: readonly BlockId[], parent: BlockId | null, index: number, side?: number, part?: number, at?: BlockId | null) => void;
1027
+ insertSpecsAt: (doc: EngineDoc, v: ModelView, parent: BlockId | null, index: number, specs: readonly import("./index.js").BlockSpec[], split?: import("./placement/slots.js").Unit | null) => void;
1034
1028
  withdrawOnUndo: (doc: EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
1035
1029
  inserts: {
1036
1030
  has(client: number, clock: number): boolean;
1037
1031
  };
1038
1032
  }) => boolean;
1039
- materializeSpec: (doc: EngineDoc, sp: import("./index.js").BlockSpec, parent: BlockId | null, rank: string) => void;
1040
1033
  collides: (doc: EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
1041
- writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, place: import("./placement/model.js").PlacementValue) => void;
1034
+ writeSplit: (doc: EngineDoc, id: BlockId, offset: number, newId: BlockId, tail: SplitTail, parent: BlockId | null, index: number) => import("./placement/slots.js").Unit;
1042
1035
  ownText: (doc: EngineDoc, id: BlockId) => {
1043
1036
  from: unknown;
1044
1037
  to: number;
1045
1038
  };
1039
+ recordsOf: (doc: EngineDoc, node: EngineNode, v?: ModelView) => {
1040
+ item: import("./placement/slots.js").SeqItem;
1041
+ stamp: import("./placement/slots.js").Stamp;
1042
+ wins: boolean;
1043
+ }[];
1046
1044
  insertBlocks: (doc: EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
1047
1045
  insertBlock: (doc: EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
1048
1046
  restoreBlocks: (doc: EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
@@ -1074,8 +1072,8 @@ export declare const bindEdytorDoc: (Y: EngineApi) => {
1074
1072
  at: number;
1075
1073
  node: EngineNode;
1076
1074
  } | null;
1077
- splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => import("./text/model.js").MergeClaim[];
1078
- claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
1075
+ splitAt: (doc: EngineDoc, blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => void;
1076
+ claimInto: (own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
1079
1077
  ownText: (doc: EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
1080
1078
  from: unknown;
1081
1079
  to: number;
@@ -48,10 +48,10 @@
48
48
  */
49
49
  import { setMarkEdges } from './text/marks.js';
50
50
  import { keepingReplaced } from './incarnations.js';
51
+ import { clientsOf } from './structs.js';
51
52
  import { SCHEMA } from './schema.js';
52
53
  import { bindModel, blockText, crdtId, listBlockIds, project, resolveBlock } from './placement/model.js';
53
54
  import { bindText } from './text/model.js';
54
- import { bindDeletes } from './text/deletes.js';
55
55
  import { bindPurge, readHorizon } from './purge.js';
56
56
  import { restoreDocument } from './restore.js';
57
57
  import { bindRuns, indexOf } from './text/runs.js';
@@ -84,15 +84,29 @@ export { DEFAULT_HISTORY_LIMIT } from './doc/history.js';
84
84
  * The per-doc facade factory over the engine `Y` (the editor's documents
85
85
  * use it directly; {@link bindDocModel} is the public binding around it).
86
86
  */
87
+ /**
88
+ * Give `doc` edytor's rule — a registry value a concurrent creation replaced
89
+ * keeps its subtree (fork patch YP14, `keepingReplaced`) — or refuse it
90
+ * loudly when it has integrated updates without it: the engine decides what
91
+ * a replaced value keeps when it integrates, so a document that took updates
92
+ * under the engine's default could lose a subtree its peers keep, forever.
93
+ * Create it with `createDoc()` (or `createDocument`/`loadDocument`), never
94
+ * `new Y.Doc()`.
95
+ */
96
+ export const adoptRules = (doc) => {
97
+ const d = doc;
98
+ if (d.keepReplaced == null && clientsOf(doc).size > 0)
99
+ throw new TypeError("edytor: this document took updates before it carried edytor's rules (create it with createDoc(), createDocument or loadDocument, not new Y.Doc())");
100
+ return keepingReplaced(d);
101
+ };
87
102
  export const docFacade = (Y, layers) => {
88
- const D = bindDeletes(Y);
89
103
  // Compact per-block attribution writes (`attribution/block.ts`).
90
104
  // One bound instance per engine binding; its suppression memory is
91
105
  // per-doc (WeakMap-keyed), so facades on the same doc share it.
92
106
  const BA = bindBlockAttribution(Y);
93
107
  const { init, seed } = bindSeed(Y, BA);
94
108
  return (doc, config = {}) => {
95
- keepingReplaced(doc);
109
+ adoptRules(doc);
96
110
  setMarkEdges(doc, config.markEdge ?? (() => undefined));
97
111
  const roleOf = config.roleOf ?? (() => undefined);
98
112
  const defaultType = config.defaultType ?? 'paragraph';
@@ -141,7 +155,7 @@ export const docFacade = (Y, layers) => {
141
155
  funnel.dispose();
142
156
  events.close();
143
157
  };
144
- const { createUndoManager } = docHistory({ ...base, ...funnel, D, init });
158
+ const { createUndoManager } = docHistory({ ...base, ...funnel, init });
145
159
  const { anchorAt, followUndo, resolveAnchor } = docAnchors(reads);
146
160
  const steps = { ...shape, ...planSteps(shape) };
147
161
  const { plan } = steps;
@@ -361,7 +375,6 @@ export const bindDocModel = (Y) => {
361
375
  const M = bindModel(Y);
362
376
  const T = bindText(Y);
363
377
  const R = bindRuns(Y);
364
- const D = bindDeletes(Y);
365
378
  const { init, seed } = bindSeed(Y, bindBlockAttribution(Y));
366
379
  const create = docFacade(Y, { model: M, text: T });
367
380
  return {
@@ -390,14 +403,6 @@ export const bindDocModel = (Y) => {
390
403
  * not have integrated anything yet.
391
404
  */
392
405
  newDoc: (opts) => keepingReplaced(new Y.Doc(opts)),
393
- /**
394
- * Keep, on `doc`, the text a replica may have to copy again: its
395
- * `gcFilter` then spares a deleted copy's content, as every facade's
396
- * history does. A doc that only relays updates (the room) calls it
397
- * before applying anything, so what it collects and encodes matches
398
- * what an editing replica keeps.
399
- */
400
- keepCopies: (doc) => void D.scope(doc),
401
406
  /** The bound engine layers (same instances the facades use). */
402
407
  model: M,
403
408
  text: T,
@@ -49,6 +49,10 @@ export type EngineItemRef = {
49
49
  */
50
50
  export interface EngineTransaction {
51
51
  changed?: Map<unknown, Set<string | null>>;
52
+ /** The visibility changes hold records made, in order: `client, clock, ±len` per item (`> 0` shown). */
53
+ holds?: number[];
54
+ /** The units whose mover changed (the fork's move records), in order: `client, clock, len` per item. */
55
+ moves?: number[];
52
56
  }
53
57
  /**
54
58
  * Structural minimum of the vendored `Y.Node` (v14) used by the placement
@@ -70,7 +74,28 @@ export interface EngineNode {
70
74
  forEachAttr(f: (value: unknown, key: string, node: EngineNode) => void): void;
71
75
  attrKeys(): IterableIterator<string>;
72
76
  insert(index: number, content: unknown): void;
73
- delete(index: number, length?: number): void;
77
+ /**
78
+ * Delete `length` live units at `index`. With `into` (the document's
79
+ * records list), they are held by one record appended there instead (a
80
+ * delete its writer can take back), except those `fresh` names, which are
81
+ * removed.
82
+ */
83
+ delete(index: number, length?: number, into?: EngineNode | null, fresh?: unknown): void;
84
+ /** Append one hold record of `ids` (`client, clock, len` triples) to this records list: they read as deleted while it lives. */
85
+ hold(ids: number[]): void;
86
+ /**
87
+ * Move the units `ids` names (`client, clock, len` triples, in order) to
88
+ * live index `index` of this sequence: one move record there (the fork's
89
+ * `ContentMove`); `side` is its class among the items of its origin (`0`
90
+ * after, `1` before), `at` the chain it stands at for the sort of two
91
+ * records at one origin — the units from the root sequence down to the
92
+ * unit it stands at, outermost first (`null`: the first unit it names) —
93
+ * and `part` its order among the records standing there.
94
+ */
95
+ move(index: number, ids: number[], side?: number, part?: number, at?: ReadonlyArray<{
96
+ client: number;
97
+ clock: number;
98
+ }> | null): void;
74
99
  }
75
100
  /** Structural minimum of the vendored `Y.Doc`. */
76
101
  export interface EngineDoc {
@@ -98,6 +123,13 @@ export interface EngineDoc {
98
123
  */
99
124
  on(name: 'commit', f: (transaction: never) => void): void;
100
125
  off(name: 'commit', f: (transaction: never) => void): void;
126
+ /**
127
+ * Doc-level `beforeCommit` channel (the fork's) — fires once per
128
+ * outermost transaction, after its function returned and while the
129
+ * transaction is still open: a listener's writes join it.
130
+ */
131
+ on(name: 'beforeCommit', f: (transaction: never) => void): void;
132
+ off(name: 'beforeCommit', f: (transaction: never) => void): void;
101
133
  /** Doc destruction event — doc-scoped state must release on destroy. */
102
134
  on(name: 'destroy', f: () => void): void;
103
135
  off(name: 'destroy', f: () => void): void;
@@ -30,7 +30,6 @@ export type EngineSymbol =
30
30
  | 'followRedone'
31
31
  | 'isParentOf'
32
32
  | 'mergeIdSets'
33
- | 'ContentAny'
34
33
  | 'createID';
35
34
 
36
35
  export declare const Y: Pick<typeof V, EngineSymbol>;