@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.
- package/LICENSE +661 -0
- package/README.md +75 -0
- package/dist/index.cjs +3285 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1414 -0
- package/dist/index.d.ts +1414 -0
- package/dist/index.js +3285 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.d.cts
ADDED
|
@@ -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 };
|