@liveblocks/server 1.0.14 → 1.1.0
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/README.md +10 -4
- package/dist/index.cjs +74 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +43 -75
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -824,12 +824,12 @@ declare class Storage {
|
|
|
824
824
|
|
|
825
825
|
declare class YjsStorage {
|
|
826
826
|
private readonly driver;
|
|
827
|
+
private readonly updateCountThreshold;
|
|
827
828
|
private readonly doc;
|
|
828
|
-
private readonly lastUpdatesById;
|
|
829
829
|
private readonly lastSnapshotById;
|
|
830
|
-
private readonly keysById;
|
|
831
830
|
private readonly initPromisesById;
|
|
832
|
-
|
|
831
|
+
private readonly storedKeysById;
|
|
832
|
+
constructor(driver: IStorageDriver, updateCountThreshold?: number);
|
|
833
833
|
getYDoc(docId: YDocId): Promise<Y.Doc>;
|
|
834
834
|
/**
|
|
835
835
|
* If passed a state vector, an update with diff will be returned, if not the entire doc is returned.
|
|
@@ -846,7 +846,9 @@ declare class YjsStorage {
|
|
|
846
846
|
}): Promise<string | null>;
|
|
847
847
|
/**
|
|
848
848
|
* @param update base64 encoded uint8array
|
|
849
|
-
* @returns
|
|
849
|
+
* @returns { isUpdated: boolean; snapshotHash: string }
|
|
850
|
+
* isUpdated: true if the update had an effect on the YDoc
|
|
851
|
+
* snapshotHash: the hash of the new snapshot
|
|
850
852
|
*/
|
|
851
853
|
addYDocUpdate(logger: Logger, update: string | Uint8Array, guid?: Guid, isV2?: boolean): Promise<{
|
|
852
854
|
isUpdated: boolean;
|
|
@@ -860,15 +862,13 @@ declare class YjsStorage {
|
|
|
860
862
|
unload(): void;
|
|
861
863
|
private _getOrPutLastSnapshot;
|
|
862
864
|
private _putLastSnapshot;
|
|
863
|
-
|
|
864
|
-
* Given a record of updates, merge them and compress if savings are significant
|
|
865
|
-
*/
|
|
866
|
-
private _loadAndCompressYJSUpdates;
|
|
865
|
+
private _compactYJSUpdates;
|
|
867
866
|
private _loadYDocFromDurableStorage;
|
|
868
867
|
private findYSubdocByGuid;
|
|
869
868
|
private calculateSnapshotHash;
|
|
870
869
|
private getYSubdoc;
|
|
871
870
|
private handleYDocUpdate;
|
|
871
|
+
private shouldCompact;
|
|
872
872
|
}
|
|
873
873
|
|
|
874
874
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -824,12 +824,12 @@ declare class Storage {
|
|
|
824
824
|
|
|
825
825
|
declare class YjsStorage {
|
|
826
826
|
private readonly driver;
|
|
827
|
+
private readonly updateCountThreshold;
|
|
827
828
|
private readonly doc;
|
|
828
|
-
private readonly lastUpdatesById;
|
|
829
829
|
private readonly lastSnapshotById;
|
|
830
|
-
private readonly keysById;
|
|
831
830
|
private readonly initPromisesById;
|
|
832
|
-
|
|
831
|
+
private readonly storedKeysById;
|
|
832
|
+
constructor(driver: IStorageDriver, updateCountThreshold?: number);
|
|
833
833
|
getYDoc(docId: YDocId): Promise<Y.Doc>;
|
|
834
834
|
/**
|
|
835
835
|
* If passed a state vector, an update with diff will be returned, if not the entire doc is returned.
|
|
@@ -846,7 +846,9 @@ declare class YjsStorage {
|
|
|
846
846
|
}): Promise<string | null>;
|
|
847
847
|
/**
|
|
848
848
|
* @param update base64 encoded uint8array
|
|
849
|
-
* @returns
|
|
849
|
+
* @returns { isUpdated: boolean; snapshotHash: string }
|
|
850
|
+
* isUpdated: true if the update had an effect on the YDoc
|
|
851
|
+
* snapshotHash: the hash of the new snapshot
|
|
850
852
|
*/
|
|
851
853
|
addYDocUpdate(logger: Logger, update: string | Uint8Array, guid?: Guid, isV2?: boolean): Promise<{
|
|
852
854
|
isUpdated: boolean;
|
|
@@ -860,15 +862,13 @@ declare class YjsStorage {
|
|
|
860
862
|
unload(): void;
|
|
861
863
|
private _getOrPutLastSnapshot;
|
|
862
864
|
private _putLastSnapshot;
|
|
863
|
-
|
|
864
|
-
* Given a record of updates, merge them and compress if savings are significant
|
|
865
|
-
*/
|
|
866
|
-
private _loadAndCompressYJSUpdates;
|
|
865
|
+
private _compactYJSUpdates;
|
|
867
866
|
private _loadYDocFromDurableStorage;
|
|
868
867
|
private findYSubdocByGuid;
|
|
869
868
|
private calculateSnapshotHash;
|
|
870
869
|
private getYSubdoc;
|
|
871
870
|
private handleYDocUpdate;
|
|
871
|
+
private shouldCompact;
|
|
872
872
|
}
|
|
873
873
|
|
|
874
874
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1369,63 +1369,45 @@ var Storage = class {
|
|
|
1369
1369
|
};
|
|
1370
1370
|
|
|
1371
1371
|
// src/YjsStorage.ts
|
|
1372
|
-
import { DefaultMap as DefaultMap2 } from "@liveblocks/core";
|
|
1373
1372
|
import { Base64 } from "js-base64";
|
|
1374
1373
|
import { nanoid } from "nanoid";
|
|
1375
1374
|
import * as Y from "yjs";
|
|
1376
|
-
var
|
|
1375
|
+
var UPDATE_COUNT_THRESHOLD = 1e3;
|
|
1377
1376
|
var YjsStorage = class {
|
|
1378
|
-
constructor(driver) {
|
|
1377
|
+
constructor(driver, updateCountThreshold = UPDATE_COUNT_THRESHOLD) {
|
|
1379
1378
|
__publicField(this, "driver");
|
|
1379
|
+
__publicField(this, "updateCountThreshold");
|
|
1380
1380
|
__publicField(this, "doc", new Y.Doc());
|
|
1381
1381
|
// the root document
|
|
1382
|
-
__publicField(this, "lastUpdatesById", /* @__PURE__ */ new Map());
|
|
1383
1382
|
__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
1383
|
__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
|
-
}
|
|
1384
|
+
__publicField(this, "storedKeysById", /* @__PURE__ */ new Map());
|
|
1385
|
+
// compact the updates into a single update and write it to the durable storage
|
|
1386
|
+
__publicField(this, "_compactYJSUpdates", async (doc, docId, storedKeys) => {
|
|
1387
|
+
const compactedUpdate = Y.encodeStateAsUpdate(doc);
|
|
1388
|
+
const newKey = nanoid();
|
|
1389
|
+
await this.driver.write_y_updates(docId, newKey, compactedUpdate);
|
|
1390
|
+
await this.driver.delete_y_updates(docId, storedKeys);
|
|
1391
|
+
this.storedKeysById.set(docId, [newKey]);
|
|
1415
1392
|
});
|
|
1416
1393
|
__publicField(this, "_loadYDocFromDurableStorage", async (doc, docId) => {
|
|
1417
1394
|
const docUpdates = Object.fromEntries(
|
|
1418
1395
|
await this.driver.iter_y_updates(docId)
|
|
1419
1396
|
);
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1397
|
+
const updates = Object.values(docUpdates);
|
|
1398
|
+
const newupdate = Y.mergeUpdates(updates);
|
|
1399
|
+
const storedKeys = Object.keys(docUpdates);
|
|
1400
|
+
Y.applyUpdate(doc, newupdate);
|
|
1401
|
+
if (this.shouldCompact(storedKeys)) {
|
|
1402
|
+
await this._compactYJSUpdates(doc, docId, storedKeys);
|
|
1403
|
+
} else {
|
|
1404
|
+
this.storedKeysById.set(docId, storedKeys);
|
|
1405
|
+
}
|
|
1425
1406
|
doc.emit("load", [doc]);
|
|
1426
1407
|
return doc;
|
|
1427
1408
|
});
|
|
1428
1409
|
this.driver = driver;
|
|
1410
|
+
this.updateCountThreshold = updateCountThreshold;
|
|
1429
1411
|
this.doc.on("subdocs", ({ removed }) => {
|
|
1430
1412
|
removed.forEach((subdoc) => {
|
|
1431
1413
|
subdoc.destroy();
|
|
@@ -1490,7 +1472,9 @@ var YjsStorage = class {
|
|
|
1490
1472
|
}
|
|
1491
1473
|
/**
|
|
1492
1474
|
* @param update base64 encoded uint8array
|
|
1493
|
-
* @returns
|
|
1475
|
+
* @returns { isUpdated: boolean; snapshotHash: string }
|
|
1476
|
+
* isUpdated: true if the update had an effect on the YDoc
|
|
1477
|
+
* snapshotHash: the hash of the new snapshot
|
|
1494
1478
|
*/
|
|
1495
1479
|
async addYDocUpdate(logger, update, guid, isV2) {
|
|
1496
1480
|
const doc = guid !== void 0 ? await this.getYSubdoc(guid) : this.doc;
|
|
@@ -1505,7 +1489,7 @@ var YjsStorage = class {
|
|
|
1505
1489
|
const afterSnapshot = this._putLastSnapshot(doc);
|
|
1506
1490
|
const updated = !Y.equalSnapshots(beforeSnapshot, afterSnapshot);
|
|
1507
1491
|
if (updated) {
|
|
1508
|
-
await this.handleYDocUpdate(doc);
|
|
1492
|
+
await this.handleYDocUpdate(doc, updateAsU8, isV2);
|
|
1509
1493
|
}
|
|
1510
1494
|
return {
|
|
1511
1495
|
isUpdated: updated,
|
|
@@ -1583,43 +1567,27 @@ var YjsStorage = class {
|
|
|
1583
1567
|
return subdoc;
|
|
1584
1568
|
}
|
|
1585
1569
|
// When the YJS doc changes, update it in durable storage
|
|
1586
|
-
async handleYDocUpdate(doc) {
|
|
1570
|
+
async handleYDocUpdate(doc, update, isV2) {
|
|
1571
|
+
const v1update = isV2 ? Y.convertUpdateFormatV2ToV1(update) : update;
|
|
1587
1572
|
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
|
-
});
|
|
1573
|
+
const storedKeys = this.storedKeysById.get(docId);
|
|
1574
|
+
if (this.shouldCompact(storedKeys)) {
|
|
1575
|
+
await this._compactYJSUpdates(doc, docId, storedKeys || []);
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
const newKey = nanoid();
|
|
1579
|
+
await this.driver.write_y_updates(docId, newKey, v1update);
|
|
1580
|
+
if (!storedKeys) {
|
|
1581
|
+
this.storedKeysById.set(docId, [newKey]);
|
|
1611
1582
|
} 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
|
-
}
|
|
1583
|
+
storedKeys.push(newKey);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
shouldCompact(storedKeys) {
|
|
1587
|
+
if (!storedKeys) {
|
|
1588
|
+
return false;
|
|
1622
1589
|
}
|
|
1590
|
+
return storedKeys.length >= this.updateCountThreshold;
|
|
1623
1591
|
}
|
|
1624
1592
|
};
|
|
1625
1593
|
|