@liveblocks/server 1.4.1 → 1.4.2-pre2
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/dist/index.cjs +115 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -13
- package/dist/index.d.ts +63 -13
- package/dist/index.js +90 -36
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, Awaitable, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
|
|
1
|
+
import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, Awaitable, CompactNode, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
|
|
2
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
3
|
import * as decoders from 'decoders';
|
|
4
4
|
import { Decoder } from 'decoders';
|
|
@@ -23,6 +23,37 @@ import * as Y from 'yjs';
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
type Pos = ReturnType<typeof asPos>;
|
|
26
|
+
declare const fromType: unique symbol;
|
|
27
|
+
/**
|
|
28
|
+
* A string known to be a valid JSON-encoded value. Use this whenever a
|
|
29
|
+
* `string` already carries JSON, to keep that fact visible in the type system
|
|
30
|
+
* (instead of letting it look like a freeform `string`).
|
|
31
|
+
*
|
|
32
|
+
* Optionally parameterised by the *parsed* shape: `jstring<CompactNode>`
|
|
33
|
+
* means "a string whose `JSON.parse` result is a `CompactNode`". Without
|
|
34
|
+
* a type argument, defaults to `jstring<Json>` (any JSON value).
|
|
35
|
+
*
|
|
36
|
+
* At storage boundaries (e.g. reading `jdata` from SQLite), `as jstring`
|
|
37
|
+
* is acceptable — we trust the bytes are valid JSON because we wrote them as
|
|
38
|
+
* JSON ourselves.
|
|
39
|
+
*
|
|
40
|
+
* For example, these are valid JSON strings:
|
|
41
|
+
* - '0', '1', '2', '3', '3.14', etc.
|
|
42
|
+
* - 'null', 'true', 'false'
|
|
43
|
+
* - '{"foo":1}', '{ "foo": 1 }' (spaces are fine)
|
|
44
|
+
* - '[]', '[1,2, 3]', '[[]]', etc.
|
|
45
|
+
* - '["hi",{}]'
|
|
46
|
+
* - '"foo"'
|
|
47
|
+
*
|
|
48
|
+
* But these are not:
|
|
49
|
+
* - 'foo'
|
|
50
|
+
* - '1,2,3'
|
|
51
|
+
* - '{'
|
|
52
|
+
* - '[1,2,3,]' or '{"foo":1},' (trailing commas are not valid)
|
|
53
|
+
*/
|
|
54
|
+
type jstring<J = Json> = string & {
|
|
55
|
+
readonly [fromType]: J;
|
|
56
|
+
};
|
|
26
57
|
type NodeTuple<T extends SerializedCrdt = SerializedCrdt> = [
|
|
27
58
|
id: string,
|
|
28
59
|
value: T
|
|
@@ -683,6 +714,27 @@ interface IStorageDriverNodeAPI {
|
|
|
683
714
|
* Yield all nodes as [id, node] pairs. Must always include the root node.
|
|
684
715
|
*/
|
|
685
716
|
iter_nodes(): Iterable<StorageNode>;
|
|
717
|
+
/**
|
|
718
|
+
* Yield each node as a pre-built `CompactNode` JSON tuple string, ready to
|
|
719
|
+
* be emitted directly into a STORAGE_CHUNK wire frame without JSON.parse()
|
|
720
|
+
* or JSON.stringify()'ing overhead. Implementations MUST produce text whose
|
|
721
|
+
* parsed shape exactly matches the `CompactNode` union type from
|
|
722
|
+
* @liveblocks/core. The emitted shapes are:
|
|
723
|
+
*
|
|
724
|
+
* - Root node: '["root",<data>]'
|
|
725
|
+
* - OBJECT / REGISTER: '["0:1",0,"root","a",<data>]'
|
|
726
|
+
* - LIST / MAP: '["0:2",1,"0:1","b"]'
|
|
727
|
+
*
|
|
728
|
+
* Invariant (implementations MUST uphold; asserted by `_generateFullTestSuite`):
|
|
729
|
+
*
|
|
730
|
+
* iter_nodes_optimized().map(JSON.parse)
|
|
731
|
+
* ≡ nodeStreamToCompactNodes(iter_nodes())
|
|
732
|
+
*
|
|
733
|
+
* i.e. parsing each yielded string yields the same sequence of CompactNodes
|
|
734
|
+
* that the canonical `iter_nodes()` + `nodeStreamToCompactNodes()` path
|
|
735
|
+
* would produce.
|
|
736
|
+
*/
|
|
737
|
+
iter_nodes_optimized(): Iterable<jstring<CompactNode>>;
|
|
686
738
|
/**
|
|
687
739
|
* Return true iff a node with the given id exists. Must return true for "root".
|
|
688
740
|
*/
|
|
@@ -704,6 +756,12 @@ interface IStorageDriverNodeAPI {
|
|
|
704
756
|
* does not have to exist already. Positions compare lexicographically.
|
|
705
757
|
*/
|
|
706
758
|
get_next_sibling(parentId: string, pos: Pos): Pos | undefined;
|
|
759
|
+
/**
|
|
760
|
+
* Return the position of the last (rightmost) child under parentId, or
|
|
761
|
+
* undefined if the node has no children. Positions compare
|
|
762
|
+
* lexicographically.
|
|
763
|
+
*/
|
|
764
|
+
get_last_sibling(parentId: string): Pos | undefined;
|
|
707
765
|
/**
|
|
708
766
|
* Insert a child node with the given id.
|
|
709
767
|
*
|
|
@@ -1228,10 +1286,9 @@ type Millis = Brand<number, "Millis">;
|
|
|
1228
1286
|
* receives it as part of its ROOM_STATE message.
|
|
1229
1287
|
*/
|
|
1230
1288
|
type SessionKey = Brand<string, "SessionKey">;
|
|
1231
|
-
type PreSerializedServerMsg = Brand<string, "PreSerializedServerMsg">;
|
|
1232
1289
|
type ClientMsg = ClientMsg$1<JsonObject, Json> | FetchFeedsClientMsg | FetchFeedMessagesClientMsg | AddFeedClientMsg | UpdateFeedClientMsg | DeleteFeedClientMsg | AddFeedMessageClientMsg | UpdateFeedMessageClientMsg | DeleteFeedMessageClientMsg;
|
|
1233
1290
|
type ServerMsg = ServerMsg$1<JsonObject, BaseUserMeta, Json> | FeedMessagesServerMsg | FeedsServerMsg;
|
|
1234
|
-
declare function serialize(msgs: ServerMsg | readonly ServerMsg[]):
|
|
1291
|
+
declare function serialize(msgs: ServerMsg | readonly ServerMsg[]): jstring<ServerMsg>;
|
|
1235
1292
|
declare function ackIgnoredOp(opId: string): IgnoredOp;
|
|
1236
1293
|
/**
|
|
1237
1294
|
* A known or anonymous user.
|
|
@@ -1278,7 +1335,7 @@ declare class BrowserSession<SM, CM extends JsonObject> {
|
|
|
1278
1335
|
get hasNotifiedClientStorageUpdateError(): boolean;
|
|
1279
1336
|
setHasNotifiedClientStorageUpdateError(): void;
|
|
1280
1337
|
sendPong(): number;
|
|
1281
|
-
send(serverMsg: ServerMsg | ServerMsg[] |
|
|
1338
|
+
send(serverMsg: ServerMsg | ServerMsg[] | jstring<ServerMsg>): number;
|
|
1282
1339
|
}
|
|
1283
1340
|
declare class BackendSession extends BrowserSession<never, never> {
|
|
1284
1341
|
}
|
|
@@ -1317,13 +1374,6 @@ type RoomOptions<SM, CM extends JsonObject, C> = {
|
|
|
1317
1374
|
*/
|
|
1318
1375
|
storage?: IStorageDriver;
|
|
1319
1376
|
logger?: Logger;
|
|
1320
|
-
/**
|
|
1321
|
-
* Whether to allow streaming storage responses. Only safe with drivers
|
|
1322
|
-
* that can guarantee that no Ops from other clients can get interleaved
|
|
1323
|
-
* between the chunk generation until the last chunk has been sent.
|
|
1324
|
-
* Defaults to true, but is notably NOT safe to use from DOS-KV backends.
|
|
1325
|
-
*/
|
|
1326
|
-
allowStreaming?: boolean;
|
|
1327
1377
|
hooks?: {
|
|
1328
1378
|
/** Customize which incoming messages from a client are allowed or disallowed. */
|
|
1329
1379
|
isClientMsgAllowed?: (msg: ClientMsg, session: BrowserSession<SM, CM>) => {
|
|
@@ -1436,7 +1486,7 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1436
1486
|
createTicket(options?: CreateTicketOptions<SM, CM>): Promise<Ticket<SM, CM>>;
|
|
1437
1487
|
createBackendSession_experimental(): Promise<[
|
|
1438
1488
|
session: BackendSession,
|
|
1439
|
-
outgoingMessages:
|
|
1489
|
+
outgoingMessages: jstring<ServerMsg>[]
|
|
1440
1490
|
]>;
|
|
1441
1491
|
/**
|
|
1442
1492
|
* Restores the given sessions as the Room server's session list. Can only be
|
|
@@ -1895,4 +1945,4 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1895
1945
|
load_nodes_api(): IStorageDriverNodeAPI;
|
|
1896
1946
|
}
|
|
1897
1947
|
|
|
1898
|
-
export { type ActorID, type AddFeedClientMsg, type AddFeedMessageClientMsg, BackendSession, BrowserSession, ConsoleTarget, type CreateTicketOptions, DefaultMap, type DeleteFeedClientMsg, type DeleteFeedMessageClientMsg, type Feed, type FeedDeletedServerMsg, type FeedMessage, type FeedMessagesAddedServerMsg, type FeedMessagesDeletedServerMsg, type FeedMessagesListServerMsg, type FeedMessagesServerMsg, type FeedMessagesUpdatedServerMsg, FeedMsgCode, FeedRequestErrorCode, type FeedRequestFailedServerMsg, type FeedsAddedServerMsg, type FeedsListServerMsg, type FeedsServerMsg, type FeedsUpdatedServerMsg, type FetchFeedMessagesClientMsg, type FetchFeedsClientMsg, type FixOp, type Guid, type IReadableSnapshot, type IServerWebSocket, type IStorageDriver, type IStorageDriverNodeAPI, type IUserData, InMemoryDriver, type LeasedSession, type ListFeedMessagesOptions, type ListFeedMessagesResult, type ListFeedsOptions, type ListFeedsResult, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, type Millis, NestedMap, type NodeMap, type NodeStream, type NodeTuple, type Pos,
|
|
1948
|
+
export { type ActorID, type AddFeedClientMsg, type AddFeedMessageClientMsg, BackendSession, BrowserSession, ConsoleTarget, type CreateTicketOptions, DefaultMap, type DeleteFeedClientMsg, type DeleteFeedMessageClientMsg, type Feed, type FeedDeletedServerMsg, type FeedMessage, type FeedMessagesAddedServerMsg, type FeedMessagesDeletedServerMsg, type FeedMessagesListServerMsg, type FeedMessagesServerMsg, type FeedMessagesUpdatedServerMsg, FeedMsgCode, FeedRequestErrorCode, type FeedRequestFailedServerMsg, type FeedsAddedServerMsg, type FeedsListServerMsg, type FeedsServerMsg, type FeedsUpdatedServerMsg, type FetchFeedMessagesClientMsg, type FetchFeedsClientMsg, type FixOp, type Guid, type IReadableSnapshot, type IServerWebSocket, type IStorageDriver, type IStorageDriverNodeAPI, type IUserData, InMemoryDriver, type LeasedSession, type ListFeedMessagesOptions, type ListFeedMessagesResult, type ListFeedsOptions, type ListFeedsResult, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, type Millis, NestedMap, type NodeMap, type NodeStream, type NodeTuple, type Pos, ProtocolVersion, ROOT_YDOC_ID, Room, type SessionKey, type Ticket, UniqueMap, type UpdateFeedClientMsg, type UpdateFeedMessageClientMsg, type YDocId, type YUpdate, type YVector, ackIgnoredOp, clientMsgDecoder, concatUint8Arrays, feedFailureServerMsg, feedMetadataIdDecoder, feedMetadataUpdateDecoder, feedRequestFailed, fetchFeedsMetadataFilterDecoder, guidDecoder, isLeasedSessionExpired, jsonObjectYolo, jsonYolo, type jstring, makeInMemorySnapshot, makeMetadataDB, mapFeedError, optionalFeedMetadataDecoder, 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, Awaitable, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
|
|
1
|
+
import { Json, asPos, IUserInfo, SerializedCrdt, JsonObject, DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, Brand, SerializedRootObject, SerializedChild, StorageNode, Awaitable, CompactNode, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, ServerMsg as ServerMsg$1, BaseUserMeta, IgnoredOp } from '@liveblocks/core';
|
|
2
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
3
|
import * as decoders from 'decoders';
|
|
4
4
|
import { Decoder } from 'decoders';
|
|
@@ -23,6 +23,37 @@ import * as Y from 'yjs';
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
type Pos = ReturnType<typeof asPos>;
|
|
26
|
+
declare const fromType: unique symbol;
|
|
27
|
+
/**
|
|
28
|
+
* A string known to be a valid JSON-encoded value. Use this whenever a
|
|
29
|
+
* `string` already carries JSON, to keep that fact visible in the type system
|
|
30
|
+
* (instead of letting it look like a freeform `string`).
|
|
31
|
+
*
|
|
32
|
+
* Optionally parameterised by the *parsed* shape: `jstring<CompactNode>`
|
|
33
|
+
* means "a string whose `JSON.parse` result is a `CompactNode`". Without
|
|
34
|
+
* a type argument, defaults to `jstring<Json>` (any JSON value).
|
|
35
|
+
*
|
|
36
|
+
* At storage boundaries (e.g. reading `jdata` from SQLite), `as jstring`
|
|
37
|
+
* is acceptable — we trust the bytes are valid JSON because we wrote them as
|
|
38
|
+
* JSON ourselves.
|
|
39
|
+
*
|
|
40
|
+
* For example, these are valid JSON strings:
|
|
41
|
+
* - '0', '1', '2', '3', '3.14', etc.
|
|
42
|
+
* - 'null', 'true', 'false'
|
|
43
|
+
* - '{"foo":1}', '{ "foo": 1 }' (spaces are fine)
|
|
44
|
+
* - '[]', '[1,2, 3]', '[[]]', etc.
|
|
45
|
+
* - '["hi",{}]'
|
|
46
|
+
* - '"foo"'
|
|
47
|
+
*
|
|
48
|
+
* But these are not:
|
|
49
|
+
* - 'foo'
|
|
50
|
+
* - '1,2,3'
|
|
51
|
+
* - '{'
|
|
52
|
+
* - '[1,2,3,]' or '{"foo":1},' (trailing commas are not valid)
|
|
53
|
+
*/
|
|
54
|
+
type jstring<J = Json> = string & {
|
|
55
|
+
readonly [fromType]: J;
|
|
56
|
+
};
|
|
26
57
|
type NodeTuple<T extends SerializedCrdt = SerializedCrdt> = [
|
|
27
58
|
id: string,
|
|
28
59
|
value: T
|
|
@@ -683,6 +714,27 @@ interface IStorageDriverNodeAPI {
|
|
|
683
714
|
* Yield all nodes as [id, node] pairs. Must always include the root node.
|
|
684
715
|
*/
|
|
685
716
|
iter_nodes(): Iterable<StorageNode>;
|
|
717
|
+
/**
|
|
718
|
+
* Yield each node as a pre-built `CompactNode` JSON tuple string, ready to
|
|
719
|
+
* be emitted directly into a STORAGE_CHUNK wire frame without JSON.parse()
|
|
720
|
+
* or JSON.stringify()'ing overhead. Implementations MUST produce text whose
|
|
721
|
+
* parsed shape exactly matches the `CompactNode` union type from
|
|
722
|
+
* @liveblocks/core. The emitted shapes are:
|
|
723
|
+
*
|
|
724
|
+
* - Root node: '["root",<data>]'
|
|
725
|
+
* - OBJECT / REGISTER: '["0:1",0,"root","a",<data>]'
|
|
726
|
+
* - LIST / MAP: '["0:2",1,"0:1","b"]'
|
|
727
|
+
*
|
|
728
|
+
* Invariant (implementations MUST uphold; asserted by `_generateFullTestSuite`):
|
|
729
|
+
*
|
|
730
|
+
* iter_nodes_optimized().map(JSON.parse)
|
|
731
|
+
* ≡ nodeStreamToCompactNodes(iter_nodes())
|
|
732
|
+
*
|
|
733
|
+
* i.e. parsing each yielded string yields the same sequence of CompactNodes
|
|
734
|
+
* that the canonical `iter_nodes()` + `nodeStreamToCompactNodes()` path
|
|
735
|
+
* would produce.
|
|
736
|
+
*/
|
|
737
|
+
iter_nodes_optimized(): Iterable<jstring<CompactNode>>;
|
|
686
738
|
/**
|
|
687
739
|
* Return true iff a node with the given id exists. Must return true for "root".
|
|
688
740
|
*/
|
|
@@ -704,6 +756,12 @@ interface IStorageDriverNodeAPI {
|
|
|
704
756
|
* does not have to exist already. Positions compare lexicographically.
|
|
705
757
|
*/
|
|
706
758
|
get_next_sibling(parentId: string, pos: Pos): Pos | undefined;
|
|
759
|
+
/**
|
|
760
|
+
* Return the position of the last (rightmost) child under parentId, or
|
|
761
|
+
* undefined if the node has no children. Positions compare
|
|
762
|
+
* lexicographically.
|
|
763
|
+
*/
|
|
764
|
+
get_last_sibling(parentId: string): Pos | undefined;
|
|
707
765
|
/**
|
|
708
766
|
* Insert a child node with the given id.
|
|
709
767
|
*
|
|
@@ -1228,10 +1286,9 @@ type Millis = Brand<number, "Millis">;
|
|
|
1228
1286
|
* receives it as part of its ROOM_STATE message.
|
|
1229
1287
|
*/
|
|
1230
1288
|
type SessionKey = Brand<string, "SessionKey">;
|
|
1231
|
-
type PreSerializedServerMsg = Brand<string, "PreSerializedServerMsg">;
|
|
1232
1289
|
type ClientMsg = ClientMsg$1<JsonObject, Json> | FetchFeedsClientMsg | FetchFeedMessagesClientMsg | AddFeedClientMsg | UpdateFeedClientMsg | DeleteFeedClientMsg | AddFeedMessageClientMsg | UpdateFeedMessageClientMsg | DeleteFeedMessageClientMsg;
|
|
1233
1290
|
type ServerMsg = ServerMsg$1<JsonObject, BaseUserMeta, Json> | FeedMessagesServerMsg | FeedsServerMsg;
|
|
1234
|
-
declare function serialize(msgs: ServerMsg | readonly ServerMsg[]):
|
|
1291
|
+
declare function serialize(msgs: ServerMsg | readonly ServerMsg[]): jstring<ServerMsg>;
|
|
1235
1292
|
declare function ackIgnoredOp(opId: string): IgnoredOp;
|
|
1236
1293
|
/**
|
|
1237
1294
|
* A known or anonymous user.
|
|
@@ -1278,7 +1335,7 @@ declare class BrowserSession<SM, CM extends JsonObject> {
|
|
|
1278
1335
|
get hasNotifiedClientStorageUpdateError(): boolean;
|
|
1279
1336
|
setHasNotifiedClientStorageUpdateError(): void;
|
|
1280
1337
|
sendPong(): number;
|
|
1281
|
-
send(serverMsg: ServerMsg | ServerMsg[] |
|
|
1338
|
+
send(serverMsg: ServerMsg | ServerMsg[] | jstring<ServerMsg>): number;
|
|
1282
1339
|
}
|
|
1283
1340
|
declare class BackendSession extends BrowserSession<never, never> {
|
|
1284
1341
|
}
|
|
@@ -1317,13 +1374,6 @@ type RoomOptions<SM, CM extends JsonObject, C> = {
|
|
|
1317
1374
|
*/
|
|
1318
1375
|
storage?: IStorageDriver;
|
|
1319
1376
|
logger?: Logger;
|
|
1320
|
-
/**
|
|
1321
|
-
* Whether to allow streaming storage responses. Only safe with drivers
|
|
1322
|
-
* that can guarantee that no Ops from other clients can get interleaved
|
|
1323
|
-
* between the chunk generation until the last chunk has been sent.
|
|
1324
|
-
* Defaults to true, but is notably NOT safe to use from DOS-KV backends.
|
|
1325
|
-
*/
|
|
1326
|
-
allowStreaming?: boolean;
|
|
1327
1377
|
hooks?: {
|
|
1328
1378
|
/** Customize which incoming messages from a client are allowed or disallowed. */
|
|
1329
1379
|
isClientMsgAllowed?: (msg: ClientMsg, session: BrowserSession<SM, CM>) => {
|
|
@@ -1436,7 +1486,7 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1436
1486
|
createTicket(options?: CreateTicketOptions<SM, CM>): Promise<Ticket<SM, CM>>;
|
|
1437
1487
|
createBackendSession_experimental(): Promise<[
|
|
1438
1488
|
session: BackendSession,
|
|
1439
|
-
outgoingMessages:
|
|
1489
|
+
outgoingMessages: jstring<ServerMsg>[]
|
|
1440
1490
|
]>;
|
|
1441
1491
|
/**
|
|
1442
1492
|
* Restores the given sessions as the Room server's session list. Can only be
|
|
@@ -1895,4 +1945,4 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1895
1945
|
load_nodes_api(): IStorageDriverNodeAPI;
|
|
1896
1946
|
}
|
|
1897
1947
|
|
|
1898
|
-
export { type ActorID, type AddFeedClientMsg, type AddFeedMessageClientMsg, BackendSession, BrowserSession, ConsoleTarget, type CreateTicketOptions, DefaultMap, type DeleteFeedClientMsg, type DeleteFeedMessageClientMsg, type Feed, type FeedDeletedServerMsg, type FeedMessage, type FeedMessagesAddedServerMsg, type FeedMessagesDeletedServerMsg, type FeedMessagesListServerMsg, type FeedMessagesServerMsg, type FeedMessagesUpdatedServerMsg, FeedMsgCode, FeedRequestErrorCode, type FeedRequestFailedServerMsg, type FeedsAddedServerMsg, type FeedsListServerMsg, type FeedsServerMsg, type FeedsUpdatedServerMsg, type FetchFeedMessagesClientMsg, type FetchFeedsClientMsg, type FixOp, type Guid, type IReadableSnapshot, type IServerWebSocket, type IStorageDriver, type IStorageDriverNodeAPI, type IUserData, InMemoryDriver, type LeasedSession, type ListFeedMessagesOptions, type ListFeedMessagesResult, type ListFeedsOptions, type ListFeedsResult, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, type Millis, NestedMap, type NodeMap, type NodeStream, type NodeTuple, type Pos,
|
|
1948
|
+
export { type ActorID, type AddFeedClientMsg, type AddFeedMessageClientMsg, BackendSession, BrowserSession, ConsoleTarget, type CreateTicketOptions, DefaultMap, type DeleteFeedClientMsg, type DeleteFeedMessageClientMsg, type Feed, type FeedDeletedServerMsg, type FeedMessage, type FeedMessagesAddedServerMsg, type FeedMessagesDeletedServerMsg, type FeedMessagesListServerMsg, type FeedMessagesServerMsg, type FeedMessagesUpdatedServerMsg, FeedMsgCode, FeedRequestErrorCode, type FeedRequestFailedServerMsg, type FeedsAddedServerMsg, type FeedsListServerMsg, type FeedsServerMsg, type FeedsUpdatedServerMsg, type FetchFeedMessagesClientMsg, type FetchFeedsClientMsg, type FixOp, type Guid, type IReadableSnapshot, type IServerWebSocket, type IStorageDriver, type IStorageDriverNodeAPI, type IUserData, InMemoryDriver, type LeasedSession, type ListFeedMessagesOptions, type ListFeedMessagesResult, type ListFeedsOptions, type ListFeedsResult, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, type Millis, NestedMap, type NodeMap, type NodeStream, type NodeTuple, type Pos, ProtocolVersion, ROOT_YDOC_ID, Room, type SessionKey, type Ticket, UniqueMap, type UpdateFeedClientMsg, type UpdateFeedMessageClientMsg, type YDocId, type YUpdate, type YVector, ackIgnoredOp, clientMsgDecoder, concatUint8Arrays, feedFailureServerMsg, feedMetadataIdDecoder, feedMetadataUpdateDecoder, feedRequestFailed, fetchFeedsMetadataFilterDecoder, guidDecoder, isLeasedSessionExpired, jsonObjectYolo, jsonYolo, type jstring, makeInMemorySnapshot, makeMetadataDB, mapFeedError, optionalFeedMetadataDecoder, 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 };
|
package/dist/index.js
CHANGED
|
@@ -129,7 +129,9 @@ var feedMetadataRecordForCreate = record(
|
|
|
129
129
|
(value) => Object.keys(value).length <= MAX_METADATA_COUNT,
|
|
130
130
|
`Must be at most ${MAX_METADATA_COUNT} items`
|
|
131
131
|
);
|
|
132
|
-
var optionalFeedMetadataDecoder = optional(
|
|
132
|
+
var optionalFeedMetadataDecoder = optional(
|
|
133
|
+
feedMetadataRecordForCreate
|
|
134
|
+
);
|
|
133
135
|
var feedMetadataUpdateDecoder = record(
|
|
134
136
|
feedMetadataIdDecoder,
|
|
135
137
|
feedMetadataNullableValueDecoder
|
|
@@ -155,7 +157,15 @@ var jsonObjectYolo = jsonYolo.refine(
|
|
|
155
157
|
|
|
156
158
|
// src/decoders/Op.ts
|
|
157
159
|
import { OpCode } from "@liveblocks/core";
|
|
158
|
-
import {
|
|
160
|
+
import {
|
|
161
|
+
constant,
|
|
162
|
+
object,
|
|
163
|
+
oneOf,
|
|
164
|
+
optional as optional2,
|
|
165
|
+
string as string2,
|
|
166
|
+
taggedUnion
|
|
167
|
+
} from "decoders";
|
|
168
|
+
var intent = oneOf(["set", "push"]);
|
|
159
169
|
var updateObjectOp = object({
|
|
160
170
|
type: constant(OpCode.UPDATE_OBJECT),
|
|
161
171
|
opId: string2,
|
|
@@ -169,7 +179,7 @@ var createObjectOp = object({
|
|
|
169
179
|
parentId: string2,
|
|
170
180
|
parentKey: string2,
|
|
171
181
|
data: jsonObjectYolo,
|
|
172
|
-
intent: optional2(
|
|
182
|
+
intent: optional2(intent),
|
|
173
183
|
deletedId: optional2(string2)
|
|
174
184
|
});
|
|
175
185
|
var createListOp = object({
|
|
@@ -178,7 +188,7 @@ var createListOp = object({
|
|
|
178
188
|
id: string2,
|
|
179
189
|
parentId: string2,
|
|
180
190
|
parentKey: string2,
|
|
181
|
-
intent: optional2(
|
|
191
|
+
intent: optional2(intent),
|
|
182
192
|
deletedId: optional2(string2)
|
|
183
193
|
});
|
|
184
194
|
var createMapOp = object({
|
|
@@ -187,7 +197,7 @@ var createMapOp = object({
|
|
|
187
197
|
id: string2,
|
|
188
198
|
parentId: string2,
|
|
189
199
|
parentKey: string2,
|
|
190
|
-
intent: optional2(
|
|
200
|
+
intent: optional2(intent),
|
|
191
201
|
deletedId: optional2(string2)
|
|
192
202
|
});
|
|
193
203
|
var createRegisterOp = object({
|
|
@@ -197,7 +207,7 @@ var createRegisterOp = object({
|
|
|
197
207
|
parentId: string2,
|
|
198
208
|
parentKey: string2,
|
|
199
209
|
data: jsonYolo,
|
|
200
|
-
intent: optional2(
|
|
210
|
+
intent: optional2(intent),
|
|
201
211
|
deletedId: optional2(string2)
|
|
202
212
|
});
|
|
203
213
|
var deleteCrdtOp = object({
|
|
@@ -820,7 +830,6 @@ function makeMetadataDB(driver) {
|
|
|
820
830
|
import {
|
|
821
831
|
assertNever as assertNever3,
|
|
822
832
|
ClientMsgCode as ClientMsgCode2,
|
|
823
|
-
nodeStreamToCompactNodes,
|
|
824
833
|
OpCode as OpCode3,
|
|
825
834
|
raise as raise3,
|
|
826
835
|
ServerMsgCode as CoreServerMsgCode,
|
|
@@ -829,7 +838,7 @@ import {
|
|
|
829
838
|
} from "@liveblocks/core";
|
|
830
839
|
import { Mutex, tryAcquire } from "async-mutex";
|
|
831
840
|
import { array as array3, formatInline } from "decoders";
|
|
832
|
-
import {
|
|
841
|
+
import { chunkedByCost } from "itertools";
|
|
833
842
|
import { nanoid as nanoid2 } from "nanoid";
|
|
834
843
|
|
|
835
844
|
// src/lib/Logger.ts
|
|
@@ -964,7 +973,13 @@ var Logger = class _Logger {
|
|
|
964
973
|
};
|
|
965
974
|
|
|
966
975
|
// src/plugins/InMemoryDriver.ts
|
|
967
|
-
import {
|
|
976
|
+
import {
|
|
977
|
+
asPos,
|
|
978
|
+
CrdtType as CrdtType4,
|
|
979
|
+
isRootStorageNode as isRootStorageNode2,
|
|
980
|
+
nn as nn2,
|
|
981
|
+
nodeStreamToCompactNodes
|
|
982
|
+
} from "@liveblocks/core";
|
|
968
983
|
import { ifilter, imap } from "itertools";
|
|
969
984
|
|
|
970
985
|
// src/lib/text.ts
|
|
@@ -1287,6 +1302,16 @@ var InMemoryDriver = class {
|
|
|
1287
1302
|
}
|
|
1288
1303
|
return nextPos;
|
|
1289
1304
|
}
|
|
1305
|
+
function get_last_sibling(parentId) {
|
|
1306
|
+
let lastPos;
|
|
1307
|
+
for (const siblingKey of revNodes.keysAt(parentId)) {
|
|
1308
|
+
const siblingPos = asPos(siblingKey);
|
|
1309
|
+
if (lastPos === void 0 || siblingPos > lastPos) {
|
|
1310
|
+
lastPos = siblingPos;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
return lastPos;
|
|
1314
|
+
}
|
|
1290
1315
|
async function set_child(id, node, allowOverwrite = false) {
|
|
1291
1316
|
const parentNode = nodes.get(node.parentId);
|
|
1292
1317
|
if (parentNode === void 0) {
|
|
@@ -1374,6 +1399,17 @@ var InMemoryDriver = class {
|
|
|
1374
1399
|
* Yield all nodes as [id, node] pairs. Must always include the root node.
|
|
1375
1400
|
*/
|
|
1376
1401
|
iter_nodes: () => nodes,
|
|
1402
|
+
/**
|
|
1403
|
+
* Yield each node as a pre-built CompactNode JSON tuple string.
|
|
1404
|
+
*
|
|
1405
|
+
* This implementation IS the canonical reference for the invariant:
|
|
1406
|
+
* iter_nodes_optimized` ≡ `nodeStreamToCompactNodes(iter_nodes())
|
|
1407
|
+
*/
|
|
1408
|
+
*iter_nodes_optimized() {
|
|
1409
|
+
for (const compact of nodeStreamToCompactNodes(nodes)) {
|
|
1410
|
+
yield JSON.stringify(compact);
|
|
1411
|
+
}
|
|
1412
|
+
},
|
|
1377
1413
|
/**
|
|
1378
1414
|
* Return true iff a node with the given id exists. Must return true for "root".
|
|
1379
1415
|
*/
|
|
@@ -1395,6 +1431,11 @@ var InMemoryDriver = class {
|
|
|
1395
1431
|
* does not have to exist already. Positions compare lexicographically.
|
|
1396
1432
|
*/
|
|
1397
1433
|
get_next_sibling,
|
|
1434
|
+
/**
|
|
1435
|
+
* Return the position of the last (rightmost) child under parentId, or
|
|
1436
|
+
* undefined if the node has no children.
|
|
1437
|
+
*/
|
|
1438
|
+
get_last_sibling,
|
|
1398
1439
|
/**
|
|
1399
1440
|
* Insert a child node with the given id.
|
|
1400
1441
|
*
|
|
@@ -1600,8 +1641,8 @@ var Storage = class {
|
|
|
1600
1641
|
}
|
|
1601
1642
|
async createChildAsListItem(op2, node) {
|
|
1602
1643
|
let fix;
|
|
1603
|
-
const
|
|
1604
|
-
if (
|
|
1644
|
+
const intent2 = op2.intent ?? "insert";
|
|
1645
|
+
if (intent2 === "insert") {
|
|
1605
1646
|
const insertedParentKey = await this.insertIntoList(op2.id, node);
|
|
1606
1647
|
if (insertedParentKey !== node.parentKey) {
|
|
1607
1648
|
op2 = { ...op2, parentKey: insertedParentKey };
|
|
@@ -1613,7 +1654,7 @@ var Storage = class {
|
|
|
1613
1654
|
return accept(op2, fix);
|
|
1614
1655
|
}
|
|
1615
1656
|
return accept(op2);
|
|
1616
|
-
} else if (
|
|
1657
|
+
} else if (intent2 === "set") {
|
|
1617
1658
|
const deletedId = op2.deletedId !== void 0 && op2.deletedId !== op2.id && this.loadedDriver.get_node(op2.deletedId)?.parentId === node.parentId ? op2.deletedId : void 0;
|
|
1618
1659
|
if (deletedId !== void 0) {
|
|
1619
1660
|
await this.loadedDriver.delete_node(deletedId);
|
|
@@ -1630,8 +1671,26 @@ var Storage = class {
|
|
|
1630
1671
|
}
|
|
1631
1672
|
await this.loadedDriver.set_child(op2.id, node, true);
|
|
1632
1673
|
return accept(op2, fix);
|
|
1674
|
+
} else if (intent2 === "push") {
|
|
1675
|
+
const lastPos = this.loadedDriver.get_last_sibling(node.parentId);
|
|
1676
|
+
const guessedKey = asPos2(node.parentKey);
|
|
1677
|
+
const pushedKey = lastPos === void 0 || guessedKey > lastPos ? guessedKey : makePosition2(lastPos);
|
|
1678
|
+
await this.loadedDriver.set_child(op2.id, {
|
|
1679
|
+
...node,
|
|
1680
|
+
parentKey: pushedKey
|
|
1681
|
+
});
|
|
1682
|
+
if (pushedKey !== node.parentKey) {
|
|
1683
|
+
op2 = { ...op2, parentKey: pushedKey };
|
|
1684
|
+
fix = {
|
|
1685
|
+
type: OpCode2.SET_PARENT_KEY,
|
|
1686
|
+
id: op2.id,
|
|
1687
|
+
parentKey: pushedKey
|
|
1688
|
+
};
|
|
1689
|
+
return accept(op2, fix);
|
|
1690
|
+
}
|
|
1691
|
+
return accept(op2);
|
|
1633
1692
|
} else {
|
|
1634
|
-
return assertNever2(
|
|
1693
|
+
return assertNever2(intent2, "Invalid intent");
|
|
1635
1694
|
}
|
|
1636
1695
|
}
|
|
1637
1696
|
async applyDeleteObjectKeyOp(op2) {
|
|
@@ -2035,6 +2094,7 @@ function isLeasedSessionExpired(leasedSession) {
|
|
|
2035
2094
|
}
|
|
2036
2095
|
|
|
2037
2096
|
// src/Room.ts
|
|
2097
|
+
var MB = 1024 * 1024;
|
|
2038
2098
|
var messagesDecoder = array3(clientMsgDecoder);
|
|
2039
2099
|
var ServerMsgCode2 = { ...CoreServerMsgCode, ...FeedMsgCode };
|
|
2040
2100
|
var HIGHEST_PROTOCOL_VERSION = Math.max(
|
|
@@ -2048,6 +2108,11 @@ var SERVER_MSG_CODE_NAMES = Object.fromEntries(
|
|
|
2048
2108
|
var BLACK_HOLE = new Logger([
|
|
2049
2109
|
/* No targets, i.e. black hole logger */
|
|
2050
2110
|
]);
|
|
2111
|
+
function groupNodesForWebSocketMessages(input) {
|
|
2112
|
+
const MAX_SIZE = 16 * MB;
|
|
2113
|
+
const idealSize = (chunkIndex) => chunkIndex < 3 ? 1 * MB : Math.min(8, chunkIndex - 1) * MB;
|
|
2114
|
+
return chunkedByCost(input, (tup) => tup.length, MAX_SIZE, idealSize);
|
|
2115
|
+
}
|
|
2051
2116
|
function collectSideEffects() {
|
|
2052
2117
|
const deferred = [];
|
|
2053
2118
|
return {
|
|
@@ -2181,7 +2246,7 @@ var BackendSession = class extends BrowserSession {
|
|
|
2181
2246
|
super(ticket, socket, debug);
|
|
2182
2247
|
}
|
|
2183
2248
|
};
|
|
2184
|
-
var __debug2
|
|
2249
|
+
var __debug2;
|
|
2185
2250
|
var Room = class {
|
|
2186
2251
|
constructor(meta, options) {
|
|
2187
2252
|
// ^^^^^^^^^^ User-defined Room Metadata, Session Metadata, and Client Metadata
|
|
@@ -2203,12 +2268,10 @@ var Room = class {
|
|
|
2203
2268
|
__publicField(this, "sessions", new UniqueMap((s) => s.actor));
|
|
2204
2269
|
__publicField(this, "hooks");
|
|
2205
2270
|
__privateAdd(this, __debug2);
|
|
2206
|
-
__privateAdd(this, __allowStreaming);
|
|
2207
2271
|
const driver = options?.storage ?? makeNewInMemoryDriver();
|
|
2208
2272
|
this.meta = meta;
|
|
2209
2273
|
this.driver = driver;
|
|
2210
2274
|
this.logger = options?.logger ?? BLACK_HOLE;
|
|
2211
|
-
__privateSet(this, __allowStreaming, options?.allowStreaming ?? true);
|
|
2212
2275
|
this.hooks = {
|
|
2213
2276
|
isClientMsgAllowed: options?.hooks?.isClientMsgAllowed ?? (() => {
|
|
2214
2277
|
return {
|
|
@@ -2973,7 +3036,13 @@ var Room = class {
|
|
|
2973
3036
|
const toReplyImmediately = [];
|
|
2974
3037
|
const toReplyAfter = [];
|
|
2975
3038
|
const replyImmediately = (msg) => {
|
|
2976
|
-
if (
|
|
3039
|
+
if (typeof msg === "string") {
|
|
3040
|
+
if (toReplyImmediately.length > 0) {
|
|
3041
|
+
session.send(toReplyImmediately);
|
|
3042
|
+
toReplyImmediately.length = 0;
|
|
3043
|
+
}
|
|
3044
|
+
session.send(msg);
|
|
3045
|
+
} else if (Array.isArray(msg)) {
|
|
2977
3046
|
for (const m of msg) {
|
|
2978
3047
|
toReplyImmediately.push(m);
|
|
2979
3048
|
}
|
|
@@ -3031,24 +3100,10 @@ var Room = class {
|
|
|
3031
3100
|
}
|
|
3032
3101
|
case ClientMsgCode2.FETCH_STORAGE: {
|
|
3033
3102
|
if (session.version >= 8 /* V8 */) {
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
NODES_PER_CHUNK
|
|
3039
|
-
)) {
|
|
3040
|
-
replyImmediately({
|
|
3041
|
-
type: ServerMsgCode2.STORAGE_CHUNK,
|
|
3042
|
-
nodes: chunk
|
|
3043
|
-
});
|
|
3044
|
-
}
|
|
3045
|
-
} else {
|
|
3046
|
-
replyImmediately({
|
|
3047
|
-
type: ServerMsgCode2.STORAGE_CHUNK,
|
|
3048
|
-
nodes: Array.from(
|
|
3049
|
-
nodeStreamToCompactNodes(this.storage.loadedDriver.iter_nodes())
|
|
3050
|
-
)
|
|
3051
|
-
});
|
|
3103
|
+
const rawStream = this.storage.loadedDriver.iter_nodes_optimized();
|
|
3104
|
+
for (const chunk of groupNodesForWebSocketMessages(rawStream)) {
|
|
3105
|
+
const frame = `{"type":${ServerMsgCode2.STORAGE_CHUNK},"nodes":[${chunk.join(",")}]}`;
|
|
3106
|
+
replyImmediately(frame);
|
|
3052
3107
|
}
|
|
3053
3108
|
replyImmediately({ type: ServerMsgCode2.STORAGE_STREAM_END });
|
|
3054
3109
|
} else {
|
|
@@ -3327,7 +3382,6 @@ var Room = class {
|
|
|
3327
3382
|
}
|
|
3328
3383
|
};
|
|
3329
3384
|
__debug2 = new WeakMap();
|
|
3330
|
-
__allowStreaming = new WeakMap();
|
|
3331
3385
|
export {
|
|
3332
3386
|
BackendSession,
|
|
3333
3387
|
BrowserSession,
|