@liveblocks/server 1.0.4 → 1.0.5
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.cjs
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 = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _35 => _35.initialActor]), () => ( -1));
|
|
857
859
|
for (const [key, value] of _nullishCoalesce(_optionalChain([options, 'optionalAccess', _36 => _36.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: _nullishCoalesce(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 = _decoders.array.call(void 0, 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: _core.ServerMsgCode.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: _core.ServerMsgCode.USER_JOINED,
|
|
2267
|
+
actor: actorId,
|
|
2268
|
+
id: sessionId,
|
|
2269
|
+
info,
|
|
2270
|
+
scopes: []
|
|
2271
|
+
},
|
|
2272
|
+
ctx,
|
|
2273
|
+
defer
|
|
2274
|
+
);
|
|
2275
|
+
this.sendToAll(
|
|
2276
|
+
{
|
|
2277
|
+
type: _core.ServerMsgCode.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: _core.ServerMsgCode.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: _core.ServerMsgCode.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 = () => {
|
|
@@ -2572,5 +2746,6 @@ __allowStreaming = new WeakMap();
|
|
|
2572
2746
|
|
|
2573
2747
|
|
|
2574
2748
|
|
|
2575
|
-
|
|
2749
|
+
|
|
2750
|
+
exports.BackendSession = BackendSession; exports.BrowserSession = BrowserSession; exports.ConsoleTarget = ConsoleTarget; exports.DefaultMap = DefaultMap; exports.InMemoryDriver = InMemoryDriver; exports.LogLevel = LogLevel; exports.LogTarget = LogTarget; exports.Logger = Logger; exports.NestedMap = NestedMap; exports.ProtocolVersion = ProtocolVersion; exports.ROOT_YDOC_ID = ROOT_YDOC_ID; exports.Room = Room; exports.UniqueMap = UniqueMap; exports.ackIgnoredOp = ackIgnoredOp; exports.clientMsgDecoder = clientMsgDecoder; exports.concatUint8Arrays = concatUint8Arrays; exports.guidDecoder = guidDecoder; exports.isLeasedSessionExpired = isLeasedSessionExpired; exports.jsonObjectYolo = jsonObjectYolo; exports.jsonYolo = jsonYolo; exports.makeInMemorySnapshot = makeInMemorySnapshot; exports.makeMetadataDB = makeMetadataDB; exports.plainLsonToNodeStream = plainLsonToNodeStream; exports.protocolVersionDecoder = protocolVersionDecoder; exports.quote = quote; exports.serializeServerMsg = serialize; exports.snapshotToLossyJson_eager = snapshotToLossyJson_eager; exports.snapshotToLossyJson_lazy = snapshotToLossyJson_lazy; exports.snapshotToNodeStream = snapshotToNodeStream; exports.snapshotToPlainLson_eager = snapshotToPlainLson_eager; exports.snapshotToPlainLson_lazy = snapshotToPlainLson_lazy; exports.test_only__Storage = Storage; exports.test_only__YjsStorage = YjsStorage; exports.transientClientMsgDecoder = transientClientMsgDecoder; exports.tryCatch = tryCatch;
|
|
2576
2751
|
//# sourceMappingURL=index.cjs.map
|