edytor 0.1.0-next.25 → 0.1.0-next.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cloudflare/DocumentRoom.d.ts +172 -5
- package/dist/cloudflare/DocumentRoom.js +611 -31
- package/dist/cloudflare/history.d.ts +144 -14
- package/dist/cloudflare/history.js +225 -6
- package/dist/cloudflare/index.d.ts +3 -1
- package/dist/cloudflare/index.js +3 -1
- package/dist/cloudflare/locks.d.ts +31 -0
- package/dist/cloudflare/locks.js +31 -0
- package/dist/cloudflare/move.d.ts +159 -0
- package/dist/cloudflare/move.js +169 -0
- package/dist/cloudflare/routeDocumentSocket.js +18 -11
- package/dist/collaboration/index.d.ts +1 -1
- package/dist/collaboration/index.js +1 -1
- package/dist/collaboration/providers.d.ts +2 -0
- package/dist/collaboration/providers.js +2 -0
- package/dist/collaboration/remoteSelection.js +10 -1
- package/dist/components/Edytor.svelte +26 -0
- package/dist/components/Edytor.svelte.d.ts +9 -0
- package/dist/crdt/edytor-doc.d.ts +4 -4
- package/dist/crdt/edytor-doc.js +3 -2
- package/dist/crdt/engine.d.ts +1 -0
- package/dist/crdt/engine.js +2 -0
- package/dist/crdt/incarnations.d.ts +69 -0
- package/dist/crdt/incarnations.js +51 -0
- package/dist/crdt/index.d.ts +5 -4
- package/dist/crdt/placement/model.js +14 -3
- package/dist/crdt/providers/index.d.ts +2 -0
- package/dist/crdt/providers/index.js +21 -0
- package/dist/crdt/purge.js +27 -2
- package/dist/crdt/restore.js +6 -2
- package/dist/crdt/text/model.d.ts +37 -5
- package/dist/crdt/text/model.js +132 -14
- package/dist/crdt/text/runs.js +365 -26
- package/dist/crdt/vendor/yjs/UPSTREAM.md +35 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +13 -0
- package/dist/crdt/vendor/yjs/dts/utils/transaction-helpers.d.ts +1 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/Item.js +7 -3
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +14 -0
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +20 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/blockHandles/BlockHandles.svelte +9 -2
- package/dist/surface/overlay.d.ts +11 -0
- package/dist/surface/overlay.js +16 -0
- package/dist/themes/notion.css +18 -0
- 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.
|
|
18
|
+
> **Work in progress.** Edytor is a pre-release (`0.1.0-next.26`, `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
|
|
|
@@ -96,7 +96,9 @@
|
|
|
96
96
|
import { DurableObject } from 'cloudflare:workers';
|
|
97
97
|
import * as E from '../crdt/index.js';
|
|
98
98
|
import type { PurgeReport } from '../crdt/purge.js';
|
|
99
|
-
import { type HistoryEntry, type HistoryOptions, type KVLike } from './history.js';
|
|
99
|
+
import { type HistoryEntry, type HistoryOptions, type KVLike, type R2BucketLike } from './history.js';
|
|
100
|
+
import { type LockOptions } from './locks.js';
|
|
101
|
+
import { type CommitResult, type ExportedBlocks, type ImportReceipt, type ImportRequest, type LateEditBatch, type MoveNamespace } from './move.js';
|
|
100
102
|
import type { AwarenessEntry, DocumentSemanticsConfig, EdytorDoc, JSONBlock, JSONDoc, YDoc } from '../crdt/index.js';
|
|
101
103
|
/** Under SQLite-backed Durable Objects' 2 MB row cap, with room for the other columns. */
|
|
102
104
|
export declare const DEFAULT_MAX_ROW_BYTES: number;
|
|
@@ -162,7 +164,7 @@ export type SavedDocument = {
|
|
|
162
164
|
};
|
|
163
165
|
/** What `onLoad` may return: JSON, a bare v14 update, or `{ update, replicas }` (a `SavedDocument`). */
|
|
164
166
|
export type LoadedDocument = JSONDoc | Uint8Array | Pick<SavedDocument, 'update' | 'replicas'>;
|
|
165
|
-
/** The header `routeDocumentSocket` sets on an authorized `lastUpdated`
|
|
167
|
+
/** The header `routeDocumentSocket` sets on an authorized probe: `lastUpdated` (H12) or `snapshot` (P8). */
|
|
166
168
|
export declare const PROBE_HEADER = "X-Edytor-Probe";
|
|
167
169
|
/** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
|
|
168
170
|
export declare const ROOM_ORIGIN: unique symbol;
|
|
@@ -204,12 +206,24 @@ export type DocumentRoomEnv = {
|
|
|
204
206
|
* retention, else 30); `off` never purges.
|
|
205
207
|
*/
|
|
206
208
|
EDYTOR_PURGE_AFTER_DAYS?: string | number;
|
|
207
|
-
/**
|
|
208
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Where the room keeps its version history (H11, `history()`): a KV
|
|
211
|
+
* namespace or an R2 bucket binding, or the string `room` (the room's own
|
|
212
|
+
* storage, {@link roomHistory}). Unset: no history.
|
|
213
|
+
*/
|
|
214
|
+
EDYTOR_HISTORY?: KVLike | R2BucketLike | 'room';
|
|
209
215
|
/** Days a version is kept (default 30). */
|
|
210
216
|
EDYTOR_HISTORY_RETENTION_DAYS?: string | number;
|
|
211
217
|
/** The IANA time zone of the history's half-day slots (default `UTC`). */
|
|
212
218
|
EDYTOR_HISTORY_TIME_ZONE?: string;
|
|
219
|
+
/** Per-block locks (`locks()`): the data key naming a block's owner, e.g. `lockedBy`. Unset: none. */
|
|
220
|
+
EDYTOR_LOCKS?: string;
|
|
221
|
+
/**
|
|
222
|
+
* The namespace of the rooms blocks move to (`rooms()`, H10): the room
|
|
223
|
+
* forwards late edits of moved blocks there itself. Unset: they wait
|
|
224
|
+
* for the host's `forwardLateEdits`.
|
|
225
|
+
*/
|
|
226
|
+
EDYTOR_ROOMS?: MoveNamespace;
|
|
213
227
|
};
|
|
214
228
|
/** What a socket is bound to: its verified user, its replica (Yjs client id), its access. */
|
|
215
229
|
export type SocketIdentity = {
|
|
@@ -323,6 +337,19 @@ export type RoomLogEntry = {
|
|
|
323
337
|
to: number;
|
|
324
338
|
blocks: number;
|
|
325
339
|
bytes: number;
|
|
340
|
+
} | {
|
|
341
|
+
edytor: 'move';
|
|
342
|
+
moveId: string;
|
|
343
|
+
role: 'out' | 'in';
|
|
344
|
+
peer: string;
|
|
345
|
+
blocks: number;
|
|
346
|
+
} | {
|
|
347
|
+
edytor: 'late';
|
|
348
|
+
moveId: string;
|
|
349
|
+
seq: number;
|
|
350
|
+
edits: number;
|
|
351
|
+
structural: number;
|
|
352
|
+
forwarded: boolean;
|
|
326
353
|
};
|
|
327
354
|
/** `pending`: deletes of items the room does not hold yet (they wait for them). */
|
|
328
355
|
type RowKind = 'generation' | 'update' | 'snapshot' | 'pending';
|
|
@@ -424,6 +451,19 @@ export type AttachDocumentOptions = {
|
|
|
424
451
|
* when set (the room then keeps an index of every block's state).
|
|
425
452
|
*/
|
|
426
453
|
validate?: (frame: FrameValidation) => boolean | void;
|
|
454
|
+
/**
|
|
455
|
+
* Per-block locks (H10, `room.locks`): a block whose data names a user
|
|
456
|
+
* under `key` (default `lockedBy`) is that user's alone — a frame from
|
|
457
|
+
* anyone else touching it is denied and compensated, as `validate`'s
|
|
458
|
+
* denials are ({@link lockedBlocks}). Asked before `validate`.
|
|
459
|
+
*/
|
|
460
|
+
locks?: LockOptions;
|
|
461
|
+
/**
|
|
462
|
+
* The rooms blocks move to (H10, `room.move`): with it, the room forwards
|
|
463
|
+
* the late edits of blocks it moved out to their destination itself, on
|
|
464
|
+
* its alarm; without it, they wait for the host's `forwardLateEdits`.
|
|
465
|
+
*/
|
|
466
|
+
rooms?: () => MoveNamespace | undefined;
|
|
427
467
|
/**
|
|
428
468
|
* The room's log (H14): one entry per compaction, quota hit, denial or
|
|
429
469
|
* fault. Default: `console.log` of the entry as JSON (Workers Logs and
|
|
@@ -549,7 +589,13 @@ export declare class AttachedDocument {
|
|
|
549
589
|
private readonly epochsTable;
|
|
550
590
|
private readonly editorsTable;
|
|
551
591
|
private readonly restoreTable;
|
|
592
|
+
private readonly movesTable;
|
|
593
|
+
private readonly lateTable;
|
|
594
|
+
/** The blocks moved out and watched for late edits, by id → their move (`null`: not read yet). */
|
|
595
|
+
private watched;
|
|
552
596
|
private _lookups;
|
|
597
|
+
private readonly tablePrefix;
|
|
598
|
+
private _validator;
|
|
553
599
|
private _history;
|
|
554
600
|
constructor(ctx: DurableObjectState, options?: AttachDocumentOptions);
|
|
555
601
|
/**
|
|
@@ -733,6 +779,11 @@ export declare class AttachedDocument {
|
|
|
733
779
|
lastUpdated(): number | null;
|
|
734
780
|
/** The room's id: its name (`getByName`), else the object's id. */
|
|
735
781
|
private get roomId();
|
|
782
|
+
/**
|
|
783
|
+
* The frame check, resolved at first use: per-block locks (`locks`),
|
|
784
|
+
* then `validate`; `null` when neither is set (no block index is kept).
|
|
785
|
+
*/
|
|
786
|
+
private get validator();
|
|
736
787
|
/** The history settings, resolved at first use (`null`: none; an unknown time zone is noted `history`). */
|
|
737
788
|
private get historyConfig();
|
|
738
789
|
/** Days after which deleted content is purged (`null`: never), `room.purge.timing`. */
|
|
@@ -772,6 +823,15 @@ export declare class AttachedDocument {
|
|
|
772
823
|
* room keeps no history.
|
|
773
824
|
*/
|
|
774
825
|
listHistory(): Promise<HistoryEntry[]>;
|
|
826
|
+
/** Every version key the store holds under this room's prefix, page by page. */
|
|
827
|
+
private storedVersions;
|
|
828
|
+
/**
|
|
829
|
+
* The `retention` task (`room.history.retention`), for a store that
|
|
830
|
+
* expires nothing itself: delete every version past its expiry, then
|
|
831
|
+
* re-arm at the next one's. A failure is noted `history` and retried
|
|
832
|
+
* an hour later.
|
|
833
|
+
*/
|
|
834
|
+
private expireVersions;
|
|
775
835
|
/** Version `key` as JSON (a preview), or `null` when this room holds no such version. */
|
|
776
836
|
readHistory(key: string): Promise<JSONDoc | null>;
|
|
777
837
|
/**
|
|
@@ -804,6 +864,77 @@ export declare class AttachedDocument {
|
|
|
804
864
|
* is released and collected (the next compaction stores it collected).
|
|
805
865
|
*/
|
|
806
866
|
private dropRestore;
|
|
867
|
+
private ensureMoves;
|
|
868
|
+
/** One move's row. */
|
|
869
|
+
private moveRow;
|
|
870
|
+
/** Days a moved-out block is watched for late edits: the purge horizon, else {@link DEFAULT_MOVE_GRACE_DAYS}. */
|
|
871
|
+
private get graceMs();
|
|
872
|
+
/**
|
|
873
|
+
* Export blocks `ids` for a move (`room.move`, step 1): their visible
|
|
874
|
+
* subtrees as JSON, the outermost only, in document order. Writes
|
|
875
|
+
* nothing to the document; records the export (`moveId`) for
|
|
876
|
+
* `commitMove`. Throws when none of them shows.
|
|
877
|
+
*/
|
|
878
|
+
exportBlocks(ids: string[]): ExportedBlocks;
|
|
879
|
+
/**
|
|
880
|
+
* Import a move's blocks (`room.move`, step 2) under `dest.parent` at
|
|
881
|
+
* `dest.index`, as one room transaction: each keeps its id unless this
|
|
882
|
+
* document holds it already (then `<id>~<n>`). Idempotent: a second
|
|
883
|
+
* call with the same `moveId` writes nothing and returns the same ids,
|
|
884
|
+
* the acknowledgement `commitMove` needs.
|
|
885
|
+
*/
|
|
886
|
+
importBlocks(request: ImportRequest): ImportReceipt;
|
|
887
|
+
/**
|
|
888
|
+
* Commit a move (`room.move`, step 3), with the destination's
|
|
889
|
+
* acknowledgement: delete the exported blocks with their subtrees, as
|
|
890
|
+
* one room transaction, and watch them for late edits (`room.move.late`)
|
|
891
|
+
* for the grace period. Idempotent; `refused` for an unknown or aborted
|
|
892
|
+
* move.
|
|
893
|
+
*/
|
|
894
|
+
commitMove(moveId: string, receipt: {
|
|
895
|
+
to: string;
|
|
896
|
+
ids: Record<string, string>;
|
|
897
|
+
}): CommitResult;
|
|
898
|
+
/** Drop an export that was not committed (`room.move`): the blocks stay. */
|
|
899
|
+
abortMove(moveId: string): {
|
|
900
|
+
moveId: string;
|
|
901
|
+
status: 'applied' | 'noop';
|
|
902
|
+
};
|
|
903
|
+
/** The late edits waiting to reach their destination (`room.move.late`), oldest first. */
|
|
904
|
+
lateEdits(): LateEditBatch[];
|
|
905
|
+
/** The destination applied a move's late edits through `seq`: they are dropped here. */
|
|
906
|
+
ackLateEdits(moveId: string, seq: number): void;
|
|
907
|
+
/**
|
|
908
|
+
* Apply a batch of late edits of a move this room imported
|
|
909
|
+
* (`room.move.late`): each block's content and data merged three ways
|
|
910
|
+
* into what it holds now (what was written here since stays), as one
|
|
911
|
+
* room transaction. Idempotent by sequence number; a block that no
|
|
912
|
+
* longer shows is skipped. Returns the last sequence applied.
|
|
913
|
+
*/
|
|
914
|
+
applyLateEdits(batch: LateEditBatch): {
|
|
915
|
+
applied: number;
|
|
916
|
+
skipped: string[];
|
|
917
|
+
};
|
|
918
|
+
/** One block's late edit, merged into what it holds (inside a room transaction). */
|
|
919
|
+
private mergeLateEdit;
|
|
920
|
+
/** The registry entries carrying a `del.<marker>` mark written between clocks `c0` and `c1`. */
|
|
921
|
+
private markedBetween;
|
|
922
|
+
/** The moved-out blocks watched for late edits (`room.move.late`), by id → their move. */
|
|
923
|
+
private watchedBlocks;
|
|
924
|
+
/** A transaction that changed a watched block marks its move dirty (inside the `update` emit: no doc write). */
|
|
925
|
+
private noteLateEdits;
|
|
926
|
+
/**
|
|
927
|
+
* The `forward` task (`room.move.late`): turn each dirty move's changes
|
|
928
|
+
* into a batch of late edits — read on a copy of the document with the
|
|
929
|
+
* move's own deletes left out — then forward the waiting batches to
|
|
930
|
+
* their destinations (with `rooms`), and stop watching moves past the
|
|
931
|
+
* grace period. A forward that fails runs again a minute later.
|
|
932
|
+
*/
|
|
933
|
+
private forwardMoves;
|
|
934
|
+
/** The document's blocks as they show without the deletes of `moves`' commits: a copy, read once. */
|
|
935
|
+
private withoutMoveDeletes;
|
|
936
|
+
/** One dirty move's changes since its last batch, as a new batch of late edits (or a report). */
|
|
937
|
+
private collectLateEdits;
|
|
807
938
|
/**
|
|
808
939
|
* The purge task, also over RPC (`room.purge.timing`): record an epoch
|
|
809
940
|
* (the state vector, now) when the document changed since the last one,
|
|
@@ -1068,10 +1199,23 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
1068
1199
|
protected semantics(): DocumentSemanticsConfig;
|
|
1069
1200
|
/**
|
|
1070
1201
|
* Version history — see {@link AttachDocumentOptions.history}. Default:
|
|
1071
|
-
*
|
|
1202
|
+
* `EDYTOR_HISTORY` — a KV namespace binding ({@link kvHistory}), an R2
|
|
1203
|
+
* bucket binding ({@link r2History}) or the string `room` (the room's
|
|
1204
|
+
* own storage, {@link roomHistory}) — with `EDYTOR_HISTORY_RETENTION_DAYS`
|
|
1072
1205
|
* and `EDYTOR_HISTORY_TIME_ZONE`; none without it. Read once, at first use.
|
|
1073
1206
|
*/
|
|
1074
1207
|
protected history(): HistoryOptions | undefined;
|
|
1208
|
+
/**
|
|
1209
|
+
* Per-block locks — see {@link AttachDocumentOptions.locks}. Default: the
|
|
1210
|
+
* `EDYTOR_LOCKS` var names the data key (`{ key }`); none without it.
|
|
1211
|
+
* Read once, at the first frame.
|
|
1212
|
+
*/
|
|
1213
|
+
protected locks(): LockOptions | undefined;
|
|
1214
|
+
/**
|
|
1215
|
+
* The rooms blocks move to — see {@link AttachDocumentOptions.rooms}.
|
|
1216
|
+
* Default: the `EDYTOR_ROOMS` binding; none without it.
|
|
1217
|
+
*/
|
|
1218
|
+
protected rooms(): MoveNamespace | undefined;
|
|
1075
1219
|
/** The room's clock — see {@link AttachDocumentOptions.now}. */
|
|
1076
1220
|
protected now(): number;
|
|
1077
1221
|
fetch(request: Request): Promise<Response>;
|
|
@@ -1096,6 +1240,29 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
1096
1240
|
}>;
|
|
1097
1241
|
/** The version writes in flight, also over RPC — see {@link AttachedDocument.historyWritten}. */
|
|
1098
1242
|
historyWritten(): Promise<void>;
|
|
1243
|
+
/** Export blocks for a move, also over RPC — see {@link AttachedDocument.exportBlocks}. */
|
|
1244
|
+
exportBlocks(ids: string[]): ExportedBlocks;
|
|
1245
|
+
/** Import a move's blocks, also over RPC — see {@link AttachedDocument.importBlocks}. */
|
|
1246
|
+
importBlocks(request: ImportRequest): ImportReceipt;
|
|
1247
|
+
/** Commit a move, also over RPC — see {@link AttachedDocument.commitMove}. */
|
|
1248
|
+
commitMove(moveId: string, receipt: {
|
|
1249
|
+
to: string;
|
|
1250
|
+
ids: Record<string, string>;
|
|
1251
|
+
}): CommitResult;
|
|
1252
|
+
/** Abort an export, also over RPC — see {@link AttachedDocument.abortMove}. */
|
|
1253
|
+
abortMove(moveId: string): {
|
|
1254
|
+
moveId: string;
|
|
1255
|
+
status: 'applied' | 'noop';
|
|
1256
|
+
};
|
|
1257
|
+
/** The late edits waiting, also over RPC — see {@link AttachedDocument.lateEdits}. */
|
|
1258
|
+
lateEdits(): LateEditBatch[];
|
|
1259
|
+
/** Drop forwarded late edits, also over RPC — see {@link AttachedDocument.ackLateEdits}. */
|
|
1260
|
+
ackLateEdits(moveId: string, seq: number): void;
|
|
1261
|
+
/** Apply late edits, also over RPC — see {@link AttachedDocument.applyLateEdits}. */
|
|
1262
|
+
applyLateEdits(batch: LateEditBatch): {
|
|
1263
|
+
applied: number;
|
|
1264
|
+
skipped: string[];
|
|
1265
|
+
};
|
|
1099
1266
|
/** Run the purge task now, also over RPC — see {@link AttachedDocument.purge}. */
|
|
1100
1267
|
purge(): (PurgeReport & {
|
|
1101
1268
|
horizon: number;
|