@liveblocks/server 1.0.1

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.
@@ -0,0 +1,1414 @@
1
+ import { DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, JsonObject, Json, Brand, asPos, SerializedRootObject, SerializedChild, StorageNode, SerializedCrdt, Awaitable, PlainLsonObject, NodeMap, NodeStream, ClientWireOp, IgnoredOp, IUserInfo, ServerMsg as ServerMsg$1, BaseUserMeta } from '@liveblocks/core';
2
+ export { BroadcastEventClientMsg, ClientMsg, ClientWireOp, CreateListOp, CreateMapOp, CreateObjectOp, CreateOp, CreateRegisterOp, DeleteCrdtOp, DeleteObjectKeyOp, FetchStorageClientMsg, FetchYDocClientMsg, HasOpId, IgnoredOp, Op, RoomStateServerMsg, ServerMsg, ServerWireOp, SetParentKeyOp, UpdateObjectOp, UpdatePresenceClientMsg, UpdateStorageClientMsg, UpdateYDocClientMsg } from '@liveblocks/core';
3
+ import * as decoders from 'decoders';
4
+ import { Decoder } from 'decoders';
5
+ import { Mutex } from 'async-mutex';
6
+ import * as Y from 'yjs';
7
+
8
+ /**
9
+ * Copyright (c) Liveblocks Inc.
10
+ *
11
+ * This program is free software: you can redistribute it and/or modify
12
+ * it under the terms of the GNU Affero General Public License as published
13
+ * by the Free Software Foundation, either version 3 of the License, or
14
+ * (at your option) any later version.
15
+ *
16
+ * This program is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ * GNU Affero General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU Affero General Public License
22
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+ */
24
+ declare enum ProtocolVersion {
25
+ /**
26
+ * V7 changes the URL params used to authorize the user.
27
+ *
28
+ * In V6 and lower, the ?token= URL param is used, which will only ever
29
+ * contain a `pub-legacy` or `sec-legacy` token.
30
+ *
31
+ * URL PARAM CHANGES:
32
+ * Starting with V7, the ?token= is no longer a legal URL param. Instead,
33
+ * either of the following params is used:
34
+ *
35
+ * - ?tok=... for ID tokens
36
+ * - ?tok=... for Access tokens
37
+ * - ?tok=... for Secret Legacy tokens
38
+ * - ?pubkey=... for public keys (no token, public key can be directly used here)
39
+ *
40
+ * Note that `pub-legacy` tokens are no longer accepted in V7, and are
41
+ * replaced by the direct use of the public key.
42
+ *
43
+ * BEHAVIORAL CHANGES:
44
+ * Starting with V7, the RoomState server message that gets sent when
45
+ * a client initially connects will now include new fields:
46
+ *
47
+ * - `actor`
48
+ * - `scopes`
49
+ *
50
+ * Since v1.2.0 (Jul 31, 2023)
51
+ */
52
+ V7 = 7,
53
+ /**
54
+ * V8 changes storage response format and allows streaming.
55
+ *
56
+ * MESSAGE FORMAT CHANGES:
57
+ * - V8: sends 1+ STORAGE_CHUNK messages, followed by 1 final
58
+ * STORAGE_STREAM_END message (with compact nodes)
59
+ * - V7: sends 1 STORAGE_STATE_V7 message (with full nodes)
60
+ *
61
+ * STREAMING BEHAVIOR in V8:
62
+ * - For SQLite-backed rooms: nodes are split into multiple STORAGE_CHUNK
63
+ * messages, followed by STORAGE_STREAM_END
64
+ * - For KV-backed rooms: all nodes are sent in a single STORAGE_CHUNK
65
+ * message that will contain all nodes, followed by STORAGE_STREAM_END
66
+ *
67
+ * Since 3.14.0
68
+ */
69
+ V8 = 8
70
+ }
71
+ declare const protocolVersionDecoder: decoders.Decoder<ProtocolVersion>;
72
+
73
+ /**
74
+ * Copyright (c) Liveblocks Inc.
75
+ *
76
+ * This program is free software: you can redistribute it and/or modify
77
+ * it under the terms of the GNU Affero General Public License as published
78
+ * by the Free Software Foundation, either version 3 of the License, or
79
+ * (at your option) any later version.
80
+ *
81
+ * This program is distributed in the hope that it will be useful,
82
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
83
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
84
+ * GNU Affero General Public License for more details.
85
+ *
86
+ * You should have received a copy of the GNU Affero General Public License
87
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
88
+ */
89
+
90
+ type FixOp = DistributiveOmit<SetParentKeyOp | DeleteCrdtOp, "opId">;
91
+
92
+ /**
93
+ * Copyright (c) Liveblocks Inc.
94
+ *
95
+ * This program is free software: you can redistribute it and/or modify
96
+ * it under the terms of the GNU Affero General Public License as published
97
+ * by the Free Software Foundation, either version 3 of the License, or
98
+ * (at your option) any later version.
99
+ *
100
+ * This program is distributed in the hope that it will be useful,
101
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
102
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103
+ * GNU Affero General Public License for more details.
104
+ *
105
+ * You should have received a copy of the GNU Affero General Public License
106
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
107
+ */
108
+
109
+ declare const clientMsgDecoder: Decoder<ClientMsg$1<JsonObject, Json>>;
110
+ declare const transientClientMsgDecoder: Decoder<ClientMsg$1<JsonObject, Json>>;
111
+
112
+ /**
113
+ * Copyright (c) Liveblocks Inc.
114
+ *
115
+ * This program is free software: you can redistribute it and/or modify
116
+ * it under the terms of the GNU Affero General Public License as published
117
+ * by the Free Software Foundation, either version 3 of the License, or
118
+ * (at your option) any later version.
119
+ *
120
+ * This program is distributed in the hope that it will be useful,
121
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
122
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
123
+ * GNU Affero General Public License for more details.
124
+ *
125
+ * You should have received a copy of the GNU Affero General Public License
126
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
127
+ */
128
+
129
+ /**
130
+ * Drop-in replacement for the `json` decoder from the decoders standard
131
+ * library, but implemented as a no-op. This is, of course, only safe to use in
132
+ * contexts where you know that the input already is valid JSON.
133
+ *
134
+ * You know this for sure, for example, if you're decoding the result of
135
+ * a `JSON.parse()` call.
136
+ *
137
+ * Done for performance reasons!
138
+ */
139
+ declare const jsonYolo: Decoder<Json>;
140
+ /**
141
+ * Drop-in replacement for the `jsonObject` decoder from the decoders standard
142
+ * library, but implemented as just a check for plain old JavaScript object.
143
+ * This is, of course, only safe to use in contexts where you know that the
144
+ * input already is valid JSON.
145
+ *
146
+ * You know this for sure, for example, if you're decoding the result of
147
+ * a `JSON.parse()` call.
148
+ *
149
+ * Done for performance reasons!
150
+ */
151
+ declare const jsonObjectYolo: Decoder<JsonObject>;
152
+
153
+ /**
154
+ * A guid, a unique identifier for a Yjs sub document.
155
+ */
156
+ type Guid = Brand<string, "Guid">;
157
+ declare const guidDecoder: decoders.Decoder<Guid>;
158
+ declare const ROOT_YDOC_ID = "root";
159
+ type YDocId = typeof ROOT_YDOC_ID | Guid;
160
+ /**
161
+ * Any string that is a valid base64 encoded YJS update.
162
+ */
163
+ type YUpdate = Brand<string, "YUpdate">;
164
+ /**
165
+ * Any string that is a valid base64 encoded YJS state vector.
166
+ */
167
+ type YVector = Brand<string, "YVector">;
168
+
169
+ /**
170
+ * Copyright (c) Liveblocks Inc.
171
+ *
172
+ * This program is free software: you can redistribute it and/or modify
173
+ * it under the terms of the GNU Affero General Public License as published
174
+ * by the Free Software Foundation, either version 3 of the License, or
175
+ * (at your option) any later version.
176
+ *
177
+ * This program is distributed in the hope that it will be useful,
178
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
179
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
180
+ * GNU Affero General Public License for more details.
181
+ *
182
+ * You should have received a copy of the GNU Affero General Public License
183
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
184
+ */
185
+ /**
186
+ * Generic, minimal, WebSocket interface that must be available or implemented
187
+ * on the server.
188
+ */
189
+ interface IServerWebSocket {
190
+ /**
191
+ * Send should be synchronous and never throw. It should return:
192
+ * - Return -1 if sending was attempted, but there was back pressure
193
+ * (i.e. the receiving end wasn't ready to accept more
194
+ * bytes right now)
195
+ * - Return 0 if sending failed (due to a connection issue)
196
+ * - Return >=1 if sent successfully (number of bytes sent)
197
+ */
198
+ send(msg: string | ArrayBuffer): number;
199
+ close(code: number, reason?: string): void;
200
+ /**
201
+ * Optional. Returns the last auto-response timestamp. Used only on
202
+ * Cloudflare, where pings and pongs are typically handled by an
203
+ * auto-response.
204
+ */
205
+ getLastPongTimestamp?(): Date | null;
206
+ }
207
+
208
+ /**
209
+ * Copyright (c) Liveblocks Inc.
210
+ *
211
+ * This program is free software: you can redistribute it and/or modify
212
+ * it under the terms of the GNU Affero General Public License as published
213
+ * by the Free Software Foundation, either version 3 of the License, or
214
+ * (at your option) any later version.
215
+ *
216
+ * This program is distributed in the hope that it will be useful,
217
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
218
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
219
+ * GNU Affero General Public License for more details.
220
+ *
221
+ * You should have received a copy of the GNU Affero General Public License
222
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
223
+ */
224
+
225
+ declare enum LogLevel {
226
+ DEBUG = 0,
227
+ INFO = 1,
228
+ WARNING = 2,
229
+ ERROR = 3
230
+ }
231
+ /**
232
+ * Inherit from this abstract log target to implement your own custom
233
+ * LogTarget.
234
+ */
235
+ declare abstract class LogTarget {
236
+ #private;
237
+ readonly level: LogLevel;
238
+ constructor(level?: LogLevel | keyof typeof LogLevelNames);
239
+ /** Helper for formatting a log level */
240
+ protected formatLevel(level: LogLevel): string;
241
+ /** Helper for formatting an Arg */
242
+ protected formatArg(arg: string | Error): string;
243
+ /**
244
+ * Helper for formatting a Context. Override this in a subclass to change the
245
+ * formatting.
246
+ */
247
+ protected formatContextImpl(context: JsonObject): string;
248
+ /**
249
+ * Helper for formatting a Context. Will only compute the string once for
250
+ * every Context instance, and keep its computed string value cached for
251
+ * performance.
252
+ */
253
+ protected formatContext(context: JsonObject): string;
254
+ /**
255
+ * Implement this in a concrete subclass. The goal is to do whatever to log
256
+ * the given log level, context, and log arg. You'll typically want to
257
+ * utilize the pre-defined helper methods .formatContext() and .formatArg()
258
+ * to implement this.
259
+ */
260
+ abstract log(level: LogLevel, context: JsonObject, arg: string | Error): void;
261
+ }
262
+ declare class ConsoleTarget extends LogTarget {
263
+ log(level: LogLevel, context: JsonObject, arg: string | Error): void;
264
+ }
265
+ declare const LogLevelNames: {
266
+ readonly debug: LogLevel.DEBUG;
267
+ readonly info: LogLevel.INFO;
268
+ readonly warning: LogLevel.WARNING;
269
+ readonly error: LogLevel.ERROR;
270
+ };
271
+ type LogFn = (arg: string | Error) => void;
272
+ /**
273
+ * Structured logger with configurable log targets.
274
+ */
275
+ declare class Logger {
276
+ readonly debug: LogFn;
277
+ readonly info: LogFn;
278
+ readonly warn: LogFn;
279
+ readonly error: LogFn;
280
+ readonly o: {
281
+ readonly debug?: LogFn;
282
+ readonly info?: LogFn;
283
+ readonly warn?: LogFn;
284
+ readonly error?: LogFn;
285
+ };
286
+ private readonly _context;
287
+ private readonly _targets;
288
+ constructor(target?: LogTarget | readonly LogTarget[], context?: JsonObject);
289
+ /**
290
+ * Creates a new Logger instance with the given extra context applied. All
291
+ * log calls made from that new Logger will carry all current _and_ the extra
292
+ * context, with the extra context taking precedence. Assign an explicit
293
+ * `undefined` value to a key to "remove" it from the context.
294
+ */
295
+ withContext(extra: JsonObject): Logger;
296
+ }
297
+
298
+ /**
299
+ * Copyright (c) Liveblocks Inc.
300
+ *
301
+ * This program is free software: you can redistribute it and/or modify
302
+ * it under the terms of the GNU Affero General Public License as published
303
+ * by the Free Software Foundation, either version 3 of the License, or
304
+ * (at your option) any later version.
305
+ *
306
+ * This program is distributed in the hope that it will be useful,
307
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
308
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
309
+ * GNU Affero General Public License for more details.
310
+ *
311
+ * You should have received a copy of the GNU Affero General Public License
312
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
313
+ */
314
+
315
+ type Pos = ReturnType<typeof asPos>;
316
+
317
+ /**
318
+ * Copyright (c) Liveblocks Inc.
319
+ *
320
+ * This program is free software: you can redistribute it and/or modify
321
+ * it under the terms of the GNU Affero General Public License as published
322
+ * by the Free Software Foundation, either version 3 of the License, or
323
+ * (at your option) any later version.
324
+ *
325
+ * This program is distributed in the hope that it will be useful,
326
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
327
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
328
+ * GNU Affero General Public License for more details.
329
+ *
330
+ * You should have received a copy of the GNU Affero General Public License
331
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
332
+ */
333
+
334
+ /**
335
+ * An isolated, read-only copy of the storage document at a point in time.
336
+ *
337
+ * Two iteration styles are supported:
338
+ *
339
+ * 1. Tree traversal (top-down):
340
+ * Call get_root() to get the root, then use iter_children() + get_node()
341
+ * to walk the tree depth-first. This preserves parent-child ordering and is
342
+ * used by the JSON/LSON serializers.
343
+ *
344
+ * 2. Flat iteration:
345
+ * Call iter_all() to get all nodes as unordered [id, crdt] pairs. Useful
346
+ * when the tree structure doesn't matter (e.g. streaming raw NDJSON).
347
+ *
348
+ * Always call destroy() when done (use a try/finally wrapper).
349
+ */
350
+ interface IReadableSnapshot {
351
+ /** Get the root node. */
352
+ get_root(): SerializedRootObject;
353
+ /** Get a child node by id. */
354
+ get_node(id: string): SerializedChild;
355
+ /**
356
+ * Iterate all children of a node. Each child is yielded as [parentKey,
357
+ * childId]. Children will be yielded in their parent_key order.
358
+ */
359
+ iter_children(nodeId: string): Iterable<[parentKey: string, childId: string]>;
360
+ /**
361
+ * Get all nodes as [id, crdt] pairs (flat, unordered).
362
+ */
363
+ iter_all(): Iterable<StorageNode>;
364
+ /**
365
+ * Release all resources held by the snapshot. Must be called when the
366
+ * snapshot is no longer needed (e.g. after the stream completes or is
367
+ * aborted).
368
+ */
369
+ destroy(): void;
370
+ }
371
+ /**
372
+ * CRDT node storage with synchronous reads and async persistence.
373
+ *
374
+ * INVARIANTS:
375
+ * - A virtual root node `{type: OBJECT, data: {}}` always exists.
376
+ * - All non-root nodes have parentId and parentKey forming a tree.
377
+ * - Reads reflect writes immediately, before the returned Promise resolves.
378
+ */
379
+ interface IStorageDriverNodeAPI {
380
+ /**
381
+ * Return the node with the given id, or undefined if no such node exists.
382
+ * Must always return a valid root node for id="root", even if empty.
383
+ */
384
+ get_node(id: string): SerializedCrdt | undefined;
385
+ /**
386
+ * Yield all nodes as [id, node] pairs. Must always include the root node.
387
+ */
388
+ iter_nodes(): Iterable<StorageNode>;
389
+ /**
390
+ * Return true iff a node with the given id exists. Must return true for "root".
391
+ */
392
+ has_node(id: string): boolean;
393
+ /**
394
+ * Return the id of the child node at (parentId, parentKey), or undefined if
395
+ * none. Only checks child nodes registered via set_child, NOT static data
396
+ * keys on OBJECT nodes.
397
+ */
398
+ get_child_at(parentId: string, parentKey: string): string | undefined;
399
+ /**
400
+ * Return true iff a child node exists at (parentId, parentKey). Static data
401
+ * keys on OBJECT nodes do not count—return false for those.
402
+ */
403
+ has_child_at(parentId: string, parentKey: string): boolean;
404
+ /**
405
+ * Return the position of the closest sibling "to the right" of `pos` under
406
+ * parentId, or undefined if no such sibling exists. The given `pos` may, but
407
+ * does not have to exist already. Positions compare lexicographically.
408
+ */
409
+ get_next_sibling(parentId: string, pos: Pos): Pos | undefined;
410
+ /**
411
+ * Insert a child node with the given id.
412
+ *
413
+ * If allowOverwrite=false (default): throw if a node with this id exists.
414
+ * If allowOverwrite=true: replace any existing node at this id, deleting its
415
+ * entire subtree if it has children.
416
+ */
417
+ set_child(id: string, node: SerializedChild, allowOverwrite?: boolean): Awaitable<void>;
418
+ /**
419
+ * Change a node's parentKey, effectively repositioning the node within its
420
+ * parent. The new position must be free.
421
+ * Throw if another node already occupies (parentId, newPos).
422
+ */
423
+ move_sibling(id: string, newPos: Pos): Awaitable<void>;
424
+ /**
425
+ * Delete a node and its entire subtree recursively.
426
+ * Ignore if id="root" (root is immortal).
427
+ */
428
+ delete_node(id: string): Awaitable<void>;
429
+ /**
430
+ * Delete a key from node `id`. Handle two cases:
431
+ *
432
+ * 1. If id is an OBJECT with `key` in its data: remove that data field.
433
+ * 2. If a child exists at (id, key): delete that child and all its
434
+ * descendants recursively.
435
+ *
436
+ * No-op if neither applies or if the node doesn't exist.
437
+ */
438
+ delete_child_key(id: string, key: string): Awaitable<void>;
439
+ /**
440
+ * Replace the data object of an OBJECT node.
441
+ *
442
+ * If allowOverwrite=false (default): throw if any key in `data` conflicts
443
+ * with an existing child's parentKey.
444
+ * If allowOverwrite=true: first delete any conflicting children (and their
445
+ * entire subtrees), then set the data.
446
+ */
447
+ set_object_data(id: string, data: JsonObject, allowOverwrite?: boolean): Awaitable<void>;
448
+ /**
449
+ * Return a readable snapshot of the storage tree.
450
+ *
451
+ * @param lowMemory When true, the call site hints that the snapshot should
452
+ * be optimized for lower memory consumption, even if that means slower
453
+ * access.
454
+ */
455
+ get_snapshot(lowMemory?: boolean): IReadableSnapshot;
456
+ }
457
+ /**
458
+ * Persistent storage backend for Liveblocks room data: CRDT nodes, metadata,
459
+ * actor IDs, and Yjs updates. All methods may be async.
460
+ */
461
+ interface IStorageDriver {
462
+ /**
463
+ * Load all nodes from storage, validate/repair corruptions (orphans, cycles,
464
+ * conflicting siblings), and return an IStorageDriverNodeAPI for operations.
465
+ *
466
+ * After DANGEROUSLY_reset_nodes(), any previously-loaded instance is
467
+ * invalid—must call this again to get a fresh one.
468
+ */
469
+ load_nodes_api(logger: Logger): Awaitable<IStorageDriverNodeAPI>;
470
+ /**
471
+ * Delete all CRDT nodes and replace them with the given document.
472
+ * Does NOT affect metadata, actor IDs, or Yjs updates.
473
+ * Invalidates any previously-loaded IStorageDriverNodeAPI.
474
+ *
475
+ * Pass `{ liveblocksType: "LiveObject", data: {} }` to reset to an empty root.
476
+ */
477
+ DANGEROUSLY_reset_nodes(doc: PlainLsonObject): Awaitable<void>;
478
+ /**
479
+ * Return the value for `key`, or undefined if not set.
480
+ */
481
+ get_meta(key: string): Awaitable<Json | undefined>;
482
+ /**
483
+ * Store `value` under `key`. Overwrite any existing value.
484
+ */
485
+ put_meta(key: string, value: Json): Awaitable<void>;
486
+ /**
487
+ * Delete the value under `key`. No-op if not set.
488
+ */
489
+ delete_meta(key: string): Awaitable<void>;
490
+ /**
491
+ * Return a unique actor ID. Each call must return a distinct integer ≥ 0.
492
+ * Concurrent calls must never return duplicates.
493
+ */
494
+ next_actor(): Awaitable<number>;
495
+ /**
496
+ * If defined, called once before each storage mutation batch. Can be used by
497
+ * the driver to more efficiently implement snapshot isolation.
498
+ */
499
+ bump_storage_version?(): void;
500
+ /**
501
+ * Return all Yjs updates for docId as [key, data] pairs. Return empty if none.
502
+ */
503
+ iter_y_updates(docId: YDocId): Awaitable<Iterable<[string, Uint8Array]>>;
504
+ /**
505
+ * Store a Yjs update under (docId, key). Overwrite if key exists.
506
+ */
507
+ write_y_updates(docId: YDocId, key: string, data: Uint8Array): Awaitable<void>;
508
+ /**
509
+ * Delete the specified keys for docId.
510
+ */
511
+ delete_y_updates(docId: YDocId, keys: string[]): Awaitable<void>;
512
+ /**
513
+ * Delete ALL Yjs updates across ALL documents.
514
+ * @private Test-only: never use in production.
515
+ */
516
+ DANGEROUSLY_wipe_all_y_updates(): Awaitable<void>;
517
+ }
518
+
519
+ /**
520
+ * Copyright (c) Liveblocks Inc.
521
+ *
522
+ * This program is free software: you can redistribute it and/or modify
523
+ * it under the terms of the GNU Affero General Public License as published
524
+ * by the Free Software Foundation, either version 3 of the License, or
525
+ * (at your option) any later version.
526
+ *
527
+ * This program is distributed in the hope that it will be useful,
528
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
529
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
530
+ * GNU Affero General Public License for more details.
531
+ *
532
+ * You should have received a copy of the GNU Affero General Public License
533
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
534
+ */
535
+
536
+ /**
537
+ * Serialize a storage snapshot to a simple JSON representation, returning a
538
+ * full in-memory JsonObject. Faster than snapshotToLossyJson_lazy for
539
+ * small/medium documents because the result can be passed straight to
540
+ * JSON.stringify(). This format is lossy — the original storage structure
541
+ * cannot be reconstructed from it, so it's output-only.
542
+ */
543
+ declare function snapshotToLossyJson_eager(snapshot: IReadableSnapshot): JsonObject;
544
+ type StringGen$1 = Generator<string, void, never>;
545
+ /**
546
+ * Serialize a storage snapshot to a simple JSON representation. This format is
547
+ * easy to consume but lossy — the original storage structure cannot be
548
+ * reconstructed from it, so it's an output-only format. Slower than
549
+ * snapshotToLossyJson_eager but can stream documents that don't fit entirely
550
+ * in memory.
551
+ *
552
+ * This generator yields text chunks that together, when concatenated, form the
553
+ * output JSON document.
554
+ */
555
+ declare function snapshotToLossyJson_lazy(snapshot: IReadableSnapshot): StringGen$1;
556
+
557
+ /**
558
+ * Copyright (c) Liveblocks Inc.
559
+ *
560
+ * This program is free software: you can redistribute it and/or modify
561
+ * it under the terms of the GNU Affero General Public License as published
562
+ * by the Free Software Foundation, either version 3 of the License, or
563
+ * (at your option) any later version.
564
+ *
565
+ * This program is distributed in the hope that it will be useful,
566
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
567
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
568
+ * GNU Affero General Public License for more details.
569
+ *
570
+ * You should have received a copy of the GNU Affero General Public License
571
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
572
+ */
573
+
574
+ /**
575
+ * Yield all nodes from a snapshot as [id, crdt] tuples.
576
+ * Destroys the snapshot when done (or aborted).
577
+ */
578
+ declare function snapshotToNodeStream(snapshot: IReadableSnapshot): Generator<StorageNode, void, never>;
579
+
580
+ /**
581
+ * Copyright (c) Liveblocks Inc.
582
+ *
583
+ * This program is free software: you can redistribute it and/or modify
584
+ * it under the terms of the GNU Affero General Public License as published
585
+ * by the Free Software Foundation, either version 3 of the License, or
586
+ * (at your option) any later version.
587
+ *
588
+ * This program is distributed in the hope that it will be useful,
589
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
590
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
591
+ * GNU Affero General Public License for more details.
592
+ *
593
+ * You should have received a copy of the GNU Affero General Public License
594
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
595
+ */
596
+
597
+ /**
598
+ * Transform a "Plain LSON" document to a lazy NodeStream. Used to initialize
599
+ * the storage with a predefined state.
600
+ * Always emits parent nodes before their children.
601
+ */
602
+ declare function plainLsonToNodeStream(root: PlainLsonObject): Generator<StorageNode, void, undefined>;
603
+ /**
604
+ * Serialize a storage snapshot to "Plain LSON" format, returning a full
605
+ * in-memory PlainLsonObject. Faster than snapshotToPlainLson_lazy for
606
+ * small/medium documents because the result can be passed straight to
607
+ * JSON.stringify().
608
+ */
609
+ declare function snapshotToPlainLson_eager(snapshot: IReadableSnapshot): PlainLsonObject;
610
+ type StringGen = Generator<string, void, never>;
611
+ /**
612
+ * Serialize a storage snapshot to "Plain LSON" format. Yields string chunks
613
+ * that, when concatenated, form a valid JSON string representing the storage
614
+ * document. Slower than snapshotToPlainLson_eager but can stream documents
615
+ * that don't fit entirely in memory.
616
+ */
617
+ declare function snapshotToPlainLson_lazy(snapshot: IReadableSnapshot): StringGen;
618
+
619
+ /**
620
+ * Copyright (c) Liveblocks Inc.
621
+ *
622
+ * This program is free software: you can redistribute it and/or modify
623
+ * it under the terms of the GNU Affero General Public License as published
624
+ * by the Free Software Foundation, either version 3 of the License, or
625
+ * (at your option) any later version.
626
+ *
627
+ * This program is distributed in the hope that it will be useful,
628
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
629
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
630
+ * GNU Affero General Public License for more details.
631
+ *
632
+ * You should have received a copy of the GNU Affero General Public License
633
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
634
+ */
635
+
636
+ /**
637
+ * Create a basic in-memory snapshot from a set of storage nodes.
638
+ *
639
+ * Takes a copy of the provided nodes, so the snapshot is isolated from
640
+ * subsequent mutations to the source.
641
+ */
642
+ declare function makeInMemorySnapshot(values: NodeMap | NodeStream): IReadableSnapshot;
643
+
644
+ /**
645
+ * Copyright (c) Liveblocks Inc.
646
+ *
647
+ * This program is free software: you can redistribute it and/or modify
648
+ * it under the terms of the GNU Affero General Public License as published
649
+ * by the Free Software Foundation, either version 3 of the License, or
650
+ * (at your option) any later version.
651
+ *
652
+ * This program is distributed in the hope that it will be useful,
653
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
654
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
655
+ * GNU Affero General Public License for more details.
656
+ *
657
+ * You should have received a copy of the GNU Affero General Public License
658
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
659
+ */
660
+
661
+ interface MetadataDB {
662
+ get(key: string): Promise<Json | undefined>;
663
+ get<T>(decoder: Decoder<T>, key: string): Promise<T | undefined>;
664
+ put(key: string, value: Json): Awaitable<void>;
665
+ delete(key: string): Awaitable<void>;
666
+ }
667
+ /**
668
+ * Returns a thin wrapper around an IStorageDriver to provide MetadataDB
669
+ * functionality, including type-safe reads.
670
+ */
671
+ declare function makeMetadataDB(driver: IStorageDriver): MetadataDB;
672
+
673
+ /**
674
+ * Copyright (c) Liveblocks Inc.
675
+ *
676
+ * This program is free software: you can redistribute it and/or modify
677
+ * it under the terms of the GNU Affero General Public License as published
678
+ * by the Free Software Foundation, either version 3 of the License, or
679
+ * (at your option) any later version.
680
+ *
681
+ * This program is distributed in the hope that it will be useful,
682
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
683
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
684
+ * GNU Affero General Public License for more details.
685
+ *
686
+ * You should have received a copy of the GNU Affero General Public License
687
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
688
+ */
689
+
690
+ type ApplyOpResult = OpAccepted | OpIgnored;
691
+ type OpAccepted = {
692
+ action: "accepted";
693
+ op: ClientWireOp;
694
+ fix?: FixOp;
695
+ };
696
+ type OpIgnored = {
697
+ action: "ignored";
698
+ ignoredOpId?: string;
699
+ };
700
+ declare class Storage {
701
+ private readonly coreDriver;
702
+ private _loadedDriver;
703
+ constructor(coreDriver: IStorageDriver);
704
+ get loadedDriver(): IStorageDriverNodeAPI;
705
+ raw_iter_nodes(): Awaitable<Iterable<[string, SerializedCrdt]>>;
706
+ /**
707
+ * Load the room data from object storage into memory. Persisted room
708
+ * data consists of the main node map, which represents the Liveblocks
709
+ * Storage tree, and special keys where we store usage metrics, or room
710
+ * metadata.
711
+ */
712
+ load(logger: Logger): Promise<void>;
713
+ unload(): void;
714
+ /**
715
+ * Applies a batch of Ops.
716
+ */
717
+ applyOps(ops: ClientWireOp[]): Promise<ApplyOpResult[]>;
718
+ /**
719
+ * Applies a single Op.
720
+ */
721
+ private applyOp;
722
+ private applyCreateOp;
723
+ private createChildAsListItem;
724
+ private applyDeleteObjectKeyOp;
725
+ private applyUpdateObjectOp;
726
+ private applyDeleteCrdtOp;
727
+ private applySetParentKeyOp;
728
+ /**
729
+ * Inserts a new node in the storage tree, under a list parent. If an
730
+ * existing sibling node already exist under this key, however, it will look
731
+ * for another free position under that parent and insert it under
732
+ * a different parent key that is guaranteed to be available.
733
+ *
734
+ * Returns the key that was used for the insertion.
735
+ */
736
+ private insertIntoList;
737
+ /**
738
+ * Tries to move a node to the given position under the same parent. If
739
+ * a conflicting sibling node already exist at this position, it will use
740
+ * another free position instead, to avoid the conflict.
741
+ *
742
+ * Returns the position (parentKey) that the node was eventually placed at.
743
+ * If the node could be inserted without conflict, it will return the same
744
+ * parentKey position.
745
+ *
746
+ * Will return `undefined` if this action could not be interpreted. Will be
747
+ * a no-op for non-list items.
748
+ */
749
+ private moveToPosInList;
750
+ /**
751
+ * Checks whether the given parentKey is a "free position" under the
752
+ * parentId, i.e. there are no siblings that have the same key. If a sibling
753
+ * exists under that key, it tries to generate new positions until it finds
754
+ * a free slot, and returns that. The returned value is therefore always safe
755
+ * to use as parentKey.
756
+ */
757
+ private findFreeListPosition;
758
+ }
759
+
760
+ /**
761
+ * Copyright (c) Liveblocks Inc.
762
+ *
763
+ * This program is free software: you can redistribute it and/or modify
764
+ * it under the terms of the GNU Affero General Public License as published
765
+ * by the Free Software Foundation, either version 3 of the License, or
766
+ * (at your option) any later version.
767
+ *
768
+ * This program is distributed in the hope that it will be useful,
769
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
770
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
771
+ * GNU Affero General Public License for more details.
772
+ *
773
+ * You should have received a copy of the GNU Affero General Public License
774
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
775
+ */
776
+
777
+ declare class YjsStorage {
778
+ private readonly driver;
779
+ private readonly doc;
780
+ private readonly lastUpdatesById;
781
+ private readonly lastSnapshotById;
782
+ private readonly keysById;
783
+ private readonly initPromisesById;
784
+ constructor(driver: IStorageDriver);
785
+ getYDoc(docId: YDocId): Promise<Y.Doc>;
786
+ /**
787
+ * If passed a state vector, an update with diff will be returned, if not the entire doc is returned.
788
+ *
789
+ * @param stateVector a base64 encoded target state vector created by running Y.encodeStateVector(Doc) on the client
790
+ * @returns a base64 encoded array of YJS updates
791
+ */
792
+ getYDocUpdate(logger: Logger, stateVector?: string, guid?: Guid, isV2?: boolean): Promise<string | null>;
793
+ getYDocUpdateBinary(logger: Logger, stateVector?: string, guid?: Guid, isV2?: boolean): Promise<Uint8Array | null>;
794
+ getYStateVector(guid?: Guid): Promise<string | null>;
795
+ getSnapshotHash(options: {
796
+ guid?: Guid;
797
+ isV2?: boolean;
798
+ }): Promise<string | null>;
799
+ /**
800
+ * @param update base64 encoded uint8array
801
+ * @returns
802
+ */
803
+ addYDocUpdate(logger: Logger, update: string | Uint8Array, guid?: Guid, isV2?: boolean): Promise<{
804
+ isUpdated: boolean;
805
+ snapshotHash: string;
806
+ }>;
807
+ loadDocByIdIfNotAlreadyLoaded(docId: YDocId): Promise<Y.Doc>;
808
+ load(_logger: Logger): Promise<void>;
809
+ /**
810
+ * Unloads the Yjs documents from memory.
811
+ */
812
+ unload(): void;
813
+ private _getOrPutLastSnapshot;
814
+ private _putLastSnapshot;
815
+ /**
816
+ * Given a record of updates, merge them and compress if savings are significant
817
+ */
818
+ private _loadAndCompressYJSUpdates;
819
+ private _loadYDocFromDurableStorage;
820
+ private findYSubdocByGuid;
821
+ private calculateSnapshotHash;
822
+ private getYSubdoc;
823
+ private handleYDocUpdate;
824
+ }
825
+
826
+ /**
827
+ * Copyright (c) Liveblocks Inc.
828
+ *
829
+ * This program is free software: you can redistribute it and/or modify
830
+ * it under the terms of the GNU Affero General Public License as published
831
+ * by the Free Software Foundation, either version 3 of the License, or
832
+ * (at your option) any later version.
833
+ *
834
+ * This program is distributed in the hope that it will be useful,
835
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
836
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
837
+ * GNU Affero General Public License for more details.
838
+ *
839
+ * You should have received a copy of the GNU Affero General Public License
840
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
841
+ */
842
+
843
+ type LoadingState = "initial" | "loading" | "loaded";
844
+ type ActorID = Brand<number, "ActorID">;
845
+ /**
846
+ * Session keys are also known as the "nonce" in the protocol. It's a random,
847
+ * unique, but PRIVATE, identifier for the session, and it's important that
848
+ * this ID is never shared to anyone except the connected client, which
849
+ * receives it as part of its ROOM_STATE message.
850
+ */
851
+ type SessionKey = Brand<string, "SessionKey">;
852
+ type PreSerializedServerMsg = Brand<string, "PreSerializedServerMsg">;
853
+ type ClientMsg = ClientMsg$1<JsonObject, Json>;
854
+ type ServerMsg = ServerMsg$1<JsonObject, BaseUserMeta, Json>;
855
+ declare function serialize(msgs: ServerMsg | readonly ServerMsg[]): PreSerializedServerMsg;
856
+ declare function ackIgnoredOp(opId: string): IgnoredOp;
857
+ /**
858
+ * A known or anonymous user.
859
+ *
860
+ * BY DEFINITION:
861
+ * A User with an assigned `id` property is a non-anonymous user.
862
+ * A User with an assigned `anonymousId` property is an anonymous user.
863
+ * A User with neither of those properties is also an anonymous user.
864
+ *
865
+ * WHAT'S THE DIFFERENCE?
866
+ * When creating a non-anonymous user, other users in the room will be able to
867
+ * observe the assigned `id` property in Presence (e.g. via the `other.user.id`
868
+ * in the Liveblocks client).
869
+ *
870
+ * When creating an anonymous user, you can _optionally_ provide an anonymous
871
+ * ID to (re)use. While not authorized, this still allows you to correlate
872
+ * unique users.
873
+ */
874
+ type IUserData = AuthorizedUser | AnonymousUser;
875
+ type AuthorizedUser = {
876
+ readonly id: string;
877
+ readonly anonymousId?: never;
878
+ readonly info?: IUserInfo;
879
+ };
880
+ type AnonymousUser = {
881
+ readonly anonymousId: string;
882
+ readonly id?: never;
883
+ readonly info?: IUserInfo;
884
+ };
885
+ /**
886
+ * Each BrowserSession is an abstraction around a socket instance, and maintains
887
+ * metadata about the connection.
888
+ */
889
+ declare class BrowserSession<SM, CM extends JsonObject> {
890
+ #private;
891
+ readonly version: ProtocolVersion;
892
+ readonly actor: ActorID;
893
+ readonly createdAt: Date;
894
+ readonly user: IUserData;
895
+ readonly scopes: string[];
896
+ readonly meta: SM;
897
+ readonly publicMeta?: CM;
898
+ get lastActiveAt(): Date;
899
+ get hasNotifiedClientStorageUpdateError(): boolean;
900
+ markActive(now?: Date): void;
901
+ setHasNotifiedClientStorageUpdateError(): void;
902
+ sendPong(): number;
903
+ send(serverMsg: ServerMsg | ServerMsg[] | PreSerializedServerMsg): number;
904
+ }
905
+ declare class BackendSession extends BrowserSession<never, never> {
906
+ }
907
+ type Ticket<SM, CM extends JsonObject> = {
908
+ readonly sessionKey: SessionKey;
909
+ readonly version: ProtocolVersion;
910
+ readonly actor: ActorID;
911
+ readonly meta?: SM;
912
+ readonly publicMeta?: CM;
913
+ readonly user: IUserData;
914
+ readonly scopes: string[];
915
+ };
916
+ type CreateTicketOptions<SM, CM extends JsonObject> = {
917
+ /** The Liveblocks protocol version this client will speak */
918
+ version?: ProtocolVersion;
919
+ meta?: SM;
920
+ publicMeta?: CM;
921
+ /** A user-provided ID to externally recognize the user by */
922
+ id?: string;
923
+ /**
924
+ * A user-provided anonymous ID to use. When `id` is provided, this field is
925
+ * ignored. When both fields are missing, a new anonymous ID will be
926
+ * generated.
927
+ */
928
+ anonymousId?: string;
929
+ /** Static user metadata to assign this session, will get broadcasted to other clients */
930
+ info?: IUserInfo;
931
+ /** Permissions to assign this session */
932
+ scopes?: string[];
933
+ /** An explicit actor ID to use. Supported for legacy use cases only. It's best to not set this and let it get assigned dynamically, as it's important for this identifier to be unique. */
934
+ actor?: ActorID;
935
+ };
936
+ type RoomOptions<SM, CM extends JsonObject, C> = {
937
+ /**
938
+ * Bring your own persistence backend
939
+ */
940
+ storage?: IStorageDriver;
941
+ logger?: Logger;
942
+ /**
943
+ * Whether to allow streaming storage responses. Only safe with drivers
944
+ * that can guarantee that no Ops from other clients can get interleaved
945
+ * between the chunk generation until the last chunk has been sent.
946
+ * Defaults to true, but is notably NOT safe to use from DOS-KV backends.
947
+ */
948
+ allowStreaming?: boolean;
949
+ hooks?: {
950
+ /** Customize which incoming messages from a client are allowed or disallowed. */
951
+ isClientMsgAllowed?: (msg: ClientMsg, session: BrowserSession<SM, CM>) => {
952
+ allowed: true;
953
+ } | {
954
+ allowed: false;
955
+ reason: string;
956
+ };
957
+ /** Called whenever the server acknowledged a ping with a pong */
958
+ onDidPong?: (ctx?: C) => void | Promise<void>;
959
+ /** Called before the room is attempted to be loaded */
960
+ onRoomWillLoad?: (ctx?: C) => void | Promise<void>;
961
+ /** Called right after the room's contents are loaded, but before any session has been started */
962
+ onRoomDidLoad?: (ctx?: C) => void | Promise<void>;
963
+ /** Called right before the room is attempted to be unloaded. Synchronous. May throw to abort the unloading. */
964
+ onRoomWillUnload?: (ctx?: C) => void;
965
+ /** Called right after the room has been unloaded from memory. Synchronous. */
966
+ onRoomDidUnload?: (ctx?: C) => void;
967
+ /** Called when a new user entered the room. */
968
+ onSessionDidStart?: (session: BrowserSession<SM, CM>, ctx?: C) => void | Promise<void>;
969
+ /** Called when a user left the room. */
970
+ onSessionDidEnd?: (session: BrowserSession<SM, CM>, ctx?: C) => void | Promise<void>;
971
+ /**
972
+ * Called when Liveblocks Storage for the room was updated.
973
+ *
974
+ * IMPORTANT! If you implement these as async functions, it's important to
975
+ * note that these run outside of the storage mutex that guarantees
976
+ * a consistent view of storage.
977
+ * Therefore, only ever use this hook to implement a side effect (like
978
+ * trigger a notification), don't read storage in this hook directly.
979
+ */
980
+ postClientMsgStorageDidUpdate?: (ctx?: C) => void | Promise<void>;
981
+ /**
982
+ * Called when Yjs Storage for the room was updated.
983
+ *
984
+ * IMPORTANT! If you implement these as async functions, it's important to
985
+ * note that these run outside of the storage mutex that guarantees
986
+ * a consistent view of storage.
987
+ * Therefore, only ever use this hook to implement a side effect (like
988
+ * trigger a notification), don't read storage in this hook directly.
989
+ */
990
+ postClientMsgYdocDidUpdate?: (ctx?: C, sess?: BrowserSession<SM, CM>) => void | Promise<void>;
991
+ };
992
+ /** Enable debug logging */
993
+ enableDebugLogging?: boolean;
994
+ };
995
+ /**
996
+ * A Liveblocks Room server.
997
+ */
998
+ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
999
+ #private;
1000
+ meta: RM;
1001
+ readonly driver: IStorageDriver;
1002
+ logger: Logger;
1003
+ private _loadData$;
1004
+ private _data;
1005
+ private _qsize;
1006
+ private readonly sessions;
1007
+ private readonly hooks;
1008
+ constructor(meta: RM, options?: RoomOptions<SM, CM, C>);
1009
+ get loadingState(): LoadingState;
1010
+ get numSessions(): number;
1011
+ get storage(): Storage;
1012
+ get yjsStorage(): YjsStorage;
1013
+ get mutex(): Mutex;
1014
+ private get data();
1015
+ /**
1016
+ * Initializes the Room, so it's ready to start accepting connections. Safe
1017
+ * to call multiple times. After awaiting `room.load()` the Room is ready to
1018
+ * be used.
1019
+ */
1020
+ load(ctx?: C): Promise<void>;
1021
+ /**
1022
+ * Releases the currently-loaded storage tree from worker memory, freeing it
1023
+ * up to be garbage collected. The next time a user will join the room, the
1024
+ * room will be reloaded from storage.
1025
+ */
1026
+ unload(ctx?: C): void;
1027
+ /**
1028
+ * Issues a Ticket with a new/unique actor ID
1029
+ *
1030
+ * IMPORTANT! As the caller of this function, you are responsible for
1031
+ * ensuring you trust the values passed in here. Never pass unauthorized
1032
+ * values in here.
1033
+ *
1034
+ * The returned Ticket can be turned into a active Session once the socket
1035
+ * connection is established. If the socket is never established, this
1036
+ * unused Ticket will simply get garbage collected.
1037
+ */
1038
+ createTicket(options?: CreateTicketOptions<SM, CM>): Promise<Ticket<SM, CM>>;
1039
+ createBackendSession_experimental(): Promise<[
1040
+ session: BackendSession,
1041
+ outgoingMessages: PreSerializedServerMsg[]
1042
+ ]>;
1043
+ /**
1044
+ * Restores the given sessions as the Room server's session list. Can only be
1045
+ * called as long as there are no existing sessions.
1046
+ *
1047
+ * The key difference with the .startBrowserSession() API is that restoreSessions is
1048
+ * used in cases where a session was hibernated and needs to be restored,
1049
+ * without _conceptually_ starting a new session.
1050
+ *
1051
+ * Because there are no side effects to restoreSession, it's synchronous.
1052
+ */
1053
+ restoreSessions(sessions: {
1054
+ ticket: Ticket<SM, CM>;
1055
+ socket: IServerWebSocket;
1056
+ lastActivity: Date;
1057
+ }[]): void;
1058
+ /**
1059
+ * Registers a new BrowserSession into the Room server's session list, along with
1060
+ * the socket connection to use for that BrowserSession, now that it is known.
1061
+ *
1062
+ * This kicks off a few side effects:
1063
+ * - Sends a ROOM_STATE message to the socket.
1064
+ * - Broadcasts a USER_JOINED message to all other sessions in the room.
1065
+ */
1066
+ startBrowserSession(ticket: Ticket<SM, CM>, socket: IServerWebSocket, ctx?: C, defer?: (promise: Promise<void>) => void): void;
1067
+ /**
1068
+ * Unregisters the BrowserSession for the given actor. Call this when the socket has
1069
+ * been closed from the client's end.
1070
+ *
1071
+ * This kicks off a few side effects:
1072
+ * - Broadcasts a USER_LEFT message to all other sessions in the room.
1073
+ */
1074
+ endBrowserSession(key: SessionKey, code: number, reason: string, ctx?: C, defer?: (promise: Promise<void>) => void): void;
1075
+ /**
1076
+ * Force-closes all sessions matching the given predicate.
1077
+ */
1078
+ endSessionBy(predicate: (session: BrowserSession<SM, CM>) => boolean, code: number, reason: string, ctx?: C, defer?: (promise: Promise<void>) => void): number;
1079
+ /**
1080
+ * Handles a raw incoming socket message, which can be a ping, or an
1081
+ * JSON-encoded message batch.
1082
+ */
1083
+ handleData(key: SessionKey, data: unknown, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
1084
+ /**
1085
+ * Processes an incoming batch of 1 or more ClientMsgs on behalf of
1086
+ * a (regular user/browser) session.
1087
+ *
1088
+ * IMPORTANT: Only use this API on "trusted" data!
1089
+ * To handle untrusted input data, use `.handleData()` instead.
1090
+ *
1091
+ * Before calling this API, make sure:
1092
+ * 1. The call site is entitled to call this message on behalf of this session; and
1093
+ * 2. The ClientMsg payload has been validated to be correct.
1094
+ */
1095
+ processClientMsg(key: SessionKey, messages: ClientMsg[], ctx?: C): Promise<void>;
1096
+ /**
1097
+ * Processes an incoming batch of 1 or more ClientMsgs on behalf of
1098
+ * a BACKEND session.
1099
+ *
1100
+ * Difference 1: HTTP RESPONSE instead of WEB SOCKET RESPONSE
1101
+ * ----------------------------------------------------------
1102
+ * For "normal" sessions that have a socket attached, any "responses" (i.e.
1103
+ * server messages like acks or fixops) will be sent back through that
1104
+ * existing socket connection.
1105
+ *
1106
+ * The key difference when using this method is that there is no such socket,
1107
+ * so any "response" ServerMsgs will get sent back as an HTTP response.
1108
+ *
1109
+ * Difference 2: No auth check
1110
+ * ---------------------------
1111
+ * Another key difference is that when processing a backend session, no
1112
+ * "isClientMsgAllowed()" check is performed, because those checks assume
1113
+ * a session.
1114
+ */
1115
+ processClientMsgFromBackendSession(session: BackendSession, messages: ClientMsg[], ctx?: C): Promise<void>;
1116
+ getSession(sessionKey: SessionKey): BrowserSession<SM, CM> | undefined;
1117
+ listSessions(): BrowserSession<SM, CM>[];
1118
+ /**
1119
+ * Will send the given ServerMsg to all Sessions, except the Session
1120
+ * where the message originates from.
1121
+ */
1122
+ sendToOthers(sender: SessionKey, serverMsg: ServerMsg | readonly ServerMsg[], ctx?: C, defer?: (promise: Promise<void>) => void): void;
1123
+ /**
1124
+ * Will broadcast the given ServerMsg to all Sessions in the Room.
1125
+ */
1126
+ sendToAll(serverMsg: ServerMsg | readonly ServerMsg[], ctx?: C, defer?: (promise: Promise<void>) => void): void;
1127
+ private _loadStorage;
1128
+ private _loadYjsStorage;
1129
+ private _load;
1130
+ /**
1131
+ * Returns a new, unique, actor ID.
1132
+ */
1133
+ private getNextActor;
1134
+ /**
1135
+ * Iterates over all *other* Sessions and their session keys.
1136
+ */
1137
+ private otherSessionEntries;
1138
+ /**
1139
+ * Iterates over all *other* Sessions.
1140
+ */
1141
+ private otherSessions;
1142
+ private _processClientMsg_withExclusiveAccess;
1143
+ private _processClientMsgFromBackendSession_withExclusiveAccess;
1144
+ private handleOne;
1145
+ }
1146
+
1147
+ /**
1148
+ * Copyright (c) Liveblocks Inc.
1149
+ *
1150
+ * This program is free software: you can redistribute it and/or modify
1151
+ * it under the terms of the GNU Affero General Public License as published
1152
+ * by the Free Software Foundation, either version 3 of the License, or
1153
+ * (at your option) any later version.
1154
+ *
1155
+ * This program is distributed in the hope that it will be useful,
1156
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1157
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1158
+ * GNU Affero General Public License for more details.
1159
+ *
1160
+ * You should have received a copy of the GNU Affero General Public License
1161
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1162
+ */
1163
+
1164
+ /**
1165
+ * Concatenates multiple Uint8Arrays into a single Uint8Array.
1166
+ */
1167
+ declare function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array;
1168
+
1169
+ /**
1170
+ * Copyright (c) Liveblocks Inc.
1171
+ *
1172
+ * This program is free software: you can redistribute it and/or modify
1173
+ * it under the terms of the GNU Affero General Public License as published
1174
+ * by the Free Software Foundation, either version 3 of the License, or
1175
+ * (at your option) any later version.
1176
+ *
1177
+ * This program is distributed in the hope that it will be useful,
1178
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1179
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1180
+ * GNU Affero General Public License for more details.
1181
+ *
1182
+ * You should have received a copy of the GNU Affero General Public License
1183
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1184
+ */
1185
+ /**
1186
+ * Given a promise or promise factory, returns a 2-tuple of success or failure.
1187
+ * This pattern avoids having to build deeply nested try / catch clauses, where
1188
+ * success variables need to be defined as a `let` outside of the `try` block.
1189
+ *
1190
+ * Turns:
1191
+ *
1192
+ * let result;
1193
+ * try {
1194
+ * result = await doSomething();
1195
+ * } catch (error) {
1196
+ * // do something with error
1197
+ * }
1198
+ *
1199
+ * doAnotherThing(result);
1200
+ *
1201
+ * Into:
1202
+ *
1203
+ * const [result, error] = await tryCatch(doSomething());
1204
+ * if (error) {
1205
+ * // do something with error
1206
+ * }
1207
+ * doAnotherThing(result);
1208
+ *
1209
+ */
1210
+ declare function tryCatch<T, E = Error>(promise: Promise<T> | (() => Promise<T>) | (() => T)): Promise<[T, undefined] | [undefined, E]>;
1211
+
1212
+ /**
1213
+ * Copyright (c) Liveblocks Inc.
1214
+ *
1215
+ * This program is free software: you can redistribute it and/or modify
1216
+ * it under the terms of the GNU Affero General Public License as published
1217
+ * by the Free Software Foundation, either version 3 of the License, or
1218
+ * (at your option) any later version.
1219
+ *
1220
+ * This program is distributed in the hope that it will be useful,
1221
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1222
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1223
+ * GNU Affero General Public License for more details.
1224
+ *
1225
+ * You should have received a copy of the GNU Affero General Public License
1226
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1227
+ */
1228
+ /**
1229
+ * Like ES6 map, but takes a default (factory) function which will be used
1230
+ * to create entries for missing keys on the fly.
1231
+ *
1232
+ * Useful for code like:
1233
+ *
1234
+ * const map = new DefaultMap(() => []);
1235
+ * map.getOrCreate('foo').push('hello');
1236
+ * map.getOrCreate('foo').push('world');
1237
+ * map.getOrCreate('foo')
1238
+ * // ['hello', 'world']
1239
+ *
1240
+ */
1241
+ declare class DefaultMap<K, V> extends Map<K, V> {
1242
+ #private;
1243
+ /**
1244
+ * If the default function is not provided to the constructor, it has to be
1245
+ * provided in each .getOrCreate() call individually.
1246
+ */
1247
+ constructor(defaultFn?: (key: K) => V, entries?: readonly (readonly [K, V])[] | null);
1248
+ /**
1249
+ * Gets the value at the given key, or creates it.
1250
+ *
1251
+ * Difference from normal Map: if the key does not exist, it will be created
1252
+ * on the fly using the factory function, and that value will get returned
1253
+ * instead of `undefined`.
1254
+ */
1255
+ getOrCreate(key: K, defaultFn?: (key: K) => V): V;
1256
+ }
1257
+
1258
+ /**
1259
+ * Copyright (c) Liveblocks Inc.
1260
+ *
1261
+ * This program is free software: you can redistribute it and/or modify
1262
+ * it under the terms of the GNU Affero General Public License as published
1263
+ * by the Free Software Foundation, either version 3 of the License, or
1264
+ * (at your option) any later version.
1265
+ *
1266
+ * This program is distributed in the hope that it will be useful,
1267
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1268
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1269
+ * GNU Affero General Public License for more details.
1270
+ *
1271
+ * You should have received a copy of the GNU Affero General Public License
1272
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1273
+ */
1274
+ /**
1275
+ * Like an ES6 Map, but two levels deep. Useful for building reverse lookup
1276
+ * tables. Will automatically delete second-level maps when they are empty.
1277
+ */
1278
+ declare class NestedMap<K1, K2, V> {
1279
+ #private;
1280
+ constructor();
1281
+ get size(): number;
1282
+ count(key1: K1): number;
1283
+ keys(): IterableIterator<[K1, K2]>;
1284
+ has(key1: K1, key2: K2): boolean;
1285
+ get(key1: K1, key2: K2): V | undefined;
1286
+ set(key1: K1, key2: K2, value: V): this;
1287
+ delete(key1: K1, key2: K2): void;
1288
+ clear(): void;
1289
+ [Symbol.iterator](): IterableIterator<[K1, K2, V]>;
1290
+ entriesAt(key1: K1): IterableIterator<[K2, V]>;
1291
+ filterAt(key1: K1, keys: Iterable<K2>): Iterable<[K2, V]>;
1292
+ keysAt(key1: K1): IterableIterator<K2>;
1293
+ valuesAt(key1: K1): IterableIterator<V>;
1294
+ deleteAll(key1: K1): void;
1295
+ }
1296
+
1297
+ /**
1298
+ * Copyright (c) Liveblocks Inc.
1299
+ *
1300
+ * This program is free software: you can redistribute it and/or modify
1301
+ * it under the terms of the GNU Affero General Public License as published
1302
+ * by the Free Software Foundation, either version 3 of the License, or
1303
+ * (at your option) any later version.
1304
+ *
1305
+ * This program is distributed in the hope that it will be useful,
1306
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1307
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1308
+ * GNU Affero General Public License for more details.
1309
+ *
1310
+ * You should have received a copy of the GNU Affero General Public License
1311
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1312
+ */
1313
+ /**
1314
+ * Wraps single-quotes around any string value. Useful for displaying field
1315
+ * names or other identifiers in error messages or logs.
1316
+ *
1317
+ * Examples:
1318
+ * quote("hi") // "'hi'"
1319
+ * quote("i'm") // "'i'm'"
1320
+ *
1321
+ * Note: no "escaping" happens here to the string value. This is because this
1322
+ * is intended to be used for human consumption, not machine consumption.
1323
+ */
1324
+ declare function quote(value: string | undefined): string;
1325
+
1326
+ /**
1327
+ * Copyright (c) Liveblocks Inc.
1328
+ *
1329
+ * This program is free software: you can redistribute it and/or modify
1330
+ * it under the terms of the GNU Affero General Public License as published
1331
+ * by the Free Software Foundation, either version 3 of the License, or
1332
+ * (at your option) any later version.
1333
+ *
1334
+ * This program is distributed in the hope that it will be useful,
1335
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1336
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1337
+ * GNU Affero General Public License for more details.
1338
+ *
1339
+ * You should have received a copy of the GNU Affero General Public License
1340
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1341
+ */
1342
+ /**
1343
+ * Like ES6 map, but also provides a unique reverse lookup index for values
1344
+ * stored in the map.
1345
+ *
1346
+ * Useful for code like:
1347
+ *
1348
+ * // Store a list of persons by their IDs, but each person's email must also
1349
+ * // be unique
1350
+ * const map = new UniqueMap((person) => person.email);
1351
+ * map.set(1, { name: 'John Doe', email: 'john@example.org' });
1352
+ * map.set(2, { name: 'John Foo', email: 'john@example.org' }); // Will error!
1353
+ * map.delete(1);
1354
+ * map.set(3, { name: 'Johnny', email: 'john@example.org' }); // Now it's allowed
1355
+ *
1356
+ * map.getReverseKey('john@example.org') // 3
1357
+ * map.getReverse('john@example.org') // { name: 'Johnny', email: 'john@example.org' }
1358
+ *
1359
+ */
1360
+ declare class UniqueMap<K, V, UK> extends Map<K, V> {
1361
+ #private;
1362
+ constructor(keyFn: (value: V) => UK);
1363
+ lookupPrimaryKey(uniqKey: UK): K | undefined;
1364
+ lookup(uniqKey: UK): V | undefined;
1365
+ set(key: K, value: V): this;
1366
+ delete(primaryKey: K): boolean;
1367
+ }
1368
+
1369
+ /**
1370
+ * Copyright (c) Liveblocks Inc.
1371
+ *
1372
+ * This program is free software: you can redistribute it and/or modify
1373
+ * it under the terms of the GNU Affero General Public License as published
1374
+ * by the Free Software Foundation, either version 3 of the License, or
1375
+ * (at your option) any later version.
1376
+ *
1377
+ * This program is distributed in the hope that it will be useful,
1378
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1379
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1380
+ * GNU Affero General Public License for more details.
1381
+ *
1382
+ * You should have received a copy of the GNU Affero General Public License
1383
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
1384
+ */
1385
+
1386
+ /**
1387
+ * Implements the most basic in-memory store. Used if no explicit store is
1388
+ * provided.
1389
+ */
1390
+ declare class InMemoryDriver implements IStorageDriver {
1391
+ private _nextActor;
1392
+ private _nodes;
1393
+ private _metadb;
1394
+ private _ydb;
1395
+ constructor(options?: {
1396
+ initialActor?: number;
1397
+ initialNodes?: Iterable<[string, SerializedCrdt]>;
1398
+ });
1399
+ raw_iter_nodes(): IterableIterator<[string, SerializedCrdt]>;
1400
+ /** Deletes all nodes and replaces them with the given document. */
1401
+ DANGEROUSLY_reset_nodes(doc: PlainLsonObject): void;
1402
+ get_meta(key: string): Promise<Json | undefined>;
1403
+ put_meta(key: string, value: Json): Promise<void>;
1404
+ delete_meta(key: string): Promise<void>;
1405
+ next_actor(): number;
1406
+ iter_y_updates(docId: YDocId): Promise<IterableIterator<[string, Uint8Array]>>;
1407
+ write_y_updates(docId: YDocId, key: string, data: Uint8Array): Promise<void>;
1408
+ delete_y_updates(docId: YDocId, keys: string[]): Promise<void>;
1409
+ /** @private Only use this in unit tests, never in production. */
1410
+ DANGEROUSLY_wipe_all_y_updates(): Promise<void>;
1411
+ load_nodes_api(): IStorageDriverNodeAPI;
1412
+ }
1413
+
1414
+ export { type ActorID, BackendSession, BrowserSession, ConsoleTarget, type CreateTicketOptions, DefaultMap, type FixOp, type Guid, type IReadableSnapshot, type IServerWebSocket, type IStorageDriver, type IStorageDriverNodeAPI, type IUserData, InMemoryDriver, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, NestedMap, type Pos, type PreSerializedServerMsg, ProtocolVersion, ROOT_YDOC_ID, Room, type SessionKey, type Ticket, UniqueMap, type YDocId, type YUpdate, type YVector, ackIgnoredOp, clientMsgDecoder, concatUint8Arrays, guidDecoder, jsonObjectYolo, jsonYolo, makeInMemorySnapshot, makeMetadataDB, plainLsonToNodeStream, protocolVersionDecoder, quote, serialize as serializeServerMsg, snapshotToLossyJson_eager, snapshotToLossyJson_lazy, snapshotToNodeStream, snapshotToPlainLson_eager, snapshotToPlainLson_lazy, Storage as test_only__Storage, YjsStorage as test_only__YjsStorage, transientClientMsgDecoder, tryCatch };