edytor 0.1.0-next.44 → 1.0.0-rc.2
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/clipboard/moveText.js +1 -1
- package/dist/cloudflare/DocumentRoom.d.ts +15 -66
- package/dist/cloudflare/DocumentRoom.js +6 -75
- package/dist/cloudflare/history.d.ts +2 -2
- package/dist/cloudflare/history.js +2 -2
- package/dist/cloudflare/room/access.js +1 -1
- package/dist/cloudflare/room/admission.js +1 -1
- package/dist/cloudflare/room/comments.d.ts +1 -1
- package/dist/cloudflare/room/comments.js +1 -1
- package/dist/cloudflare/room/context.d.ts +129 -129
- package/dist/cloudflare/room/context.js +1 -1
- package/dist/cloudflare/room/history.d.ts +1 -1
- package/dist/cloudflare/room/history.js +1 -1
- package/dist/cloudflare/room/moves.js +1 -1
- package/dist/cloudflare/room/presence.d.ts +1 -1
- package/dist/cloudflare/room/presence.js +1 -1
- package/dist/cloudflare/room/purge.js +1 -1
- package/dist/cloudflare/room/replicas.d.ts +1 -1
- package/dist/cloudflare/room/replicas.js +1 -1
- package/dist/cloudflare/room/scheduler.d.ts +1 -1
- package/dist/cloudflare/room/scheduler.js +2 -2
- package/dist/cloudflare/room/shared.d.ts +60 -0
- package/dist/cloudflare/room/shared.js +79 -0
- package/dist/cloudflare/room/storage.d.ts +1 -1
- package/dist/cloudflare/room/storage.js +1 -1
- package/dist/cloudflare/room/updates.js +1 -1
- package/dist/cloudflare/room/validation.d.ts +1 -1
- package/dist/cloudflare/room/validation.js +1 -1
- package/dist/cloudflare/routeDocumentSocket.js +1 -1
- package/dist/collaboration/history/HistoryPanel.svelte +2 -2
- package/dist/components/Edytor.svelte +2 -2
- package/dist/components/Text.svelte +1 -1
- package/dist/crdt/document.d.ts +15 -9
- package/dist/crdt/document.js +20 -10
- package/dist/crdt/index.d.ts +1 -0
- package/dist/crdt/index.js +3 -2
- package/dist/crdt/nodes.d.ts +3 -2
- package/dist/crdt/operations.d.ts +161 -0
- package/dist/crdt/operations.js +97 -0
- package/dist/crdt/protocols/envelope.d.ts +2 -2
- package/dist/crdt/providers/container.d.ts +2 -2
- package/dist/edytor.svelte.js +2 -2
- package/dist/events/onBeforeInput.js +2 -2
- package/dist/events/onDrop.js +1 -1
- package/dist/events/onFocus.js +14 -7
- package/dist/index.d.ts +18 -2
- package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +2 -41
- package/dist/plugins/blockHandles/BlockHandleController.svelte.js +29 -287
- package/dist/plugins/blockHandles/blockHandlesPlugin.d.ts +2 -1
- package/dist/plugins/blockHandles/blockHandlesPlugin.js +70 -24
- package/dist/plugins/blockHandles/dropIndicator.d.ts +37 -0
- package/dist/plugins/blockHandles/dropIndicator.js +252 -0
- package/dist/plugins/blockHandles/geometry.d.ts +28 -0
- package/dist/plugins/blockHandles/geometry.js +38 -0
- package/dist/selection/pointer.svelte.d.ts +32 -0
- package/dist/selection/pointer.svelte.js +316 -0
- package/dist/selection/selection.svelte.d.ts +5 -16
- package/dist/selection/selection.svelte.js +33 -293
- package/dist/session/commands.d.ts +5 -53
- package/dist/session/commands.js +30 -7
- package/dist/session/history.js +1 -1
- package/dist/session/selection.d.ts +1 -1
- package/dist/surface/projector.svelte.js +1 -1
- package/package.json +1 -1
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
|
+
> **Release candidate.** Edytor `1.0.0-rc.2` is the 1.0 release candidate (`edytor` on npm): its API is frozen, and a change to it before 1.0 is a listed breaking change. 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
|
|
|
@@ -42,7 +42,7 @@ Edytor aims to be for Svelte what Slate.js is for React: a heavily customizable
|
|
|
42
42
|
The pre-release is on npm under the `next` tag. Name the tag: a bare `edytor` is the old, incompatible `0.0.11`.
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
pnpm add edytor
|
|
45
|
+
pnpm add edytor
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
`svelte@^5` is a peer dependency. Do not install `yjs`: the v14 engine is vendored (`edytor/crdt`). See [installation](https://edytor.dev/docs/getting-started) and [platform support](https://edytor.dev/docs/reference/platform-support) (Chrome, Edge, Firefox and Safari; real phones are not tested yet).
|
|
@@ -28,7 +28,7 @@ export const dropText = (edytor, drag, to) => {
|
|
|
28
28
|
const { copy } = drag;
|
|
29
29
|
/** Nothing written: the range stays selected (the drop's point is not adopted). */
|
|
30
30
|
const keep = () => {
|
|
31
|
-
edytor.selection.select(drag.range
|
|
31
|
+
edytor.selection.select(drag.range);
|
|
32
32
|
return false;
|
|
33
33
|
};
|
|
34
34
|
// A move onto itself: nothing to write.
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
* replace: those deletes wait with them, in memory); compaction keeps
|
|
48
48
|
* the pending records apart, and rewrites them as the deletes still
|
|
49
49
|
* waiting; a frame that discards a forged stamp drops them. At most
|
|
50
|
-
*
|
|
50
|
+
* `MAX_WAITING_DELETES` ranges wait, stored or in memory: a frame
|
|
51
51
|
* that would pass it has its waiting deletes dropped (refusal `waiting`,
|
|
52
52
|
* left out of the ack), the rest applied. Compaction reclaims the
|
|
53
53
|
* waiting deletes of client ids no user registered and no socket holds;
|
|
@@ -110,8 +110,9 @@ import { type HistoryEntry, type HistoryOptions, type KVLike, type R2BucketLike
|
|
|
110
110
|
import type { LockOptions } from './locks.js';
|
|
111
111
|
import type { CommitResult, ExportedBlocks, ImportReceipt, ImportRequest, LateEditBatch, MoveNamespace } from './move.js';
|
|
112
112
|
import type { AwarenessEntry } from '../crdt/protocol.js';
|
|
113
|
-
import type { DocumentSemanticsConfig,
|
|
113
|
+
import type { DocumentSemanticsConfig, DocumentOperations, JSONBlock, JSONDoc, YDoc } from '../crdt/index.js';
|
|
114
114
|
import type { CommentActor, CommentChange, CommentOutcome, CommentRequest, CommentSnapshot } from '../crdt/protocols/comments.js';
|
|
115
|
+
export { closedSocket, COMMENTS_HEADER, DEFAULT_MAX_COMMENT_BYTES, DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND, HISTORY_HEADER, HISTORY_KEY_HEADER, IDENTITY_HEADERS, MAX_REFUSALS, MAX_WAITING_DELETES, noTimers, PROBE_HEADER, PURGE_ORIGIN, RESTORE_ORIGIN, ROOM_ORIGIN, SOCKET_TAG } from './room/shared.js';
|
|
115
116
|
/** Under SQLite-backed Durable Objects' 2 MB row cap, with room for the other columns. */
|
|
116
117
|
export declare const DEFAULT_MAX_ROW_BYTES: number;
|
|
117
118
|
/** Update records before the rows are merged into one snapshot. */
|
|
@@ -166,14 +167,6 @@ export declare const DEFAULT_MAX_PRESENCE_PER_SECOND = 50;
|
|
|
166
167
|
* would send 50,000 frames a second.
|
|
167
168
|
*/
|
|
168
169
|
export declare const DEFAULT_MAX_PRESENCE_FANOUT = 2000;
|
|
169
|
-
/**
|
|
170
|
-
* Delete ranges the room keeps waiting for items it does not hold (a
|
|
171
|
-
* relayer's delete of an author's edit a restore lost). Every update the
|
|
172
|
-
* engine applies re-reads them, so they are capped.
|
|
173
|
-
*/
|
|
174
|
-
export declare const MAX_WAITING_DELETES = 1024;
|
|
175
|
-
/** Refusals kept in `refusals` (the newest); `refusalCounts` counts every one. */
|
|
176
|
-
export declare const MAX_REFUSALS = 100;
|
|
177
170
|
/**
|
|
178
171
|
* A client id and the user who owns it (the room's replica registry). An
|
|
179
172
|
* empty `user` marks an id a restore without a registry left unowned: its
|
|
@@ -208,33 +201,6 @@ export type SavedDocument = {
|
|
|
208
201
|
};
|
|
209
202
|
/** What `onLoad` may return: JSON, a bare v14 update, or `{ update, replicas }` (a `SavedDocument`). */
|
|
210
203
|
export type LoadedDocument = JSONDoc | Uint8Array | Pick<SavedDocument, 'update' | 'replicas'>;
|
|
211
|
-
/** The header `routeDocumentSocket` sets on an authorized probe: `lastUpdated` or `snapshot`. */
|
|
212
|
-
export declare const PROBE_HEADER = "X-Edytor-Probe";
|
|
213
|
-
/** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
|
|
214
|
-
export declare const ROOM_ORIGIN: unique symbol;
|
|
215
|
-
/** The transaction origin of a history restore: the room's restore history tracks it. */
|
|
216
|
-
export declare const RESTORE_ORIGIN: unique symbol;
|
|
217
|
-
/** The transaction origin of the room's purge: tracked by no history. */
|
|
218
|
-
export declare const PURGE_ORIGIN: unique symbol;
|
|
219
|
-
/** The header `routeDocumentHistory` sets on an authorized history request. */
|
|
220
|
-
export declare const HISTORY_HEADER = "X-Edytor-History";
|
|
221
|
-
/** The version key of a history `read` or `restore` request. */
|
|
222
|
-
export declare const HISTORY_KEY_HEADER = "X-Edytor-History-Key";
|
|
223
|
-
/** The header `routeDocumentComments` sets on an authorized comments request: `list` or `post`. */
|
|
224
|
-
export declare const COMMENTS_HEADER = "X-Edytor-Comments";
|
|
225
|
-
/**
|
|
226
|
-
* Headers carrying the identity `routeDocumentSocket` verified — never the
|
|
227
|
-
* client's. The user id is percent-encoded (`encodeURIComponent`): a header
|
|
228
|
-
* value is trimmed and cannot carry every character, and a user id is any
|
|
229
|
-
* string.
|
|
230
|
-
*/
|
|
231
|
-
export declare const IDENTITY_HEADERS: {
|
|
232
|
-
readonly user: "X-Edytor-User";
|
|
233
|
-
readonly replica: "X-Edytor-Replica";
|
|
234
|
-
readonly access: "X-Edytor-Access";
|
|
235
|
-
/** When the credential expires, ms since the epoch (absent: never). */
|
|
236
|
-
readonly expires: "X-Edytor-Expires";
|
|
237
|
-
};
|
|
238
204
|
/**
|
|
239
205
|
* Optional knobs (strings, as `vars` arrive): row size, outgoing frame
|
|
240
206
|
* limit and compaction threshold. The defaults are the platform limits.
|
|
@@ -434,15 +400,6 @@ export type RoomLogEntry = {
|
|
|
434
400
|
};
|
|
435
401
|
/** `pending`: deletes of items the room does not hold yet (they wait for them). */
|
|
436
402
|
type RowKind = 'generation' | 'update' | 'snapshot' | 'pending';
|
|
437
|
-
/**
|
|
438
|
-
* An upgrade accepted and closed at once, so the client reads `code`: a
|
|
439
|
-
* browser sees an HTTP error at the upgrade only as `1006`, which a
|
|
440
|
-
* provider cannot tell from a network failure. Refuse a dial your Worker
|
|
441
|
-
* turns away itself with it: the provider stops at `1008` or `4xxx`
|
|
442
|
-
* (except `4401`, redialed), and redials after anything else (`CLOSE`,
|
|
443
|
-
* `isRefusal` in `crdt/providers/room.ts`).
|
|
444
|
-
*/
|
|
445
|
-
export declare const closedSocket: (code: number, reason: string) => Response;
|
|
446
403
|
/** A stored record, reassembled: its kind, record number, bytes, and whether they are v2 (a v2 container's snapshot). */
|
|
447
404
|
export type StoredRecord = {
|
|
448
405
|
kind: RowKind;
|
|
@@ -450,8 +407,6 @@ export type StoredRecord = {
|
|
|
450
407
|
bytes: Uint8Array<ArrayBuffer>;
|
|
451
408
|
v2: boolean;
|
|
452
409
|
};
|
|
453
|
-
/** Run an entry point with timers forbidden — a pending timer keeps a Durable Object from hibernating. */
|
|
454
|
-
export declare const noTimers: <T>(fn: () => T) => T;
|
|
455
410
|
/** A block as `validate` reads it, before or after a frame. */
|
|
456
411
|
export type ValidatedBlock = {
|
|
457
412
|
id: string;
|
|
@@ -481,7 +436,7 @@ export type FrameValidation = {
|
|
|
481
436
|
/** A block as it is now (`null`: it does not show). */
|
|
482
437
|
after: (id: string) => ValidatedBlock | null;
|
|
483
438
|
/** The live document. Read it; never write from `validate` (defer a `transact` instead). */
|
|
484
|
-
facade:
|
|
439
|
+
facade: DocumentOperations;
|
|
485
440
|
};
|
|
486
441
|
/** The room's comment threads (`comments` of {@link AttachRoomOptions}). */
|
|
487
442
|
export type CommentOptions = {
|
|
@@ -496,24 +451,20 @@ export type CommentOptions = {
|
|
|
496
451
|
maxLength?: number;
|
|
497
452
|
/**
|
|
498
453
|
* The bytes the room's comments may hold (default
|
|
499
|
-
*
|
|
454
|
+
* `DEFAULT_MAX_COMMENT_BYTES`, 4 MiB): each body and quote in UTF-8
|
|
500
455
|
* plus 128 a comment. A thread or reply past it is refused `413`; a
|
|
501
456
|
* delete frees its bytes.
|
|
502
457
|
*/
|
|
503
458
|
maxBytes?: number;
|
|
504
459
|
/**
|
|
505
460
|
* Comment requests per second (default
|
|
506
|
-
*
|
|
461
|
+
* `DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND`, a ten-second burst):
|
|
507
462
|
* over HTTP per verified user (past it, `429`), and a socket's comment
|
|
508
463
|
* messages per socket (past it, dropped). RPC calls are the host's own and
|
|
509
464
|
* are not counted.
|
|
510
465
|
*/
|
|
511
466
|
maxRequestsPerSecond?: number;
|
|
512
467
|
};
|
|
513
|
-
/** The bytes a room's comments hold at most, by default (4 MiB). */
|
|
514
|
-
export declare const DEFAULT_MAX_COMMENT_BYTES: number;
|
|
515
|
-
/** Comment requests per second and user (or socket), by default. */
|
|
516
|
-
export declare const DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND = 2;
|
|
517
468
|
/** What {@link attachRoom} (and `DocumentRoom`) takes. */
|
|
518
469
|
export type AttachRoomOptions = {
|
|
519
470
|
/**
|
|
@@ -599,7 +550,8 @@ export type AttachRoomOptions = {
|
|
|
599
550
|
*/
|
|
600
551
|
semantics?: DocumentSemanticsConfig;
|
|
601
552
|
/**
|
|
602
|
-
* Version history in
|
|
553
|
+
* Version history, in the store `HistoryOptions.store` names (a KV namespace, an R2 bucket or
|
|
554
|
+
* the room's own storage): the room writes its
|
|
603
555
|
* state twice a day — the morning's at local noon, the evening's at
|
|
604
556
|
* midnight, in `timeZone` — when it changed, for `retentionDays`; read
|
|
605
557
|
* and restore them with `listHistory`, `readHistory`, `restoreHistory`
|
|
@@ -622,8 +574,6 @@ export type AttachRoomOptions = {
|
|
|
622
574
|
*/
|
|
623
575
|
comments?: CommentOptions | false;
|
|
624
576
|
};
|
|
625
|
-
/** The tag of the document's sockets: other sockets of the object are left to you. */
|
|
626
|
-
export declare const SOCKET_TAG = "edytor";
|
|
627
577
|
/**
|
|
628
578
|
* One edytor document living in a Durable Object's storage: the room
|
|
629
579
|
* logic, independent of the class that hosts it. Create it with
|
|
@@ -648,7 +598,7 @@ export declare class AttachedDocument {
|
|
|
648
598
|
failure: Error | null;
|
|
649
599
|
/** Latest presence entry per replica, for join snapshots (memory: refills after a wake). */
|
|
650
600
|
presence: Map<number, E.AwarenessEntry>;
|
|
651
|
-
/** The newest
|
|
601
|
+
/** The newest `MAX_REFUSALS` refusals since this instance started (diagnostics; memory only). */
|
|
652
602
|
refusals: Refusal[];
|
|
653
603
|
/** Every refusal since this instance started, by reason. */
|
|
654
604
|
refusalCounts: Partial<Record<Refusal['reason'], number>>;
|
|
@@ -678,7 +628,7 @@ export declare class AttachedDocument {
|
|
|
678
628
|
* first drops a direct write whose append failed (`heal`), so the next
|
|
679
629
|
* write through it is stored.
|
|
680
630
|
*/
|
|
681
|
-
get facade():
|
|
631
|
+
get facade(): DocumentOperations;
|
|
682
632
|
/**
|
|
683
633
|
* Manipulate: run `fn` on the facade in one transaction. The edit is
|
|
684
634
|
* stored, then broadcast to every socket. It is not a rollback, like
|
|
@@ -695,7 +645,7 @@ export declare class AttachedDocument {
|
|
|
695
645
|
* without writing: the engine would queue the write until that one
|
|
696
646
|
* ends, so it could not be stored before returning.
|
|
697
647
|
*/
|
|
698
|
-
transact<T>(fn: (facade:
|
|
648
|
+
transact<T>(fn: (facade: DocumentOperations) => T): T;
|
|
699
649
|
/** The document as JSON. */
|
|
700
650
|
read(): JSONDoc;
|
|
701
651
|
/**
|
|
@@ -746,7 +696,7 @@ export declare class AttachedDocument {
|
|
|
746
696
|
};
|
|
747
697
|
/**
|
|
748
698
|
* When the room last stored a change (ms since the epoch), or `null`
|
|
749
|
-
* for a room that stored none since
|
|
699
|
+
* for a room that stored none since it records the time. Cheap: one
|
|
750
700
|
* row, no document. `routeDocumentSocket` answers it over HTTP
|
|
751
701
|
* (`GET <room>?lastUpdated`), also over RPC.
|
|
752
702
|
*/
|
|
@@ -857,7 +807,7 @@ export declare class AttachedDocument {
|
|
|
857
807
|
metrics(): RoomMetrics;
|
|
858
808
|
/**
|
|
859
809
|
* Drop every waiting delete, stored or in memory (callable over RPC):
|
|
860
|
-
* the way out when a writer filled
|
|
810
|
+
* the way out when a writer filled `MAX_WAITING_DELETES` with
|
|
861
811
|
* deletes of registered ids' items, which compaction keeps. A dropped
|
|
862
812
|
* delete's item shows if it ever arrives — but for an entry a waiting
|
|
863
813
|
* rewrite replaces, which that rewrite deletes should it integrate.
|
|
@@ -1044,10 +994,10 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
1044
994
|
/** Generation cutover, also over RPC — see {@link AttachedDocument.reset}. */
|
|
1045
995
|
reset(): Promise<void>;
|
|
1046
996
|
/** Server-side edit — see {@link AttachedDocument.transact}. */
|
|
1047
|
-
transact<T>(fn: (facade:
|
|
997
|
+
transact<T>(fn: (facade: DocumentOperations) => T): T;
|
|
1048
998
|
/** The document as JSON. */
|
|
1049
999
|
read(): JSONDoc;
|
|
1050
|
-
get facade():
|
|
1000
|
+
get facade(): DocumentOperations;
|
|
1051
1001
|
get doc(): YDoc | null;
|
|
1052
1002
|
get failure(): Error | null;
|
|
1053
1003
|
get refusals(): Refusal[];
|
|
@@ -1056,4 +1006,3 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
1056
1006
|
get origin(): AttachedDocument['origin'];
|
|
1057
1007
|
records(): ReturnType<AttachedDocument['records']>;
|
|
1058
1008
|
}
|
|
1059
|
-
export {};
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
* replace: those deletes wait with them, in memory); compaction keeps
|
|
48
48
|
* the pending records apart, and rewrites them as the deletes still
|
|
49
49
|
* waiting; a frame that discards a forged stamp drops them. At most
|
|
50
|
-
*
|
|
50
|
+
* `MAX_WAITING_DELETES` ranges wait, stored or in memory: a frame
|
|
51
51
|
* that would pass it has its waiting deletes dropped (refusal `waiting`,
|
|
52
52
|
* left out of the ack), the rest applied. Compaction reclaims the
|
|
53
53
|
* waiting deletes of client ids no user registered and no socket holds;
|
|
@@ -120,6 +120,8 @@ import { ReplicaRegistry } from './room/replicas.js';
|
|
|
120
120
|
import { Scheduler } from './room/scheduler.js';
|
|
121
121
|
import { RoomStorage } from './room/storage.js';
|
|
122
122
|
import { RoomValidation } from './room/validation.js';
|
|
123
|
+
import { noTimers, SOCKET_TAG } from './room/shared.js';
|
|
124
|
+
export { closedSocket, COMMENTS_HEADER, DEFAULT_MAX_COMMENT_BYTES, DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND, HISTORY_HEADER, HISTORY_KEY_HEADER, IDENTITY_HEADERS, MAX_REFUSALS, MAX_WAITING_DELETES, noTimers, PROBE_HEADER, PURGE_ORIGIN, RESTORE_ORIGIN, ROOM_ORIGIN, SOCKET_TAG } from './room/shared.js';
|
|
123
125
|
/** Under SQLite-backed Durable Objects' 2 MB row cap, with room for the other columns. */
|
|
124
126
|
export const DEFAULT_MAX_ROW_BYTES = 2_000_000 - 4096;
|
|
125
127
|
/** Update records before the rows are merged into one snapshot. */
|
|
@@ -177,75 +179,6 @@ export const DEFAULT_MAX_PRESENCE_FANOUT = 2000;
|
|
|
177
179
|
/** Seconds of the update rate a socket may spend at once. */
|
|
178
180
|
/** The ceiling of a quota knob (a host may raise the defaults up to it). */
|
|
179
181
|
const QUOTA_CEILING = 2 ** 40;
|
|
180
|
-
/**
|
|
181
|
-
* Delete ranges the room keeps waiting for items it does not hold (a
|
|
182
|
-
* relayer's delete of an author's edit a restore lost). Every update the
|
|
183
|
-
* engine applies re-reads them, so they are capped.
|
|
184
|
-
*/
|
|
185
|
-
export const MAX_WAITING_DELETES = 1024;
|
|
186
|
-
/** Refusals kept in `refusals` (the newest); `refusalCounts` counts every one. */
|
|
187
|
-
export const MAX_REFUSALS = 100;
|
|
188
|
-
/** The header `routeDocumentSocket` sets on an authorized probe: `lastUpdated` or `snapshot`. */
|
|
189
|
-
export const PROBE_HEADER = 'X-Edytor-Probe';
|
|
190
|
-
/** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
|
|
191
|
-
export const ROOM_ORIGIN = Symbol('edytor-room');
|
|
192
|
-
/** The transaction origin of a history restore: the room's restore history tracks it. */
|
|
193
|
-
export const RESTORE_ORIGIN = Symbol('edytor-restore');
|
|
194
|
-
/** The transaction origin of the room's purge: tracked by no history. */
|
|
195
|
-
export const PURGE_ORIGIN = Symbol('edytor-purge');
|
|
196
|
-
/** The header `routeDocumentHistory` sets on an authorized history request. */
|
|
197
|
-
export const HISTORY_HEADER = 'X-Edytor-History';
|
|
198
|
-
/** The version key of a history `read` or `restore` request. */
|
|
199
|
-
export const HISTORY_KEY_HEADER = 'X-Edytor-History-Key';
|
|
200
|
-
/** The header `routeDocumentComments` sets on an authorized comments request: `list` or `post`. */
|
|
201
|
-
export const COMMENTS_HEADER = 'X-Edytor-Comments';
|
|
202
|
-
/**
|
|
203
|
-
* Headers carrying the identity `routeDocumentSocket` verified — never the
|
|
204
|
-
* client's. The user id is percent-encoded (`encodeURIComponent`): a header
|
|
205
|
-
* value is trimmed and cannot carry every character, and a user id is any
|
|
206
|
-
* string.
|
|
207
|
-
*/
|
|
208
|
-
export const IDENTITY_HEADERS = {
|
|
209
|
-
user: 'X-Edytor-User',
|
|
210
|
-
replica: 'X-Edytor-Replica',
|
|
211
|
-
access: 'X-Edytor-Access',
|
|
212
|
-
/** When the credential expires, ms since the epoch (absent: never). */
|
|
213
|
-
expires: 'X-Edytor-Expires'
|
|
214
|
-
};
|
|
215
|
-
/**
|
|
216
|
-
* An upgrade accepted and closed at once, so the client reads `code`: a
|
|
217
|
-
* browser sees an HTTP error at the upgrade only as `1006`, which a
|
|
218
|
-
* provider cannot tell from a network failure. Refuse a dial your Worker
|
|
219
|
-
* turns away itself with it: the provider stops at `1008` or `4xxx`
|
|
220
|
-
* (except `4401`, redialed), and redials after anything else (`CLOSE`,
|
|
221
|
-
* `isRefusal` in `crdt/providers/room.ts`).
|
|
222
|
-
*/
|
|
223
|
-
export const closedSocket = (code, reason) => {
|
|
224
|
-
const [client, server] = Object.values(new WebSocketPair());
|
|
225
|
-
server.accept();
|
|
226
|
-
server.close(code, reason);
|
|
227
|
-
return new Response(null, { status: 101, webSocket: client });
|
|
228
|
-
};
|
|
229
|
-
/** Run an entry point with timers forbidden — a pending timer keeps a Durable Object from hibernating. */
|
|
230
|
-
export const noTimers = (fn) => {
|
|
231
|
-
const g = globalThis;
|
|
232
|
-
const saved = [g.setTimeout, g.setInterval];
|
|
233
|
-
const forbidden = () => {
|
|
234
|
-
throw new Error('room scheduled a timer (a Durable Object could not hibernate)');
|
|
235
|
-
};
|
|
236
|
-
g.setTimeout = forbidden;
|
|
237
|
-
g.setInterval = forbidden;
|
|
238
|
-
try {
|
|
239
|
-
return fn();
|
|
240
|
-
}
|
|
241
|
-
finally {
|
|
242
|
-
[g.setTimeout, g.setInterval] = saved;
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
/** The bytes a room's comments hold at most, by default (4 MiB). */
|
|
246
|
-
export const DEFAULT_MAX_COMMENT_BYTES = 4 * 1024 * 1024;
|
|
247
|
-
/** Comment requests per second and user (or socket), by default. */
|
|
248
|
-
export const DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND = 2;
|
|
249
182
|
/** `EDYTOR_PURGE_AFTER_DAYS`: `off` (or `false`) never purges; a number of days; anything else the default. */
|
|
250
183
|
const purgeAfterDays = (value) => {
|
|
251
184
|
if (value === 'off' || value === 'false' || value === false)
|
|
@@ -253,8 +186,6 @@ const purgeAfterDays = (value) => {
|
|
|
253
186
|
const n = Number(value);
|
|
254
187
|
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
255
188
|
};
|
|
256
|
-
/** The tag of the document's sockets: other sockets of the object are left to you. */
|
|
257
|
-
export const SOCKET_TAG = 'edytor';
|
|
258
189
|
/**
|
|
259
190
|
* One edytor document living in a Durable Object's storage: the room
|
|
260
191
|
* logic, independent of the class that hosts it. Create it with
|
|
@@ -279,7 +210,7 @@ export class AttachedDocument {
|
|
|
279
210
|
failure = null;
|
|
280
211
|
/** Latest presence entry per replica, for join snapshots (memory: refills after a wake). */
|
|
281
212
|
presence = new Map();
|
|
282
|
-
/** The newest
|
|
213
|
+
/** The newest `MAX_REFUSALS` refusals since this instance started (diagnostics; memory only). */
|
|
283
214
|
refusals = [];
|
|
284
215
|
/** Every refusal since this instance started, by reason. */
|
|
285
216
|
refusalCounts = {};
|
|
@@ -427,7 +358,7 @@ export class AttachedDocument {
|
|
|
427
358
|
}
|
|
428
359
|
/**
|
|
429
360
|
* When the room last stored a change (ms since the epoch), or `null`
|
|
430
|
-
* for a room that stored none since
|
|
361
|
+
* for a room that stored none since it records the time. Cheap: one
|
|
431
362
|
* row, no document. `routeDocumentSocket` answers it over HTTP
|
|
432
363
|
* (`GET <room>?lastUpdated`), also over RPC.
|
|
433
364
|
*/
|
|
@@ -582,7 +513,7 @@ export class AttachedDocument {
|
|
|
582
513
|
}
|
|
583
514
|
/**
|
|
584
515
|
* Drop every waiting delete, stored or in memory (callable over RPC):
|
|
585
|
-
* the way out when a writer filled
|
|
516
|
+
* the way out when a writer filled `MAX_WAITING_DELETES` with
|
|
586
517
|
* deletes of registered ids' items, which compaction keeps. A dropped
|
|
587
518
|
* delete's item shows if it ever arrives — but for an entry a waiting
|
|
588
519
|
* rewrite replaces, which that rewrite deletes should it integrate.
|
|
@@ -200,8 +200,8 @@ export declare const historyEntry: (room: string, name: string, metadata: unknow
|
|
|
200
200
|
* Versions in a Workers KV namespace (the room history's first store): KV's
|
|
201
201
|
* TTL expires them (`nativeTtl`, `expirationTtl` = the retention, at least
|
|
202
202
|
* KV's 60 s), its listing's `expiration` is each one's `expiresAt`, its
|
|
203
|
-
* metadata the room's. Values up to KV's 25 MiB. Versions written by
|
|
204
|
-
*
|
|
203
|
+
* metadata the room's. Values up to KV's 25 MiB. Versions written by the
|
|
204
|
+
* pre-releases that took a bare namespace as the store read as they are.
|
|
205
205
|
*/
|
|
206
206
|
export declare const kvHistory: (namespace: KVLike) => HistoryStore;
|
|
207
207
|
/**
|
|
@@ -145,8 +145,8 @@ const finite = (value) => {
|
|
|
145
145
|
* Versions in a Workers KV namespace (the room history's first store): KV's
|
|
146
146
|
* TTL expires them (`nativeTtl`, `expirationTtl` = the retention, at least
|
|
147
147
|
* KV's 60 s), its listing's `expiration` is each one's `expiresAt`, its
|
|
148
|
-
* metadata the room's. Values up to KV's 25 MiB. Versions written by
|
|
149
|
-
*
|
|
148
|
+
* metadata the room's. Values up to KV's 25 MiB. Versions written by the
|
|
149
|
+
* pre-releases that took a bare namespace as the store read as they are.
|
|
150
150
|
*/
|
|
151
151
|
export const kvHistory = (namespace) => ({
|
|
152
152
|
maxValueBytes: HISTORY_MAX_VALUE_BYTES,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* revocation, a changed access, an expired credential.
|
|
7
7
|
*/
|
|
8
8
|
import { CLOSE } from '../../crdt/providers/room.js';
|
|
9
|
-
import { COMMENTS_HEADER, HISTORY_HEADER, HISTORY_KEY_HEADER, IDENTITY_HEADERS, PROBE_HEADER, SOCKET_TAG, closedSocket, noTimers } from '
|
|
9
|
+
import { COMMENTS_HEADER, HISTORY_HEADER, HISTORY_KEY_HEADER, IDENTITY_HEADERS, PROBE_HEADER, SOCKET_TAG, closedSocket, noTimers } from './shared.js';
|
|
10
10
|
import { STORAGE_FAILURE } from './context.js';
|
|
11
11
|
import { readOnlyFrame, step1Frame } from './frames.js';
|
|
12
12
|
import { StorageFault } from './replicas.js';
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { Y } from '../../crdt/engine.js';
|
|
13
13
|
import * as E from '../../crdt/protocol.js';
|
|
14
14
|
import { ChunkLimitError, ChunkSequenceError, CLOSE } from '../../crdt/providers/room.js';
|
|
15
|
-
import { MAX_WAITING_DELETES, noTimers } from '
|
|
15
|
+
import { MAX_WAITING_DELETES, noTimers } from './shared.js';
|
|
16
16
|
import { STORAGE_FAILURE, now, sync, tally } from './context.js';
|
|
17
17
|
import { attributionWrites, BINDING_PREFIX, forgedAttributionDeletes, forgedDeletes, forgedWriters } from './forged.js';
|
|
18
18
|
import { MalformedFrame, decode, readOnlyDenialFrame, savedFrame, step1Frame, storedStep2, updateFrame } from './frames.js';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as E from '../../crdt/protocol.js';
|
|
10
10
|
import { type CommentActor, type CommentOutcome, type CommentRequest, type CommentSnapshot } from '../../crdt/protocols/comments.js';
|
|
11
|
-
import {
|
|
11
|
+
import type { Attachment, SocketIdentity } from '../DocumentRoom.js';
|
|
12
12
|
import { type RoomContext } from './context.js';
|
|
13
13
|
/** The largest request body `routeDocumentComments` forwards, in bytes. */
|
|
14
14
|
export declare const MAX_COMMENT_REQUEST_BYTES: number;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as E from '../../crdt/protocol.js';
|
|
10
10
|
import { COMMENT_MARK, COMMENT_ROW_BYTES, COMMENT_STATUS, commentAnchors, decideComment, OVER_BYTE_QUOTA, parseCommentRequest, validCommentId } from '../../crdt/protocols/comments.js';
|
|
11
|
-
import { DEFAULT_MAX_COMMENT_BYTES, DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND, SOCKET_TAG, noTimers } from '
|
|
11
|
+
import { DEFAULT_MAX_COMMENT_BYTES, DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND, SOCKET_TAG, noTimers } from './shared.js';
|
|
12
12
|
import { allowance } from './admission.js';
|
|
13
13
|
import { knob } from './context.js';
|
|
14
14
|
import { decode } from './frames.js';
|