edytor 0.1.0-next.23 → 0.1.0-next.25

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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/cloudflare/DocumentRoom.d.ts +264 -10
  3. package/dist/cloudflare/DocumentRoom.js +867 -58
  4. package/dist/cloudflare/history.d.ts +110 -0
  5. package/dist/cloudflare/history.js +125 -0
  6. package/dist/cloudflare/index.d.ts +4 -2
  7. package/dist/cloudflare/index.js +4 -2
  8. package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
  9. package/dist/cloudflare/routeDocumentSocket.js +82 -17
  10. package/dist/crdt/data.js +2 -1
  11. package/dist/crdt/document.d.ts +151 -0
  12. package/dist/crdt/document.js +11 -146
  13. package/dist/crdt/edytor-doc.d.ts +137 -4
  14. package/dist/crdt/edytor-doc.js +54 -2
  15. package/dist/crdt/engine.d.ts +3 -1
  16. package/dist/crdt/engine.js +4 -1
  17. package/dist/crdt/index.d.ts +29 -4
  18. package/dist/crdt/index.js +25 -1
  19. package/dist/crdt/migration/generation.d.ts +58 -0
  20. package/dist/crdt/migration/generation.js +68 -0
  21. package/dist/crdt/migration/migrate.js +1 -1
  22. package/dist/crdt/placement/model.d.ts +32 -0
  23. package/dist/crdt/placement/model.js +151 -7
  24. package/dist/crdt/placement/rank.d.ts +38 -24
  25. package/dist/crdt/placement/rank.js +147 -93
  26. package/dist/crdt/protocols/envelope.d.ts +9 -3
  27. package/dist/crdt/protocols/envelope.js +9 -3
  28. package/dist/crdt/protocols/sync.d.ts +13 -6
  29. package/dist/crdt/protocols/sync.js +31 -9
  30. package/dist/crdt/providers/index.js +2 -1
  31. package/dist/crdt/providers/indexeddb.d.ts +10 -0
  32. package/dist/crdt/providers/indexeddb.js +53 -6
  33. package/dist/crdt/providers/room.js +16 -1
  34. package/dist/crdt/purge.d.ts +46 -0
  35. package/dist/crdt/purge.js +206 -0
  36. package/dist/crdt/restore.d.ts +63 -0
  37. package/dist/crdt/restore.js +217 -0
  38. package/dist/crdt/schema.d.ts +9 -1
  39. package/dist/crdt/schema.js +12 -1
  40. package/dist/crdt/semantics.d.ts +46 -1
  41. package/dist/crdt/semantics.js +32 -3
  42. package/dist/crdt/text/deletes.d.ts +17 -0
  43. package/dist/crdt/text/deletes.js +218 -21
  44. package/dist/crdt/text/marks.d.ts +58 -0
  45. package/dist/crdt/text/marks.js +85 -0
  46. package/dist/crdt/text/model.d.ts +1 -0
  47. package/dist/crdt/text/model.js +33 -8
  48. package/dist/crdt/text/runs.js +48 -3
  49. package/dist/crdt/vendor/yjs/UPSTREAM.md +40 -0
  50. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -0
  51. package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +83 -0
  52. package/dist/crdt/vendor/yjs/dts/utils/transaction-helpers.d.ts +1 -1
  53. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +25 -0
  54. package/dist/crdt/vendor/yjs/src/structs/Item.js +12 -2
  55. package/dist/crdt/vendor/yjs/src/utils/Doc.js +6 -0
  56. package/dist/crdt/vendor/yjs/src/utils/RangeCursor.js +2 -0
  57. package/dist/crdt/vendor/yjs/src/utils/marks.js +149 -0
  58. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +10 -4
  59. package/dist/crdt/vendor/yjs/src/ynode.js +226 -2
  60. package/dist/edytor.svelte.js +19 -2
  61. package/dist/plugins/richtext/RichTextPlugin.svelte +2 -2
  62. package/dist/session/editing/text.d.ts +6 -2
  63. package/package.json +1 -1
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  Edytor aims to be for Svelte what Slate.js is for React: a heavily customizable editor with an API to build any kind of collaborative rich text editor.
17
17
 
18
- > **Work in progress.** Edytor is a pre-release (`0.1.0-next.23`, `edytor@next` on npm) and not ready for production; the API changes between releases without a compatibility layer. The untagged `edytor@0.0.11` on npm predates the current API. Issues and PRs are welcome: when you report a bug, include the document's JSON value.
18
+ > **Work in progress.** Edytor is a pre-release (`0.1.0-next.25`, `edytor@next` on npm) and not ready for production; the API changes between releases without a compatibility layer. The untagged `edytor@0.0.11` on npm predates the current API. Issues and PRs are welcome: when you report a bug, include the document's JSON value.
19
19
 
20
20
  ## Features
21
21
 
@@ -95,6 +95,8 @@
95
95
  */
96
96
  import { DurableObject } from 'cloudflare:workers';
97
97
  import * as E from '../crdt/index.js';
98
+ import type { PurgeReport } from '../crdt/purge.js';
99
+ import { type HistoryEntry, type HistoryOptions, type KVLike } from './history.js';
98
100
  import type { AwarenessEntry, DocumentSemanticsConfig, EdytorDoc, JSONBlock, JSONDoc, YDoc } from '../crdt/index.js';
99
101
  /** Under SQLite-backed Durable Objects' 2 MB row cap, with room for the other columns. */
100
102
  export declare const DEFAULT_MAX_ROW_BYTES: number;
@@ -135,6 +137,16 @@ export type ReplicaOwner = {
135
137
  replica: number;
136
138
  user: string;
137
139
  };
140
+ /** What `restoreHistory` did (`room.history.restore`): `refused` when the room holds no such version. */
141
+ export type RestoreResult = {
142
+ status: 'applied' | 'noop' | 'refused';
143
+ key: string;
144
+ revived?: number;
145
+ moved?: number;
146
+ rewritten?: number;
147
+ created?: number;
148
+ deleted?: number;
149
+ };
138
150
  /** The document as `onSave` receives it. */
139
151
  export type SavedDocument = {
140
152
  /** The document as JSON. */
@@ -154,6 +166,14 @@ export type LoadedDocument = JSONDoc | Uint8Array | Pick<SavedDocument, 'update'
154
166
  export declare const PROBE_HEADER = "X-Edytor-Probe";
155
167
  /** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
156
168
  export declare const ROOM_ORIGIN: unique symbol;
169
+ /** The transaction origin of a history restore (H11): the room's restore history tracks it. */
170
+ export declare const RESTORE_ORIGIN: unique symbol;
171
+ /** The transaction origin of the room's purge (H7): tracked by no history. */
172
+ export declare const PURGE_ORIGIN: unique symbol;
173
+ /** The header `routeDocumentHistory` sets on an authorized history request (H11). */
174
+ export declare const HISTORY_HEADER = "X-Edytor-History";
175
+ /** The version key of a history `read` or `restore` request. */
176
+ export declare const HISTORY_KEY_HEADER = "X-Edytor-History-Key";
157
177
  /**
158
178
  * Headers carrying the identity `routeDocumentSocket` verified — never the
159
179
  * client's. The user id is percent-encoded (`encodeURIComponent`): a header
@@ -179,6 +199,17 @@ export type DocumentRoomEnv = {
179
199
  EDYTOR_MAX_UPDATES_PER_SECOND?: string | number;
180
200
  /** `off`: the room logs nothing (default: one JSON line per compaction, quota hit, denial, fault). */
181
201
  EDYTOR_LOG?: string;
202
+ /**
203
+ * Days after which deleted content is purged (H7; default: the history
204
+ * retention, else 30); `off` never purges.
205
+ */
206
+ EDYTOR_PURGE_AFTER_DAYS?: string | number;
207
+ /** A KV namespace binding: the room keeps its version history there (H11, `history()`). */
208
+ EDYTOR_HISTORY?: KVLike;
209
+ /** Days a version is kept (default 30). */
210
+ EDYTOR_HISTORY_RETENTION_DAYS?: string | number;
211
+ /** The IANA time zone of the history's half-day slots (default `UTC`). */
212
+ EDYTOR_HISTORY_TIME_ZONE?: string;
182
213
  };
183
214
  /** What a socket is bound to: its verified user, its replica (Yjs client id), its access. */
184
215
  export type SocketIdentity = {
@@ -192,7 +223,7 @@ export type Attachment = SocketIdentity & {
192
223
  clock: number | null;
193
224
  };
194
225
  export type Refusal = {
195
- reason: 'generation' | 'schema' | 'malformed' | 'container' | 'identity' | 'replica' | 'read-only' | 'waiting' | 'storage' | 'internal' | 'quota' | 'mark' | 'denied' | 'orphan' | 'relayed';
226
+ reason: 'generation' | 'schema' | 'malformed' | 'container' | 'identity' | 'replica' | 'read-only' | 'waiting' | 'storage' | 'internal' | 'quota' | 'mark' | 'denied' | 'orphan' | 'relayed' | 'history';
196
227
  detail: unknown;
197
228
  };
198
229
  /** A duration series: how many, their total and longest (ms; see {@link RoomMetrics}). */
@@ -239,6 +270,17 @@ export type RoomMetrics = {
239
270
  validationDenials: number;
240
271
  /** Every refusal, by reason (as `refusalCounts`). */
241
272
  refusals: Partial<Record<Refusal['reason'], number>>;
273
+ /** Versions written to the history (H11), skipped (`history` refusals), and the last key written. */
274
+ history: {
275
+ written: number;
276
+ skipped: number;
277
+ lastKey: string | null;
278
+ };
279
+ /** Purges run (H7) and what they wrote, summed; the last horizon purged (ms since the epoch). */
280
+ purge: PurgeReport & {
281
+ runs: number;
282
+ horizon: number | null;
283
+ };
242
284
  /** When this instance started (ms since the epoch). */
243
285
  since: number;
244
286
  };
@@ -250,6 +292,20 @@ export type RoomLogEntry = {
250
292
  records: number;
251
293
  rows: number;
252
294
  } | {
295
+ edytor: 'history';
296
+ key: string;
297
+ bytes: number;
298
+ editors: number;
299
+ } | {
300
+ edytor: 'restore';
301
+ key: string;
302
+ user: string | null;
303
+ undo: boolean;
304
+ } | ({
305
+ edytor: 'purge';
306
+ horizon: number;
307
+ bytes: number;
308
+ } & PurgeReport) | {
253
309
  edytor: 'quota';
254
310
  user: string;
255
311
  quota: string;
@@ -261,6 +317,12 @@ export type RoomLogEntry = {
261
317
  edytor: 'fault';
262
318
  reason: 'storage' | 'internal';
263
319
  detail: string;
320
+ } | {
321
+ edytor: 'convert';
322
+ from: number;
323
+ to: number;
324
+ blocks: number;
325
+ bytes: number;
264
326
  };
265
327
  /** `pending`: deletes of items the room does not hold yet (they wait for them). */
266
328
  type RowKind = 'generation' | 'update' | 'snapshot' | 'pending';
@@ -375,6 +437,22 @@ export type AttachDocumentOptions = {
375
437
  * {@link E.defaultSemantics} (the bundled rich-text, code and image kinds).
376
438
  */
377
439
  semantics?: DocumentSemanticsConfig;
440
+ /**
441
+ * Version history in KV (H11, `room.history.*`): the room writes its
442
+ * state twice a day — the morning's at local noon, the evening's at
443
+ * midnight, in `timeZone` — when it changed, for `retentionDays`; read
444
+ * and restore them with `listHistory`, `readHistory`, `restoreHistory`
445
+ * and `undoRestore` (or `routeDocumentHistory`). Without it, no history.
446
+ */
447
+ history?: HistoryOptions;
448
+ /**
449
+ * Days after which deleted content is purged from the room and every
450
+ * replica (H7, `room.purge.*`): default the history's `retentionDays`,
451
+ * else 30; `false` never purges.
452
+ */
453
+ purgeAfterDays?: number | false;
454
+ /** The room's clock, ms since the epoch (default `Date.now`): its slots, epochs and alarm read it. */
455
+ now?: () => number;
378
456
  };
379
457
  /** The tag of the document's sockets: other sockets of the object are left to you. */
380
458
  export declare const SOCKET_TAG = "edytor";
@@ -409,6 +487,10 @@ export declare class AttachedDocument {
409
487
  } | {
410
488
  kind: 'restored';
411
489
  records: number;
490
+ } | {
491
+ kind: 'converted';
492
+ from: number;
493
+ records: number;
412
494
  };
413
495
  private nextRecord;
414
496
  private updates;
@@ -442,14 +524,33 @@ export declare class AttachedDocument {
442
524
  private retryable;
443
525
  private readonly sql;
444
526
  private _facade;
445
- private saveScheduled;
446
527
  /** Alarms in a row that found the rows unreadable: each re-arms later. */
447
528
  private saveRetries;
529
+ /** Each alarm task's due time (`room.alarm.tasks`), mirrored in the meta table (`due.<task>`). */
530
+ private dues;
531
+ /** The alarm this instance set, or found set at its start (`null`: none). */
532
+ private armed;
533
+ /** The room's clock (`now`). */
534
+ private readonly clock;
535
+ /** The verified users the open slot recorded (memory: a wake records them again, `OR IGNORE`). */
536
+ private readonly slotEditors;
537
+ /** Who the room writes for while a restore or its undo runs (the slot's editor). */
538
+ private writer;
539
+ /** The last restore's step (`room.history.undo`), read from the `restore` table at load. */
540
+ private restoreStep;
541
+ /** The history recording restores (`RESTORE_ORIGIN`), on the live facade. */
542
+ private restoreManager;
543
+ /** The version writes in flight (`historyWritten()`). */
544
+ private writing;
448
545
  private readonly options;
449
546
  private readonly rowsTable;
450
547
  private readonly replicasTable;
451
548
  private readonly metaTable;
549
+ private readonly epochsTable;
550
+ private readonly editorsTable;
551
+ private readonly restoreTable;
452
552
  private _lookups;
553
+ private _history;
453
554
  constructor(ctx: DurableObjectState, options?: AttachDocumentOptions);
454
555
  /**
455
556
  * Restore from storage, or seed from `onLoad`. Never throws: a throw
@@ -508,15 +609,33 @@ export declare class AttachedDocument {
508
609
  /** The document as JSON. */
509
610
  read(): JSONDoc;
510
611
  /**
511
- * Runs `onSave` — the object's alarm, `saveAfter` ms after the first
512
- * unsaved change. While the rows cannot be read (a retryable failure),
513
- * it starts the room again; if that fails too, the save stays due: the
514
- * alarm is set again, later at each failure (up to 5 minutes), so the
515
- * mirror never silently lags what the room stored.
612
+ * The room's one alarm (`room.alarm.tasks`): every task due at its time
613
+ * runs — the history slot (`room.history.slots`), `onSave`, the purge
614
+ * (`room.purge.timing`) — each re-arming itself or clearing its due
615
+ * row; then the alarm is set to the earliest due time left. While the
616
+ * rows cannot be read (a retryable failure), it starts the room again;
617
+ * if that fails too, the save stays due, later at each failure (up to 5
618
+ * minutes), so the mirror never silently lags what the room stored. A
619
+ * throw from `onSave` keeps it due and is rethrown (the platform retries).
516
620
  */
517
621
  alarm(): Promise<void>;
518
- /** The first unsaved change arms the save alarm; a wake keeps one already armed (`start`). */
622
+ /** The `save` task: `onSave` with the document, or the save kept due while the rows cannot be read. */
623
+ private save;
624
+ /** The first unsaved change arms the save alarm; a save already due keeps its time. */
519
625
  private scheduleSave;
626
+ /**
627
+ * Arm `task` at `at` (`room.alarm.tasks`): a task already due keeps its
628
+ * time (a wake or an edit never moves a pending save), unless `earlier`
629
+ * (an earlier time wins: the purge tick) or `replace`. The due time is
630
+ * stored (meta `due.<task>`) and the alarm set to the earliest one.
631
+ */
632
+ private schedule;
633
+ /** Clear `task`'s due time. */
634
+ private unschedule;
635
+ /** Set the alarm to the earliest due time (none due: the alarm set is left, and finds nothing). */
636
+ private arm;
637
+ /** The due times stored (`due.<task>`); an alarm armed by 0.1.0-next.23 (none stored) is a due save. */
638
+ private readDues;
520
639
  /**
521
640
  * Generation cutover: drop the stored document (both tables) of a room
522
641
  * whose storage belongs to another edytor generation, and start again
@@ -542,6 +661,18 @@ export declare class AttachedDocument {
542
661
  * engine refuses) is refused for good.
543
662
  */
544
663
  private load;
664
+ /**
665
+ * The generation cutover at load (`room.generation.convert`): the
666
+ * records of a generation-4 container are read as JSON
667
+ * (`crdt.generations.previousJSON`), which this generation seeds as
668
+ * `onLoad`'s JSON is (deterministic: every replica converting the same
669
+ * state writes the same update), and the container is replaced by it in
670
+ * one storage transaction: rows, replicas, purge epochs, slot editors
671
+ * and the stored restore go; the document data, the alarm's due tasks
672
+ * and `lastUpdated` stay. What a generation-4 client never sent is not
673
+ * in it; history, attribution and CRDT identity do not cross.
674
+ */
675
+ private convert;
545
676
  /** The bytes of the stored `pending` records (uncompressed, as every record but a snapshot). */
546
677
  private pendingBytes;
547
678
  /**
@@ -600,6 +731,93 @@ export declare class AttachedDocument {
600
731
  * (`GET <room>?lastUpdated`), also over RPC.
601
732
  */
602
733
  lastUpdated(): number | null;
734
+ /** The room's id: its name (`getByName`), else the object's id. */
735
+ private get roomId();
736
+ /** The history settings, resolved at first use (`null`: none; an unknown time zone is noted `history`). */
737
+ private get historyConfig();
738
+ /** Days after which deleted content is purged (`null`: never), `room.purge.timing`. */
739
+ private get purgeDays();
740
+ /** The last restore's deleted content, kept for its undo (`prepareRoomDoc`). */
741
+ private get restoreKeep();
742
+ /** The verified user a stored change is written for (a socket's, or the restorer's), or `null`. */
743
+ private editorOf;
744
+ /**
745
+ * A change was stored: arm the purge tick (`room.purge.timing`) and open
746
+ * the history slot it falls in (`room.history.slots`) — a purge opens
747
+ * none: it changes nothing a reader sees.
748
+ */
749
+ private noteChange;
750
+ /** Before a write: a slot past its end is captured as it is, and written (`room.history.slots`). */
751
+ private closeSlotIfPast;
752
+ /** Close the open slot: capture it now, write it (the alarm, a start past its end). */
753
+ private closeSlot;
754
+ /**
755
+ * The open slot's version, read now (synchronous: nothing writes
756
+ * between the read and the slot's close), and the slot closed.
757
+ */
758
+ private captureSlot;
759
+ /** Write a captured version (`room.history.value`); a failure is noted `history` and the slot skipped. */
760
+ private putSlot;
761
+ private skipVersion;
762
+ /** The history settings, or a throw: the room keeps no history. */
763
+ private requireHistory;
764
+ /**
765
+ * Resolves once the versions the room is writing are stored (a slot a
766
+ * write closed is written in the background).
767
+ */
768
+ historyWritten(): Promise<void>;
769
+ /**
770
+ * The room's versions, newest first (H11): one per half-day slot that
771
+ * changed, for the retention (`room.history.slots`). Throws when the
772
+ * room keeps no history.
773
+ */
774
+ listHistory(): Promise<HistoryEntry[]>;
775
+ /** Version `key` as JSON (a preview), or `null` when this room holds no such version. */
776
+ readHistory(key: string): Promise<JSONDoc | null>;
777
+ /**
778
+ * Restore version `key` as a forward edit (`room.history.restore`): one
779
+ * room transaction makes the visible document equal the version, keeping
780
+ * every id the registry holds and writing only what differs; stored,
781
+ * relayed to every socket, one step `undoRestore` undoes. `user` (the
782
+ * restorer) is recorded as the slot's editor and in the log. `refused`:
783
+ * this room holds no such version (expired, another room's key).
784
+ */
785
+ restoreHistory(key: string, options?: {
786
+ user?: string;
787
+ }): Promise<RestoreResult>;
788
+ /**
789
+ * Undo the last restore (`room.history.undo`): the history undo of
790
+ * exactly its transaction, edits made since kept. One level: `noop` when
791
+ * no restore stands (none, undone already, or past the purge horizon).
792
+ */
793
+ undoRestore(options?: {
794
+ user?: string;
795
+ }): Promise<{
796
+ status: 'applied' | 'noop';
797
+ }>;
798
+ /** The history recording restores, on the live facade (`RESTORE_ORIGIN` only, one step each). */
799
+ private restoreHistoryOf;
800
+ /** The last restore's step, from its table (`null`: none). */
801
+ private readRestore;
802
+ /**
803
+ * Forget the last restore's step: its table row goes, then what it kept
804
+ * is released and collected (the next compaction stores it collected).
805
+ */
806
+ private dropRestore;
807
+ /**
808
+ * The purge task, also over RPC (`room.purge.timing`): record an epoch
809
+ * (the state vector, now) when the document changed since the last one,
810
+ * purge what was deleted before the horizon — the newest epoch at least
811
+ * `purgeAfterDays` old — when it is newer than the last purged, and
812
+ * re-arm for the next epoch to pass it. Returns what the purge wrote, or
813
+ * `null` when nothing was past the horizon.
814
+ */
815
+ purge(): (PurgeReport & {
816
+ horizon: number;
817
+ }) | null;
818
+ private tick;
819
+ /** Purge what was deleted before `horizon`, as the room's own transaction, then compact. */
820
+ private purgeTo;
603
821
  /** The room's counters (H14), also over RPC — see {@link RoomMetrics}. */
604
822
  metrics(): RoomMetrics;
605
823
  /** What the rows take: bytes, logical records, rows. */
@@ -687,6 +905,13 @@ export declare class AttachedDocument {
687
905
  */
688
906
  private attribute;
689
907
  fetch(request: Request): Promise<Response>;
908
+ /**
909
+ * One history request (H11): `list`, `read` (a version as JSON), and,
910
+ * for a write identity, `restore` and `undo`. `404` when the room keeps
911
+ * no history or holds no such version, `403` for a read-only identity's
912
+ * write, `503` when the room cannot serve it.
913
+ */
914
+ private historyRequest;
690
915
  /** Is `ws` one of this document's sockets? */
691
916
  owns(ws: WebSocket): boolean;
692
917
  webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): boolean;
@@ -806,7 +1031,8 @@ export declare class AttachedDocument {
806
1031
  * `edytor_replicas`) live beside yours and its sockets carry
807
1032
  * {@link SOCKET_TAG}. Each handler your class does not define —
808
1033
  * `fetch`, `webSocketMessage`, `webSocketClose`, `webSocketError`, and
809
- * `alarm` when `onSave` is set — is installed on the object; a class that
1034
+ * `alarm` unless the document needs none (no `onSave`, no `history`,
1035
+ * `purgeAfterDays: false`) — is installed on the object; a class that
810
1036
  * defines one delegates to the returned document's method (which returns
811
1037
  * `false` for a socket that is not the document's).
812
1038
  */
@@ -840,12 +1066,40 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
840
1066
  * at first use (after construction: it may return a subclass field).
841
1067
  */
842
1068
  protected semantics(): DocumentSemanticsConfig;
1069
+ /**
1070
+ * Version history — see {@link AttachDocumentOptions.history}. Default:
1071
+ * the `EDYTOR_HISTORY` KV binding, with `EDYTOR_HISTORY_RETENTION_DAYS`
1072
+ * and `EDYTOR_HISTORY_TIME_ZONE`; none without it. Read once, at first use.
1073
+ */
1074
+ protected history(): HistoryOptions | undefined;
1075
+ /** The room's clock — see {@link AttachDocumentOptions.now}. */
1076
+ protected now(): number;
843
1077
  fetch(request: Request): Promise<Response>;
844
1078
  webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void;
845
1079
  webSocketClose(ws: WebSocket, code: number, reason: string): void;
846
1080
  webSocketError(ws: WebSocket): void;
847
- /** Runs `onSave`. Call `super.alarm()` if you override it. */
1081
+ /** Runs the room's tasks: `onSave`, the history slots, the purge. Call `super.alarm()` if you override it. */
848
1082
  alarm(): Promise<void>;
1083
+ /** The versions, newest first, also over RPC — see {@link AttachedDocument.listHistory}. */
1084
+ listHistory(): Promise<HistoryEntry[]>;
1085
+ /** A version as JSON, also over RPC — see {@link AttachedDocument.readHistory}. */
1086
+ readHistory(key: string): Promise<JSONDoc | null>;
1087
+ /** Restore a version, also over RPC — see {@link AttachedDocument.restoreHistory}. */
1088
+ restoreHistory(key: string, options?: {
1089
+ user?: string;
1090
+ }): Promise<RestoreResult>;
1091
+ /** Undo the last restore, also over RPC — see {@link AttachedDocument.undoRestore}. */
1092
+ undoRestore(options?: {
1093
+ user?: string;
1094
+ }): Promise<{
1095
+ status: 'applied' | 'noop';
1096
+ }>;
1097
+ /** The version writes in flight, also over RPC — see {@link AttachedDocument.historyWritten}. */
1098
+ historyWritten(): Promise<void>;
1099
+ /** Run the purge task now, also over RPC — see {@link AttachedDocument.purge}. */
1100
+ purge(): (PurgeReport & {
1101
+ horizon: number;
1102
+ }) | null;
849
1103
  /** Compaction, also over RPC. */
850
1104
  compact(): {
851
1105
  rows: number;