@liveblocks/server 1.0.4 → 1.0.6
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 +196 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -5
- package/dist/index.d.ts +81 -5
- package/dist/index.js +195 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, JsonObject, Json, Brand, asPos, SerializedRootObject, SerializedChild, StorageNode,
|
|
1
|
+
import { DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, JsonObject, Json, Brand, asPos, SerializedCrdt, IUserInfo, SerializedRootObject, SerializedChild, StorageNode, Awaitable, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, IgnoredOp, ServerMsg as ServerMsg$1, BaseUserMeta } 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';
|
|
@@ -313,6 +313,32 @@ declare class Logger {
|
|
|
313
313
|
*/
|
|
314
314
|
|
|
315
315
|
type Pos = ReturnType<typeof asPos>;
|
|
316
|
+
type NodeTuple<T extends SerializedCrdt = SerializedCrdt> = [
|
|
317
|
+
id: string,
|
|
318
|
+
value: T
|
|
319
|
+
];
|
|
320
|
+
type NodeMap = {
|
|
321
|
+
size: number;
|
|
322
|
+
[Symbol.iterator]: () => IterableIterator<[id: string, node: SerializedCrdt]>;
|
|
323
|
+
clear: () => void;
|
|
324
|
+
delete: (key: string) => boolean;
|
|
325
|
+
get: (key: string) => SerializedCrdt | undefined;
|
|
326
|
+
has: (key: string) => boolean;
|
|
327
|
+
keys: () => Iterable<string>;
|
|
328
|
+
set(key: string, value: SerializedCrdt): void;
|
|
329
|
+
};
|
|
330
|
+
type NodeStream = Iterable<NodeTuple>;
|
|
331
|
+
/**
|
|
332
|
+
* Leased session data structure for server-side sessions with temporarily persisted presence.
|
|
333
|
+
*/
|
|
334
|
+
type LeasedSession = {
|
|
335
|
+
sessionId: string;
|
|
336
|
+
presence: Json;
|
|
337
|
+
updatedAt: number;
|
|
338
|
+
info: IUserInfo;
|
|
339
|
+
ttl: number;
|
|
340
|
+
actorId: number;
|
|
341
|
+
};
|
|
316
342
|
|
|
317
343
|
/**
|
|
318
344
|
* Copyright (c) Liveblocks Inc.
|
|
@@ -514,6 +540,28 @@ interface IStorageDriver {
|
|
|
514
540
|
* @private Test-only: never use in production.
|
|
515
541
|
*/
|
|
516
542
|
DANGEROUSLY_wipe_all_y_updates(): Awaitable<void>;
|
|
543
|
+
/**
|
|
544
|
+
* List all leased sessions.
|
|
545
|
+
* Note: Does NOT filter by expiration - returns all stored sessions.
|
|
546
|
+
* Expiration logic is handled at the Room.ts level.
|
|
547
|
+
*/
|
|
548
|
+
list_leased_sessions(): Awaitable<Iterable<[sessionId: string, session: LeasedSession]>>;
|
|
549
|
+
/**
|
|
550
|
+
* Get a specific leased session by session ID.
|
|
551
|
+
* Note: Does NOT check expiration - returns the stored session if it exists.
|
|
552
|
+
* Expiration logic is handled at the Room.ts level.
|
|
553
|
+
*/
|
|
554
|
+
get_leased_session(sessionId: string): Awaitable<LeasedSession | undefined>;
|
|
555
|
+
/**
|
|
556
|
+
* Create or update a leased session.
|
|
557
|
+
* Note: This is a full replace operation - the caller is responsible for
|
|
558
|
+
* merging/patching presence if needed.
|
|
559
|
+
*/
|
|
560
|
+
put_leased_session(session: LeasedSession): Awaitable<void>;
|
|
561
|
+
/**
|
|
562
|
+
* Delete a leased session by session ID.
|
|
563
|
+
*/
|
|
564
|
+
delete_leased_session(sessionId: string): Awaitable<void>;
|
|
517
565
|
}
|
|
518
566
|
|
|
519
567
|
/**
|
|
@@ -639,7 +687,7 @@ declare function snapshotToPlainLson_lazy(snapshot: IReadableSnapshot): StringGe
|
|
|
639
687
|
* Takes a copy of the provided nodes, so the snapshot is isolated from
|
|
640
688
|
* subsequent mutations to the source.
|
|
641
689
|
*/
|
|
642
|
-
declare function makeInMemorySnapshot(values: NodeMap | NodeStream): IReadableSnapshot;
|
|
690
|
+
declare function makeInMemorySnapshot(values: NodeMap$1 | NodeStream$1): IReadableSnapshot;
|
|
643
691
|
|
|
644
692
|
/**
|
|
645
693
|
* Copyright (c) Liveblocks Inc.
|
|
@@ -1055,6 +1103,7 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1055
1103
|
socket: IServerWebSocket;
|
|
1056
1104
|
lastActivity: Date;
|
|
1057
1105
|
}[]): void;
|
|
1106
|
+
private sendSessionStartMessages;
|
|
1058
1107
|
/**
|
|
1059
1108
|
* Registers a new BrowserSession into the Room server's session list, along with
|
|
1060
1109
|
* the socket connection to use for that BrowserSession, now that it is known.
|
|
@@ -1063,7 +1112,7 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1063
1112
|
* - Sends a ROOM_STATE message to the socket.
|
|
1064
1113
|
* - Broadcasts a USER_JOINED message to all other sessions in the room.
|
|
1065
1114
|
*/
|
|
1066
|
-
startBrowserSession(ticket: Ticket<SM, CM>, socket: IServerWebSocket, ctx?: C, defer?: (promise: Promise<void>) => void): void
|
|
1115
|
+
startBrowserSession(ticket: Ticket<SM, CM>, socket: IServerWebSocket, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1067
1116
|
/**
|
|
1068
1117
|
* Unregisters the BrowserSession for the given actor. Call this when the socket has
|
|
1069
1118
|
* been closed from the client's end.
|
|
@@ -1116,7 +1165,24 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1116
1165
|
getSession(sessionKey: SessionKey): BrowserSession<SM, CM> | undefined;
|
|
1117
1166
|
listSessions(): BrowserSession<SM, CM>[];
|
|
1118
1167
|
/**
|
|
1119
|
-
*
|
|
1168
|
+
* Upsert a leased session. Creates a new session if it doesn't exist (or is expired),
|
|
1169
|
+
* or updates an existing session with merged presence.
|
|
1170
|
+
*/
|
|
1171
|
+
upsertLeasedSession(sessionId: string, presence: JsonObject, ttl: number, info: IUserInfo, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1172
|
+
/**
|
|
1173
|
+
* List all server sessions. As a side effect, it will delete expired sessions.
|
|
1174
|
+
*/
|
|
1175
|
+
listLeasedSessions(ctx?: C, defer?: (promise: Promise<void>) => void): Promise<LeasedSession[]>;
|
|
1176
|
+
/**
|
|
1177
|
+
* Delete a server session and broadcast USER_LEFT to all sessions.
|
|
1178
|
+
*/
|
|
1179
|
+
deleteLeasedSession(session: LeasedSession, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1180
|
+
/**
|
|
1181
|
+
* Delete all server sessions and broadcast USER_LEFT to all sessions.
|
|
1182
|
+
*/
|
|
1183
|
+
deleteAllLeasedSessions(ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1184
|
+
/**
|
|
1185
|
+
* Will send the given ServerMsg through all Session, except the Session
|
|
1120
1186
|
* where the message originates from.
|
|
1121
1187
|
*/
|
|
1122
1188
|
sendToOthers(sender: SessionKey, serverMsg: ServerMsg | readonly ServerMsg[], ctx?: C, defer?: (promise: Promise<void>) => void): void;
|
|
@@ -1165,6 +1231,11 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1165
1231
|
* Concatenates multiple Uint8Arrays into a single Uint8Array.
|
|
1166
1232
|
*/
|
|
1167
1233
|
declare function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array;
|
|
1234
|
+
/**
|
|
1235
|
+
* Check if a leased session is expired.
|
|
1236
|
+
* Returns true if the current time is greater than or equal to updatedAt + ttl.
|
|
1237
|
+
*/
|
|
1238
|
+
declare function isLeasedSessionExpired(leasedSession: LeasedSession): boolean;
|
|
1168
1239
|
|
|
1169
1240
|
/**
|
|
1170
1241
|
* Copyright (c) Liveblocks Inc.
|
|
@@ -1392,6 +1463,7 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1392
1463
|
private _nodes;
|
|
1393
1464
|
private _metadb;
|
|
1394
1465
|
private _ydb;
|
|
1466
|
+
private _leasedSessions;
|
|
1395
1467
|
constructor(options?: {
|
|
1396
1468
|
initialActor?: number;
|
|
1397
1469
|
initialNodes?: Iterable<[string, SerializedCrdt]>;
|
|
@@ -1402,6 +1474,10 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1402
1474
|
get_meta(key: string): Promise<Json | undefined>;
|
|
1403
1475
|
put_meta(key: string, value: Json): Promise<void>;
|
|
1404
1476
|
delete_meta(key: string): Promise<void>;
|
|
1477
|
+
list_leased_sessions(): Promise<IterableIterator<[string, LeasedSession]>>;
|
|
1478
|
+
get_leased_session(sessionId: string): Promise<LeasedSession | undefined>;
|
|
1479
|
+
put_leased_session(session: LeasedSession): Promise<void>;
|
|
1480
|
+
delete_leased_session(sessionId: string): Promise<void>;
|
|
1405
1481
|
next_actor(): number;
|
|
1406
1482
|
iter_y_updates(docId: YDocId): Promise<IterableIterator<[string, Uint8Array]>>;
|
|
1407
1483
|
write_y_updates(docId: YDocId, key: string, data: Uint8Array): Promise<void>;
|
|
@@ -1411,4 +1487,4 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1411
1487
|
load_nodes_api(): IStorageDriverNodeAPI;
|
|
1412
1488
|
}
|
|
1413
1489
|
|
|
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 };
|
|
1490
|
+
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 LeasedSession, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, NestedMap, type NodeMap, type NodeStream, type NodeTuple, type Pos, type PreSerializedServerMsg, ProtocolVersion, ROOT_YDOC_ID, Room, type SessionKey, type Ticket, UniqueMap, type YDocId, type YUpdate, type YVector, ackIgnoredOp, clientMsgDecoder, concatUint8Arrays, guidDecoder, isLeasedSessionExpired, 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, JsonObject, Json, Brand, asPos, SerializedRootObject, SerializedChild, StorageNode,
|
|
1
|
+
import { DistributiveOmit, SetParentKeyOp, DeleteCrdtOp, ClientMsg as ClientMsg$1, JsonObject, Json, Brand, asPos, SerializedCrdt, IUserInfo, SerializedRootObject, SerializedChild, StorageNode, Awaitable, PlainLsonObject, NodeMap as NodeMap$1, NodeStream as NodeStream$1, ClientWireOp, IgnoredOp, ServerMsg as ServerMsg$1, BaseUserMeta } 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';
|
|
@@ -313,6 +313,32 @@ declare class Logger {
|
|
|
313
313
|
*/
|
|
314
314
|
|
|
315
315
|
type Pos = ReturnType<typeof asPos>;
|
|
316
|
+
type NodeTuple<T extends SerializedCrdt = SerializedCrdt> = [
|
|
317
|
+
id: string,
|
|
318
|
+
value: T
|
|
319
|
+
];
|
|
320
|
+
type NodeMap = {
|
|
321
|
+
size: number;
|
|
322
|
+
[Symbol.iterator]: () => IterableIterator<[id: string, node: SerializedCrdt]>;
|
|
323
|
+
clear: () => void;
|
|
324
|
+
delete: (key: string) => boolean;
|
|
325
|
+
get: (key: string) => SerializedCrdt | undefined;
|
|
326
|
+
has: (key: string) => boolean;
|
|
327
|
+
keys: () => Iterable<string>;
|
|
328
|
+
set(key: string, value: SerializedCrdt): void;
|
|
329
|
+
};
|
|
330
|
+
type NodeStream = Iterable<NodeTuple>;
|
|
331
|
+
/**
|
|
332
|
+
* Leased session data structure for server-side sessions with temporarily persisted presence.
|
|
333
|
+
*/
|
|
334
|
+
type LeasedSession = {
|
|
335
|
+
sessionId: string;
|
|
336
|
+
presence: Json;
|
|
337
|
+
updatedAt: number;
|
|
338
|
+
info: IUserInfo;
|
|
339
|
+
ttl: number;
|
|
340
|
+
actorId: number;
|
|
341
|
+
};
|
|
316
342
|
|
|
317
343
|
/**
|
|
318
344
|
* Copyright (c) Liveblocks Inc.
|
|
@@ -514,6 +540,28 @@ interface IStorageDriver {
|
|
|
514
540
|
* @private Test-only: never use in production.
|
|
515
541
|
*/
|
|
516
542
|
DANGEROUSLY_wipe_all_y_updates(): Awaitable<void>;
|
|
543
|
+
/**
|
|
544
|
+
* List all leased sessions.
|
|
545
|
+
* Note: Does NOT filter by expiration - returns all stored sessions.
|
|
546
|
+
* Expiration logic is handled at the Room.ts level.
|
|
547
|
+
*/
|
|
548
|
+
list_leased_sessions(): Awaitable<Iterable<[sessionId: string, session: LeasedSession]>>;
|
|
549
|
+
/**
|
|
550
|
+
* Get a specific leased session by session ID.
|
|
551
|
+
* Note: Does NOT check expiration - returns the stored session if it exists.
|
|
552
|
+
* Expiration logic is handled at the Room.ts level.
|
|
553
|
+
*/
|
|
554
|
+
get_leased_session(sessionId: string): Awaitable<LeasedSession | undefined>;
|
|
555
|
+
/**
|
|
556
|
+
* Create or update a leased session.
|
|
557
|
+
* Note: This is a full replace operation - the caller is responsible for
|
|
558
|
+
* merging/patching presence if needed.
|
|
559
|
+
*/
|
|
560
|
+
put_leased_session(session: LeasedSession): Awaitable<void>;
|
|
561
|
+
/**
|
|
562
|
+
* Delete a leased session by session ID.
|
|
563
|
+
*/
|
|
564
|
+
delete_leased_session(sessionId: string): Awaitable<void>;
|
|
517
565
|
}
|
|
518
566
|
|
|
519
567
|
/**
|
|
@@ -639,7 +687,7 @@ declare function snapshotToPlainLson_lazy(snapshot: IReadableSnapshot): StringGe
|
|
|
639
687
|
* Takes a copy of the provided nodes, so the snapshot is isolated from
|
|
640
688
|
* subsequent mutations to the source.
|
|
641
689
|
*/
|
|
642
|
-
declare function makeInMemorySnapshot(values: NodeMap | NodeStream): IReadableSnapshot;
|
|
690
|
+
declare function makeInMemorySnapshot(values: NodeMap$1 | NodeStream$1): IReadableSnapshot;
|
|
643
691
|
|
|
644
692
|
/**
|
|
645
693
|
* Copyright (c) Liveblocks Inc.
|
|
@@ -1055,6 +1103,7 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1055
1103
|
socket: IServerWebSocket;
|
|
1056
1104
|
lastActivity: Date;
|
|
1057
1105
|
}[]): void;
|
|
1106
|
+
private sendSessionStartMessages;
|
|
1058
1107
|
/**
|
|
1059
1108
|
* Registers a new BrowserSession into the Room server's session list, along with
|
|
1060
1109
|
* the socket connection to use for that BrowserSession, now that it is known.
|
|
@@ -1063,7 +1112,7 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1063
1112
|
* - Sends a ROOM_STATE message to the socket.
|
|
1064
1113
|
* - Broadcasts a USER_JOINED message to all other sessions in the room.
|
|
1065
1114
|
*/
|
|
1066
|
-
startBrowserSession(ticket: Ticket<SM, CM>, socket: IServerWebSocket, ctx?: C, defer?: (promise: Promise<void>) => void): void
|
|
1115
|
+
startBrowserSession(ticket: Ticket<SM, CM>, socket: IServerWebSocket, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1067
1116
|
/**
|
|
1068
1117
|
* Unregisters the BrowserSession for the given actor. Call this when the socket has
|
|
1069
1118
|
* been closed from the client's end.
|
|
@@ -1116,7 +1165,24 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1116
1165
|
getSession(sessionKey: SessionKey): BrowserSession<SM, CM> | undefined;
|
|
1117
1166
|
listSessions(): BrowserSession<SM, CM>[];
|
|
1118
1167
|
/**
|
|
1119
|
-
*
|
|
1168
|
+
* Upsert a leased session. Creates a new session if it doesn't exist (or is expired),
|
|
1169
|
+
* or updates an existing session with merged presence.
|
|
1170
|
+
*/
|
|
1171
|
+
upsertLeasedSession(sessionId: string, presence: JsonObject, ttl: number, info: IUserInfo, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1172
|
+
/**
|
|
1173
|
+
* List all server sessions. As a side effect, it will delete expired sessions.
|
|
1174
|
+
*/
|
|
1175
|
+
listLeasedSessions(ctx?: C, defer?: (promise: Promise<void>) => void): Promise<LeasedSession[]>;
|
|
1176
|
+
/**
|
|
1177
|
+
* Delete a server session and broadcast USER_LEFT to all sessions.
|
|
1178
|
+
*/
|
|
1179
|
+
deleteLeasedSession(session: LeasedSession, ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1180
|
+
/**
|
|
1181
|
+
* Delete all server sessions and broadcast USER_LEFT to all sessions.
|
|
1182
|
+
*/
|
|
1183
|
+
deleteAllLeasedSessions(ctx?: C, defer?: (promise: Promise<void>) => void): Promise<void>;
|
|
1184
|
+
/**
|
|
1185
|
+
* Will send the given ServerMsg through all Session, except the Session
|
|
1120
1186
|
* where the message originates from.
|
|
1121
1187
|
*/
|
|
1122
1188
|
sendToOthers(sender: SessionKey, serverMsg: ServerMsg | readonly ServerMsg[], ctx?: C, defer?: (promise: Promise<void>) => void): void;
|
|
@@ -1165,6 +1231,11 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1165
1231
|
* Concatenates multiple Uint8Arrays into a single Uint8Array.
|
|
1166
1232
|
*/
|
|
1167
1233
|
declare function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array;
|
|
1234
|
+
/**
|
|
1235
|
+
* Check if a leased session is expired.
|
|
1236
|
+
* Returns true if the current time is greater than or equal to updatedAt + ttl.
|
|
1237
|
+
*/
|
|
1238
|
+
declare function isLeasedSessionExpired(leasedSession: LeasedSession): boolean;
|
|
1168
1239
|
|
|
1169
1240
|
/**
|
|
1170
1241
|
* Copyright (c) Liveblocks Inc.
|
|
@@ -1392,6 +1463,7 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1392
1463
|
private _nodes;
|
|
1393
1464
|
private _metadb;
|
|
1394
1465
|
private _ydb;
|
|
1466
|
+
private _leasedSessions;
|
|
1395
1467
|
constructor(options?: {
|
|
1396
1468
|
initialActor?: number;
|
|
1397
1469
|
initialNodes?: Iterable<[string, SerializedCrdt]>;
|
|
@@ -1402,6 +1474,10 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1402
1474
|
get_meta(key: string): Promise<Json | undefined>;
|
|
1403
1475
|
put_meta(key: string, value: Json): Promise<void>;
|
|
1404
1476
|
delete_meta(key: string): Promise<void>;
|
|
1477
|
+
list_leased_sessions(): Promise<IterableIterator<[string, LeasedSession]>>;
|
|
1478
|
+
get_leased_session(sessionId: string): Promise<LeasedSession | undefined>;
|
|
1479
|
+
put_leased_session(session: LeasedSession): Promise<void>;
|
|
1480
|
+
delete_leased_session(sessionId: string): Promise<void>;
|
|
1405
1481
|
next_actor(): number;
|
|
1406
1482
|
iter_y_updates(docId: YDocId): Promise<IterableIterator<[string, Uint8Array]>>;
|
|
1407
1483
|
write_y_updates(docId: YDocId, key: string, data: Uint8Array): Promise<void>;
|
|
@@ -1411,4 +1487,4 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1411
1487
|
load_nodes_api(): IStorageDriverNodeAPI;
|
|
1412
1488
|
}
|
|
1413
1489
|
|
|
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 };
|
|
1490
|
+
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 LeasedSession, type LoadingState, LogLevel, LogTarget, Logger, type MetadataDB, NestedMap, type NodeMap, type NodeStream, type NodeTuple, type Pos, type PreSerializedServerMsg, ProtocolVersion, ROOT_YDOC_ID, Room, type SessionKey, type Ticket, UniqueMap, type YDocId, type YUpdate, type YVector, ackIgnoredOp, clientMsgDecoder, concatUint8Arrays, guidDecoder, isLeasedSessionExpired, 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 };
|
package/dist/index.js
CHANGED
|
@@ -850,9 +850,11 @@ var InMemoryDriver = class {
|
|
|
850
850
|
__publicField(this, "_nodes");
|
|
851
851
|
__publicField(this, "_metadb");
|
|
852
852
|
__publicField(this, "_ydb");
|
|
853
|
+
__publicField(this, "_leasedSessions");
|
|
853
854
|
this._nodes = /* @__PURE__ */ new Map();
|
|
854
855
|
this._metadb = /* @__PURE__ */ new Map();
|
|
855
856
|
this._ydb = /* @__PURE__ */ new Map();
|
|
857
|
+
this._leasedSessions = /* @__PURE__ */ new Map();
|
|
856
858
|
this._nextActor = options?.initialActor ?? -1;
|
|
857
859
|
for (const [key, value] of options?.initialNodes ?? []) {
|
|
858
860
|
this._nodes.set(key, value);
|
|
@@ -877,6 +879,18 @@ var InMemoryDriver = class {
|
|
|
877
879
|
async delete_meta(key) {
|
|
878
880
|
this._metadb.delete(key);
|
|
879
881
|
}
|
|
882
|
+
async list_leased_sessions() {
|
|
883
|
+
return this._leasedSessions.entries();
|
|
884
|
+
}
|
|
885
|
+
async get_leased_session(sessionId) {
|
|
886
|
+
return this._leasedSessions.get(sessionId);
|
|
887
|
+
}
|
|
888
|
+
async put_leased_session(session) {
|
|
889
|
+
this._leasedSessions.set(session.sessionId, session);
|
|
890
|
+
}
|
|
891
|
+
async delete_leased_session(sessionId) {
|
|
892
|
+
this._leasedSessions.delete(sessionId);
|
|
893
|
+
}
|
|
880
894
|
next_actor() {
|
|
881
895
|
return ++this._nextActor;
|
|
882
896
|
}
|
|
@@ -1681,6 +1695,10 @@ function makeRoomStateMsg(actor, nonce, scopes, users, publicMeta) {
|
|
|
1681
1695
|
meta: publicMeta ?? {}
|
|
1682
1696
|
};
|
|
1683
1697
|
}
|
|
1698
|
+
function isLeasedSessionExpired(leasedSession) {
|
|
1699
|
+
const now = Date.now();
|
|
1700
|
+
return now >= leasedSession.updatedAt + leasedSession.ttl;
|
|
1701
|
+
}
|
|
1684
1702
|
|
|
1685
1703
|
// src/Room.ts
|
|
1686
1704
|
var messagesDecoder = array2(clientMsgDecoder);
|
|
@@ -1993,6 +2011,50 @@ var Room = class {
|
|
|
1993
2011
|
newSession.markActive(lastActivity);
|
|
1994
2012
|
}
|
|
1995
2013
|
}
|
|
2014
|
+
async sendSessionStartMessages(newSession, ticket, ctx, defer = () => {
|
|
2015
|
+
throw new Error(
|
|
2016
|
+
"One of your hook handlers returned a promise, but no side effect collector was provided. Pass a `defer` callback to sendSessionStartMessages() to collect async side effects."
|
|
2017
|
+
);
|
|
2018
|
+
}) {
|
|
2019
|
+
const users = {};
|
|
2020
|
+
for (const session of this.otherSessions(ticket.sessionKey)) {
|
|
2021
|
+
users[session.actor] = {
|
|
2022
|
+
id: session.user.id,
|
|
2023
|
+
info: session.user.info,
|
|
2024
|
+
scopes: session.scopes
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
const leasedSessions = await this.listLeasedSessions(
|
|
2028
|
+
ctx,
|
|
2029
|
+
defer
|
|
2030
|
+
);
|
|
2031
|
+
for (const leasedSession of leasedSessions) {
|
|
2032
|
+
users[leasedSession.actorId] = {
|
|
2033
|
+
id: leasedSession.sessionId,
|
|
2034
|
+
info: leasedSession.info,
|
|
2035
|
+
scopes: []
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
newSession.send(
|
|
2039
|
+
makeRoomStateMsg(
|
|
2040
|
+
newSession.actor,
|
|
2041
|
+
ticket.sessionKey,
|
|
2042
|
+
// called "nonce" in the protocol
|
|
2043
|
+
newSession.scopes,
|
|
2044
|
+
users,
|
|
2045
|
+
ticket.publicMeta
|
|
2046
|
+
)
|
|
2047
|
+
);
|
|
2048
|
+
for (const leasedSession of leasedSessions) {
|
|
2049
|
+
newSession.send({
|
|
2050
|
+
type: ServerMsgCode2.UPDATE_PRESENCE,
|
|
2051
|
+
actor: leasedSession.actorId,
|
|
2052
|
+
targetActor: newSession.actor,
|
|
2053
|
+
// full presence to new user
|
|
2054
|
+
data: leasedSession.presence
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
1996
2058
|
/**
|
|
1997
2059
|
* Registers a new BrowserSession into the Room server's session list, along with
|
|
1998
2060
|
* the socket connection to use for that BrowserSession, now that it is known.
|
|
@@ -2001,7 +2063,7 @@ var Room = class {
|
|
|
2001
2063
|
* - Sends a ROOM_STATE message to the socket.
|
|
2002
2064
|
* - Broadcasts a USER_JOINED message to all other sessions in the room.
|
|
2003
2065
|
*/
|
|
2004
|
-
startBrowserSession(ticket, socket, ctx, defer = () => {
|
|
2066
|
+
async startBrowserSession(ticket, socket, ctx, defer = () => {
|
|
2005
2067
|
throw new Error(
|
|
2006
2068
|
"One of your hook handlers returned a promise, but no side effect collector was provided. Pass a `defer` callback to startBrowserSession() to collect async side effects."
|
|
2007
2069
|
);
|
|
@@ -2021,24 +2083,7 @@ var Room = class {
|
|
|
2021
2083
|
}
|
|
2022
2084
|
const newSession = new BrowserSession(ticket, socket, __privateGet(this, __debug2));
|
|
2023
2085
|
this.sessions.set(ticket.sessionKey, newSession);
|
|
2024
|
-
|
|
2025
|
-
for (const session of this.otherSessions(ticket.sessionKey)) {
|
|
2026
|
-
users[session.actor] = {
|
|
2027
|
-
id: session.user.id,
|
|
2028
|
-
info: session.user.info,
|
|
2029
|
-
scopes: session.scopes
|
|
2030
|
-
};
|
|
2031
|
-
}
|
|
2032
|
-
newSession.send(
|
|
2033
|
-
makeRoomStateMsg(
|
|
2034
|
-
newSession.actor,
|
|
2035
|
-
ticket.sessionKey,
|
|
2036
|
-
// called "nonce" in the protocol
|
|
2037
|
-
newSession.scopes,
|
|
2038
|
-
users,
|
|
2039
|
-
ticket.publicMeta
|
|
2040
|
-
)
|
|
2041
|
-
);
|
|
2086
|
+
await this.sendSessionStartMessages(newSession, ticket, ctx, defer);
|
|
2042
2087
|
this.sendToOthers(
|
|
2043
2088
|
ticket.sessionKey,
|
|
2044
2089
|
{
|
|
@@ -2191,7 +2236,136 @@ var Room = class {
|
|
|
2191
2236
|
return Array.from(this.sessions.values());
|
|
2192
2237
|
}
|
|
2193
2238
|
/**
|
|
2194
|
-
*
|
|
2239
|
+
* Upsert a leased session. Creates a new session if it doesn't exist (or is expired),
|
|
2240
|
+
* or updates an existing session with merged presence.
|
|
2241
|
+
*/
|
|
2242
|
+
async upsertLeasedSession(sessionId, presence, ttl, info, ctx, defer = () => {
|
|
2243
|
+
throw new Error(
|
|
2244
|
+
"One of your hook handlers returned a promise, but no side effect collector was provided. Pass a `defer` callback to upsertLeasedSession() to collect async side effects."
|
|
2245
|
+
);
|
|
2246
|
+
}) {
|
|
2247
|
+
const existingSession = await this.driver.get_leased_session(sessionId);
|
|
2248
|
+
const isExpired = existingSession !== void 0 && isLeasedSessionExpired(existingSession);
|
|
2249
|
+
if (isExpired) {
|
|
2250
|
+
await this.deleteLeasedSession(existingSession, ctx, defer);
|
|
2251
|
+
}
|
|
2252
|
+
if (existingSession === void 0 || isExpired) {
|
|
2253
|
+
const actorId = await this.getNextActor();
|
|
2254
|
+
const now = Date.now();
|
|
2255
|
+
const session = {
|
|
2256
|
+
sessionId,
|
|
2257
|
+
presence,
|
|
2258
|
+
updatedAt: now,
|
|
2259
|
+
info,
|
|
2260
|
+
ttl,
|
|
2261
|
+
actorId
|
|
2262
|
+
};
|
|
2263
|
+
await this.driver.put_leased_session(session);
|
|
2264
|
+
this.sendToAll(
|
|
2265
|
+
{
|
|
2266
|
+
type: ServerMsgCode2.USER_JOINED,
|
|
2267
|
+
actor: actorId,
|
|
2268
|
+
id: sessionId,
|
|
2269
|
+
info,
|
|
2270
|
+
scopes: []
|
|
2271
|
+
},
|
|
2272
|
+
ctx,
|
|
2273
|
+
defer
|
|
2274
|
+
);
|
|
2275
|
+
this.sendToAll(
|
|
2276
|
+
{
|
|
2277
|
+
type: ServerMsgCode2.UPDATE_PRESENCE,
|
|
2278
|
+
actor: actorId,
|
|
2279
|
+
data: presence,
|
|
2280
|
+
targetActor: 1
|
|
2281
|
+
},
|
|
2282
|
+
ctx,
|
|
2283
|
+
defer
|
|
2284
|
+
);
|
|
2285
|
+
} else {
|
|
2286
|
+
const mergedPresence = {
|
|
2287
|
+
...existingSession.presence,
|
|
2288
|
+
...presence
|
|
2289
|
+
};
|
|
2290
|
+
const updatedSession = {
|
|
2291
|
+
...existingSession,
|
|
2292
|
+
//info, UserInfo is immutable after creation
|
|
2293
|
+
presence: mergedPresence,
|
|
2294
|
+
updatedAt: Date.now(),
|
|
2295
|
+
ttl
|
|
2296
|
+
};
|
|
2297
|
+
await this.driver.put_leased_session(updatedSession);
|
|
2298
|
+
this.sendToAll(
|
|
2299
|
+
{
|
|
2300
|
+
type: ServerMsgCode2.UPDATE_PRESENCE,
|
|
2301
|
+
actor: existingSession.actorId,
|
|
2302
|
+
data: presence
|
|
2303
|
+
// Send only the patch, not the full merged presence
|
|
2304
|
+
// NO targetActor - this makes it a partial presence patch
|
|
2305
|
+
},
|
|
2306
|
+
ctx,
|
|
2307
|
+
defer
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* List all server sessions. As a side effect, it will delete expired sessions.
|
|
2313
|
+
*/
|
|
2314
|
+
async listLeasedSessions(ctx, defer = () => {
|
|
2315
|
+
throw new Error(
|
|
2316
|
+
"One of your hook handlers returned a promise, but no side effect collector was provided. Pass a `defer` callback to listLeasedSessions() to collect async side effects."
|
|
2317
|
+
);
|
|
2318
|
+
}) {
|
|
2319
|
+
await this.load(ctx);
|
|
2320
|
+
const sessions = await this.driver.list_leased_sessions();
|
|
2321
|
+
const validSessions = [];
|
|
2322
|
+
const toDelete = [];
|
|
2323
|
+
for (const [_, session] of sessions) {
|
|
2324
|
+
if (isLeasedSessionExpired(session)) {
|
|
2325
|
+
toDelete.push(session);
|
|
2326
|
+
} else {
|
|
2327
|
+
validSessions.push(session);
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
for (const session of toDelete) {
|
|
2331
|
+
await this.deleteLeasedSession(session, ctx, defer);
|
|
2332
|
+
}
|
|
2333
|
+
return validSessions;
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Delete a server session and broadcast USER_LEFT to all sessions.
|
|
2337
|
+
*/
|
|
2338
|
+
async deleteLeasedSession(session, ctx, defer = () => {
|
|
2339
|
+
throw new Error(
|
|
2340
|
+
"One of your hook handlers returned a promise, but no side effect collector was provided. Pass a `defer` callback to deleteLeasedSession() to collect async side effects."
|
|
2341
|
+
);
|
|
2342
|
+
}) {
|
|
2343
|
+
this.sendToAll(
|
|
2344
|
+
{
|
|
2345
|
+
type: ServerMsgCode2.USER_LEFT,
|
|
2346
|
+
actor: session.actorId
|
|
2347
|
+
},
|
|
2348
|
+
ctx,
|
|
2349
|
+
defer
|
|
2350
|
+
);
|
|
2351
|
+
await this.driver.delete_leased_session(session.sessionId);
|
|
2352
|
+
}
|
|
2353
|
+
/**
|
|
2354
|
+
* Delete all server sessions and broadcast USER_LEFT to all sessions.
|
|
2355
|
+
*/
|
|
2356
|
+
async deleteAllLeasedSessions(ctx, defer = () => {
|
|
2357
|
+
throw new Error(
|
|
2358
|
+
"One of your hook handlers returned a promise, but no side effect collector was provided. Pass a `defer` callback to deleteAllLeasedSessions() to collect async side effects."
|
|
2359
|
+
);
|
|
2360
|
+
}) {
|
|
2361
|
+
await this.load(ctx);
|
|
2362
|
+
const sessions = await this.driver.list_leased_sessions();
|
|
2363
|
+
for (const [_, session] of sessions) {
|
|
2364
|
+
await this.deleteLeasedSession(session, ctx, defer);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Will send the given ServerMsg through all Session, except the Session
|
|
2195
2369
|
* where the message originates from.
|
|
2196
2370
|
*/
|
|
2197
2371
|
sendToOthers(sender, serverMsg, ctx, defer = () => {
|
|
@@ -2555,6 +2729,7 @@ export {
|
|
|
2555
2729
|
clientMsgDecoder,
|
|
2556
2730
|
concatUint8Arrays,
|
|
2557
2731
|
guidDecoder,
|
|
2732
|
+
isLeasedSessionExpired,
|
|
2558
2733
|
jsonObjectYolo,
|
|
2559
2734
|
jsonYolo,
|
|
2560
2735
|
makeInMemorySnapshot,
|