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.
Files changed (137) hide show
  1. package/README.md +3 -3
  2. package/dist/cloudflare/DocumentRoom.d.ts +77 -2
  3. package/dist/cloudflare/DocumentRoom.js +37 -1
  4. package/dist/cloudflare/index.d.ts +1 -1
  5. package/dist/cloudflare/index.js +1 -1
  6. package/dist/cloudflare/room/access.d.ts +6 -1
  7. package/dist/cloudflare/room/access.js +30 -4
  8. package/dist/cloudflare/room/admission.d.ts +11 -0
  9. package/dist/cloudflare/room/admission.js +43 -10
  10. package/dist/cloudflare/room/context.d.ts +73 -76
  11. package/dist/cloudflare/room/context.js +17 -6
  12. package/dist/cloudflare/room/forged.d.ts +1 -1
  13. package/dist/cloudflare/room/forged.js +11 -9
  14. package/dist/cloudflare/room/frames.d.ts +2 -0
  15. package/dist/cloudflare/room/frames.js +2 -0
  16. package/dist/cloudflare/room/presence.js +1 -4
  17. package/dist/cloudflare/room/reseed.d.ts +93 -0
  18. package/dist/cloudflare/room/reseed.js +500 -0
  19. package/dist/cloudflare/room/scheduler.d.ts +1 -1
  20. package/dist/cloudflare/room/scheduler.js +9 -1
  21. package/dist/cloudflare/room/shared.d.ts +10 -0
  22. package/dist/cloudflare/room/shared.js +10 -0
  23. package/dist/cloudflare/room/storage.d.ts +7 -0
  24. package/dist/cloudflare/room/storage.js +33 -1
  25. package/dist/components/Edytor.svelte +18 -3
  26. package/dist/components/Edytor.svelte.d.ts +10 -1
  27. package/dist/components/EdytorRoot.svelte +17 -0
  28. package/dist/components/EdytorRoot.svelte.d.ts +4 -0
  29. package/dist/components/Suggestion.svelte +1 -1
  30. package/dist/crdt/attribution/block.d.ts +3 -0
  31. package/dist/crdt/attribution/block.js +3 -2
  32. package/dist/crdt/doc/history.js +2 -3
  33. package/dist/crdt/doc/ops.d.ts +81 -0
  34. package/dist/crdt/doc/ops.js +1898 -0
  35. package/dist/crdt/doc/suggest.d.ts +6 -2
  36. package/dist/crdt/doc/suggest.js +6 -5
  37. package/dist/crdt/edytor-doc.d.ts +157 -157
  38. package/dist/crdt/edytor-doc.js +15 -43
  39. package/dist/crdt/engine-room.d.ts +4 -0
  40. package/dist/crdt/engine-room.js +6 -0
  41. package/dist/crdt/flow.d.ts +4 -46
  42. package/dist/crdt/flow.js +2 -262
  43. package/dist/crdt/index.d.ts +64 -75
  44. package/dist/crdt/operations.d.ts +1 -1
  45. package/dist/crdt/placement/resolve.d.ts +1 -1
  46. package/dist/crdt/protocol.d.ts +2 -2
  47. package/dist/crdt/protocol.js +2 -2
  48. package/dist/crdt/protocols/auth.d.ts +8 -1
  49. package/dist/crdt/protocols/auth.js +14 -1
  50. package/dist/crdt/protocols/awareness.js +46 -81
  51. package/dist/crdt/providers/index.d.ts +11 -22
  52. package/dist/crdt/providers/index.js +22 -3
  53. package/dist/crdt/providers/indexeddb.d.ts +6 -10
  54. package/dist/crdt/providers/indexeddb.js +10 -16
  55. package/dist/crdt/providers/room.d.ts +26 -0
  56. package/dist/crdt/providers/room.js +41 -12
  57. package/dist/crdt/providers/websocket.d.ts +10 -14
  58. package/dist/crdt/providers/websocket.js +43 -23
  59. package/dist/crdt/rangeDelete.d.ts +2 -47
  60. package/dist/crdt/rangeDelete.js +1 -169
  61. package/dist/crdt/reseed.d.ts +127 -0
  62. package/dist/crdt/reseed.js +533 -0
  63. package/dist/crdt/restore.d.ts +13 -1
  64. package/dist/crdt/restore.js +8 -3
  65. package/dist/crdt/schema.d.ts +6 -0
  66. package/dist/crdt/schema.js +6 -0
  67. package/dist/crdt/structs.d.ts +8 -11
  68. package/dist/crdt/structs.js +25 -44
  69. package/dist/crdt/text/index/build.d.ts +119 -0
  70. package/dist/crdt/text/index/build.js +2801 -0
  71. package/dist/crdt/text/index/shared.d.ts +2 -6
  72. package/dist/crdt/text/index/shared.js +2 -5
  73. package/dist/crdt/text/model.js +2 -3
  74. package/dist/crdt/text/runs.d.ts +9 -12
  75. package/dist/crdt/text/runs.js +2 -165
  76. package/dist/crdt/vendor/yjs/UPSTREAM.md +101 -5
  77. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  78. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +10 -10
  79. package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +1 -0
  80. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  81. package/dist/crdt/vendor/yjs/src/structs/Item.js +24 -13
  82. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +12 -10
  83. package/dist/crdt/vendor/yjs/src/utils/encoding.js +42 -1
  84. package/dist/events/domTextOffset.d.ts +2 -0
  85. package/dist/events/domTextOffset.js +8 -0
  86. package/dist/export/forms.d.ts +50 -50
  87. package/dist/index.d.ts +1 -1
  88. package/dist/index.js +1 -1
  89. package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
  90. package/dist/plugins/richtext/richTextOperations.js +26 -32
  91. package/dist/selection/selection.svelte.d.ts +1 -1
  92. package/dist/selection/selection.svelte.js +87 -106
  93. package/dist/selection/selection.utils.js +5 -8
  94. package/dist/session/commands.js +49 -51
  95. package/dist/session/handles.js +3 -7
  96. package/dist/surface/attributes.d.ts +3 -3
  97. package/dist/surface/attributes.js +2 -2
  98. package/dist/surface/observer.svelte.d.ts +7 -7
  99. package/dist/surface/observer.svelte.js +225 -336
  100. package/dist/surface/projector.svelte.js +2 -2
  101. package/dist/utils.d.ts +5 -0
  102. package/dist/utils.js +7 -0
  103. package/package.json +1 -1
  104. package/dist/crdt/doc/capability.d.ts +0 -56
  105. package/dist/crdt/doc/capability.js +0 -207
  106. package/dist/crdt/doc/content.d.ts +0 -16
  107. package/dist/crdt/doc/content.js +0 -77
  108. package/dist/crdt/doc/delete.d.ts +0 -15
  109. package/dist/crdt/doc/delete.js +0 -84
  110. package/dist/crdt/doc/layout.d.ts +0 -9
  111. package/dist/crdt/doc/layout.js +0 -178
  112. package/dist/crdt/doc/meta.d.ts +0 -19
  113. package/dist/crdt/doc/meta.js +0 -198
  114. package/dist/crdt/doc/moves.d.ts +0 -21
  115. package/dist/crdt/doc/moves.js +0 -274
  116. package/dist/crdt/doc/split-merge.d.ts +0 -10
  117. package/dist/crdt/doc/split-merge.js +0 -179
  118. package/dist/crdt/doc/steps.d.ts +0 -65
  119. package/dist/crdt/doc/steps.js +0 -311
  120. package/dist/crdt/text/index/cache.d.ts +0 -16
  121. package/dist/crdt/text/index/cache.js +0 -175
  122. package/dist/crdt/text/index/claims.d.ts +0 -18
  123. package/dist/crdt/text/index/claims.js +0 -203
  124. package/dist/crdt/text/index/fold.d.ts +0 -21
  125. package/dist/crdt/text/index/fold.js +0 -656
  126. package/dist/crdt/text/index/layout.d.ts +0 -22
  127. package/dist/crdt/text/index/layout.js +0 -258
  128. package/dist/crdt/text/index/placement.d.ts +0 -47
  129. package/dist/crdt/text/index/placement.js +0 -738
  130. package/dist/crdt/text/index/records.d.ts +0 -13
  131. package/dist/crdt/text/index/records.js +0 -139
  132. package/dist/crdt/text/index/report.d.ts +0 -18
  133. package/dist/crdt/text/index/report.js +0 -337
  134. package/dist/crdt/text/index/state.d.ts +0 -197
  135. package/dist/crdt/text/index/state.js +0 -116
  136. package/dist/crdt/text/index/streams.d.ts +0 -25
  137. package/dist/crdt/text/index/streams.js +0 -248
@@ -18,6 +18,7 @@ import type { RoomHistory } from './history.js';
18
18
  import type { RoomMoves } from './moves.js';
19
19
  import type { RoomPresence } from './presence.js';
20
20
  import type { RoomPurge } from './purge.js';
21
+ import type { RoomReseed } from './reseed.js';
21
22
  import type { ReplicaRegistry } from './replicas.js';
22
23
  import type { Scheduler } from './scheduler.js';
23
24
  import type { RoomStorage } from './storage.js';
@@ -157,34 +158,8 @@ export declare const crdt: {
157
158
  api: object;
158
159
  } & {
159
160
  prepare: {
160
- insertFlow: (target: import("../../index.js").FlowTarget, flow: import("../../index.js").Flow, view?: import("../../index.js").FlowView) => import("../../index.js").Prepared;
161
- deleteRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView) => import("../../index.js").Prepared;
162
- replaceRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView) => import("../../index.js").Prepared;
163
161
  nestBlock: (id: import("../../index.js").BlockId, parent: import("../../index.js").BlockId) => import("../../index.js").Prepared;
164
162
  deleteBlocks: (ids: readonly import("../../index.js").BlockId[]) => import("../../index.js").Prepared;
165
- insertText: (id: import("../../index.js").BlockId, offset: number, text: string, marks?: Record<string, unknown>) => import("../../index.js").Prepared;
166
- deleteText: (id: import("../../index.js").BlockId, offset: number, length: number) => import("../../index.js").Prepared;
167
- formatRange: (id: import("../../index.js").BlockId, offset: number, length: number, marks: Record<string, unknown>) => import("../../index.js").Prepared;
168
- clearMarks: (id: import("../../index.js").BlockId, offset: number, length: number) => import("../../index.js").Prepared;
169
- insertInline: (id: import("../../index.js").BlockId, offset: number, atom: import("../../crdt/doc/content.js").NewInline) => import("../../index.js").Prepared;
170
- removeInline: (id: import("../../index.js").BlockId, inlineId: string) => import("../../index.js").Prepared;
171
- setBlockType: (id: import("../../index.js").BlockId, type: string) => import("../../index.js").Prepared;
172
- patchData: (target: import("../../index.js").DataTarget, patches: readonly import("../../index.js").DataPatch[]) => import("../../index.js").Prepared;
173
- setBlock: (id: import("../../index.js").BlockId, value: {
174
- type?: string;
175
- data?: Record<string, unknown>;
176
- content?: (import("../../index.js").ContentItem | import("../../index.js").JSONText | import("../../index.js").JSONInlineBlock)[];
177
- children?: (import("../../index.js").BlockSpec | import("../../index.js").JSONBlock)[];
178
- }) => import("../../index.js").Prepared;
179
- deleteBlock: (id: import("../../index.js").BlockId, opts?: {
180
- keepChildren?: boolean;
181
- }) => import("../../index.js").Prepared;
182
- splitBlock: (id: import("../../index.js").BlockId, offset: number, newId: import("../../index.js").BlockId, tail?: import("../../index.js").SplitTail) => import("../../index.js").Prepared;
183
- mergeBlocks: (fromId: import("../../index.js").BlockId, intoId: import("../../index.js").BlockId) => import("../../index.js").Prepared;
184
- mergeBackward: (id: import("../../index.js").BlockId) => import("../../index.js").Prepared;
185
- mergeForward: (id: import("../../index.js").BlockId) => import("../../index.js").Prepared;
186
- placeBeside: (ids: readonly import("../../index.js").BlockId[], target: import("../../index.js").BlockId, side: "left" | "right", kind?: string) => import("../../index.js").Prepared;
187
- wrapInLayout: (ids: readonly import("../../index.js").BlockId[], kind?: string, columns?: number) => import("../../index.js").Prepared;
188
163
  insertBlocks: (dest: import("../../index.js").Destination, specs: readonly (import("../../index.js").BlockSpec | import("../../index.js").JSONBlock)[]) => import("../../index.js").Prepared;
189
164
  moveBlocks: (ids: readonly import("../../index.js").BlockId[], dest: import("../../index.js").Destination) => import("../../index.js").Prepared;
190
165
  unNestBlocks: (ids: readonly import("../../index.js").BlockId[]) => import("../../index.js").Prepared;
@@ -194,39 +169,39 @@ export declare const crdt: {
194
169
  after?: readonly import("../../index.js").BlockSpec[];
195
170
  }) => import("../../index.js").Prepared;
196
171
  duplicateBlock: (id: import("../../index.js").BlockId, freshId?: (oldId: string, kind: "block" | "inline") => string) => import("../../index.js").Prepared;
172
+ placeBeside: (ids: readonly import("../../index.js").BlockId[], target: import("../../index.js").BlockId, side: "left" | "right", kind?: string) => import("../../index.js").Prepared;
173
+ wrapInLayout: (ids: readonly import("../../index.js").BlockId[], kind?: string, columns?: number) => import("../../index.js").Prepared;
174
+ splitBlock: (id: import("../../index.js").BlockId, offset: number, newId: import("../../index.js").BlockId, tail?: import("../../index.js").SplitTail) => import("../../index.js").Prepared;
175
+ mergeBlocks: (fromId: import("../../index.js").BlockId, intoId: import("../../index.js").BlockId) => import("../../index.js").Prepared;
176
+ mergeBackward: (id: import("../../index.js").BlockId) => import("../../index.js").Prepared;
177
+ mergeForward: (id: import("../../index.js").BlockId) => import("../../index.js").Prepared;
178
+ deleteBlock: (id: import("../../index.js").BlockId, opts?: {
179
+ keepChildren?: boolean;
180
+ }) => import("../../index.js").Prepared;
181
+ setBlockType: (id: import("../../index.js").BlockId, type: string) => import("../../index.js").Prepared;
182
+ patchData: (target: import("../../index.js").DataTarget, patches: readonly import("../../index.js").DataPatch[]) => import("../../index.js").Prepared;
183
+ setBlock: (id: import("../../index.js").BlockId, value: {
184
+ type?: string;
185
+ data?: Record<string, unknown>;
186
+ content?: (import("../../index.js").ContentItem | import("../../index.js").JSONText | import("../../index.js").JSONInlineBlock)[];
187
+ children?: (import("../../index.js").BlockSpec | import("../../index.js").JSONBlock)[];
188
+ }) => import("../../index.js").Prepared;
189
+ insertText: (id: import("../../index.js").BlockId, offset: number, text: string, marks?: Record<string, unknown>) => import("../../index.js").Prepared;
190
+ deleteText: (id: import("../../index.js").BlockId, offset: number, length: number) => import("../../index.js").Prepared;
191
+ formatRange: (id: import("../../index.js").BlockId, offset: number, length: number, marks: Record<string, unknown>) => import("../../index.js").Prepared;
192
+ clearMarks: (id: import("../../index.js").BlockId, offset: number, length: number) => import("../../index.js").Prepared;
193
+ insertInline: (id: import("../../index.js").BlockId, offset: number, atom: import("../../crdt/doc/ops.js").NewInline) => import("../../index.js").Prepared;
194
+ removeInline: (id: import("../../index.js").BlockId, inlineId: string) => import("../../index.js").Prepared;
195
+ insertFlow: (target: import("../../index.js").FlowTarget, flow: import("../../index.js").Flow, view?: import("../../index.js").FlowView) => import("../../index.js").Prepared;
196
+ deleteRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView) => import("../../index.js").Prepared;
197
+ replaceRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView) => import("../../index.js").Prepared;
197
198
  };
198
199
  apply: (p: import("../../index.js").Prepared, quiet?: boolean) => import("../../index.js").OpResult;
199
200
  compose: (...parts: import("../../index.js").Prepared[]) => import("../../index.js").Prepared;
200
201
  transact: <R>(fn: () => R, origin?: unknown) => R;
201
202
  version: number;
202
- insertFlow: (target: import("../../index.js").FlowTarget, flow: import("../../index.js").Flow, view?: import("../../index.js").FlowView | undefined) => import("../../index.js").OpResult;
203
- deleteRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView | undefined) => import("../../index.js").OpResult;
204
- replaceRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView | undefined) => import("../../index.js").OpResult;
205
203
  nestBlock: (id: string, parent: string) => import("../../index.js").OpResult;
206
204
  deleteBlocks: (ids: readonly string[]) => import("../../index.js").OpResult;
207
- insertText: (id: string, offset: number, text: string, marks?: Record<string, unknown> | undefined) => import("../../index.js").OpResult;
208
- deleteText: (id: string, offset: number, length: number) => import("../../index.js").OpResult;
209
- formatRange: (id: string, offset: number, length: number, marks: Record<string, unknown>) => import("../../index.js").OpResult;
210
- clearMarks: (id: string, offset: number, length: number) => import("../../index.js").OpResult;
211
- insertInline: (id: string, offset: number, atom: import("../../crdt/doc/content.js").NewInline) => import("../../index.js").OpResult;
212
- removeInline: (id: string, inlineId: string) => import("../../index.js").OpResult;
213
- setBlockType: (id: string, type: string) => import("../../index.js").OpResult;
214
- patchData: (target: import("../../index.js").DataTarget, patches: readonly import("../../index.js").DataPatch[]) => import("../../index.js").OpResult;
215
- setBlock: (id: string, value: {
216
- type?: string;
217
- data?: Record<string, unknown>;
218
- content?: (import("../../index.js").ContentItem | import("../../index.js").JSONText | import("../../index.js").JSONInlineBlock)[];
219
- children?: (import("../../index.js").BlockSpec | import("../../index.js").JSONBlock)[];
220
- }) => import("../../index.js").OpResult;
221
- deleteBlock: (id: string, opts?: {
222
- keepChildren?: boolean;
223
- } | undefined) => import("../../index.js").OpResult;
224
- splitBlock: (id: string, offset: number, newId: string, tail?: import("../../index.js").SplitTail | undefined) => import("../../index.js").OpResult;
225
- mergeBlocks: (fromId: string, intoId: string) => import("../../index.js").OpResult;
226
- mergeBackward: (id: string) => import("../../index.js").OpResult;
227
- mergeForward: (id: string) => import("../../index.js").OpResult;
228
- placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => import("../../index.js").OpResult;
229
- wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => import("../../index.js").OpResult;
230
205
  insertBlocks: (dest: import("../../index.js").Destination, specs: readonly (import("../../index.js").JSONBlock | import("../../index.js").BlockSpec)[]) => import("../../index.js").OpResult;
231
206
  moveBlocks: (ids: readonly string[], dest: import("../../index.js").Destination) => import("../../index.js").OpResult;
232
207
  unNestBlocks: (ids: readonly string[]) => import("../../index.js").OpResult;
@@ -236,6 +211,32 @@ export declare const crdt: {
236
211
  after?: readonly import("../../index.js").BlockSpec[];
237
212
  } | undefined) => import("../../index.js").OpResult;
238
213
  duplicateBlock: (id: string, freshId?: ((oldId: string, kind: "block" | "inline") => string) | undefined) => import("../../index.js").OpResult;
214
+ placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => import("../../index.js").OpResult;
215
+ wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => import("../../index.js").OpResult;
216
+ splitBlock: (id: string, offset: number, newId: string, tail?: import("../../index.js").SplitTail | undefined) => import("../../index.js").OpResult;
217
+ mergeBlocks: (fromId: string, intoId: string) => import("../../index.js").OpResult;
218
+ mergeBackward: (id: string) => import("../../index.js").OpResult;
219
+ mergeForward: (id: string) => import("../../index.js").OpResult;
220
+ deleteBlock: (id: string, opts?: {
221
+ keepChildren?: boolean;
222
+ } | undefined) => import("../../index.js").OpResult;
223
+ setBlockType: (id: string, type: string) => import("../../index.js").OpResult;
224
+ patchData: (target: import("../../index.js").DataTarget, patches: readonly import("../../index.js").DataPatch[]) => import("../../index.js").OpResult;
225
+ setBlock: (id: string, value: {
226
+ type?: string;
227
+ data?: Record<string, unknown>;
228
+ content?: (import("../../index.js").ContentItem | import("../../index.js").JSONText | import("../../index.js").JSONInlineBlock)[];
229
+ children?: (import("../../index.js").BlockSpec | import("../../index.js").JSONBlock)[];
230
+ }) => import("../../index.js").OpResult;
231
+ insertText: (id: string, offset: number, text: string, marks?: Record<string, unknown> | undefined) => import("../../index.js").OpResult;
232
+ deleteText: (id: string, offset: number, length: number) => import("../../index.js").OpResult;
233
+ formatRange: (id: string, offset: number, length: number, marks: Record<string, unknown>) => import("../../index.js").OpResult;
234
+ clearMarks: (id: string, offset: number, length: number) => import("../../index.js").OpResult;
235
+ insertInline: (id: string, offset: number, atom: import("../../crdt/doc/ops.js").NewInline) => import("../../index.js").OpResult;
236
+ removeInline: (id: string, inlineId: string) => import("../../index.js").OpResult;
237
+ insertFlow: (target: import("../../index.js").FlowTarget, flow: import("../../index.js").Flow, view?: import("../../index.js").FlowView | undefined) => import("../../index.js").OpResult;
238
+ deleteRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView | undefined) => import("../../index.js").OpResult;
239
+ replaceRange: (from: import("../../index.js").DocPosition, to: import("../../index.js").DocPosition, view?: import("../../index.js").RangeView | undefined) => import("../../index.js").OpResult;
239
240
  project: () => import("../../index.js").ProjectedDoc;
240
241
  toJSON: (options?: {
241
242
  suggestions?: boolean;
@@ -312,7 +313,7 @@ export declare const crdt: {
312
313
  text: import("../../crdt/text/model.js").TextEngine;
313
314
  }, horizon: import("../../crdt/purge.js").Horizon | null) => import("../index.js").PurgeReport;
314
315
  horizonOf: (doc: E.EngineDoc) => import("../../crdt/purge.js").Horizon | null;
315
- restoreTo: <Plan>(doc: E.EngineDoc, facade: import("../../crdt/restore.js").RestoreFacade<Plan>, json: JSONDoc) => import("../../crdt/restore.js").RestoreReport;
316
+ restoreTo: <Plan>(doc: E.EngineDoc, facade: import("../../crdt/restore.js").RestoreFacade<Plan>, json: JSONDoc, { rewrite, suggestions }?: import("../../crdt/restore.js").RestoreOptions<Plan>) => import("../../crdt/restore.js").RestoreReport;
316
317
  model: {
317
318
  REGISTRY_KEY: string;
318
319
  registryOf: (doc: E.EngineDoc) => E.EngineNode;
@@ -407,17 +408,12 @@ export declare const crdt: {
407
408
  loadError: unknown;
408
409
  _storeTimeout: number;
409
410
  _storeTimeoutId: ReturnType<typeof setTimeout> | null;
410
- _storeUpdate: (update: Uint8Array, origin: unknown) => void;
411
+ _unlisten: () => void;
411
412
  awareness: import("../../index.js").Awareness;
412
413
  bcconnected: boolean;
413
414
  disableBc: boolean;
414
415
  _ownsAwareness: boolean;
415
416
  _bcSubscriber: (data: ArrayBuffer | Uint8Array, origin: unknown) => void;
416
- _awarenessUpdateHandler: (updates: {
417
- added: number[];
418
- updated: number[];
419
- removed: number[];
420
- }, origin: unknown) => void;
421
417
  get synced(): boolean;
422
418
  messageHandlers: Record<number, import("../../crdt/providers/room.js").RoomMessageHandler</*elided*/ any>>;
423
419
  readMessage: (buf: Uint8Array, emitSynced: boolean) => E.Encoder;
@@ -425,7 +421,7 @@ export declare const crdt: {
425
421
  connectBc(): void;
426
422
  disconnectBc(): void;
427
423
  destroy(): Promise<void>;
428
- emit<N extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: N, args: Parameters<{
424
+ emit<N extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: N, args: Parameters<{
429
425
  synced: (provider: /*elided*/ any) => void;
430
426
  'protocol-mismatch': (mismatch: E.ProtocolMismatch, provider: /*elided*/ any) => void;
431
427
  'load-error': (error: unknown, provider: /*elided*/ any) => void;
@@ -434,7 +430,7 @@ export declare const crdt: {
434
430
  failed: (error: unknown, provider: /*elided*/ any) => void;
435
431
  }[N]>): void;
436
432
  _observers: Map<string, Set<any>>;
437
- on<NAME extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
433
+ on<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
438
434
  synced: (provider: /*elided*/ any) => void;
439
435
  'protocol-mismatch': (mismatch: E.ProtocolMismatch, provider: /*elided*/ any) => void;
440
436
  'load-error': (error: unknown, provider: /*elided*/ any) => void;
@@ -449,7 +445,7 @@ export declare const crdt: {
449
445
  'schema-mismatch': (detail: E.SchemaMismatchDetail, provider: /*elided*/ any) => void;
450
446
  failed: (error: unknown, provider: /*elided*/ any) => void;
451
447
  }[NAME];
452
- once<NAME extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
448
+ once<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
453
449
  synced: (provider: /*elided*/ any) => void;
454
450
  'protocol-mismatch': (mismatch: E.ProtocolMismatch, provider: /*elided*/ any) => void;
455
451
  'load-error': (error: unknown, provider: /*elided*/ any) => void;
@@ -457,7 +453,7 @@ export declare const crdt: {
457
453
  'schema-mismatch': (detail: E.SchemaMismatchDetail, provider: /*elided*/ any) => void;
458
454
  failed: (error: unknown, provider: /*elided*/ any) => void;
459
455
  }[NAME]): void;
460
- off<NAME extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
456
+ off<NAME extends "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
461
457
  synced: (provider: /*elided*/ any) => void;
462
458
  'protocol-mismatch': (mismatch: E.ProtocolMismatch, provider: /*elided*/ any) => void;
463
459
  'load-error': (error: unknown, provider: /*elided*/ any) => void;
@@ -508,6 +504,7 @@ export declare const crdt: {
508
504
  deny(): void;
509
505
  allow(): void;
510
506
  };
507
+ _rebasing: boolean;
511
508
  messageHandlers: Record<number, import("../../crdt/providers/room.js").RoomMessageHandler</*elided*/ any>>;
512
509
  hasSynced: boolean;
513
510
  whenSynced: Promise<unknown>;
@@ -518,14 +515,7 @@ export declare const crdt: {
518
515
  _resync: ReturnType<typeof setInterval> | undefined;
519
516
  _connectTimer: ReturnType<typeof setTimeout> | undefined;
520
517
  _checkInterval: ReturnType<typeof setInterval>;
521
- _updateHandler: (update: Uint8Array, origin: unknown, doc?: unknown, transaction?: {
522
- local: boolean;
523
- }) => void;
524
- _awarenessUpdateHandler: (updates: {
525
- added: number[];
526
- updated: number[];
527
- removed: number[];
528
- }, origin: unknown) => void;
518
+ _unlisten: () => void;
529
519
  get url(): string;
530
520
  get unsaved(): number;
531
521
  get saved(): boolean;
@@ -535,11 +525,11 @@ export declare const crdt: {
535
525
  destroy(): void;
536
526
  disconnect(): void;
537
527
  connect(): void;
538
- emit<N extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: N, args: Parameters<E.WebsocketProviderEvents[N]>): void;
528
+ emit<N extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: N, args: Parameters<E.WebsocketProviderEvents[N]>): void;
539
529
  _observers: Map<string, Set<any>>;
540
- on<NAME extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: E.WebsocketProviderEvents[NAME]): E.WebsocketProviderEvents[NAME];
541
- once<NAME extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: E.WebsocketProviderEvents[NAME]): void;
542
- off<NAME extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: E.WebsocketProviderEvents[NAME]): void;
530
+ on<NAME extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: E.WebsocketProviderEvents[NAME]): E.WebsocketProviderEvents[NAME];
531
+ once<NAME extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: E.WebsocketProviderEvents[NAME]): void;
532
+ off<NAME extends "refused" | "status" | "failed" | "message-error" | "schema-mismatch" | "protocol-mismatch" | "synced" | "access" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: E.WebsocketProviderEvents[NAME]): void;
543
533
  };
544
534
  };
545
535
  watchComments: (server: string | null, roomName: string, listener: (message: E.CommentMessage) => void) => (() => void);
@@ -656,6 +646,7 @@ export type RoomTables = {
656
646
  late: string;
657
647
  threads: string;
658
648
  comments: string;
649
+ cuts: string;
659
650
  };
660
651
  /** The room's public diagnostics, kept on the `AttachedDocument` itself. */
661
652
  export type RoomState = Pick<AttachedDocument, 'failure' | 'presence' | 'refusals' | 'refusalCounts' | 'origin'>;
@@ -725,6 +716,7 @@ export declare class RoomContext {
725
716
  presence: RoomPresence;
726
717
  access: RoomAccess;
727
718
  comments: RoomComments;
719
+ reseed: RoomReseed;
728
720
  constructor(ctx: DurableObjectState, options: AttachRoomOptions, limits: RoomLimits, state: RoomState);
729
721
  /** The room's id: its name (`getByName`), else the object's id. */
730
722
  get roomId(): string;
@@ -735,8 +727,11 @@ export declare class RoomContext {
735
727
  /**
736
728
  * A facade over `doc` obeying the room's block roles (`semantics`), its
737
729
  * shared suggestions built: its `toJSON()` leaves the pending ones out.
730
+ * With `actor`, its writes are that actor's (a rebase's: `room.reseed.rebase`).
738
731
  */
739
- facadeOf(doc: YDoc): EdytorDoc;
732
+ facadeOf(doc: YDoc, actor?: {
733
+ id: string;
734
+ }): EdytorDoc;
740
735
  /**
741
736
  * The facade over the live document. Read while the room is idle, it
742
737
  * first drops a direct write whose append failed (`heal`).
@@ -763,6 +758,8 @@ export declare class RoomContext {
763
758
  log(entry: RoomLogEntry): void;
764
759
  /** Send one frame — as chunks when it exceeds `maxFrameBytes` (bounded catch-up). */
765
760
  send(ws: WebSocket, bytes: Uint8Array): void;
761
+ /** The open sockets of the current epoch: what the room relays to (`room.reseed.protocol`). */
762
+ sockets(): WebSocket[];
766
763
  broadcast(bytes: Uint8Array, except: unknown): void;
767
764
  close(ws: WebSocket, code: number, reason: string): void;
768
765
  /** A refused frame is dropped whole and its socket closed (1008 policy violation). */
@@ -13,7 +13,7 @@ import { CLOSE } from '../../crdt/providers/room.js';
13
13
  import { asEngineDoc } from '../../crdt/structs.js';
14
14
  import { suggestionsOf } from '../../crdt/doc/suggest.js';
15
15
  import { defaultSemantics, facadeConfigOf, semanticsDigest } from '../../crdt/semantics.js';
16
- import { MAX_REFUSALS, ROOM_ORIGIN, SOCKET_TAG, noTimers } from './shared.js';
16
+ import { MAX_REFUSALS, ROOM_ORIGIN, SOCKET_TAG, STALE_TAG, noTimers } from './shared.js';
17
17
  /** The engine API the room is built on (the Worker-safe CRDT entry). */
18
18
  export const crdt = E.bindCrdt(Y);
19
19
  export const sync = crdt.sync;
@@ -94,6 +94,7 @@ export class RoomContext {
94
94
  presence;
95
95
  access;
96
96
  comments;
97
+ reseed;
97
98
  #_facade = null;
98
99
  #_lookups = null;
99
100
  #_digest = null;
@@ -117,7 +118,8 @@ export class RoomContext {
117
118
  moves: `${prefix}moves`,
118
119
  late: `${prefix}late`,
119
120
  threads: `${prefix}threads`,
120
- comments: `${prefix}comments`
121
+ comments: `${prefix}comments`,
122
+ cuts: `${prefix}cuts`
121
123
  };
122
124
  this.clock = options.now ?? Date.now;
123
125
  }
@@ -137,9 +139,11 @@ export class RoomContext {
137
139
  /**
138
140
  * A facade over `doc` obeying the room's block roles (`semantics`), its
139
141
  * shared suggestions built: its `toJSON()` leaves the pending ones out.
142
+ * With `actor`, its writes are that actor's (a rebase's: `room.reseed.rebase`).
140
143
  */
141
- facadeOf(doc) {
142
- const facade = crdt.doc.create(asEngineDoc(doc), this.lookups);
144
+ facadeOf(doc, actor) {
145
+ const config = actor ? { ...this.lookups, actor: () => actor } : this.lookups;
146
+ const facade = crdt.doc.create(asEngineDoc(doc), config);
143
147
  suggestionsOf(facade);
144
148
  return facade;
145
149
  }
@@ -259,10 +263,17 @@ export class RoomContext {
259
263
  for (const piece of E.chunkFrame(bytes, this.limits.maxFrameBytes))
260
264
  ws.send(piece);
261
265
  }
266
+ /** The open sockets of the current epoch: what the room relays to (`room.reseed.protocol`). */
267
+ sockets() {
268
+ const stale = new Set(this.ctx.getWebSockets(STALE_TAG));
269
+ return this.ctx
270
+ .getWebSockets(SOCKET_TAG)
271
+ .filter((ws) => ws.readyState === WebSocket.OPEN && !stale.has(ws));
272
+ }
262
273
  broadcast(bytes, except) {
263
274
  const pieces = E.chunkFrame(bytes, this.limits.maxFrameBytes);
264
- for (const ws of this.ctx.getWebSockets(SOCKET_TAG)) {
265
- if (ws === except || ws.readyState !== WebSocket.OPEN)
275
+ for (const ws of this.sockets()) {
276
+ if (ws === except)
266
277
  continue;
267
278
  try {
268
279
  for (const piece of pieces)
@@ -9,7 +9,7 @@ export declare const isId: (value: unknown) => value is Id;
9
9
  * The clients of a frame (but `skip`) that write a per-writer block mark
10
10
  * of another writer: a struct whose key is `del.<n>` or `wd.<n>` on a
11
11
  * block node, from a client other than `n`. Only `n` writes its mark.
12
- * A struct in the purge horizon's root forges the room's.
12
+ * A struct in the purge horizon's root, or the epoch's, forges the room's.
13
13
  */
14
14
  export declare const forgedWriters: (doc: YDoc, structs: Struct[], skip: Set<number>) => Set<number>;
15
15
  /**
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Forged writes: what a frame writes or deletes that its sender may not —
3
3
  * another writer's per-writer block mark (`room.marks.writer`), the purge
4
- * horizon (the room's alone), another replica's binding or another user's
4
+ * horizon and the epoch (the room's alone), another replica's binding or another user's
5
5
  * profile in the attribution root (`room.attribution.trust`). Pure reads of
6
6
  * the room document and the frame; `Admission` strips or drops what they
7
7
  * find.
8
8
  */
9
9
  import { Y } from '../../crdt/engine.js';
10
- import { ATTRIBUTION_ROOT, DEL_PREFIX, HORIZON_ROOT, REGISTRY_KEY, SUGGEST_ROOT, WITHDRAW_PREFIX } from '../../crdt/schema.js';
10
+ import { ATTRIBUTION_ROOT, DEL_PREFIX, EPOCH_ROOT, HORIZON_ROOT, REGISTRY_KEY, SUGGEST_ROOT, WITHDRAW_PREFIX } from '../../crdt/schema.js';
11
11
  import { heldClock, runsOf, storedStruct, structAt } from './updates.js';
12
12
  // ── Per-writer marks ──────────────────────────────────────────
13
13
  /** The writer `n` of a per-writer block mark key (`del.<n>`, `wd.<n>`), or `null`. */
@@ -63,14 +63,16 @@ const isBlockNode = (doc, frame, parent) => {
63
63
  const item = parent?._item;
64
64
  return item?.parent === registry && registry !== undefined;
65
65
  };
66
- /** Is `parent` (a {@link Place}'s) the purge horizon's root, which only the room writes? */
67
- const isHorizon = (doc, parent) => parent === HORIZON_ROOT ||
68
- (parent != null && parent === doc.share.get(HORIZON_ROOT));
66
+ /**
67
+ * Is `parent` (a {@link Place}'s) a root only the room writes: the purge
68
+ * horizon's, or the epoch's (`room.reseed.protocol`)?
69
+ */
70
+ const isRoomRoot = (doc, parent) => [HORIZON_ROOT, EPOCH_ROOT].some((root) => parent === root || (parent != null && parent === doc.share.get(root)));
69
71
  /**
70
72
  * The clients of a frame (but `skip`) that write a per-writer block mark
71
73
  * of another writer: a struct whose key is `del.<n>` or `wd.<n>` on a
72
74
  * block node, from a client other than `n`. Only `n` writes its mark.
73
- * A struct in the purge horizon's root forges the room's.
75
+ * A struct in the purge horizon's root, or the epoch's, forges the room's.
74
76
  */
75
77
  export const forgedWriters = (doc, structs, skip) => {
76
78
  const frame = runsOf(structs);
@@ -83,7 +85,7 @@ export const forgedWriters = (doc, structs, skip) => {
83
85
  continue;
84
86
  const place = placeOf(doc, frame, struct.id);
85
87
  const writer = markWriter(place?.key);
86
- if ((place !== null && isHorizon(doc, place.parent)) ||
88
+ if ((place !== null && isRoomRoot(doc, place.parent)) ||
87
89
  (writer !== null && writer !== client && isBlockNode(doc, frame, place.parent))) {
88
90
  forged.add(client);
89
91
  }
@@ -113,8 +115,8 @@ export const forgedDeletes = (doc, ds, mayDelete) => {
113
115
  break;
114
116
  if (!(struct instanceof Y.Item) || struct.deleted)
115
117
  continue;
116
- // The purge horizon is the room's alone.
117
- if (!isHorizon(doc, struct.parent)) {
118
+ // The purge horizon and the epoch are the room's alone.
119
+ if (!isRoomRoot(doc, struct.parent)) {
118
120
  const writer = markWriter(struct.parentSub);
119
121
  if (writer === null || mayDelete(writer))
120
122
  continue;
@@ -29,6 +29,8 @@ export declare const readOnlyFrame: () => Uint8Array;
29
29
  export declare const readOnlyDenialFrame: () => Uint8Array;
30
30
  /** The denial of what a suggest-only socket may not write (stripped): it stays, and still syncs. */
31
31
  export declare const suggestOnlyDenialFrame: () => Uint8Array;
32
+ /** The room's epoch, to a socket that dialed an older one (`room.reseed.protocol`). */
33
+ export declare const epochFrame: (epoch: number) => Uint8Array;
32
34
  /** The socket's access, at the join and at a change in place (`write`, `suggest`, `comment`, `read`). */
33
35
  export declare const accessFrame: (access: string) => Uint8Array;
34
36
  /** A Step2 of what the room STORED: the engine's pending store (never stored) is left out. */
@@ -51,6 +51,8 @@ export const readOnlyFrame = () => E.frame(E.messageAuth, (e) => E.writeReadOnly
51
51
  export const readOnlyDenialFrame = () => E.frame(E.messageAuth, (e) => E.writePermissionDenied(e, READ_ONLY_DENIAL));
52
52
  /** The denial of what a suggest-only socket may not write (stripped): it stays, and still syncs. */
53
53
  export const suggestOnlyDenialFrame = () => E.frame(E.messageAuth, (e) => E.writePermissionDenied(e, SUGGEST_ONLY_DENIAL));
54
+ /** The room's epoch, to a socket that dialed an older one (`room.reseed.protocol`). */
55
+ export const epochFrame = (epoch) => E.frame(E.messageAuth, (e) => E.writeEpoch(e, epoch));
54
56
  /** The socket's access, at the join and at a change in place (`write`, `suggest`, `comment`, `read`). */
55
57
  export const accessFrame = (access) => E.frame(E.messageAuth, (e) => E.writeAccess(e, access));
56
58
  /** A Step2 of what the room STORED: the engine's pending store (never stored) is left out. */
@@ -7,7 +7,6 @@
7
7
  * the socket goes.
8
8
  */
9
9
  import { semanticsMismatch } from '../../crdt/semantics.js';
10
- import { SOCKET_TAG } from './shared.js';
11
10
  import { allowance } from './admission.js';
12
11
  import { encodeJSON } from './context.js';
13
12
  import { encodePresenceEntry, presenceFrame, presenceFrameOf } from './frames.js';
@@ -142,9 +141,7 @@ export class RoomPresence {
142
141
  * replica, and {@link drain} sends each recipient's in one frame.
143
142
  */
144
143
  fanOut(entries) {
145
- const sockets = this.room.ctx
146
- .getWebSockets(SOCKET_TAG)
147
- .filter((socket) => socket.readyState === WebSocket.OPEN);
144
+ const sockets = this.room.sockets();
148
145
  if (sockets.length === 0)
149
146
  return;
150
147
  if (this.waiting.size === 0 && this.take(sockets.length)) {
@@ -0,0 +1,93 @@
1
+ import * as E from '../../crdt/protocol.js';
2
+ import type { YDoc } from '../../crdt/index.js';
3
+ import type { Attachment, SocketIdentity } from '../DocumentRoom.js';
4
+ import type { Decoded } from './context.js';
5
+ import { type RoomContext } from './context.js';
6
+ /** An epoch a dial names (`?epoch=`; `new`: a document that holds nothing yet), or `null`. */
7
+ export declare const parseEpoch: (raw: string | null) => number | "new" | null;
8
+ export declare class RoomReseed {
9
+ #private;
10
+ private readonly room;
11
+ constructor(room: RoomContext);
12
+ /** The re-seed's settings (`null`: off). */
13
+ get config(): {
14
+ at: number;
15
+ idleMs: number;
16
+ gain: number;
17
+ force: number;
18
+ graceDays: number;
19
+ } | null;
20
+ /** The room's epoch: what its document says (`epoch.n`, which only the room writes), 0 before a re-seed. */
21
+ get epoch(): number;
22
+ /**
23
+ * A change was stored at `at` (`room.reseed.timing`): past the
24
+ * threshold the re-seed is due once nobody writes for `idleMs` (each
25
+ * change pushes it back), past `force` at once.
26
+ */
27
+ noteChange(at: number): void;
28
+ /**
29
+ * The `reseed` task: the cuts past their grace go (`room.reseed.grace`),
30
+ * then it re-seeds when still due (past the threshold and the floor); a
31
+ * re-seed that failed is due again at the first change a minute later or
32
+ * more. It is due again when the oldest cut left expires.
33
+ */
34
+ task(): void;
35
+ /**
36
+ * Re-seed now (`room.reseed.cut`): the new epoch replaces the rows, in
37
+ * one storage transaction with its cut; the replica registry, the
38
+ * purge's epochs and the last restore's step go (they name the old
39
+ * identities); the live document is rebuilt from the rows and every
40
+ * socket closed `4410`. `idle` (the task, once nobody wrote) waits while
41
+ * a suggestion is pending (an idle period later); `full` (the task, past
42
+ * `force` of the quota) drops them; both do nothing when the new epoch
43
+ * would give back less than `gain` (and wait for the document to grow by
44
+ * a quarter of `at` before trying again). `manual` (`reseed()`) runs
45
+ * whatever it gives back and drops them. Answers the new epoch, or `null`.
46
+ */
47
+ run(mode: 'idle' | 'full' | 'manual'): number | null;
48
+ /**
49
+ * The door (`RoomAccess.accept`, `room.reseed.protocol`): a dial of
50
+ * epoch `asked` is current, stale (an older epoch whose cuts are kept),
51
+ * or closed: none, in a room that re-seeded, `1008` (a client that
52
+ * predates the re-seed); any other epoch the room cannot rebase (past
53
+ * the cuts kept, or later than its own: the room lost it), `4410`, so
54
+ * the client drops its copy and loads the room's.
55
+ */
56
+ door(asked: number | 'new' | null): 'current' | 'stale' | {
57
+ code: number;
58
+ reason: string;
59
+ };
60
+ /**
61
+ * Whether `decoded`, from a socket of the current epoch, holds an older
62
+ * epoch's content (`room.reseed.protocol`): a struct of a client the
63
+ * cuts kept name, which the document holds nothing of (a tab took it
64
+ * from another tab of the old epoch). Its socket is closed `4410`.
65
+ */
66
+ holdsOld(decoded: Decoded, doc: YDoc): boolean;
67
+ /** The largest frame a socket of an older epoch may send: its whole old document. */
68
+ get staleFrameBytes(): number;
69
+ /**
70
+ * A stale socket joined: one that may write is told the room's epoch
71
+ * (its provider then hands over its old document); any other has no
72
+ * edit to merge and is closed `4410`. Past its user's rebase rate it is
73
+ * closed `1011` before it uploads anything: its provider dials again
74
+ * later.
75
+ */
76
+ join(ws: WebSocket, identity: SocketIdentity): void;
77
+ /**
78
+ * A stale socket's message: its old document (`messageRebase`) is
79
+ * rebased onto a copy of the current one, and what that wrote is
80
+ * admitted as an update of its user, from a client id of its own (the
81
+ * admission's quotas, access, validation and attribution, as any
82
+ * update); then it is closed `4410`. The merged state is kept per user
83
+ * and cut (`merged:<user>`), so another tab's upload merges nothing
84
+ * twice and another user's cannot claim it. A hand-over that cannot be
85
+ * read or rebased is dropped, its socket closed `4410` all the same (its
86
+ * client loads the room's document); past the rebase rate (one per user
87
+ * every ten seconds), `1011`, and the provider hands over again later.
88
+ * Anything else a stale socket sends is dropped.
89
+ */
90
+ onStale(ws: WebSocket, attachment: Attachment, type: number, decoder: E.Decoder): void;
91
+ /** Drop the cuts older than the grace, with what their rebases merged (`room.reseed.grace`). */
92
+ prune(at: number): void;
93
+ }