@liveblocks/server 1.0.15 → 1.1.1-pnpmtest1
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 +86 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +55 -79
- package/dist/index.js.map +1 -1
- package/package.json +34 -17
package/dist/index.d.cts
CHANGED
|
@@ -562,6 +562,11 @@ interface IStorageDriver {
|
|
|
562
562
|
* Delete a leased session by session ID.
|
|
563
563
|
*/
|
|
564
564
|
delete_leased_session(sessionId: string): Awaitable<void>;
|
|
565
|
+
/**
|
|
566
|
+
* Return the number of storage rows written since last call to this method,
|
|
567
|
+
* and reset the counter.
|
|
568
|
+
*/
|
|
569
|
+
takeRowsWritten?(): number;
|
|
565
570
|
}
|
|
566
571
|
|
|
567
572
|
/**
|
|
@@ -824,12 +829,12 @@ declare class Storage {
|
|
|
824
829
|
|
|
825
830
|
declare class YjsStorage {
|
|
826
831
|
private readonly driver;
|
|
832
|
+
private readonly updateCountThreshold;
|
|
827
833
|
private readonly doc;
|
|
828
|
-
private readonly lastUpdatesById;
|
|
829
834
|
private readonly lastSnapshotById;
|
|
830
|
-
private readonly keysById;
|
|
831
835
|
private readonly initPromisesById;
|
|
832
|
-
|
|
836
|
+
private readonly storedKeysById;
|
|
837
|
+
constructor(driver: IStorageDriver, updateCountThreshold?: number);
|
|
833
838
|
getYDoc(docId: YDocId): Promise<Y.Doc>;
|
|
834
839
|
/**
|
|
835
840
|
* If passed a state vector, an update with diff will be returned, if not the entire doc is returned.
|
|
@@ -846,7 +851,9 @@ declare class YjsStorage {
|
|
|
846
851
|
}): Promise<string | null>;
|
|
847
852
|
/**
|
|
848
853
|
* @param update base64 encoded uint8array
|
|
849
|
-
* @returns
|
|
854
|
+
* @returns { isUpdated: boolean; snapshotHash: string }
|
|
855
|
+
* isUpdated: true if the update had an effect on the YDoc
|
|
856
|
+
* snapshotHash: the hash of the new snapshot
|
|
850
857
|
*/
|
|
851
858
|
addYDocUpdate(logger: Logger, update: string | Uint8Array, guid?: Guid, isV2?: boolean): Promise<{
|
|
852
859
|
isUpdated: boolean;
|
|
@@ -860,15 +867,13 @@ declare class YjsStorage {
|
|
|
860
867
|
unload(): void;
|
|
861
868
|
private _getOrPutLastSnapshot;
|
|
862
869
|
private _putLastSnapshot;
|
|
863
|
-
|
|
864
|
-
* Given a record of updates, merge them and compress if savings are significant
|
|
865
|
-
*/
|
|
866
|
-
private _loadAndCompressYJSUpdates;
|
|
870
|
+
private _compactYJSUpdates;
|
|
867
871
|
private _loadYDocFromDurableStorage;
|
|
868
872
|
private findYSubdocByGuid;
|
|
869
873
|
private calculateSnapshotHash;
|
|
870
874
|
private getYSubdoc;
|
|
871
875
|
private handleYDocUpdate;
|
|
876
|
+
private shouldCompact;
|
|
872
877
|
}
|
|
873
878
|
|
|
874
879
|
/**
|
|
@@ -1102,6 +1107,8 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1102
1107
|
ticket: Ticket<SM, CM>;
|
|
1103
1108
|
socket: IServerWebSocket;
|
|
1104
1109
|
lastActivity: Date;
|
|
1110
|
+
/** Original session creation time (e.g. from persisted attachment). Required for correct session duration after hibernation. */
|
|
1111
|
+
createdAt?: Date;
|
|
1105
1112
|
}[]): void;
|
|
1106
1113
|
private sendSessionStartMessages;
|
|
1107
1114
|
/**
|
|
@@ -1478,6 +1485,7 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1478
1485
|
get_leased_session(sessionId: string): Promise<LeasedSession | undefined>;
|
|
1479
1486
|
put_leased_session(session: LeasedSession): Promise<void>;
|
|
1480
1487
|
delete_leased_session(sessionId: string): Promise<void>;
|
|
1488
|
+
takeRowsWritten(): number;
|
|
1481
1489
|
next_actor(): number;
|
|
1482
1490
|
iter_y_updates(docId: YDocId): Promise<IterableIterator<[string, Uint8Array]>>;
|
|
1483
1491
|
write_y_updates(docId: YDocId, key: string, data: Uint8Array): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -562,6 +562,11 @@ interface IStorageDriver {
|
|
|
562
562
|
* Delete a leased session by session ID.
|
|
563
563
|
*/
|
|
564
564
|
delete_leased_session(sessionId: string): Awaitable<void>;
|
|
565
|
+
/**
|
|
566
|
+
* Return the number of storage rows written since last call to this method,
|
|
567
|
+
* and reset the counter.
|
|
568
|
+
*/
|
|
569
|
+
takeRowsWritten?(): number;
|
|
565
570
|
}
|
|
566
571
|
|
|
567
572
|
/**
|
|
@@ -824,12 +829,12 @@ declare class Storage {
|
|
|
824
829
|
|
|
825
830
|
declare class YjsStorage {
|
|
826
831
|
private readonly driver;
|
|
832
|
+
private readonly updateCountThreshold;
|
|
827
833
|
private readonly doc;
|
|
828
|
-
private readonly lastUpdatesById;
|
|
829
834
|
private readonly lastSnapshotById;
|
|
830
|
-
private readonly keysById;
|
|
831
835
|
private readonly initPromisesById;
|
|
832
|
-
|
|
836
|
+
private readonly storedKeysById;
|
|
837
|
+
constructor(driver: IStorageDriver, updateCountThreshold?: number);
|
|
833
838
|
getYDoc(docId: YDocId): Promise<Y.Doc>;
|
|
834
839
|
/**
|
|
835
840
|
* If passed a state vector, an update with diff will be returned, if not the entire doc is returned.
|
|
@@ -846,7 +851,9 @@ declare class YjsStorage {
|
|
|
846
851
|
}): Promise<string | null>;
|
|
847
852
|
/**
|
|
848
853
|
* @param update base64 encoded uint8array
|
|
849
|
-
* @returns
|
|
854
|
+
* @returns { isUpdated: boolean; snapshotHash: string }
|
|
855
|
+
* isUpdated: true if the update had an effect on the YDoc
|
|
856
|
+
* snapshotHash: the hash of the new snapshot
|
|
850
857
|
*/
|
|
851
858
|
addYDocUpdate(logger: Logger, update: string | Uint8Array, guid?: Guid, isV2?: boolean): Promise<{
|
|
852
859
|
isUpdated: boolean;
|
|
@@ -860,15 +867,13 @@ declare class YjsStorage {
|
|
|
860
867
|
unload(): void;
|
|
861
868
|
private _getOrPutLastSnapshot;
|
|
862
869
|
private _putLastSnapshot;
|
|
863
|
-
|
|
864
|
-
* Given a record of updates, merge them and compress if savings are significant
|
|
865
|
-
*/
|
|
866
|
-
private _loadAndCompressYJSUpdates;
|
|
870
|
+
private _compactYJSUpdates;
|
|
867
871
|
private _loadYDocFromDurableStorage;
|
|
868
872
|
private findYSubdocByGuid;
|
|
869
873
|
private calculateSnapshotHash;
|
|
870
874
|
private getYSubdoc;
|
|
871
875
|
private handleYDocUpdate;
|
|
876
|
+
private shouldCompact;
|
|
872
877
|
}
|
|
873
878
|
|
|
874
879
|
/**
|
|
@@ -1102,6 +1107,8 @@ declare class Room<RM, SM, CM extends JsonObject, C = undefined> {
|
|
|
1102
1107
|
ticket: Ticket<SM, CM>;
|
|
1103
1108
|
socket: IServerWebSocket;
|
|
1104
1109
|
lastActivity: Date;
|
|
1110
|
+
/** Original session creation time (e.g. from persisted attachment). Required for correct session duration after hibernation. */
|
|
1111
|
+
createdAt?: Date;
|
|
1105
1112
|
}[]): void;
|
|
1106
1113
|
private sendSessionStartMessages;
|
|
1107
1114
|
/**
|
|
@@ -1478,6 +1485,7 @@ declare class InMemoryDriver implements IStorageDriver {
|
|
|
1478
1485
|
get_leased_session(sessionId: string): Promise<LeasedSession | undefined>;
|
|
1479
1486
|
put_leased_session(session: LeasedSession): Promise<void>;
|
|
1480
1487
|
delete_leased_session(sessionId: string): Promise<void>;
|
|
1488
|
+
takeRowsWritten(): number;
|
|
1481
1489
|
next_actor(): number;
|
|
1482
1490
|
iter_y_updates(docId: YDocId): Promise<IterableIterator<[string, Uint8Array]>>;
|
|
1483
1491
|
write_y_updates(docId: YDocId, key: string, data: Uint8Array): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -891,6 +891,9 @@ var InMemoryDriver = class {
|
|
|
891
891
|
async delete_leased_session(sessionId) {
|
|
892
892
|
this._leasedSessions.delete(sessionId);
|
|
893
893
|
}
|
|
894
|
+
takeRowsWritten() {
|
|
895
|
+
return 0;
|
|
896
|
+
}
|
|
894
897
|
next_actor() {
|
|
895
898
|
return ++this._nextActor;
|
|
896
899
|
}
|
|
@@ -1369,63 +1372,45 @@ var Storage = class {
|
|
|
1369
1372
|
};
|
|
1370
1373
|
|
|
1371
1374
|
// src/YjsStorage.ts
|
|
1372
|
-
import { DefaultMap as DefaultMap2 } from "@liveblocks/core";
|
|
1373
1375
|
import { Base64 } from "js-base64";
|
|
1374
1376
|
import { nanoid } from "nanoid";
|
|
1375
1377
|
import * as Y from "yjs";
|
|
1376
|
-
var
|
|
1378
|
+
var UPDATE_COUNT_THRESHOLD = 1e3;
|
|
1377
1379
|
var YjsStorage = class {
|
|
1378
|
-
constructor(driver) {
|
|
1380
|
+
constructor(driver, updateCountThreshold = UPDATE_COUNT_THRESHOLD) {
|
|
1379
1381
|
__publicField(this, "driver");
|
|
1382
|
+
__publicField(this, "updateCountThreshold");
|
|
1380
1383
|
__publicField(this, "doc", new Y.Doc());
|
|
1381
1384
|
// the root document
|
|
1382
|
-
__publicField(this, "lastUpdatesById", /* @__PURE__ */ new Map());
|
|
1383
1385
|
__publicField(this, "lastSnapshotById", /* @__PURE__ */ new Map());
|
|
1384
|
-
// Keeps track of which keys are loaded, so we can clean them up without calling `.list()`
|
|
1385
|
-
__publicField(this, "keysById", new DefaultMap2(
|
|
1386
|
-
() => /* @__PURE__ */ new Set()
|
|
1387
|
-
));
|
|
1388
1386
|
__publicField(this, "initPromisesById", /* @__PURE__ */ new Map());
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
}, 0);
|
|
1398
|
-
if (updates.length > 0) {
|
|
1399
|
-
const docKeys = Object.keys(docUpdates);
|
|
1400
|
-
this.keysById.set(docId, new Set(docKeys));
|
|
1401
|
-
const mergedUpdate = Y.mergeUpdates(updates);
|
|
1402
|
-
Y.applyUpdate(doc, mergedUpdate);
|
|
1403
|
-
const garbageCollectedUpdate = Y.encodeStateAsUpdate(doc);
|
|
1404
|
-
if (garbageCollectedUpdate.length < sizeOnDisk * (1 - SAVINGS_THRESHOLD)) {
|
|
1405
|
-
const newKey = nanoid();
|
|
1406
|
-
await this.driver.write_y_updates(
|
|
1407
|
-
docId,
|
|
1408
|
-
newKey,
|
|
1409
|
-
garbageCollectedUpdate
|
|
1410
|
-
);
|
|
1411
|
-
await this.driver.delete_y_updates(docId, docKeys);
|
|
1412
|
-
this.keysById.set(docId, /* @__PURE__ */ new Set([newKey]));
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1387
|
+
__publicField(this, "storedKeysById", /* @__PURE__ */ new Map());
|
|
1388
|
+
// compact the updates into a single update and write it to the durable storage
|
|
1389
|
+
__publicField(this, "_compactYJSUpdates", async (doc, docId, storedKeys) => {
|
|
1390
|
+
const compactedUpdate = Y.encodeStateAsUpdate(doc);
|
|
1391
|
+
const newKey = nanoid();
|
|
1392
|
+
await this.driver.write_y_updates(docId, newKey, compactedUpdate);
|
|
1393
|
+
await this.driver.delete_y_updates(docId, storedKeys);
|
|
1394
|
+
this.storedKeysById.set(docId, [newKey]);
|
|
1415
1395
|
});
|
|
1416
1396
|
__publicField(this, "_loadYDocFromDurableStorage", async (doc, docId) => {
|
|
1417
1397
|
const docUpdates = Object.fromEntries(
|
|
1418
1398
|
await this.driver.iter_y_updates(docId)
|
|
1419
1399
|
);
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1400
|
+
const updates = Object.values(docUpdates);
|
|
1401
|
+
const newupdate = Y.mergeUpdates(updates);
|
|
1402
|
+
const storedKeys = Object.keys(docUpdates);
|
|
1403
|
+
Y.applyUpdate(doc, newupdate);
|
|
1404
|
+
if (this.shouldCompact(storedKeys)) {
|
|
1405
|
+
await this._compactYJSUpdates(doc, docId, storedKeys);
|
|
1406
|
+
} else {
|
|
1407
|
+
this.storedKeysById.set(docId, storedKeys);
|
|
1408
|
+
}
|
|
1425
1409
|
doc.emit("load", [doc]);
|
|
1426
1410
|
return doc;
|
|
1427
1411
|
});
|
|
1428
1412
|
this.driver = driver;
|
|
1413
|
+
this.updateCountThreshold = updateCountThreshold;
|
|
1429
1414
|
this.doc.on("subdocs", ({ removed }) => {
|
|
1430
1415
|
removed.forEach((subdoc) => {
|
|
1431
1416
|
subdoc.destroy();
|
|
@@ -1490,7 +1475,9 @@ var YjsStorage = class {
|
|
|
1490
1475
|
}
|
|
1491
1476
|
/**
|
|
1492
1477
|
* @param update base64 encoded uint8array
|
|
1493
|
-
* @returns
|
|
1478
|
+
* @returns { isUpdated: boolean; snapshotHash: string }
|
|
1479
|
+
* isUpdated: true if the update had an effect on the YDoc
|
|
1480
|
+
* snapshotHash: the hash of the new snapshot
|
|
1494
1481
|
*/
|
|
1495
1482
|
async addYDocUpdate(logger, update, guid, isV2) {
|
|
1496
1483
|
const doc = guid !== void 0 ? await this.getYSubdoc(guid) : this.doc;
|
|
@@ -1505,7 +1492,7 @@ var YjsStorage = class {
|
|
|
1505
1492
|
const afterSnapshot = this._putLastSnapshot(doc);
|
|
1506
1493
|
const updated = !Y.equalSnapshots(beforeSnapshot, afterSnapshot);
|
|
1507
1494
|
if (updated) {
|
|
1508
|
-
await this.handleYDocUpdate(doc);
|
|
1495
|
+
await this.handleYDocUpdate(doc, updateAsU8, isV2);
|
|
1509
1496
|
}
|
|
1510
1497
|
return {
|
|
1511
1498
|
isUpdated: updated,
|
|
@@ -1583,43 +1570,27 @@ var YjsStorage = class {
|
|
|
1583
1570
|
return subdoc;
|
|
1584
1571
|
}
|
|
1585
1572
|
// When the YJS doc changes, update it in durable storage
|
|
1586
|
-
async handleYDocUpdate(doc) {
|
|
1573
|
+
async handleYDocUpdate(doc, update, isV2) {
|
|
1574
|
+
const v1update = isV2 ? Y.convertUpdateFormatV2ToV1(update) : update;
|
|
1587
1575
|
const docId = doc.guid === this.doc.guid ? ROOT_YDOC_ID : doc.guid;
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
doc,
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
const
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
docId,
|
|
1598
|
-
newKey,
|
|
1599
|
-
Y.encodeStateAsUpdate(doc)
|
|
1600
|
-
);
|
|
1601
|
-
await this.driver.delete_y_updates(
|
|
1602
|
-
docId,
|
|
1603
|
-
Array.from(this.keysById.getOrCreate(docId))
|
|
1604
|
-
);
|
|
1605
|
-
this.keysById.set(docId, /* @__PURE__ */ new Set([newKey]));
|
|
1606
|
-
this.lastUpdatesById.set(docId, {
|
|
1607
|
-
currentKey: nanoid(),
|
|
1608
|
-
// start writing to a new key
|
|
1609
|
-
lastVector: Y.encodeStateVector(doc)
|
|
1610
|
-
});
|
|
1576
|
+
const storedKeys = this.storedKeysById.get(docId);
|
|
1577
|
+
if (this.shouldCompact(storedKeys)) {
|
|
1578
|
+
await this._compactYJSUpdates(doc, docId, storedKeys || []);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
const newKey = nanoid();
|
|
1582
|
+
await this.driver.write_y_updates(docId, newKey, v1update);
|
|
1583
|
+
if (!storedKeys) {
|
|
1584
|
+
this.storedKeysById.set(docId, [newKey]);
|
|
1611
1585
|
} else {
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
const currentKeys = this.keysById.getOrCreate(docId);
|
|
1619
|
-
for (const key of keys) {
|
|
1620
|
-
currentKeys.add(key);
|
|
1621
|
-
}
|
|
1586
|
+
storedKeys.push(newKey);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
shouldCompact(storedKeys) {
|
|
1590
|
+
if (!storedKeys) {
|
|
1591
|
+
return false;
|
|
1622
1592
|
}
|
|
1593
|
+
return storedKeys.length >= this.updateCountThreshold;
|
|
1623
1594
|
}
|
|
1624
1595
|
};
|
|
1625
1596
|
|
|
@@ -1733,7 +1704,7 @@ function stripOpId(op2) {
|
|
|
1733
1704
|
var __socket, __debug, __lastActiveAt, __hasNotifiedClientStorageUpdateError;
|
|
1734
1705
|
var BrowserSession = class {
|
|
1735
1706
|
/** @internal - Never create a BrowserSession instance manually. Use the room.startBrowserSession() API instead. */
|
|
1736
|
-
constructor(ticket, socket, debug) {
|
|
1707
|
+
constructor(ticket, socket, debug, createdAt) {
|
|
1737
1708
|
// ^^ User-defined Session Metadata
|
|
1738
1709
|
// ^^ User-defined Client Metadata (sent to client in ROOM_STATE)
|
|
1739
1710
|
__publicField(this, "version");
|
|
@@ -1762,7 +1733,7 @@ var BrowserSession = class {
|
|
|
1762
1733
|
this.publicMeta = ticket.publicMeta;
|
|
1763
1734
|
__privateSet(this, __socket, socket);
|
|
1764
1735
|
__privateSet(this, __debug, debug);
|
|
1765
|
-
const now = /* @__PURE__ */ new Date();
|
|
1736
|
+
const now = createdAt ?? /* @__PURE__ */ new Date();
|
|
1766
1737
|
this.createdAt = now;
|
|
1767
1738
|
__privateSet(this, __lastActiveAt, now);
|
|
1768
1739
|
__privateSet(this, __hasNotifiedClientStorageUpdateError, false);
|
|
@@ -2005,8 +1976,13 @@ var Room = class {
|
|
|
2005
1976
|
if (this.sessions.size > 0) {
|
|
2006
1977
|
throw new Error("This API can only be called before any sessions exist");
|
|
2007
1978
|
}
|
|
2008
|
-
for (const { ticket, socket, lastActivity } of sessions) {
|
|
2009
|
-
const newSession = new BrowserSession(
|
|
1979
|
+
for (const { ticket, socket, lastActivity, createdAt } of sessions) {
|
|
1980
|
+
const newSession = new BrowserSession(
|
|
1981
|
+
ticket,
|
|
1982
|
+
socket,
|
|
1983
|
+
__privateGet(this, __debug2),
|
|
1984
|
+
createdAt
|
|
1985
|
+
);
|
|
2010
1986
|
this.sessions.set(ticket.sessionKey, newSession);
|
|
2011
1987
|
newSession.markActive(lastActivity);
|
|
2012
1988
|
}
|