edytor 1.0.0-rc.1 → 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 +1 -1
- package/dist/cloudflare/DocumentRoom.d.ts +9 -60
- 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 +1 -1
- 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/components/Edytor.svelte +2 -2
- package/dist/components/Text.svelte +1 -1
- package/dist/crdt/document.d.ts +0 -6
- package/dist/crdt/document.js +8 -16
- 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 +1 -1
- package/dist/events/onDrop.js +1 -1
- package/dist/events/onFocus.js +8 -8
- package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +2 -41
- package/dist/plugins/blockHandles/BlockHandleController.svelte.js +29 -287
- 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 +0 -8
- package/dist/selection/selection.svelte.js +22 -289
- 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
|
-
> **Release candidate.** Edytor `1.0.0-rc.
|
|
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
|
|
|
@@ -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;
|
|
@@ -112,6 +112,7 @@ import type { CommitResult, ExportedBlocks, ImportReceipt, ImportRequest, LateEd
|
|
|
112
112
|
import type { AwarenessEntry } from '../crdt/protocol.js';
|
|
113
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;
|
|
@@ -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>>;
|
|
@@ -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.
|
|
@@ -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';
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { Y } from '../../crdt/engine.js';
|
|
11
11
|
import * as E from '../../crdt/protocol.js';
|
|
12
12
|
import type { DocumentSemanticsConfig, EdytorDoc, JSONDoc, YDoc } from '../../crdt/index.js';
|
|
13
|
-
import {
|
|
13
|
+
import type { AttachRoomOptions, AttachedDocument, Refusal, RoomLogEntry, Timing } from '../DocumentRoom.js';
|
|
14
14
|
import type { RoomAccess } from './access.js';
|
|
15
15
|
import type { Admission } from './admission.js';
|
|
16
16
|
import type { RoomComments } from './comments.js';
|
|
@@ -12,7 +12,7 @@ import * as E from '../../crdt/protocol.js';
|
|
|
12
12
|
import { CLOSE } from '../../crdt/providers/room.js';
|
|
13
13
|
import { asEngineDoc } from '../../crdt/structs.js';
|
|
14
14
|
import { defaultSemantics, facadeConfigOf, semanticsDigest } from '../../crdt/semantics.js';
|
|
15
|
-
import { MAX_REFUSALS, ROOM_ORIGIN, SOCKET_TAG, noTimers } from '
|
|
15
|
+
import { MAX_REFUSALS, ROOM_ORIGIN, SOCKET_TAG, noTimers } from './shared.js';
|
|
16
16
|
/** The engine API the room is built on (the Worker-safe CRDT entry). */
|
|
17
17
|
export const crdt = E.bindCrdt(Y);
|
|
18
18
|
export const sync = crdt.sync;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSONDoc } from '../../crdt/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { RestoreResult, SocketIdentity } from '../DocumentRoom.js';
|
|
3
3
|
import { type HistoryEntry, type HistoryOptions, type HistoryStore } from '../history.js';
|
|
4
4
|
import { type Decoded, type RoomContext } from './context.js';
|
|
5
5
|
/** The history settings, resolved. */
|
|
@@ -8,7 +8,7 @@ import { Y } from '../../crdt/engine.js';
|
|
|
8
8
|
import * as E from '../../crdt/protocol.js';
|
|
9
9
|
import { gunzip, packed } from '../../crdt/storage.js';
|
|
10
10
|
import { asEngineDoc, collectable, collectNow, collects, engineOps, stepsOf } from '../../crdt/structs.js';
|
|
11
|
-
import { PURGE_ORIGIN, RESTORE_ORIGIN, noTimers } from '
|
|
11
|
+
import { PURGE_ORIGIN, RESTORE_ORIGIN, noTimers } from './shared.js';
|
|
12
12
|
import { DEFAULT_RETENTION_DAYS, HISTORY_MAX_VALUE_BYTES, fitMetadata, historyEntry, historyKey, historyPrefix, parseHistoryKey, resolveHistoryStore, slotAt, slotEnd, validTimeZone } from '../history.js';
|
|
13
13
|
import { DAY, crdt, knob } from './context.js';
|
|
14
14
|
import { admit, deletesUpdate, liveState } from './updates.js';
|
|
@@ -8,7 +8,7 @@ import { Y } from '../../crdt/engine.js';
|
|
|
8
8
|
import { DEL_PREFIX, REGISTRY_KEY } from '../../crdt/schema.js';
|
|
9
9
|
import { asEngineDoc, attrItems } from '../../crdt/structs.js';
|
|
10
10
|
import { toBlockSpec } from '../../utils/json.js';
|
|
11
|
-
import { noTimers } from '
|
|
11
|
+
import { noTimers } from './shared.js';
|
|
12
12
|
import { DEFAULT_MOVE_GRACE_DAYS, contentOf, flattenMoved, hunk, mergeData, subtreesOf, threeWay, unitsOf } from '../move.js';
|
|
13
13
|
import { DAY, crdt } from './context.js';
|
|
14
14
|
/** A forward of late edits that failed runs again this much later (`room.move.late`). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AwarenessEntry } from '../../crdt/protocol.js';
|
|
2
2
|
import type { YDoc } from '../../crdt/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { Attachment } from '../DocumentRoom.js';
|
|
4
4
|
import { type RoomContext } from './context.js';
|
|
5
5
|
export declare class RoomPresence {
|
|
6
6
|
private readonly room;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the socket goes.
|
|
8
8
|
*/
|
|
9
9
|
import { semanticsMismatch } from '../../crdt/semantics.js';
|
|
10
|
-
import { SOCKET_TAG } from '
|
|
10
|
+
import { SOCKET_TAG } from './shared.js';
|
|
11
11
|
import { allowance } from './admission.js';
|
|
12
12
|
import { encodeJSON } from './context.js';
|
|
13
13
|
import { encodePresenceEntry, presenceFrame, presenceFrameOf } from './frames.js';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Y } from '../../crdt/engine.js';
|
|
8
8
|
import { asEngineDoc } from '../../crdt/structs.js';
|
|
9
|
-
import { PURGE_ORIGIN, noTimers } from '
|
|
9
|
+
import { PURGE_ORIGIN, noTimers } from './shared.js';
|
|
10
10
|
import { DEFAULT_RETENTION_DAYS } from '../history.js';
|
|
11
11
|
import { DAY, crdt, knob } from './context.js';
|
|
12
12
|
import { sameBytes } from './updates.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { YDoc } from '../../crdt/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { Attachment } from '../DocumentRoom.js';
|
|
3
3
|
import type { RoomContext } from './context.js';
|
|
4
4
|
/** A SQLite fault outside the append path (the replica registry). */
|
|
5
5
|
export declare class StorageFault extends Error {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ATTRIBUTION_ROOT } from '../../crdt/schema.js';
|
|
9
9
|
import { asEngineDoc } from '../../crdt/structs.js';
|
|
10
|
-
import { ROOM_ORIGIN } from '
|
|
10
|
+
import { ROOM_ORIGIN } from './shared.js';
|
|
11
11
|
import { BINDING_PREFIX } from './forged.js';
|
|
12
12
|
import { stateVector } from './updates.js';
|
|
13
13
|
/** A SQLite fault outside the append path (the replica registry). */
|
|
@@ -34,6 +34,6 @@ export declare class Scheduler {
|
|
|
34
34
|
unschedule(task: Task): void;
|
|
35
35
|
/** Set the alarm to the earliest due time (none due: the alarm set is left, and finds nothing). */
|
|
36
36
|
private arm;
|
|
37
|
-
/** The due times stored (`due.<task>`); an alarm armed
|
|
37
|
+
/** The due times stored (`due.<task>`); an alarm armed before the scheduler (none stored) is a due save. */
|
|
38
38
|
readDues(): void;
|
|
39
39
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* earliest. The tasks themselves are their owners' (`AttachedDocument`
|
|
5
5
|
* wires them): this module decides only when each runs.
|
|
6
6
|
*/
|
|
7
|
-
import { noTimers } from '
|
|
7
|
+
import { noTimers } from './shared.js';
|
|
8
8
|
const TASKS = ['history', 'save', 'purge', 'retention', 'forward', 'expiry'];
|
|
9
9
|
export class Scheduler {
|
|
10
10
|
room;
|
|
@@ -103,7 +103,7 @@ export class Scheduler {
|
|
|
103
103
|
this.armed = next;
|
|
104
104
|
void this.room.ctx.storage.setAlarm(next);
|
|
105
105
|
}
|
|
106
|
-
/** The due times stored (`due.<task>`); an alarm armed
|
|
106
|
+
/** The due times stored (`due.<task>`); an alarm armed before the scheduler (none stored) is a due save. */
|
|
107
107
|
readDues() {
|
|
108
108
|
const rows = this.room.sql
|
|
109
109
|
.exec(`SELECT key, value FROM ${this.room.tables.meta} WHERE key LIKE 'due.%'`)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The room's shared constants and two helpers every room module reads:
|
|
3
|
+
* the headers the router sets, the transaction origins, the socket tag,
|
|
4
|
+
* the refusal caps and the comment defaults, `noTimers` and
|
|
5
|
+
* `closedSocket`. A leaf: it imports nothing of the room, so the room's
|
|
6
|
+
* modules read it without importing `DocumentRoom.ts` (which re-exports
|
|
7
|
+
* every name here). Worker-safe.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Delete ranges the room keeps waiting for items it does not hold (a
|
|
11
|
+
* relayer's delete of an author's edit a restore lost). Every update the
|
|
12
|
+
* engine applies re-reads them, so they are capped.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MAX_WAITING_DELETES = 1024;
|
|
15
|
+
/** Refusals kept in `refusals` (the newest); `refusalCounts` counts every one. */
|
|
16
|
+
export declare const MAX_REFUSALS = 100;
|
|
17
|
+
/** The header `routeDocumentSocket` sets on an authorized probe: `lastUpdated` or `snapshot`. */
|
|
18
|
+
export declare const PROBE_HEADER = "X-Edytor-Probe";
|
|
19
|
+
/** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
|
|
20
|
+
export declare const ROOM_ORIGIN: unique symbol;
|
|
21
|
+
/** The transaction origin of a history restore: the room's restore history tracks it. */
|
|
22
|
+
export declare const RESTORE_ORIGIN: unique symbol;
|
|
23
|
+
/** The transaction origin of the room's purge: tracked by no history. */
|
|
24
|
+
export declare const PURGE_ORIGIN: unique symbol;
|
|
25
|
+
/** The header `routeDocumentHistory` sets on an authorized history request. */
|
|
26
|
+
export declare const HISTORY_HEADER = "X-Edytor-History";
|
|
27
|
+
/** The version key of a history `read` or `restore` request. */
|
|
28
|
+
export declare const HISTORY_KEY_HEADER = "X-Edytor-History-Key";
|
|
29
|
+
/** The header `routeDocumentComments` sets on an authorized comments request: `list` or `post`. */
|
|
30
|
+
export declare const COMMENTS_HEADER = "X-Edytor-Comments";
|
|
31
|
+
/**
|
|
32
|
+
* Headers carrying the identity `routeDocumentSocket` verified — never the
|
|
33
|
+
* client's. The user id is percent-encoded (`encodeURIComponent`): a header
|
|
34
|
+
* value is trimmed and cannot carry every character, and a user id is any
|
|
35
|
+
* string.
|
|
36
|
+
*/
|
|
37
|
+
export declare const IDENTITY_HEADERS: {
|
|
38
|
+
readonly user: "X-Edytor-User";
|
|
39
|
+
readonly replica: "X-Edytor-Replica";
|
|
40
|
+
readonly access: "X-Edytor-Access";
|
|
41
|
+
/** When the credential expires, ms since the epoch (absent: never). */
|
|
42
|
+
readonly expires: "X-Edytor-Expires";
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* An upgrade accepted and closed at once, so the client reads `code`: a
|
|
46
|
+
* browser sees an HTTP error at the upgrade only as `1006`, which a
|
|
47
|
+
* provider cannot tell from a network failure. Refuse a dial your Worker
|
|
48
|
+
* turns away itself with it: the provider stops at `1008` or `4xxx`
|
|
49
|
+
* (except `4401`, redialed), and redials after anything else (`CLOSE`,
|
|
50
|
+
* `isRefusal` in `crdt/providers/room.ts`).
|
|
51
|
+
*/
|
|
52
|
+
export declare const closedSocket: (code: number, reason: string) => Response;
|
|
53
|
+
/** Run an entry point with timers forbidden — a pending timer keeps a Durable Object from hibernating. */
|
|
54
|
+
export declare const noTimers: <T>(fn: () => T) => T;
|
|
55
|
+
/** The bytes a room's comments hold at most, by default (4 MiB). */
|
|
56
|
+
export declare const DEFAULT_MAX_COMMENT_BYTES: number;
|
|
57
|
+
/** Comment requests per second and user (or socket), by default. */
|
|
58
|
+
export declare const DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND = 2;
|
|
59
|
+
/** The tag of the document's sockets: other sockets of the object are left to you. */
|
|
60
|
+
export declare const SOCKET_TAG = "edytor";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The room's shared constants and two helpers every room module reads:
|
|
3
|
+
* the headers the router sets, the transaction origins, the socket tag,
|
|
4
|
+
* the refusal caps and the comment defaults, `noTimers` and
|
|
5
|
+
* `closedSocket`. A leaf: it imports nothing of the room, so the room's
|
|
6
|
+
* modules read it without importing `DocumentRoom.ts` (which re-exports
|
|
7
|
+
* every name here). Worker-safe.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Delete ranges the room keeps waiting for items it does not hold (a
|
|
11
|
+
* relayer's delete of an author's edit a restore lost). Every update the
|
|
12
|
+
* engine applies re-reads them, so they are capped.
|
|
13
|
+
*/
|
|
14
|
+
export const MAX_WAITING_DELETES = 1024;
|
|
15
|
+
/** Refusals kept in `refusals` (the newest); `refusalCounts` counts every one. */
|
|
16
|
+
export const MAX_REFUSALS = 100;
|
|
17
|
+
/** The header `routeDocumentSocket` sets on an authorized probe: `lastUpdated` or `snapshot`. */
|
|
18
|
+
export const PROBE_HEADER = 'X-Edytor-Probe';
|
|
19
|
+
/** The transaction origin of the room's own edits (`transact`, `onLoad` seeds). */
|
|
20
|
+
export const ROOM_ORIGIN = Symbol('edytor-room');
|
|
21
|
+
/** The transaction origin of a history restore: the room's restore history tracks it. */
|
|
22
|
+
export const RESTORE_ORIGIN = Symbol('edytor-restore');
|
|
23
|
+
/** The transaction origin of the room's purge: tracked by no history. */
|
|
24
|
+
export const PURGE_ORIGIN = Symbol('edytor-purge');
|
|
25
|
+
/** The header `routeDocumentHistory` sets on an authorized history request. */
|
|
26
|
+
export const HISTORY_HEADER = 'X-Edytor-History';
|
|
27
|
+
/** The version key of a history `read` or `restore` request. */
|
|
28
|
+
export const HISTORY_KEY_HEADER = 'X-Edytor-History-Key';
|
|
29
|
+
/** The header `routeDocumentComments` sets on an authorized comments request: `list` or `post`. */
|
|
30
|
+
export const COMMENTS_HEADER = 'X-Edytor-Comments';
|
|
31
|
+
/**
|
|
32
|
+
* Headers carrying the identity `routeDocumentSocket` verified — never the
|
|
33
|
+
* client's. The user id is percent-encoded (`encodeURIComponent`): a header
|
|
34
|
+
* value is trimmed and cannot carry every character, and a user id is any
|
|
35
|
+
* string.
|
|
36
|
+
*/
|
|
37
|
+
export const IDENTITY_HEADERS = {
|
|
38
|
+
user: 'X-Edytor-User',
|
|
39
|
+
replica: 'X-Edytor-Replica',
|
|
40
|
+
access: 'X-Edytor-Access',
|
|
41
|
+
/** When the credential expires, ms since the epoch (absent: never). */
|
|
42
|
+
expires: 'X-Edytor-Expires'
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* An upgrade accepted and closed at once, so the client reads `code`: a
|
|
46
|
+
* browser sees an HTTP error at the upgrade only as `1006`, which a
|
|
47
|
+
* provider cannot tell from a network failure. Refuse a dial your Worker
|
|
48
|
+
* turns away itself with it: the provider stops at `1008` or `4xxx`
|
|
49
|
+
* (except `4401`, redialed), and redials after anything else (`CLOSE`,
|
|
50
|
+
* `isRefusal` in `crdt/providers/room.ts`).
|
|
51
|
+
*/
|
|
52
|
+
export const closedSocket = (code, reason) => {
|
|
53
|
+
const [client, server] = Object.values(new WebSocketPair());
|
|
54
|
+
server.accept();
|
|
55
|
+
server.close(code, reason);
|
|
56
|
+
return new Response(null, { status: 101, webSocket: client });
|
|
57
|
+
};
|
|
58
|
+
/** Run an entry point with timers forbidden — a pending timer keeps a Durable Object from hibernating. */
|
|
59
|
+
export const noTimers = (fn) => {
|
|
60
|
+
const g = globalThis;
|
|
61
|
+
const saved = [g.setTimeout, g.setInterval];
|
|
62
|
+
const forbidden = () => {
|
|
63
|
+
throw new Error('room scheduled a timer (a Durable Object could not hibernate)');
|
|
64
|
+
};
|
|
65
|
+
g.setTimeout = forbidden;
|
|
66
|
+
g.setInterval = forbidden;
|
|
67
|
+
try {
|
|
68
|
+
return fn();
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
[g.setTimeout, g.setInterval] = saved;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** The bytes a room's comments hold at most, by default (4 MiB). */
|
|
75
|
+
export const DEFAULT_MAX_COMMENT_BYTES = 4 * 1024 * 1024;
|
|
76
|
+
/** Comment requests per second and user (or socket), by default. */
|
|
77
|
+
export const DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND = 2;
|
|
78
|
+
/** The tag of the document's sockets: other sockets of the object are left to you. */
|
|
79
|
+
export const SOCKET_TAG = 'edytor';
|