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.
- package/README.md +2 -2
- package/dist/cloudflare/DocumentRoom.d.ts +13 -9
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/room/context.d.ts +27 -26
- package/dist/cloudflare/room/context.js +2 -2
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +44 -19
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/crdt/attribution/attribution.d.ts +31 -12
- package/dist/crdt/attribution/attribution.js +56 -18
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.js +29 -24
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +25 -9
- package/dist/crdt/doc/history.js +150 -40
- package/dist/crdt/doc/layout.js +11 -22
- package/dist/crdt/doc/meta.js +12 -17
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.js +2 -2
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +52 -122
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +16 -5
- package/dist/crdt/document.js +21 -4
- package/dist/crdt/edytor-doc.d.ts +62 -64
- package/dist/crdt/edytor-doc.js +18 -13
- package/dist/crdt/engine-api.d.ts +33 -1
- package/dist/crdt/engine.d.ts +0 -1
- package/dist/crdt/engine.js +1 -4
- package/dist/crdt/flow.d.ts +1 -1
- package/dist/crdt/flow.js +25 -27
- package/dist/crdt/index.d.ts +26 -25
- package/dist/crdt/index.js +1 -1
- package/dist/crdt/placement/display.d.ts +5 -30
- package/dist/crdt/placement/display.js +39 -144
- package/dist/crdt/placement/keys.d.ts +58 -0
- package/dist/crdt/placement/keys.js +90 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +187 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.js +5 -3
- package/dist/crdt/purge.d.ts +17 -8
- package/dist/crdt/purge.js +236 -126
- package/dist/crdt/restore.d.ts +2 -2
- package/dist/crdt/restore.js +18 -34
- package/dist/crdt/schema.d.ts +22 -21
- package/dist/crdt/schema.js +29 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +15 -15
- package/dist/crdt/structs.js +8 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +12 -0
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.js +5 -8
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +404 -138
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +12 -3
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +77 -62
- package/dist/crdt/text/model.js +84 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +17 -12
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
- package/dist/edytor.svelte.js +4 -1
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +25 -25
- package/dist/surface/observer.svelte.js +2 -2
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +59 -8
- package/package.json +2 -2
- package/dist/crdt/placement/rank.d.ts +0 -87
- package/dist/crdt/placement/rank.js +0 -332
- package/dist/crdt/placement/source.d.ts +0 -43
- package/dist/crdt/placement/source.js +0 -66
- package/dist/crdt/text/delete-index.d.ts +0 -129
- package/dist/crdt/text/delete-index.js +0 -365
- package/dist/crdt/text/deletes.d.ts +0 -76
- package/dist/crdt/text/deletes.js +0 -362
- package/dist/crdt/text/index/anchored.d.ts +0 -17
- package/dist/crdt/text/index/anchored.js +0 -197
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
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.
|
|
19
19
|
|
|
20
|
-
> **
|
|
20
|
+
> **4.0.** Edytor `4.0.0` is on npm as `edytor`: its API follows semantic versioning, and every change is listed in the changelog. It has no production track record yet: read the [limitations](https://edytor.dev/docs/reference/limitations) before you build on it. Issues and PRs are welcome: when you report a bug, include the document's JSON value.
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
@@ -40,7 +40,7 @@ Edytor aims to be for Svelte what Slate.js is for React: a heavily customizable
|
|
|
40
40
|
|
|
41
41
|
## Install
|
|
42
42
|
|
|
43
|
-
Edytor `
|
|
43
|
+
Edytor `4.0.0` is on npm under `latest`:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
pnpm add edytor
|
|
@@ -121,9 +121,11 @@ export declare const DEFAULT_SAVE_AFTER = 2000;
|
|
|
121
121
|
* Room quotas, each refused with `4413` (`quota: <name>`). The
|
|
122
122
|
* document's size: what its records hold (uncompressed) and the updates the
|
|
123
123
|
* engine holds waiting. An isolate has 128 MB, and the live document takes
|
|
124
|
-
*
|
|
125
|
-
* block, its index included; `bench/room-memory.mjs
|
|
126
|
-
* about
|
|
124
|
+
* 15 to 77 bytes of heap per stored byte in Node (prose to one short line
|
|
125
|
+
* per block, its index included; `bench/room-memory.mjs`; a Worker takes
|
|
126
|
+
* less): 2 MiB is about 150 to 160 MB at worst, a list of some 23,000 short
|
|
127
|
+
* blocks (1.25 MiB keeps that near 100 MB). Raise it for documents of long
|
|
128
|
+
* text, lower it for long lists of short blocks.
|
|
127
129
|
*/
|
|
128
130
|
export declare const DEFAULT_MAX_DOCUMENT_BYTES: number;
|
|
129
131
|
/** The share of the document quota past which the room logs its size, by default. */
|
|
@@ -364,7 +366,7 @@ export type RoomLogEntry = {
|
|
|
364
366
|
user: string;
|
|
365
367
|
} | ({
|
|
366
368
|
edytor: 'purge';
|
|
367
|
-
horizon: number;
|
|
369
|
+
horizon: number | null;
|
|
368
370
|
bytes: number;
|
|
369
371
|
} & PurgeReport) | {
|
|
370
372
|
edytor: 'quota';
|
|
@@ -801,12 +803,14 @@ export declare class AttachedDocument {
|
|
|
801
803
|
* The purge task, also over RPC (`room.purge.timing`): record an epoch
|
|
802
804
|
* (the state vector, now) when the document changed since the last one,
|
|
803
805
|
* purge what was deleted before the horizon — the newest epoch at least
|
|
804
|
-
* `purgeAfterDays` old — when it is newer than the last purged,
|
|
805
|
-
*
|
|
806
|
-
* `null`
|
|
806
|
+
* `purgeAfterDays` old — when it is newer than the last purged, with what
|
|
807
|
+
* the sessions whose history ended deleted (`hist.release.end`; with no
|
|
808
|
+
* epoch due, those alone, `horizon: null`), and re-arm for the next epoch
|
|
809
|
+
* to pass it. Returns what the purge wrote, or `null` when nothing was
|
|
810
|
+
* past the horizon and no session had ended.
|
|
807
811
|
*/
|
|
808
812
|
purge(): (PurgeReport & {
|
|
809
|
-
horizon: number;
|
|
813
|
+
horizon: number | null;
|
|
810
814
|
}) | null;
|
|
811
815
|
/** The room's counters, also over RPC — see {@link RoomMetrics}. */
|
|
812
816
|
metrics(): RoomMetrics;
|
|
@@ -968,7 +972,7 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
968
972
|
};
|
|
969
973
|
/** Run the purge task now, also over RPC — see {@link AttachedDocument.purge}. */
|
|
970
974
|
purge(): (PurgeReport & {
|
|
971
|
-
horizon: number;
|
|
975
|
+
horizon: number | null;
|
|
972
976
|
}) | null;
|
|
973
977
|
/** Every comment thread, also over RPC — see {@link AttachedDocument.listComments}. */
|
|
974
978
|
listComments(): CommentSnapshot;
|
|
@@ -130,9 +130,11 @@ export const DEFAULT_SAVE_AFTER = 2000;
|
|
|
130
130
|
* Room quotas, each refused with `4413` (`quota: <name>`). The
|
|
131
131
|
* document's size: what its records hold (uncompressed) and the updates the
|
|
132
132
|
* engine holds waiting. An isolate has 128 MB, and the live document takes
|
|
133
|
-
*
|
|
134
|
-
* block, its index included; `bench/room-memory.mjs
|
|
135
|
-
* about
|
|
133
|
+
* 15 to 77 bytes of heap per stored byte in Node (prose to one short line
|
|
134
|
+
* per block, its index included; `bench/room-memory.mjs`; a Worker takes
|
|
135
|
+
* less): 2 MiB is about 150 to 160 MB at worst, a list of some 23,000 short
|
|
136
|
+
* blocks (1.25 MiB keeps that near 100 MB). Raise it for documents of long
|
|
137
|
+
* text, lower it for long lists of short blocks.
|
|
136
138
|
*/
|
|
137
139
|
export const DEFAULT_MAX_DOCUMENT_BYTES = 2 * 1024 * 1024;
|
|
138
140
|
/** The share of the document quota past which the room logs its size, by default. */
|
|
@@ -480,9 +482,11 @@ export class AttachedDocument {
|
|
|
480
482
|
* The purge task, also over RPC (`room.purge.timing`): record an epoch
|
|
481
483
|
* (the state vector, now) when the document changed since the last one,
|
|
482
484
|
* purge what was deleted before the horizon — the newest epoch at least
|
|
483
|
-
* `purgeAfterDays` old — when it is newer than the last purged,
|
|
484
|
-
*
|
|
485
|
-
* `null`
|
|
485
|
+
* `purgeAfterDays` old — when it is newer than the last purged, with what
|
|
486
|
+
* the sessions whose history ended deleted (`hist.release.end`; with no
|
|
487
|
+
* epoch due, those alone, `horizon: null`), and re-arm for the next epoch
|
|
488
|
+
* to pass it. Returns what the purge wrote, or `null` when nothing was
|
|
489
|
+
* past the horizon and no session had ended.
|
|
486
490
|
*/
|
|
487
491
|
purge() {
|
|
488
492
|
return this.room.purge.tick();
|
|
@@ -28,7 +28,7 @@ export declare const crdt: {
|
|
|
28
28
|
Awareness: typeof import("../../index.js").Awareness;
|
|
29
29
|
doc: {
|
|
30
30
|
SCHEMA: {
|
|
31
|
-
readonly version:
|
|
31
|
+
readonly version: 10;
|
|
32
32
|
readonly name: "edytor-doc";
|
|
33
33
|
readonly roots: {
|
|
34
34
|
readonly registry: "blocks";
|
|
@@ -39,7 +39,7 @@ export declare const crdt: {
|
|
|
39
39
|
readonly schema: "schema";
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
-
SCHEMA_VERSION:
|
|
42
|
+
SCHEMA_VERSION: 10;
|
|
43
43
|
SCHEMA_NAME: "edytor-doc";
|
|
44
44
|
META_KEY: "meta";
|
|
45
45
|
init: (doc: E.EngineDoc, opts?: {
|
|
@@ -103,29 +103,30 @@ export declare const crdt: {
|
|
|
103
103
|
blockNodeOf: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => E.EngineNode | null;
|
|
104
104
|
liveNodeOf: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => E.EngineNode | null;
|
|
105
105
|
isLive: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => boolean;
|
|
106
|
-
candidatesOf: (node: E.EngineNode) => import("../../crdt/placement/model.js").PlacementCand[];
|
|
107
106
|
view: (doc: E.EngineDoc) => import("../../crdt/placement/model.js").ModelView;
|
|
108
|
-
resolvePlacements: (blocks: Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").BlockRec>, ownerOf?: (b: import("../../index.js").BlockId) => import("../../crdt/text/model.js").Owner) => Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").ResolvedPlacement>;
|
|
109
107
|
childrenIndex: (placements: Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").ResolvedPlacement>, own: import("../../crdt/placement/model.js").DisplayOwnership) => Map<import("../../index.js").BlockId | null, import("../../crdt/placement/model.js").ChildSlot[]>;
|
|
110
108
|
positionInView: (v: import("../../crdt/placement/model.js").ModelView, id: import("../../index.js").BlockId) => import("../../index.js").Destination | null;
|
|
111
109
|
buildInline: (atom: import("../../index.js").InlineSpec) => E.EngineNode;
|
|
112
110
|
isSelfOrDescendant: (placements: Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").ResolvedPlacement>, own: import("../../crdt/text/model.js").Ownership, id: import("../../index.js").BlockId, maybeAncestor: import("../../index.js").BlockId) => boolean;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
writePlacement: (doc: E.EngineDoc, node: E.EngineNode, p: import("../../index.js").BlockId | null, r: string) => void;
|
|
111
|
+
placeBlocks: (doc: E.EngineDoc, v: import("../../crdt/placement/model.js").ModelView, parent: import("../../index.js").BlockId | null, left: import("../../index.js").BlockId | null, right: import("../../index.js").BlockId | null, slots: readonly import("../../crdt/placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("../../crdt/placement/slots.js").Unit | null) => void;
|
|
112
|
+
moveBlocksTo: (doc: E.EngineDoc, v: import("../../crdt/placement/model.js").ModelView, ids: readonly import("../../index.js").BlockId[], parent: import("../../index.js").BlockId | null, index: number, side?: number, part?: number, at?: import("../../index.js").BlockId | null) => void;
|
|
113
|
+
insertSpecsAt: (doc: E.EngineDoc, v: import("../../crdt/placement/model.js").ModelView, parent: import("../../index.js").BlockId | null, index: number, specs: readonly import("../../index.js").BlockSpec[], split?: import("../../crdt/placement/slots.js").Unit | null) => void;
|
|
117
114
|
withdrawOnUndo: (doc: E.EngineDoc) => (item: E.EngineItemRef, stackItem: {
|
|
118
115
|
inserts: {
|
|
119
116
|
has(client: number, clock: number): boolean;
|
|
120
117
|
};
|
|
121
118
|
}) => boolean;
|
|
122
|
-
materializeSpec: (doc: E.EngineDoc, sp: import("../../index.js").BlockSpec, parent: import("../../index.js").BlockId | null, rank: string) => void;
|
|
123
119
|
collides: (doc: E.EngineDoc, specs: readonly import("../../index.js").BlockSpec[]) => boolean;
|
|
124
|
-
writeSplit: (doc: E.EngineDoc, id: import("../../index.js").BlockId, offset: number, newId: import("../../index.js").BlockId, tail: import("../../index.js").SplitTail,
|
|
120
|
+
writeSplit: (doc: E.EngineDoc, id: import("../../index.js").BlockId, offset: number, newId: import("../../index.js").BlockId, tail: import("../../index.js").SplitTail, parent: import("../../index.js").BlockId | null, index: number) => import("../../crdt/placement/slots.js").Unit;
|
|
125
121
|
ownText: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => {
|
|
126
122
|
from: unknown;
|
|
127
123
|
to: number;
|
|
128
124
|
};
|
|
125
|
+
recordsOf: (doc: E.EngineDoc, node: E.EngineNode, v?: import("../../crdt/placement/model.js").ModelView) => {
|
|
126
|
+
item: import("../../crdt/placement/slots.js").SeqItem;
|
|
127
|
+
stamp: import("../../crdt/placement/slots.js").Stamp;
|
|
128
|
+
wins: boolean;
|
|
129
|
+
}[];
|
|
129
130
|
insertBlocks: (doc: E.EngineDoc, dest: import("../../index.js").Destination, specs: import("../../index.js").BlockSpec[]) => boolean;
|
|
130
131
|
insertBlock: (doc: E.EngineDoc, dest: import("../../index.js").Destination, spec: import("../../index.js").BlockSpec) => boolean;
|
|
131
132
|
restoreBlocks: (doc: E.EngineDoc, specs: import("../../index.js").BlockSpec[]) => void;
|
|
@@ -157,8 +158,8 @@ export declare const crdt: {
|
|
|
157
158
|
at: number;
|
|
158
159
|
node: E.EngineNode;
|
|
159
160
|
} | null;
|
|
160
|
-
splitAt: (blocks: ReadonlyMap<import("../../crdt/text/model.js").BlockId, import("../../crdt/text/model.js").TextBlockRec>, own: import("../../crdt/text/model.js").Ownership, b: import("../../crdt/text/model.js").BlockId, offset: number, newId: import("../../crdt/text/model.js").BlockId, n: number) =>
|
|
161
|
-
claimInto: (
|
|
161
|
+
splitAt: (doc: E.EngineDoc, blocks: ReadonlyMap<import("../../crdt/text/model.js").BlockId, import("../../crdt/text/model.js").TextBlockRec>, own: import("../../crdt/text/model.js").Ownership, b: import("../../crdt/text/model.js").BlockId, offset: number, newId: import("../../crdt/text/model.js").BlockId, n: number) => void;
|
|
162
|
+
claimInto: (own: Pick<import("../../crdt/text/model.js").Ownership, "streamOf">, from: import("../../crdt/text/model.js").BlockId, into: import("../../crdt/text/model.js").BlockId) => void;
|
|
162
163
|
ownText: (doc: E.EngineDoc, rec: import("../../crdt/text/model.js").TextBlockRec) => {
|
|
163
164
|
from: unknown;
|
|
164
165
|
to: number;
|
|
@@ -178,7 +179,7 @@ export declare const crdt: {
|
|
|
178
179
|
keep?: boolean;
|
|
179
180
|
after?: readonly import("../../index.js").BlockSpec[];
|
|
180
181
|
} | undefined) => import("../../index.js").OpResult;
|
|
181
|
-
placeBeside: (ids: readonly string[], target: string, side: "
|
|
182
|
+
placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => import("../../index.js").OpResult;
|
|
182
183
|
wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => import("../../index.js").OpResult;
|
|
183
184
|
splitBlock: (id: string, offset: number, newId: string, tail?: import("../../index.js").SplitTail | undefined) => import("../../index.js").OpResult;
|
|
184
185
|
mergeBlocks: (fromId: string, intoId: string) => import("../../index.js").OpResult;
|
|
@@ -301,11 +302,10 @@ export declare const crdt: {
|
|
|
301
302
|
onChange: (cb: (change: import("../../index.js").DocChange) => void) => (() => void);
|
|
302
303
|
};
|
|
303
304
|
newDoc: (opts?: ConstructorParameters<E.EngineApi["Doc"]>[0]) => YDoc;
|
|
304
|
-
keepCopies: (doc: E.EngineDoc) => void;
|
|
305
305
|
purge: (doc: E.EngineDoc, facade: {
|
|
306
306
|
model: import("../../crdt/placement/model.js").PlacementModel;
|
|
307
307
|
text: import("../../crdt/text/model.js").TextEngine;
|
|
308
|
-
}, horizon: import("../../crdt/purge.js").Horizon) => import("../index.js").PurgeReport;
|
|
308
|
+
}, horizon: import("../../crdt/purge.js").Horizon | null) => import("../index.js").PurgeReport;
|
|
309
309
|
horizonOf: (doc: E.EngineDoc) => import("../../crdt/purge.js").Horizon | null;
|
|
310
310
|
restoreTo: <Plan>(doc: E.EngineDoc, facade: import("../../crdt/restore.js").RestoreFacade<Plan>, json: JSONDoc) => import("../../crdt/restore.js").RestoreReport;
|
|
311
311
|
model: {
|
|
@@ -314,29 +314,30 @@ export declare const crdt: {
|
|
|
314
314
|
blockNodeOf: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => E.EngineNode | null;
|
|
315
315
|
liveNodeOf: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => E.EngineNode | null;
|
|
316
316
|
isLive: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => boolean;
|
|
317
|
-
candidatesOf: (node: E.EngineNode) => import("../../crdt/placement/model.js").PlacementCand[];
|
|
318
317
|
view: (doc: E.EngineDoc) => import("../../crdt/placement/model.js").ModelView;
|
|
319
|
-
resolvePlacements: (blocks: Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").BlockRec>, ownerOf?: (b: import("../../index.js").BlockId) => import("../../crdt/text/model.js").Owner) => Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").ResolvedPlacement>;
|
|
320
318
|
childrenIndex: (placements: Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").ResolvedPlacement>, own: import("../../crdt/placement/model.js").DisplayOwnership) => Map<import("../../index.js").BlockId | null, import("../../crdt/placement/model.js").ChildSlot[]>;
|
|
321
319
|
positionInView: (v: import("../../crdt/placement/model.js").ModelView, id: import("../../index.js").BlockId) => import("../../index.js").Destination | null;
|
|
322
320
|
buildInline: (atom: import("../../index.js").InlineSpec) => E.EngineNode;
|
|
323
321
|
isSelfOrDescendant: (placements: Map<import("../../index.js").BlockId, import("../../crdt/placement/model.js").ResolvedPlacement>, own: import("../../crdt/text/model.js").Ownership, id: import("../../index.js").BlockId, maybeAncestor: import("../../index.js").BlockId) => boolean;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
writePlacement: (doc: E.EngineDoc, node: E.EngineNode, p: import("../../index.js").BlockId | null, r: string) => void;
|
|
322
|
+
placeBlocks: (doc: E.EngineDoc, v: import("../../crdt/placement/model.js").ModelView, parent: import("../../index.js").BlockId | null, left: import("../../index.js").BlockId | null, right: import("../../index.js").BlockId | null, slots: readonly import("../../crdt/placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("../../crdt/placement/slots.js").Unit | null) => void;
|
|
323
|
+
moveBlocksTo: (doc: E.EngineDoc, v: import("../../crdt/placement/model.js").ModelView, ids: readonly import("../../index.js").BlockId[], parent: import("../../index.js").BlockId | null, index: number, side?: number, part?: number, at?: import("../../index.js").BlockId | null) => void;
|
|
324
|
+
insertSpecsAt: (doc: E.EngineDoc, v: import("../../crdt/placement/model.js").ModelView, parent: import("../../index.js").BlockId | null, index: number, specs: readonly import("../../index.js").BlockSpec[], split?: import("../../crdt/placement/slots.js").Unit | null) => void;
|
|
328
325
|
withdrawOnUndo: (doc: E.EngineDoc) => (item: E.EngineItemRef, stackItem: {
|
|
329
326
|
inserts: {
|
|
330
327
|
has(client: number, clock: number): boolean;
|
|
331
328
|
};
|
|
332
329
|
}) => boolean;
|
|
333
|
-
materializeSpec: (doc: E.EngineDoc, sp: import("../../index.js").BlockSpec, parent: import("../../index.js").BlockId | null, rank: string) => void;
|
|
334
330
|
collides: (doc: E.EngineDoc, specs: readonly import("../../index.js").BlockSpec[]) => boolean;
|
|
335
|
-
writeSplit: (doc: E.EngineDoc, id: import("../../index.js").BlockId, offset: number, newId: import("../../index.js").BlockId, tail: import("../../index.js").SplitTail,
|
|
331
|
+
writeSplit: (doc: E.EngineDoc, id: import("../../index.js").BlockId, offset: number, newId: import("../../index.js").BlockId, tail: import("../../index.js").SplitTail, parent: import("../../index.js").BlockId | null, index: number) => import("../../crdt/placement/slots.js").Unit;
|
|
336
332
|
ownText: (doc: E.EngineDoc, id: import("../../index.js").BlockId) => {
|
|
337
333
|
from: unknown;
|
|
338
334
|
to: number;
|
|
339
335
|
};
|
|
336
|
+
recordsOf: (doc: E.EngineDoc, node: E.EngineNode, v?: import("../../crdt/placement/model.js").ModelView) => {
|
|
337
|
+
item: import("../../crdt/placement/slots.js").SeqItem;
|
|
338
|
+
stamp: import("../../crdt/placement/slots.js").Stamp;
|
|
339
|
+
wins: boolean;
|
|
340
|
+
}[];
|
|
340
341
|
insertBlocks: (doc: E.EngineDoc, dest: import("../../index.js").Destination, specs: import("../../index.js").BlockSpec[]) => boolean;
|
|
341
342
|
insertBlock: (doc: E.EngineDoc, dest: import("../../index.js").Destination, spec: import("../../index.js").BlockSpec) => boolean;
|
|
342
343
|
restoreBlocks: (doc: E.EngineDoc, specs: import("../../index.js").BlockSpec[]) => void;
|
|
@@ -368,8 +369,8 @@ export declare const crdt: {
|
|
|
368
369
|
at: number;
|
|
369
370
|
node: E.EngineNode;
|
|
370
371
|
} | null;
|
|
371
|
-
splitAt: (blocks: ReadonlyMap<import("../../crdt/text/model.js").BlockId, import("../../crdt/text/model.js").TextBlockRec>, own: import("../../crdt/text/model.js").Ownership, b: import("../../crdt/text/model.js").BlockId, offset: number, newId: import("../../crdt/text/model.js").BlockId, n: number) =>
|
|
372
|
-
claimInto: (
|
|
372
|
+
splitAt: (doc: E.EngineDoc, blocks: ReadonlyMap<import("../../crdt/text/model.js").BlockId, import("../../crdt/text/model.js").TextBlockRec>, own: import("../../crdt/text/model.js").Ownership, b: import("../../crdt/text/model.js").BlockId, offset: number, newId: import("../../crdt/text/model.js").BlockId, n: number) => void;
|
|
373
|
+
claimInto: (own: Pick<import("../../crdt/text/model.js").Ownership, "streamOf">, from: import("../../crdt/text/model.js").BlockId, into: import("../../crdt/text/model.js").BlockId) => void;
|
|
373
374
|
ownText: (doc: E.EngineDoc, rec: import("../../crdt/text/model.js").TextBlockRec) => {
|
|
374
375
|
from: unknown;
|
|
375
376
|
to: number;
|
|
@@ -691,9 +692,9 @@ export declare class RoomContext {
|
|
|
691
692
|
emptied: number;
|
|
692
693
|
marks: number;
|
|
693
694
|
records: number;
|
|
694
|
-
candidates: number;
|
|
695
695
|
claims: number;
|
|
696
696
|
merged: number;
|
|
697
|
+
released: number;
|
|
697
698
|
};
|
|
698
699
|
since: number;
|
|
699
700
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PurgeReport } from '../../crdt/purge.js';
|
|
2
2
|
import { type RoomContext } from './context.js';
|
|
3
3
|
export declare class RoomPurge {
|
|
4
4
|
#private;
|
|
@@ -8,8 +8,11 @@ export declare class RoomPurge {
|
|
|
8
8
|
get days(): number | null;
|
|
9
9
|
/** A change was stored at `at`: the purge tick runs a day later at the latest. */
|
|
10
10
|
noteChange(at: number): void;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* The purge task (`AttachedDocument.purge`): its report, `horizon` the
|
|
13
|
+
* epoch it purged to (`null`: only ended sessions' deletes).
|
|
14
|
+
*/
|
|
12
15
|
tick(): (PurgeReport & {
|
|
13
|
-
horizon: number;
|
|
16
|
+
horizon: number | null;
|
|
14
17
|
}) | null;
|
|
15
18
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The purge (`room.purge.*`): the daily tick records an epoch (the state
|
|
3
3
|
* vector) when the document changed, and purges what was deleted before
|
|
4
|
-
* the horizon — the newest epoch at least `purgeAfterDays` old —
|
|
5
|
-
*
|
|
4
|
+
* the horizon — the newest epoch at least `purgeAfterDays` old — and what
|
|
5
|
+
* the sessions whose history ended deleted (`hist.release.end`), as the
|
|
6
|
+
* room's own transaction (`crdt/purge.ts`), then compacts. With no epoch
|
|
7
|
+
* due, a tick still purges the ended sessions' deletes.
|
|
6
8
|
*/
|
|
7
9
|
import { Y } from '../../crdt/engine.js';
|
|
10
|
+
import { releasesOf } from '../../crdt/purge.js';
|
|
8
11
|
import { asEngineDoc } from '../../crdt/structs.js';
|
|
9
12
|
import { PURGE_ORIGIN, noTimers } from './shared.js';
|
|
10
13
|
import { DEFAULT_RETENTION_DAYS } from '../history.js';
|
|
@@ -27,7 +30,10 @@ export class RoomPurge {
|
|
|
27
30
|
if (this.days !== null)
|
|
28
31
|
this.room.scheduler.schedule('purge', at + DAY, 'earlier');
|
|
29
32
|
}
|
|
30
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The purge task (`AttachedDocument.purge`): its report, `horizon` the
|
|
35
|
+
* epoch it purged to (`null`: only ended sessions' deletes).
|
|
36
|
+
*/
|
|
31
37
|
tick() {
|
|
32
38
|
const room = this.room;
|
|
33
39
|
const { scheduler, sql, tables } = room;
|
|
@@ -61,6 +67,10 @@ export class RoomPurge {
|
|
|
61
67
|
if (horizon !== undefined && horizon.at > purged) {
|
|
62
68
|
result = this.#purgeTo({ at: horizon.at, sv: new Uint8Array(horizon.sv) });
|
|
63
69
|
}
|
|
70
|
+
else if (releasesOf(asEngineDoc(doc)).size > 0) {
|
|
71
|
+
// No epoch is due: the ended sessions' deletes go all the same.
|
|
72
|
+
result = this.#purgeTo(null);
|
|
73
|
+
}
|
|
64
74
|
// The next epoch to pass the horizon, or the next change, arms it again.
|
|
65
75
|
const next = sql
|
|
66
76
|
.exec(`SELECT at FROM ${tables.epochs} WHERE at > ? ORDER BY at LIMIT 1`, Math.max(purged, horizon?.at ?? -1))
|
|
@@ -72,18 +82,27 @@ export class RoomPurge {
|
|
|
72
82
|
return result;
|
|
73
83
|
});
|
|
74
84
|
}
|
|
75
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Purge what was deleted before `horizon` and what ended sessions deleted
|
|
87
|
+
* (`null`: only those), as the room's own transaction, then compact. A
|
|
88
|
+
* purge of ended sessions' deletes that finds none left compacts nothing
|
|
89
|
+
* and reports `null`.
|
|
90
|
+
*/
|
|
76
91
|
#purgeTo(horizon) {
|
|
77
92
|
const room = this.room;
|
|
78
93
|
const doc = room.requireDoc();
|
|
79
94
|
// A restore past the horizon can no longer be undone (`room.history.undo`).
|
|
80
|
-
|
|
95
|
+
if (horizon !== null)
|
|
96
|
+
room.history.expireRestore(horizon.at);
|
|
81
97
|
room.history.closeSlotIfPast();
|
|
82
98
|
let report;
|
|
99
|
+
let wrote = true;
|
|
83
100
|
room.transacting = true;
|
|
84
101
|
try {
|
|
85
102
|
room.facade.transact(() => {
|
|
86
103
|
report = crdt.doc.purge(asEngineDoc(doc), room.facade, horizon);
|
|
104
|
+
const tr = asEngineDoc(doc)._transaction;
|
|
105
|
+
wrote = !tr.insertSet.isEmpty() || !tr.deleteSet.isEmpty();
|
|
87
106
|
}, PURGE_ORIGIN);
|
|
88
107
|
}
|
|
89
108
|
finally {
|
|
@@ -94,30 +113,36 @@ export class RoomPurge {
|
|
|
94
113
|
room.storage.heal();
|
|
95
114
|
throw error;
|
|
96
115
|
}
|
|
97
|
-
|
|
98
|
-
room.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
counters.runs++;
|
|
104
|
-
counters.horizon = horizon.at;
|
|
105
|
-
for (const key of [
|
|
116
|
+
if (horizon !== null)
|
|
117
|
+
room.ctx.storage.transactionSync(() => {
|
|
118
|
+
room.sql.exec(`INSERT OR REPLACE INTO ${room.tables.meta} (key, value) VALUES ('purged', ?)`, horizon.at);
|
|
119
|
+
room.sql.exec(`DELETE FROM ${room.tables.epochs} WHERE at < ?`, horizon.at);
|
|
120
|
+
});
|
|
121
|
+
const keys = [
|
|
106
122
|
'removed',
|
|
107
123
|
'emptied',
|
|
108
124
|
'marks',
|
|
109
125
|
'records',
|
|
110
|
-
'candidates',
|
|
111
126
|
'claims',
|
|
112
|
-
'merged'
|
|
113
|
-
|
|
127
|
+
'merged',
|
|
128
|
+
'released'
|
|
129
|
+
];
|
|
130
|
+
// Nothing an ended session deleted was left: no compaction, no run.
|
|
131
|
+
if (horizon === null && !wrote)
|
|
132
|
+
return null;
|
|
133
|
+
room.storage.compact();
|
|
134
|
+
const counters = room.counters.purge;
|
|
135
|
+
counters.runs++;
|
|
136
|
+
if (horizon !== null)
|
|
137
|
+
counters.horizon = horizon.at;
|
|
138
|
+
for (const key of keys)
|
|
114
139
|
counters[key] += report[key];
|
|
115
140
|
room.log({
|
|
116
141
|
edytor: 'purge',
|
|
117
|
-
horizon: horizon
|
|
142
|
+
horizon: horizon?.at ?? null,
|
|
118
143
|
bytes: room.counters.compaction.lastBytes,
|
|
119
144
|
...report
|
|
120
145
|
});
|
|
121
|
-
return { ...report, horizon: horizon
|
|
146
|
+
return { ...report, horizon: horizon?.at ?? null };
|
|
122
147
|
}
|
|
123
148
|
}
|
|
@@ -77,10 +77,9 @@ export declare const heldDeletes: (doc: YDoc, ds: Decoded["ds"]) => Decoded["ds"
|
|
|
77
77
|
export declare const withoutPending: <T>(doc: YDoc, read: () => T) => T;
|
|
78
78
|
/**
|
|
79
79
|
* The live document as one update: its healed state — the engine
|
|
80
|
-
* merged what each keystroke wrote and collected deleted content
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* holds exactly what the stored records hold, collected.
|
|
80
|
+
* merged what each keystroke wrote and collected deleted content —,
|
|
81
|
+
* without what waits (`withoutPending`). Memory never runs ahead of
|
|
82
|
+
* storage, so it holds exactly what the stored records hold, collected.
|
|
84
83
|
*/
|
|
85
84
|
export declare const liveState: (doc: YDoc) => Uint8Array;
|
|
86
85
|
/** A fresh room document (`prepareRoomDoc`'s collection rules). */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as encoding from 'lib0-v14/encoding';
|
|
7
7
|
import { Y } from '../../crdt/engine.js';
|
|
8
|
-
import {
|
|
8
|
+
import { keepFromCollection, structAt as storeStructAt } from '../../crdt/structs.js';
|
|
9
9
|
import { ROOM_ORIGIN } from './shared.js';
|
|
10
10
|
import { crdt } from './context.js';
|
|
11
11
|
export const stateVector = (doc) => Y.decodeStateVector(Y.encodeStateVector(doc));
|
|
@@ -295,19 +295,17 @@ export const withoutPending = (doc, read) => {
|
|
|
295
295
|
};
|
|
296
296
|
/**
|
|
297
297
|
* The live document as one update: its healed state — the engine
|
|
298
|
-
* merged what each keystroke wrote and collected deleted content
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* holds exactly what the stored records hold, collected.
|
|
298
|
+
* merged what each keystroke wrote and collected deleted content —,
|
|
299
|
+
* without what waits (`withoutPending`). Memory never runs ahead of
|
|
300
|
+
* storage, so it holds exactly what the stored records hold, collected.
|
|
302
301
|
*/
|
|
303
302
|
export const liveState = (doc) => withoutPending(doc, () => Y.encodeStateAsUpdate(doc));
|
|
304
303
|
/**
|
|
305
304
|
* A room document's collection rules, before any update applies: it keeps
|
|
306
|
-
*
|
|
307
|
-
*
|
|
305
|
+
* the content the last restore deleted while its undo stands (`keep`,
|
|
306
|
+
* `room.history.undo`).
|
|
308
307
|
*/
|
|
309
308
|
const prepareRoomDoc = (doc, keep) => {
|
|
310
|
-
crdt.doc.keepCopies(asEngineDoc(doc));
|
|
311
309
|
keepFromCollection(doc, (it) => !(keep()?.intersects(it.id.client, it.id.clock, it.length) ?? false));
|
|
312
310
|
};
|
|
313
311
|
/** A fresh room document (`prepareRoomDoc`'s collection rules). */
|
|
@@ -17,10 +17,13 @@
|
|
|
17
17
|
* `u/<actorId>` → profile `{name?, color?}` — one record per
|
|
18
18
|
* stable application actor, referenced by id.
|
|
19
19
|
*
|
|
20
|
-
* Published
|
|
21
|
-
* transaction
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* Published with the replica's first write: inside the first local
|
|
21
|
+
* transaction that writes under its client id (the engine's
|
|
22
|
+
* `beforeCommit`, fork patch YP30), so a replica that only reads writes
|
|
23
|
+
* nothing, and every struct it writes travels with its binding. Readable
|
|
24
|
+
* through `actors`/`actorOf`. These records also travel inside `encode()`
|
|
25
|
+
* output, so `loadDocument` restores the dictionary without
|
|
26
|
+
* republishing.
|
|
24
27
|
*
|
|
25
28
|
* LEGACY RECORDS — documents saved by early builds carry `a/` per-edit
|
|
26
29
|
* records under the same root. They are plain replicated state, preserved
|
|
@@ -35,9 +38,10 @@ import { type BlockAttribution } from './block.js';
|
|
|
35
38
|
/** Reserved replicated root for all attribution metadata (`SCHEMA.roots.attribution`). */
|
|
36
39
|
export { ATTRIBUTION_ROOT };
|
|
37
40
|
/**
|
|
38
|
-
* Origin of
|
|
39
|
-
*
|
|
40
|
-
* out of undo capture; tooling classifies
|
|
41
|
+
* Origin of the profile writes this module performs on its own
|
|
42
|
+
* (`setProfile`; the binding joins the transaction of the replica's first
|
|
43
|
+
* write, whatever its origin). Kept out of undo capture; tooling classifies
|
|
44
|
+
* it as bookkeeping.
|
|
41
45
|
*/
|
|
42
46
|
export declare const ATTRIBUTION_ORIGIN: unique symbol;
|
|
43
47
|
/** The stable application-level author identity (mirrors `DocumentActor`). */
|
|
@@ -57,14 +61,23 @@ export type ActorProfile = {
|
|
|
57
61
|
* records identically and stay correct as old replicas append records.
|
|
58
62
|
*/
|
|
59
63
|
export type DocumentAttribution = {
|
|
60
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* actorId → profile — the replicated actor dictionary (`u/` records): the
|
|
66
|
+
* actors whose replicas wrote, each published with its replica's first
|
|
67
|
+
* write (or `setProfile`).
|
|
68
|
+
*/
|
|
61
69
|
readonly actors: ReadonlyMap<string, ActorProfile>;
|
|
62
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Resolve an item's inserting replica (clientID) to an actor id (`c/`
|
|
72
|
+
* records). A replica's binding is written with its first write, so every
|
|
73
|
+
* client id that wrote a struct resolves; one that never wrote does not.
|
|
74
|
+
*/
|
|
63
75
|
actorOf(client: number): string | undefined;
|
|
64
76
|
/**
|
|
65
|
-
* Re-publish the local actor's profile record (`u/<actorId>` — LWW)
|
|
66
|
-
*
|
|
67
|
-
*
|
|
77
|
+
* Re-publish the local actor's profile record (`u/<actorId>` — LWW), a
|
|
78
|
+
* write of this replica (its binding goes with it, as with any first
|
|
79
|
+
* write). Attribution is never relabeled: historical records keep the
|
|
80
|
+
* stable actor id; only the resolvable profile changes.
|
|
68
81
|
*/
|
|
69
82
|
setProfile(profile: ActorProfile): void;
|
|
70
83
|
/**
|
|
@@ -94,6 +107,12 @@ export type AttributionController = {
|
|
|
94
107
|
type AttachOptions = {
|
|
95
108
|
actor: AttributionActor;
|
|
96
109
|
};
|
|
110
|
+
/**
|
|
111
|
+
* The actor `doc`'s own replica writes for: its binding (`c/<clientID>`),
|
|
112
|
+
* else, before its first write, the actor its attribution controller was
|
|
113
|
+
* attached with (`undefined`: a raw doc). The provider's `saved` reads it.
|
|
114
|
+
*/
|
|
115
|
+
export declare const writerOf: (doc: EngineDoc) => string | undefined;
|
|
97
116
|
/** The display profile of `actor`: its name and color, those it has. */
|
|
98
117
|
export declare const profileOf: (actor: AttributionActor) => ActorProfile;
|
|
99
118
|
/**
|