edytor 0.1.0-next.21 → 0.1.0-next.23
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 +302 -10
- package/dist/cloudflare/DocumentRoom.js +965 -88
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/routeDocumentSocket.js +30 -6
- package/dist/collaboration/awarenessSelection.d.ts +7 -1
- package/dist/collaboration/awarenessSelection.js +7 -1
- package/dist/collaboration/index.d.ts +2 -2
- package/dist/collaboration/index.js +2 -2
- package/dist/collaboration/providers.d.ts +4 -0
- package/dist/collaboration/providers.js +4 -0
- package/dist/components/Edytor.svelte +14 -2
- package/dist/components/Edytor.svelte.d.ts +8 -1
- package/dist/crdt/admission.d.ts +6 -1
- package/dist/crdt/admission.js +9 -2
- package/dist/crdt/data.d.ts +3 -1
- package/dist/crdt/data.js +42 -32
- package/dist/crdt/document.d.ts +31 -8
- package/dist/crdt/document.js +53 -18
- package/dist/crdt/edytor-doc.d.ts +31 -3
- package/dist/crdt/edytor-doc.js +131 -14
- package/dist/crdt/engine.d.ts +6 -0
- package/dist/crdt/engine.js +7 -0
- package/dist/crdt/flow.d.ts +2 -0
- package/dist/crdt/flow.js +2 -2
- package/dist/crdt/index.d.ts +20 -9
- package/dist/crdt/index.js +3 -3
- package/dist/crdt/migration/migrate.js +9 -3
- package/dist/crdt/placement/model.d.ts +9 -1
- package/dist/crdt/placement/model.js +29 -10
- package/dist/crdt/placement/rank.d.ts +20 -0
- package/dist/crdt/placement/rank.js +79 -0
- package/dist/crdt/protocols/envelope.d.ts +20 -1
- package/dist/crdt/protocols/envelope.js +14 -2
- package/dist/crdt/providers/container.d.ts +15 -0
- package/dist/crdt/providers/container.js +17 -3
- package/dist/crdt/providers/index.d.ts +21 -1
- package/dist/crdt/providers/index.js +86 -3
- package/dist/crdt/providers/indexeddb.js +60 -40
- package/dist/crdt/providers/room.d.ts +18 -2
- package/dist/crdt/providers/room.js +29 -5
- package/dist/crdt/providers/websocket.d.ts +9 -1
- package/dist/crdt/providers/websocket.js +21 -6
- package/dist/crdt/storage.d.ts +21 -0
- package/dist/crdt/storage.js +37 -0
- package/dist/crdt/structs.d.ts +17 -0
- package/dist/crdt/structs.js +41 -0
- package/dist/crdt/text/model.d.ts +9 -1
- package/dist/crdt/text/model.js +24 -14
- package/dist/crdt/text/rows.d.ts +94 -0
- package/dist/crdt/text/rows.js +193 -0
- package/dist/crdt/text/runs.d.ts +30 -10
- package/dist/crdt/text/runs.js +1235 -264
- package/dist/edytor.svelte.d.ts +3 -1
- package/dist/edytor.svelte.js +9 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/image/ImageEmpty.svelte +19 -6
- package/dist/plugins/image/ImagePlugin.svelte +9 -3
- package/dist/plugins/image/ImagePlugin.svelte.d.ts +2 -2
- package/dist/plugins/image/image.d.ts +16 -0
- package/dist/plugins/image/image.js +19 -0
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/plugins.d.ts +11 -0
- package/dist/session/commands.d.ts +1 -0
- package/dist/session/commands.js +4 -1
- 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.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.
|
|
19
19
|
|
|
20
20
|
## Features
|
|
21
21
|
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
* what storage does not.
|
|
35
35
|
* - every INTEGRATED update is appended to SQLite as one record split into
|
|
36
36
|
* rows ≤ `maxRowBytes` (2 MB row cap) in one `transactionSync`, then
|
|
37
|
-
* broadcast; after `compactAfter` update records the rows are
|
|
38
|
-
*
|
|
37
|
+
* broadcast; after `compactAfter` update records the rows are replaced
|
|
38
|
+
* by one snapshot record of the live document's state (its healed
|
|
39
|
+
* state, deleted content collected: P2), after the message is
|
|
39
40
|
* acknowledged — never inside the engine's `update` observer, where one
|
|
40
41
|
* throw would silence every later emit. Memory never runs ahead of
|
|
41
42
|
* storage: when an append fails, nothing is relayed or acknowledged, the
|
|
@@ -51,6 +52,11 @@
|
|
|
51
52
|
* left out of the ack), the rest applied. Compaction reclaims the
|
|
52
53
|
* waiting deletes of client ids no user registered and no socket holds;
|
|
53
54
|
* `dropWaitingDeletes()` every one.
|
|
55
|
+
* - storage format (P5): the generation record says `storage: 'v2'` —
|
|
56
|
+
* snapshots in the v2 encoding, compressed in place after they are
|
|
57
|
+
* stored (`compressLater`), inflated at start (`inflate`); update and
|
|
58
|
+
* pending records stay v1. A container without `storage` (0.1.0-next.22)
|
|
59
|
+
* is all v1, and its next compaction rewrites it.
|
|
54
60
|
* - a Step2 serves the STORED state: the engine's pending structs (waiting
|
|
55
61
|
* for a dependency, never stored) are not served.
|
|
56
62
|
* - a frame larger than `maxFrameBytes` (32 MiB) goes out as chunks the
|
|
@@ -89,13 +95,29 @@
|
|
|
89
95
|
*/
|
|
90
96
|
import { DurableObject } from 'cloudflare:workers';
|
|
91
97
|
import * as E from '../crdt/index.js';
|
|
92
|
-
import type { AwarenessEntry, DocumentSemanticsConfig, EdytorDoc, JSONDoc, YDoc } from '../crdt/index.js';
|
|
98
|
+
import type { AwarenessEntry, DocumentSemanticsConfig, EdytorDoc, JSONBlock, JSONDoc, YDoc } from '../crdt/index.js';
|
|
93
99
|
/** Under SQLite-backed Durable Objects' 2 MB row cap, with room for the other columns. */
|
|
94
100
|
export declare const DEFAULT_MAX_ROW_BYTES: number;
|
|
95
101
|
/** Update records before the rows are merged into one snapshot. */
|
|
96
102
|
export declare const DEFAULT_COMPACT_AFTER = 500;
|
|
97
103
|
/** ms between the first unsaved change and `onSave`. */
|
|
98
104
|
export declare const DEFAULT_SAVE_AFTER = 2000;
|
|
105
|
+
/**
|
|
106
|
+
* Room quotas (H3), each refused with `4413` (`quota: <name>`). The
|
|
107
|
+
* document's size: what its records hold (uncompressed) and the updates the
|
|
108
|
+
* engine holds waiting — the live document holds about that, several
|
|
109
|
+
* times over in memory for text (an isolate has 128 MB). Raise or lower
|
|
110
|
+
* it for your documents.
|
|
111
|
+
*/
|
|
112
|
+
export declare const DEFAULT_MAX_DOCUMENT_BYTES: number;
|
|
113
|
+
/** The largest frame a socket may send, reassembled from chunks (the platform caps one message at 32 MiB). */
|
|
114
|
+
export declare const DEFAULT_MAX_INBOUND_FRAME_BYTES: number;
|
|
115
|
+
/**
|
|
116
|
+
* Sync messages a socket may send per second, sustained; a burst of ten
|
|
117
|
+
* seconds' worth is allowed (a reconnect, a paste). Far above typing
|
|
118
|
+
* speed: a script should batch its edits into transactions.
|
|
119
|
+
*/
|
|
120
|
+
export declare const DEFAULT_MAX_UPDATES_PER_SECOND = 50;
|
|
99
121
|
/**
|
|
100
122
|
* Delete ranges the room keeps waiting for items it does not hold (a
|
|
101
123
|
* relayer's delete of an author's edit a restore lost). Every update the
|
|
@@ -128,6 +150,8 @@ export type SavedDocument = {
|
|
|
128
150
|
};
|
|
129
151
|
/** What `onLoad` may return: JSON, a bare v14 update, or `{ update, replicas }` (a `SavedDocument`). */
|
|
130
152
|
export type LoadedDocument = JSONDoc | Uint8Array | Pick<SavedDocument, 'update' | 'replicas'>;
|
|
153
|
+
/** The header `routeDocumentSocket` sets on an authorized `lastUpdated` probe (H12). */
|
|
154
|
+
export declare const PROBE_HEADER = "X-Edytor-Probe";
|
|
131
155
|
/** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
|
|
132
156
|
export declare const ROOM_ORIGIN: unique symbol;
|
|
133
157
|
/**
|
|
@@ -150,6 +174,11 @@ export type DocumentRoomEnv = {
|
|
|
150
174
|
EDYTOR_MAX_FRAME_BYTES?: string | number;
|
|
151
175
|
EDYTOR_COMPACT_AFTER?: string | number;
|
|
152
176
|
EDYTOR_SAVE_AFTER?: string | number;
|
|
177
|
+
EDYTOR_MAX_DOCUMENT_BYTES?: string | number;
|
|
178
|
+
EDYTOR_MAX_INBOUND_FRAME_BYTES?: string | number;
|
|
179
|
+
EDYTOR_MAX_UPDATES_PER_SECOND?: string | number;
|
|
180
|
+
/** `off`: the room logs nothing (default: one JSON line per compaction, quota hit, denial, fault). */
|
|
181
|
+
EDYTOR_LOG?: string;
|
|
153
182
|
};
|
|
154
183
|
/** What a socket is bound to: its verified user, its replica (Yjs client id), its access. */
|
|
155
184
|
export type SocketIdentity = {
|
|
@@ -163,9 +192,76 @@ export type Attachment = SocketIdentity & {
|
|
|
163
192
|
clock: number | null;
|
|
164
193
|
};
|
|
165
194
|
export type Refusal = {
|
|
166
|
-
reason: 'generation' | 'schema' | 'malformed' | 'container' | 'identity' | 'replica' | 'read-only' | 'waiting' | 'storage' | 'internal' | 'orphan' | 'relayed';
|
|
195
|
+
reason: 'generation' | 'schema' | 'malformed' | 'container' | 'identity' | 'replica' | 'read-only' | 'waiting' | 'storage' | 'internal' | 'quota' | 'mark' | 'denied' | 'orphan' | 'relayed';
|
|
167
196
|
detail: unknown;
|
|
168
197
|
};
|
|
198
|
+
/** A duration series: how many, their total and longest (ms; see {@link RoomMetrics}). */
|
|
199
|
+
export type Timing = {
|
|
200
|
+
count: number;
|
|
201
|
+
totalMs: number;
|
|
202
|
+
maxMs: number;
|
|
203
|
+
lastMs: number;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* The room's counters (H14), since this instance started (memory: a wake
|
|
207
|
+
* starts them again), read with `metrics()` (also over RPC). Times are
|
|
208
|
+
* what the runtime's clock reports: Workers advance it only across I/O, so
|
|
209
|
+
* in production synchronous work (a compaction, a fold) often reads `0`;
|
|
210
|
+
* sizes and counts are exact.
|
|
211
|
+
*/
|
|
212
|
+
export type RoomMetrics = {
|
|
213
|
+
/** The document's size as the document quota measures it: its records, uncompressed. */
|
|
214
|
+
documentBytes: number;
|
|
215
|
+
/** What its rows take in storage (a snapshot compressed). */
|
|
216
|
+
storedBytes: number;
|
|
217
|
+
/** Logical records stored (the generation record included), and their rows. */
|
|
218
|
+
records: number;
|
|
219
|
+
rows: number;
|
|
220
|
+
/** Update records since the last compaction (compaction runs at `compactAfter`). */
|
|
221
|
+
updateRecords: number;
|
|
222
|
+
/** Bytes of updates held waiting for a dependency (in memory, never stored). */
|
|
223
|
+
waitingBytes: number;
|
|
224
|
+
/** The document's open sockets. */
|
|
225
|
+
sockets: number;
|
|
226
|
+
/** Compactions run, with their time. */
|
|
227
|
+
compaction: Timing & {
|
|
228
|
+
lastBytes: number;
|
|
229
|
+
};
|
|
230
|
+
/** Client sync frames folded (applied, indexed, stored and relayed), with their time. */
|
|
231
|
+
fold: Timing;
|
|
232
|
+
/** Frames sent to other sockets (relays, presence, acknowledgements to others), and their bytes. */
|
|
233
|
+
fanOut: {
|
|
234
|
+
messages: number;
|
|
235
|
+
bytes: number;
|
|
236
|
+
};
|
|
237
|
+
/** Writes refused by a quota (`quota` refusals) and frames `validate` denied. */
|
|
238
|
+
quotaHits: number;
|
|
239
|
+
validationDenials: number;
|
|
240
|
+
/** Every refusal, by reason (as `refusalCounts`). */
|
|
241
|
+
refusals: Partial<Record<Refusal['reason'], number>>;
|
|
242
|
+
/** When this instance started (ms since the epoch). */
|
|
243
|
+
since: number;
|
|
244
|
+
};
|
|
245
|
+
/** One line of the room's log (H14): a compaction, a quota hit, a denial, a fault. */
|
|
246
|
+
export type RoomLogEntry = {
|
|
247
|
+
edytor: 'compaction';
|
|
248
|
+
ms: number;
|
|
249
|
+
bytes: number;
|
|
250
|
+
records: number;
|
|
251
|
+
rows: number;
|
|
252
|
+
} | {
|
|
253
|
+
edytor: 'quota';
|
|
254
|
+
user: string;
|
|
255
|
+
quota: string;
|
|
256
|
+
} | {
|
|
257
|
+
edytor: 'denied';
|
|
258
|
+
user: string;
|
|
259
|
+
touched: number;
|
|
260
|
+
} | {
|
|
261
|
+
edytor: 'fault';
|
|
262
|
+
reason: 'storage' | 'internal';
|
|
263
|
+
detail: string;
|
|
264
|
+
};
|
|
169
265
|
/** `pending`: deletes of items the room does not hold yet (they wait for them). */
|
|
170
266
|
type RowKind = 'generation' | 'update' | 'snapshot' | 'pending';
|
|
171
267
|
/**
|
|
@@ -177,12 +273,50 @@ type RowKind = 'generation' | 'update' | 'snapshot' | 'pending';
|
|
|
177
273
|
* `isRefusal` in `crdt/providers/room.ts`).
|
|
178
274
|
*/
|
|
179
275
|
export declare const closedSocket: (code: number, reason: string) => Response;
|
|
276
|
+
/** A stored record, reassembled: its kind, record number, bytes, and whether they are v2 (a v2 container's snapshot). */
|
|
277
|
+
export type StoredRecord = {
|
|
278
|
+
kind: RowKind;
|
|
279
|
+
record: number;
|
|
280
|
+
bytes: Uint8Array<ArrayBuffer>;
|
|
281
|
+
v2: boolean;
|
|
282
|
+
};
|
|
180
283
|
/** Run an entry point with timers forbidden — a pending timer keeps a Durable Object from hibernating. */
|
|
181
284
|
export declare const noTimers: <T>(fn: () => T) => T;
|
|
182
285
|
/** A Yjs client id (a non-negative safe integer), or `null`. */
|
|
183
286
|
export declare const parseReplica: (raw: unknown) => number | null;
|
|
184
287
|
/** An awareness frame carrying `entries` — no `Awareness` instance involved. */
|
|
185
288
|
export declare const presenceFrame: (entries: AwarenessEntry[]) => Uint8Array;
|
|
289
|
+
/** A block as `validate` reads it, before or after a frame. */
|
|
290
|
+
export type ValidatedBlock = {
|
|
291
|
+
id: string;
|
|
292
|
+
type: string;
|
|
293
|
+
data: Record<string, unknown>;
|
|
294
|
+
content: NonNullable<JSONBlock['content']>;
|
|
295
|
+
/** Its display parent (`null`: the document's root). */
|
|
296
|
+
parent: string | null;
|
|
297
|
+
};
|
|
298
|
+
/** One client frame the room applied, as `validate` reads it. */
|
|
299
|
+
export type FrameValidation = {
|
|
300
|
+
/** The sender's verified user. */
|
|
301
|
+
user: string;
|
|
302
|
+
/** The sender's replica (client id), when bound. */
|
|
303
|
+
replica: number | null;
|
|
304
|
+
/**
|
|
305
|
+
* The blocks the frame changed, from the facade's change report: shown
|
|
306
|
+
* or hidden (with their subtrees), given another parent or moved among
|
|
307
|
+
* their siblings (not shifted by a sibling's move), retyped, given
|
|
308
|
+
* other data, or other content.
|
|
309
|
+
*/
|
|
310
|
+
touched: string[];
|
|
311
|
+
/** The frame changed the document's own data. */
|
|
312
|
+
dataChanged: boolean;
|
|
313
|
+
/** A block as it was before the frame (`null`: it did not show). */
|
|
314
|
+
before: (id: string) => ValidatedBlock | null;
|
|
315
|
+
/** A block as it is now (`null`: it does not show). */
|
|
316
|
+
after: (id: string) => ValidatedBlock | null;
|
|
317
|
+
/** The live document. Read it; never write from `validate` (defer a `transact` instead). */
|
|
318
|
+
facade: EdytorDoc;
|
|
319
|
+
};
|
|
186
320
|
/** What {@link attachDocument} (and `DocumentRoom`) takes. */
|
|
187
321
|
export type AttachDocumentOptions = {
|
|
188
322
|
/**
|
|
@@ -211,6 +345,29 @@ export type AttachDocumentOptions = {
|
|
|
211
345
|
maxFrameBytes?: number;
|
|
212
346
|
/** Prefix of the document's SQL tables, beside your own (default `'edytor_'`). */
|
|
213
347
|
tablePrefix?: string;
|
|
348
|
+
/** Largest document, in stored bytes (default {@link DEFAULT_MAX_DOCUMENT_BYTES}). */
|
|
349
|
+
maxDocumentBytes?: number;
|
|
350
|
+
/** Largest frame a socket may send, reassembled (default {@link DEFAULT_MAX_INBOUND_FRAME_BYTES}). */
|
|
351
|
+
maxInboundFrameBytes?: number;
|
|
352
|
+
/** Sync messages per second a socket may send (default {@link DEFAULT_MAX_UPDATES_PER_SECOND}). */
|
|
353
|
+
maxUpdatesPerSecond?: number;
|
|
354
|
+
/**
|
|
355
|
+
* Accept, then compensate (H2): after the room applied and stored a
|
|
356
|
+
* client frame that changed blocks, it is asked whether to keep it.
|
|
357
|
+
* `false` (or a throw) denies it: the room writes the frame's inverse as
|
|
358
|
+
* its own transaction — the history undo of exactly that frame
|
|
359
|
+
* (`room.validate.inverse`) — stores it and sends it to every socket,
|
|
360
|
+
* the sender's included, so every replica converges. The frame stays
|
|
361
|
+
* stored and acknowledged and the socket open. Synchronous; runs only
|
|
362
|
+
* when set (the room then keeps an index of every block's state).
|
|
363
|
+
*/
|
|
364
|
+
validate?: (frame: FrameValidation) => boolean | void;
|
|
365
|
+
/**
|
|
366
|
+
* The room's log (H14): one entry per compaction, quota hit, denial or
|
|
367
|
+
* fault. Default: `console.log` of the entry as JSON (Workers Logs and
|
|
368
|
+
* `wrangler tail` collect it); `false` logs nothing.
|
|
369
|
+
*/
|
|
370
|
+
log?: ((entry: RoomLogEntry) => void) | false;
|
|
214
371
|
/**
|
|
215
372
|
* The block roles `transact` edits obey — the document semantics your
|
|
216
373
|
* clients' plugins declare (`defaultType` also names the block an empty
|
|
@@ -234,6 +391,9 @@ export declare class AttachedDocument {
|
|
|
234
391
|
readonly maxFrameBytes: number;
|
|
235
392
|
readonly compactAfter: number;
|
|
236
393
|
readonly saveAfter: number;
|
|
394
|
+
readonly maxDocumentBytes: number;
|
|
395
|
+
readonly maxInboundFrameBytes: number;
|
|
396
|
+
readonly maxUpdatesPerSecond: number;
|
|
237
397
|
private live;
|
|
238
398
|
/** Why the stored container was refused (another generation, a torn record, `onLoad`…). */
|
|
239
399
|
failure: Error | null;
|
|
@@ -252,6 +412,16 @@ export declare class AttachedDocument {
|
|
|
252
412
|
};
|
|
253
413
|
private nextRecord;
|
|
254
414
|
private updates;
|
|
415
|
+
/** What the records hold, uncompressed (the generation record aside): the document quota's measure. */
|
|
416
|
+
private documentBytes;
|
|
417
|
+
/** The counters `metrics()` reads (H14). */
|
|
418
|
+
private readonly counters;
|
|
419
|
+
/** The index `validate` reads (built at its first frame, for the live document). */
|
|
420
|
+
private validation;
|
|
421
|
+
/** Each socket's update allowance (a token bucket; memory: a wake refills it). */
|
|
422
|
+
private readonly allowances;
|
|
423
|
+
/** Each socket's chunked frame in flight (memory: a wake loses it, and the socket is faulted). */
|
|
424
|
+
private readonly chunkReaders;
|
|
255
425
|
/** A failed append: the live doc holds what storage does not, until it is rebuilt. */
|
|
256
426
|
private unstored;
|
|
257
427
|
/** Inside `transact`, its commit included (a nested call is refused but while `fn` runs). */
|
|
@@ -260,6 +430,12 @@ export declare class AttachedDocument {
|
|
|
260
430
|
private running;
|
|
261
431
|
/** Inside a client frame's apply, its events included: the frame's handler settles it. */
|
|
262
432
|
private handling;
|
|
433
|
+
/** How the container stores its snapshots (its generation record says; a fresh one: this build's). */
|
|
434
|
+
private format;
|
|
435
|
+
/** The snapshot compression in flight (`compressed()`). */
|
|
436
|
+
private compressing;
|
|
437
|
+
/** The raw bytes of the compressed snapshot record (`records` reads it; only `start` can inflate). */
|
|
438
|
+
private inflated;
|
|
263
439
|
/** The waiting deletes stored as `pending` records (the engine may hold more, in memory). */
|
|
264
440
|
private storedWaiting;
|
|
265
441
|
/** The failure is the storage's (a failed read, `onLoad`'s store down): the next dial starts again. */
|
|
@@ -272,6 +448,7 @@ export declare class AttachedDocument {
|
|
|
272
448
|
private readonly options;
|
|
273
449
|
private readonly rowsTable;
|
|
274
450
|
private readonly replicasTable;
|
|
451
|
+
private readonly metaTable;
|
|
275
452
|
private _lookups;
|
|
276
453
|
constructor(ctx: DurableObjectState, options?: AttachDocumentOptions);
|
|
277
454
|
/**
|
|
@@ -279,6 +456,12 @@ export declare class AttachedDocument {
|
|
|
279
456
|
* out of `blockConcurrencyWhile` resets the object on every request.
|
|
280
457
|
*/
|
|
281
458
|
private start;
|
|
459
|
+
/**
|
|
460
|
+
* Inflate a compressed snapshot record (P5) for `records`, which reads
|
|
461
|
+
* synchronously (a rebuild does): decompression is asynchronous, so
|
|
462
|
+
* only `start` can. One that does not inflate is a corrupt container.
|
|
463
|
+
*/
|
|
464
|
+
private inflate;
|
|
282
465
|
/** A read of the rows or `onLoad` failed (storage was down): start again. */
|
|
283
466
|
private retryStart;
|
|
284
467
|
private fail;
|
|
@@ -359,20 +542,43 @@ export declare class AttachedDocument {
|
|
|
359
542
|
* engine refuses) is refused for good.
|
|
360
543
|
*/
|
|
361
544
|
private load;
|
|
362
|
-
/**
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
545
|
+
/** The bytes of the stored `pending` records (uncompressed, as every record but a snapshot). */
|
|
546
|
+
private pendingBytes;
|
|
547
|
+
/**
|
|
548
|
+
* Reassembled logical records in write order (of one kind, with
|
|
549
|
+
* `only`); a torn record throws. A v2 container's snapshot is v2
|
|
550
|
+
* (`v2`), inflated when it is stored compressed (P5).
|
|
551
|
+
*/
|
|
552
|
+
records(only?: RowKind): StoredRecord[];
|
|
367
553
|
/**
|
|
368
554
|
* One logical record split into rows ≤ `maxRowBytes` (callers run it in
|
|
369
555
|
* a transaction). The first record of an empty container brings the
|
|
370
556
|
* generation record with it.
|
|
371
557
|
*/
|
|
372
558
|
private insert;
|
|
559
|
+
/** Record `record`'s rows (at the row positions `seqs`, when given). */
|
|
560
|
+
private writeRecord;
|
|
561
|
+
/**
|
|
562
|
+
* Compress snapshot `record` in place once it is stored (P5): gzip is
|
|
563
|
+
* asynchronous, and the store-before-ack path is not. Its rows are
|
|
564
|
+
* rewritten (at their positions) only while it is still the stored
|
|
565
|
+
* snapshot and only when that shrinks it; the raw bytes stay in memory
|
|
566
|
+
* (`inflated`) for a rebuild's synchronous read. A platform without
|
|
567
|
+
* `CompressionStream` keeps it raw.
|
|
568
|
+
*/
|
|
569
|
+
private compressLater;
|
|
570
|
+
/**
|
|
571
|
+
* Resolves once the snapshot compression in flight, if any, is stored
|
|
572
|
+
* (P5): a compaction stores its snapshot raw, then compresses it in
|
|
573
|
+
* place. Wait for it before reading the rows' sizes.
|
|
574
|
+
*/
|
|
575
|
+
compressed(): Promise<void>;
|
|
373
576
|
/**
|
|
374
577
|
* Compaction: the rows become the generation record + one chunked
|
|
375
|
-
* snapshot
|
|
578
|
+
* snapshot of the live document's state (P2: the healed state, deleted
|
|
579
|
+
* content collected but the text a replica may copy again, P11 —
|
|
580
|
+
* never a merge of the records, which keeps every keystroke's struct
|
|
581
|
+
* and every deleted character), atomically, + one
|
|
376
582
|
* `pending` record of the stored deletes still waiting. A waiting delete
|
|
377
583
|
* of a client id no user registered and no socket holds is reclaimed,
|
|
378
584
|
* in memory too: nothing can deliver its item (a made-up id) — the
|
|
@@ -385,6 +591,21 @@ export declare class AttachedDocument {
|
|
|
385
591
|
compact(): {
|
|
386
592
|
rows: number;
|
|
387
593
|
};
|
|
594
|
+
/** Record now as the last stored change (callers run it in the change's transaction). */
|
|
595
|
+
private touch;
|
|
596
|
+
/**
|
|
597
|
+
* When the room last stored a change (ms since the epoch), or `null`
|
|
598
|
+
* for a room that stored none since 0.1.0-next.23 (H12). Cheap: one
|
|
599
|
+
* row, no document. `routeDocumentSocket` answers it over HTTP
|
|
600
|
+
* (`GET <room>?lastUpdated`), also over RPC.
|
|
601
|
+
*/
|
|
602
|
+
lastUpdated(): number | null;
|
|
603
|
+
/** The room's counters (H14), also over RPC — see {@link RoomMetrics}. */
|
|
604
|
+
metrics(): RoomMetrics;
|
|
605
|
+
/** What the rows take: bytes, logical records, rows. */
|
|
606
|
+
private storage;
|
|
607
|
+
/** Write one log entry (H14) — the `log` option's, `console.log` by default. */
|
|
608
|
+
private log;
|
|
388
609
|
/**
|
|
389
610
|
* Drop every waiting delete, stored or in memory (callable over RPC):
|
|
390
611
|
* the way out when a writer filled {@link MAX_WAITING_DELETES} with
|
|
@@ -469,11 +690,65 @@ export declare class AttachedDocument {
|
|
|
469
690
|
/** Is `ws` one of this document's sockets? */
|
|
470
691
|
owns(ws: WebSocket): boolean;
|
|
471
692
|
webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): boolean;
|
|
693
|
+
/** One frame: whole as it came, or reassembled from its chunks. */
|
|
694
|
+
private onFrame;
|
|
695
|
+
/**
|
|
696
|
+
* One chunk of a frame too large to send whole (H6: a provider's
|
|
697
|
+
* reconnect diff): buffered per socket, the whole frame handled once its
|
|
698
|
+
* sequence ends. A sequence announcing more than `maxInboundFrameBytes`
|
|
699
|
+
* is a frame quota refusal, before anything is buffered. A part with no
|
|
700
|
+
* sequence started — the room woke between two chunks and lost the
|
|
701
|
+
* buffer — faults the socket (1011): its provider redials and resends.
|
|
702
|
+
*/
|
|
703
|
+
private onChunk;
|
|
704
|
+
/**
|
|
705
|
+
* A quota refused the socket's frame (H3): it is not applied, and the
|
|
706
|
+
* socket is closed `4413` (`quota: <name>`), a refusal its provider
|
|
707
|
+
* reports (`onSyncRefused`) and does not redial. Never a dropped frame
|
|
708
|
+
* on a live socket: the sender's later frames would build on it and
|
|
709
|
+
* wait in the room's memory for good, unstored and unacknowledged; and
|
|
710
|
+
* a redial would resend it and meet the same quota.
|
|
711
|
+
*/
|
|
712
|
+
private overQuota;
|
|
713
|
+
/**
|
|
714
|
+
* The update rate quota (H3): a token bucket per socket, refilled at
|
|
715
|
+
* `maxUpdatesPerSecond` up to ten seconds' worth. `false`: over it.
|
|
716
|
+
*/
|
|
717
|
+
private allow;
|
|
718
|
+
/**
|
|
719
|
+
* The document quota (H3): would `incoming` bytes take the document
|
|
720
|
+
* past `maxDocumentBytes`? Its measure is what its records hold and what
|
|
721
|
+
* the engine holds waiting; update records count until compaction
|
|
722
|
+
* collapses them, so it compacts first when they would.
|
|
723
|
+
*/
|
|
724
|
+
private overDocument;
|
|
472
725
|
webSocketClose(ws: WebSocket, code: number, reason: string): boolean;
|
|
473
726
|
webSocketError(ws: WebSocket): boolean;
|
|
474
727
|
/** The departure the client may not have announced — from the attachment, so it works after a wake. */
|
|
475
728
|
private depart;
|
|
476
729
|
private onSync;
|
|
730
|
+
/** A client's Step2 or Update: admitted, applied, stored, relayed, validated, acknowledged. */
|
|
731
|
+
private onUpdate;
|
|
732
|
+
/** A shown block's state, read from the facade (`null`: it does not show). */
|
|
733
|
+
private blockState;
|
|
734
|
+
/**
|
|
735
|
+
* The index `validate` reads, for `doc`, set to record the frame of
|
|
736
|
+
* `origin`: every shown block's state and each parent's children, built
|
|
737
|
+
* once and kept from every change report. Its history (once the
|
|
738
|
+
* document is initialized: a history first initializes its document)
|
|
739
|
+
* records the frame for its compensation.
|
|
740
|
+
*/
|
|
741
|
+
private validating;
|
|
742
|
+
/** Keep the index from one change report, recording what the frame being validated touched. */
|
|
743
|
+
private indexChange;
|
|
744
|
+
/**
|
|
745
|
+
* Ask `validate` about the frame just applied (H2); a denial is
|
|
746
|
+
* compensated by the room's own transaction: the history undo of that
|
|
747
|
+
* frame (`room.validate.inverse`), or, for the frame that initialized
|
|
748
|
+
* the document (no history recorded it), a delete of the blocks it
|
|
749
|
+
* added. Stored and sent to every socket, the sender's included.
|
|
750
|
+
*/
|
|
751
|
+
private validateFrame;
|
|
477
752
|
/**
|
|
478
753
|
* A delete of items the room does not hold (a relayer's delete of an
|
|
479
754
|
* author's edit a restore lost) waits in the engine for them: it is
|
|
@@ -549,6 +824,17 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
549
824
|
protected onLoad(): Promise<LoadedDocument | null | undefined>;
|
|
550
825
|
/** Save — see {@link AttachDocumentOptions.onSave}. Not overridden: no alarm is ever set. */
|
|
551
826
|
protected onSave(_document: SavedDocument): Promise<void>;
|
|
827
|
+
/**
|
|
828
|
+
* Validate a client frame — see {@link AttachDocumentOptions.validate}:
|
|
829
|
+
* `false` denies it, and the room writes its inverse. Not overridden: no
|
|
830
|
+
* frame is validated, and no block index is kept.
|
|
831
|
+
*/
|
|
832
|
+
protected validate(_frame: FrameValidation): boolean | void;
|
|
833
|
+
/**
|
|
834
|
+
* One log entry (H14): `console.log` of it as JSON, unless the
|
|
835
|
+
* `EDYTOR_LOG` var is `off`. Override it to send entries elsewhere.
|
|
836
|
+
*/
|
|
837
|
+
protected log(entry: RoomLogEntry): void;
|
|
552
838
|
/**
|
|
553
839
|
* Block roles — see {@link AttachDocumentOptions.semantics}. Read once,
|
|
554
840
|
* at first use (after construction: it may return a subclass field).
|
|
@@ -564,6 +850,12 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
564
850
|
compact(): {
|
|
565
851
|
rows: number;
|
|
566
852
|
};
|
|
853
|
+
/** The room's counters, also over RPC — see {@link RoomMetrics}. */
|
|
854
|
+
metrics(): RoomMetrics;
|
|
855
|
+
/** When the room last stored a change, also over RPC — see {@link AttachedDocument.lastUpdated}. */
|
|
856
|
+
lastUpdated(): number | null;
|
|
857
|
+
/** The snapshot compression in flight, also over RPC — see {@link AttachedDocument.compressed}. */
|
|
858
|
+
compressed(): Promise<void>;
|
|
567
859
|
/** Drop the stored waiting deletes, also over RPC — see {@link AttachedDocument.dropWaitingDeletes}. */
|
|
568
860
|
dropWaitingDeletes(): {
|
|
569
861
|
ranges: number;
|