@liveblocks/server 1.5.0 → 1.6.1
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 +364 -363
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +266 -261
- package/dist/index.d.ts +266 -261
- package/dist/index.js +339 -338
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -812,11 +812,11 @@ function makeInMemorySnapshot(values) {
|
|
|
812
812
|
|
|
813
813
|
// src/MetadataDB.ts
|
|
814
814
|
function makeMetadataDB(driver) {
|
|
815
|
-
|
|
815
|
+
function get(a1, a2) {
|
|
816
816
|
if (a2 === void 0) {
|
|
817
|
-
return
|
|
817
|
+
return driver.get_meta(a1);
|
|
818
818
|
} else {
|
|
819
|
-
return a1.value(
|
|
819
|
+
return a1.value(driver.get_meta(a2));
|
|
820
820
|
}
|
|
821
821
|
}
|
|
822
822
|
return {
|
|
@@ -1039,6 +1039,7 @@ var InMemoryDriver = class {
|
|
|
1039
1039
|
constructor(options) {
|
|
1040
1040
|
__publicField(this, "_nextActor");
|
|
1041
1041
|
__publicField(this, "_nodes");
|
|
1042
|
+
__publicField(this, "_nodesApi");
|
|
1042
1043
|
__publicField(this, "_metadb");
|
|
1043
1044
|
__publicField(this, "_ydb");
|
|
1044
1045
|
__publicField(this, "_leasedSessions");
|
|
@@ -1058,32 +1059,36 @@ var InMemoryDriver = class {
|
|
|
1058
1059
|
raw_iter_nodes() {
|
|
1059
1060
|
return this._nodes[Symbol.iterator]();
|
|
1060
1061
|
}
|
|
1062
|
+
reinitialize() {
|
|
1063
|
+
this._nodesApi = void 0;
|
|
1064
|
+
}
|
|
1061
1065
|
/** Deletes all nodes and replaces them with the given document. */
|
|
1062
1066
|
DANGEROUSLY_reset_nodes(doc) {
|
|
1067
|
+
this.reinitialize();
|
|
1063
1068
|
this._nodes.clear();
|
|
1064
1069
|
for (const [id, node] of plainLsonToNodeStream(doc)) {
|
|
1065
1070
|
this._nodes.set(id, node);
|
|
1066
1071
|
}
|
|
1067
1072
|
}
|
|
1068
|
-
|
|
1073
|
+
get_meta(key) {
|
|
1069
1074
|
return this._metadb.get(key);
|
|
1070
1075
|
}
|
|
1071
|
-
|
|
1076
|
+
put_meta(key, value) {
|
|
1072
1077
|
this._metadb.set(key, value);
|
|
1073
1078
|
}
|
|
1074
|
-
|
|
1079
|
+
delete_meta(key) {
|
|
1075
1080
|
this._metadb.delete(key);
|
|
1076
1081
|
}
|
|
1077
|
-
|
|
1082
|
+
list_leased_sessions() {
|
|
1078
1083
|
return this._leasedSessions.entries();
|
|
1079
1084
|
}
|
|
1080
|
-
|
|
1085
|
+
get_leased_session(sessionId) {
|
|
1081
1086
|
return this._leasedSessions.get(sessionId);
|
|
1082
1087
|
}
|
|
1083
|
-
|
|
1088
|
+
put_leased_session(session) {
|
|
1084
1089
|
this._leasedSessions.set(session.sessionId, session);
|
|
1085
1090
|
}
|
|
1086
|
-
|
|
1091
|
+
delete_leased_session(sessionId) {
|
|
1087
1092
|
this._leasedSessions.delete(sessionId);
|
|
1088
1093
|
}
|
|
1089
1094
|
takeRowsWritten() {
|
|
@@ -1092,7 +1097,7 @@ var InMemoryDriver = class {
|
|
|
1092
1097
|
// ---------------------------------------------------------------------------
|
|
1093
1098
|
// Feed APIs
|
|
1094
1099
|
// ---------------------------------------------------------------------------
|
|
1095
|
-
|
|
1100
|
+
list_feeds(options) {
|
|
1096
1101
|
const limit = Math.min(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _37 => _37.limit]), () => ( 20)), 100);
|
|
1097
1102
|
const since = _optionalChain([options, 'optionalAccess', _38 => _38.since]);
|
|
1098
1103
|
const cursor = _optionalChain([options, 'optionalAccess', _39 => _39.cursor]);
|
|
@@ -1148,14 +1153,14 @@ var InMemoryDriver = class {
|
|
|
1148
1153
|
}
|
|
1149
1154
|
return { feeds, nextCursor };
|
|
1150
1155
|
}
|
|
1151
|
-
|
|
1156
|
+
get_feed(feedId) {
|
|
1152
1157
|
const feed = this._feeds.get(feedId);
|
|
1153
1158
|
if (feed === void 0) {
|
|
1154
1159
|
return void 0;
|
|
1155
1160
|
}
|
|
1156
1161
|
return feed;
|
|
1157
1162
|
}
|
|
1158
|
-
|
|
1163
|
+
create_feed(feed) {
|
|
1159
1164
|
if (this._feeds.has(feed.feedId)) {
|
|
1160
1165
|
throw new Error(`Feed ${feed.feedId} already exists`);
|
|
1161
1166
|
}
|
|
@@ -1166,7 +1171,7 @@ var InMemoryDriver = class {
|
|
|
1166
1171
|
updatedAt: feed.updatedAt
|
|
1167
1172
|
});
|
|
1168
1173
|
}
|
|
1169
|
-
|
|
1174
|
+
update_feed_metadata(feedId, metadata) {
|
|
1170
1175
|
const existing = this._feeds.get(feedId);
|
|
1171
1176
|
if (existing === void 0) {
|
|
1172
1177
|
throw new Error(`Feed ${feedId} not found`);
|
|
@@ -1176,7 +1181,7 @@ var InMemoryDriver = class {
|
|
|
1176
1181
|
metadata
|
|
1177
1182
|
});
|
|
1178
1183
|
}
|
|
1179
|
-
|
|
1184
|
+
delete_feed(feedId) {
|
|
1180
1185
|
const messageKeys = [];
|
|
1181
1186
|
for (const [key] of this._feedMessages.entries()) {
|
|
1182
1187
|
if (key.startsWith(`${feedId}:`)) {
|
|
@@ -1188,7 +1193,7 @@ var InMemoryDriver = class {
|
|
|
1188
1193
|
}
|
|
1189
1194
|
this._feeds.delete(feedId);
|
|
1190
1195
|
}
|
|
1191
|
-
|
|
1196
|
+
list_feed_messages(feedId, options) {
|
|
1192
1197
|
const limit = Math.min(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _41 => _41.limit]), () => ( 20)), 100);
|
|
1193
1198
|
const since = _optionalChain([options, 'optionalAccess', _42 => _42.since]);
|
|
1194
1199
|
const cursor = _optionalChain([options, 'optionalAccess', _43 => _43.cursor]);
|
|
@@ -1235,14 +1240,14 @@ var InMemoryDriver = class {
|
|
|
1235
1240
|
}
|
|
1236
1241
|
return { messages, nextCursor };
|
|
1237
1242
|
}
|
|
1238
|
-
|
|
1243
|
+
add_feed_message(feedId, message) {
|
|
1239
1244
|
const feed = this._feeds.get(feedId);
|
|
1240
1245
|
if (feed === void 0) {
|
|
1241
1246
|
throw new Error(`Feed ${feedId} not found`);
|
|
1242
1247
|
}
|
|
1243
1248
|
this._feedMessages.set(`${feedId}:${message.id}`, message);
|
|
1244
1249
|
}
|
|
1245
|
-
|
|
1250
|
+
update_feed_message(feedId, messageId, data, timestamp) {
|
|
1246
1251
|
const key = `${feedId}:${messageId}`;
|
|
1247
1252
|
const message = this._feedMessages.get(key);
|
|
1248
1253
|
if (message === void 0) {
|
|
@@ -1260,33 +1265,85 @@ var InMemoryDriver = class {
|
|
|
1260
1265
|
this._feedMessages.set(key, updatedMessage);
|
|
1261
1266
|
return updatedMessage;
|
|
1262
1267
|
}
|
|
1263
|
-
|
|
1268
|
+
delete_feed_message(feedId, messageId) {
|
|
1264
1269
|
this._feedMessages.delete(`${feedId}:${messageId}`);
|
|
1265
1270
|
}
|
|
1266
1271
|
next_actor() {
|
|
1267
1272
|
return ++this._nextActor;
|
|
1268
1273
|
}
|
|
1269
|
-
|
|
1274
|
+
iter_y_updates(docId) {
|
|
1270
1275
|
const prefix = `${docId}@|@`;
|
|
1271
1276
|
return _itertools.imap.call(void 0,
|
|
1272
1277
|
_itertools.ifilter.call(void 0, this._ydb.entries(), ([k]) => k.startsWith(prefix)),
|
|
1273
1278
|
([k, v]) => [k.slice(prefix.length), v]
|
|
1274
1279
|
);
|
|
1275
1280
|
}
|
|
1276
|
-
|
|
1281
|
+
write_y_updates(docId, key, data) {
|
|
1277
1282
|
this._ydb.set(`${docId}@|@${key}`, data);
|
|
1278
1283
|
}
|
|
1279
|
-
|
|
1284
|
+
delete_y_updates(docId, keys) {
|
|
1280
1285
|
for (const key of keys) {
|
|
1281
1286
|
this._ydb.delete(`${docId}@|@${key}`);
|
|
1282
1287
|
}
|
|
1283
1288
|
}
|
|
1284
1289
|
/** @private Only use this in unit tests, never in production. */
|
|
1285
|
-
|
|
1290
|
+
DANGEROUSLY_wipe_all_y_updates() {
|
|
1286
1291
|
this._ydb.clear();
|
|
1287
1292
|
}
|
|
1288
|
-
//
|
|
1289
|
-
|
|
1293
|
+
// ---------------------------------------------------------------------------
|
|
1294
|
+
// Node APIs
|
|
1295
|
+
// ---------------------------------------------------------------------------
|
|
1296
|
+
/**
|
|
1297
|
+
* The lazily-built node API. Building it ensures the root node exists and
|
|
1298
|
+
* constructs the reverse-lookup index. Invalidated by
|
|
1299
|
+
* DANGEROUSLY_reset_nodes(), after which the next access rebuilds it.
|
|
1300
|
+
*/
|
|
1301
|
+
get loadedApi() {
|
|
1302
|
+
return _nullishCoalesce(this._nodesApi, () => ( (this._nodesApi = this._loadNodesApi())));
|
|
1303
|
+
}
|
|
1304
|
+
get_node(id) {
|
|
1305
|
+
return this.loadedApi.get_node(id);
|
|
1306
|
+
}
|
|
1307
|
+
iter_nodes() {
|
|
1308
|
+
return this.loadedApi.iter_nodes();
|
|
1309
|
+
}
|
|
1310
|
+
iter_nodes_optimized() {
|
|
1311
|
+
return this.loadedApi.iter_nodes_optimized();
|
|
1312
|
+
}
|
|
1313
|
+
has_node(id) {
|
|
1314
|
+
return this.loadedApi.has_node(id);
|
|
1315
|
+
}
|
|
1316
|
+
get_child_at(parentId, parentKey) {
|
|
1317
|
+
return this.loadedApi.get_child_at(parentId, parentKey);
|
|
1318
|
+
}
|
|
1319
|
+
has_child_at(parentId, parentKey) {
|
|
1320
|
+
return this.loadedApi.has_child_at(parentId, parentKey);
|
|
1321
|
+
}
|
|
1322
|
+
get_next_sibling(parentId, pos) {
|
|
1323
|
+
return this.loadedApi.get_next_sibling(parentId, pos);
|
|
1324
|
+
}
|
|
1325
|
+
get_last_sibling(parentId) {
|
|
1326
|
+
return this.loadedApi.get_last_sibling(parentId);
|
|
1327
|
+
}
|
|
1328
|
+
set_child(id, node, allowOverwrite) {
|
|
1329
|
+
this.loadedApi.set_child(id, node, allowOverwrite);
|
|
1330
|
+
}
|
|
1331
|
+
move_sibling(id, newPos) {
|
|
1332
|
+
this.loadedApi.move_sibling(id, newPos);
|
|
1333
|
+
}
|
|
1334
|
+
delete_node(id) {
|
|
1335
|
+
this.loadedApi.delete_node(id);
|
|
1336
|
+
}
|
|
1337
|
+
delete_child_key(id, key) {
|
|
1338
|
+
this.loadedApi.delete_child_key(id, key);
|
|
1339
|
+
}
|
|
1340
|
+
set_object_data(id, data, allowOverwrite) {
|
|
1341
|
+
this.loadedApi.set_object_data(id, data, allowOverwrite);
|
|
1342
|
+
}
|
|
1343
|
+
get_snapshot(lowMemory) {
|
|
1344
|
+
return this.loadedApi.get_snapshot(lowMemory);
|
|
1345
|
+
}
|
|
1346
|
+
_loadNodesApi() {
|
|
1290
1347
|
const nodes = this._nodes;
|
|
1291
1348
|
if (!nodes.has("root")) {
|
|
1292
1349
|
nodes.set("root", { type: _core.CrdtType.OBJECT, data: {} });
|
|
@@ -1312,7 +1369,7 @@ var InMemoryDriver = class {
|
|
|
1312
1369
|
}
|
|
1313
1370
|
return lastPos;
|
|
1314
1371
|
}
|
|
1315
|
-
|
|
1372
|
+
function set_child(id, node, allowOverwrite = false) {
|
|
1316
1373
|
const parentNode = nodes.get(node.parentId);
|
|
1317
1374
|
if (parentNode === void 0) {
|
|
1318
1375
|
throw new Error(`No such parent ${quote(node.parentId)}`);
|
|
@@ -1335,7 +1392,7 @@ var InMemoryDriver = class {
|
|
|
1335
1392
|
}
|
|
1336
1393
|
nodes.set(id, node);
|
|
1337
1394
|
}
|
|
1338
|
-
|
|
1395
|
+
function move_sibling(id, newPos) {
|
|
1339
1396
|
const node = nodes.get(id);
|
|
1340
1397
|
if (_optionalChain([node, 'optionalAccess', _44 => _44.parentId]) === void 0) {
|
|
1341
1398
|
return;
|
|
@@ -1347,7 +1404,7 @@ var InMemoryDriver = class {
|
|
|
1347
1404
|
nodes.set(id, newNode);
|
|
1348
1405
|
revNodes.set(node.parentId, newPos, id);
|
|
1349
1406
|
}
|
|
1350
|
-
|
|
1407
|
+
function set_object_data(id, data, allowOverwrite = false) {
|
|
1351
1408
|
const node = nodes.get(id);
|
|
1352
1409
|
if (_optionalChain([node, 'optionalAccess', _45 => _45.type]) !== _core.CrdtType.OBJECT) {
|
|
1353
1410
|
return;
|
|
@@ -1506,6 +1563,13 @@ function accept(op2, fix) {
|
|
|
1506
1563
|
function ignore(ignoredOp) {
|
|
1507
1564
|
return { action: "ignored", ignoredOpId: ignoredOp.opId };
|
|
1508
1565
|
}
|
|
1566
|
+
function rectify(op2, parentKey) {
|
|
1567
|
+
return {
|
|
1568
|
+
action: "rectified",
|
|
1569
|
+
ackOp: { ...op2, parentKey },
|
|
1570
|
+
fix: { type: _core.OpCode.SET_PARENT_KEY, id: op2.id, parentKey }
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1509
1573
|
function nodeFromCreateChildOp(op2) {
|
|
1510
1574
|
switch (op2.type) {
|
|
1511
1575
|
case _core.OpCode.CREATE_LIST:
|
|
@@ -1540,45 +1604,26 @@ function nodeFromCreateChildOp(op2) {
|
|
|
1540
1604
|
}
|
|
1541
1605
|
}
|
|
1542
1606
|
var Storage = class {
|
|
1543
|
-
constructor(
|
|
1607
|
+
constructor(driver) {
|
|
1544
1608
|
// The actual underlying storage API (could be backed by in-memory store,
|
|
1545
|
-
// SQLite,
|
|
1546
|
-
__publicField(this, "
|
|
1547
|
-
|
|
1548
|
-
this.coreDriver = coreDriver;
|
|
1609
|
+
// SQLite, Postgres, etc.)
|
|
1610
|
+
__publicField(this, "driver");
|
|
1611
|
+
this.driver = driver;
|
|
1549
1612
|
}
|
|
1550
1613
|
// -------------------------------------------------------------------------
|
|
1551
1614
|
// Public API (for Storage)
|
|
1552
1615
|
// -------------------------------------------------------------------------
|
|
1553
|
-
get loadedDriver() {
|
|
1554
|
-
if (this._loadedDriver === void 0) {
|
|
1555
|
-
throw new Error("Cannot access tree before it's been loaded");
|
|
1556
|
-
}
|
|
1557
|
-
return this._loadedDriver;
|
|
1558
|
-
}
|
|
1559
1616
|
// REFACTOR NOTE: Eventually raw_iter_nodes has to be removed here
|
|
1560
1617
|
raw_iter_nodes() {
|
|
1561
|
-
return this.
|
|
1562
|
-
}
|
|
1563
|
-
/**
|
|
1564
|
-
* Load the room data from object storage into memory. Persisted room
|
|
1565
|
-
* data consists of the main node map, which represents the Liveblocks
|
|
1566
|
-
* Storage tree, and special keys where we store usage metrics, or room
|
|
1567
|
-
* metadata.
|
|
1568
|
-
*/
|
|
1569
|
-
async load(logger) {
|
|
1570
|
-
this._loadedDriver = await this.coreDriver.load_nodes_api(logger);
|
|
1571
|
-
}
|
|
1572
|
-
unload() {
|
|
1573
|
-
this._loadedDriver = void 0;
|
|
1618
|
+
return this.driver.raw_iter_nodes();
|
|
1574
1619
|
}
|
|
1575
1620
|
/**
|
|
1576
1621
|
* Applies a batch of Ops.
|
|
1577
1622
|
*/
|
|
1578
|
-
|
|
1623
|
+
applyOps(ops) {
|
|
1579
1624
|
const results = [];
|
|
1580
1625
|
for (const op2 of ops) {
|
|
1581
|
-
results.push(
|
|
1626
|
+
results.push(this.applyOp(op2));
|
|
1582
1627
|
}
|
|
1583
1628
|
return results;
|
|
1584
1629
|
}
|
|
@@ -1588,21 +1633,21 @@ var Storage = class {
|
|
|
1588
1633
|
/**
|
|
1589
1634
|
* Applies a single Op.
|
|
1590
1635
|
*/
|
|
1591
|
-
|
|
1636
|
+
applyOp(op2) {
|
|
1592
1637
|
switch (op2.type) {
|
|
1593
1638
|
case _core.OpCode.CREATE_LIST:
|
|
1594
1639
|
case _core.OpCode.CREATE_MAP:
|
|
1595
1640
|
case _core.OpCode.CREATE_REGISTER:
|
|
1596
1641
|
case _core.OpCode.CREATE_OBJECT:
|
|
1597
|
-
return
|
|
1642
|
+
return this.applyCreateOp(op2);
|
|
1598
1643
|
case _core.OpCode.UPDATE_OBJECT:
|
|
1599
|
-
return
|
|
1644
|
+
return this.applyUpdateObjectOp(op2);
|
|
1600
1645
|
case _core.OpCode.SET_PARENT_KEY:
|
|
1601
|
-
return
|
|
1646
|
+
return this.applySetParentKeyOp(op2);
|
|
1602
1647
|
case _core.OpCode.DELETE_OBJECT_KEY:
|
|
1603
|
-
return
|
|
1648
|
+
return this.applyDeleteObjectKeyOp(op2);
|
|
1604
1649
|
case _core.OpCode.DELETE_CRDT:
|
|
1605
|
-
return
|
|
1650
|
+
return this.applyDeleteCrdtOp(op2);
|
|
1606
1651
|
// istanbul ignore next
|
|
1607
1652
|
default:
|
|
1608
1653
|
if (process.env.NODE_ENV === "production") {
|
|
@@ -1612,12 +1657,18 @@ var Storage = class {
|
|
|
1612
1657
|
}
|
|
1613
1658
|
}
|
|
1614
1659
|
}
|
|
1615
|
-
|
|
1616
|
-
if (this.
|
|
1660
|
+
applyCreateOp(op2) {
|
|
1661
|
+
if (this.driver.has_node(op2.id)) {
|
|
1662
|
+
if (op2.intent === "push") {
|
|
1663
|
+
const stored = this.driver.get_node(op2.id);
|
|
1664
|
+
if (_optionalChain([stored, 'optionalAccess', _47 => _47.parentId]) !== void 0 && _optionalChain([this, 'access', _48 => _48.driver, 'access', _49 => _49.get_node, 'call', _50 => _50(stored.parentId), 'optionalAccess', _51 => _51.type]) === _core.CrdtType.LIST) {
|
|
1665
|
+
return rectify(op2, stored.parentKey);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1617
1668
|
return ignore(op2);
|
|
1618
1669
|
}
|
|
1619
1670
|
const node = nodeFromCreateChildOp(op2);
|
|
1620
|
-
const parent = this.
|
|
1671
|
+
const parent = this.driver.get_node(node.parentId);
|
|
1621
1672
|
if (parent === void 0) {
|
|
1622
1673
|
return ignore(op2);
|
|
1623
1674
|
}
|
|
@@ -1628,7 +1679,7 @@ var Storage = class {
|
|
|
1628
1679
|
}
|
|
1629
1680
|
// fall through
|
|
1630
1681
|
case _core.CrdtType.MAP:
|
|
1631
|
-
|
|
1682
|
+
this.driver.set_child(op2.id, node, true);
|
|
1632
1683
|
return accept(op2);
|
|
1633
1684
|
case _core.CrdtType.LIST:
|
|
1634
1685
|
return this.createChildAsListItem(op2, node);
|
|
@@ -1639,23 +1690,19 @@ var Storage = class {
|
|
|
1639
1690
|
return _core.assertNever.call(void 0, parent, "Unhandled CRDT type");
|
|
1640
1691
|
}
|
|
1641
1692
|
}
|
|
1642
|
-
|
|
1693
|
+
createChildAsListItem(op2, node) {
|
|
1643
1694
|
const intent2 = _nullishCoalesce(op2.intent, () => ( "insert"));
|
|
1644
1695
|
if (intent2 === "insert") {
|
|
1645
|
-
return this.acceptAndFix(
|
|
1646
|
-
op2,
|
|
1647
|
-
node,
|
|
1648
|
-
await this.insertIntoList(op2.id, node)
|
|
1649
|
-
);
|
|
1696
|
+
return this.acceptAndFix(op2, node, this.insertIntoList(op2.id, node));
|
|
1650
1697
|
} else if (intent2 === "push") {
|
|
1651
|
-
return this.acceptAndFix(op2, node,
|
|
1698
|
+
return this.acceptAndFix(op2, node, this.appendToList(op2.id, node));
|
|
1652
1699
|
} else if (intent2 === "set") {
|
|
1653
1700
|
let fix;
|
|
1654
|
-
const deletedId = op2.deletedId !== void 0 && op2.deletedId !== op2.id && _optionalChain([this, 'access',
|
|
1701
|
+
const deletedId = op2.deletedId !== void 0 && op2.deletedId !== op2.id && _optionalChain([this, 'access', _52 => _52.driver, 'access', _53 => _53.get_node, 'call', _54 => _54(op2.deletedId), 'optionalAccess', _55 => _55.parentId]) === node.parentId ? op2.deletedId : void 0;
|
|
1655
1702
|
if (deletedId !== void 0) {
|
|
1656
|
-
|
|
1703
|
+
this.driver.delete_node(deletedId);
|
|
1657
1704
|
}
|
|
1658
|
-
const prevItemId = this.
|
|
1705
|
+
const prevItemId = this.driver.get_child_at(
|
|
1659
1706
|
node.parentId,
|
|
1660
1707
|
node.parentKey
|
|
1661
1708
|
);
|
|
@@ -1665,7 +1712,7 @@ var Storage = class {
|
|
|
1665
1712
|
id: prevItemId
|
|
1666
1713
|
};
|
|
1667
1714
|
}
|
|
1668
|
-
|
|
1715
|
+
this.driver.set_child(op2.id, node, true);
|
|
1669
1716
|
return accept(op2, fix);
|
|
1670
1717
|
} else {
|
|
1671
1718
|
return _core.assertNever.call(void 0, intent2, "Invalid intent");
|
|
@@ -1685,20 +1732,20 @@ var Storage = class {
|
|
|
1685
1732
|
}
|
|
1686
1733
|
return accept(op2);
|
|
1687
1734
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1735
|
+
applyDeleteObjectKeyOp(op2) {
|
|
1736
|
+
this.driver.delete_child_key(op2.id, op2.key);
|
|
1690
1737
|
return accept(op2);
|
|
1691
1738
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1739
|
+
applyUpdateObjectOp(op2) {
|
|
1740
|
+
this.driver.set_object_data(op2.id, op2.data, true);
|
|
1694
1741
|
return accept(op2);
|
|
1695
1742
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1743
|
+
applyDeleteCrdtOp(op2) {
|
|
1744
|
+
this.driver.delete_node(op2.id);
|
|
1698
1745
|
return accept(op2);
|
|
1699
1746
|
}
|
|
1700
|
-
|
|
1701
|
-
const newPosition =
|
|
1747
|
+
applySetParentKeyOp(op2) {
|
|
1748
|
+
const newPosition = this.moveToPosInList(op2.id, op2.parentKey);
|
|
1702
1749
|
if (newPosition === void 0) {
|
|
1703
1750
|
return ignore(op2);
|
|
1704
1751
|
}
|
|
@@ -1722,12 +1769,12 @@ var Storage = class {
|
|
|
1722
1769
|
*
|
|
1723
1770
|
* Returns the key that was used for the insertion.
|
|
1724
1771
|
*/
|
|
1725
|
-
|
|
1772
|
+
insertIntoList(id, node) {
|
|
1726
1773
|
const key = this.findFreeListPosition(node.parentId, _core.asPos.call(void 0, node.parentKey));
|
|
1727
1774
|
if (key !== node.parentKey) {
|
|
1728
1775
|
node = { ...node, parentKey: key };
|
|
1729
1776
|
}
|
|
1730
|
-
|
|
1777
|
+
this.driver.set_child(id, node);
|
|
1731
1778
|
return node.parentKey;
|
|
1732
1779
|
}
|
|
1733
1780
|
/**
|
|
@@ -1740,11 +1787,11 @@ var Storage = class {
|
|
|
1740
1787
|
*
|
|
1741
1788
|
* Returns the final key that was used for the insertion.
|
|
1742
1789
|
*/
|
|
1743
|
-
|
|
1744
|
-
const lastPos = this.
|
|
1790
|
+
appendToList(id, node) {
|
|
1791
|
+
const lastPos = this.driver.get_last_sibling(node.parentId);
|
|
1745
1792
|
const preferredPos = _core.asPos.call(void 0, node.parentKey);
|
|
1746
1793
|
const finalKey = lastPos === void 0 || preferredPos > lastPos ? preferredPos : _core.makePosition.call(void 0, lastPos);
|
|
1747
|
-
|
|
1794
|
+
this.driver.set_child(
|
|
1748
1795
|
id,
|
|
1749
1796
|
finalKey !== node.parentKey ? { ...node, parentKey: finalKey } : node
|
|
1750
1797
|
);
|
|
@@ -1762,12 +1809,12 @@ var Storage = class {
|
|
|
1762
1809
|
* Will return `undefined` if this action could not be interpreted. Will be
|
|
1763
1810
|
* a no-op for non-list items.
|
|
1764
1811
|
*/
|
|
1765
|
-
|
|
1766
|
-
const node = this.
|
|
1767
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
1812
|
+
moveToPosInList(id, targetKey) {
|
|
1813
|
+
const node = this.driver.get_node(id);
|
|
1814
|
+
if (_optionalChain([node, 'optionalAccess', _56 => _56.parentId]) === void 0) {
|
|
1768
1815
|
return;
|
|
1769
1816
|
}
|
|
1770
|
-
if (_optionalChain([this, 'access',
|
|
1817
|
+
if (_optionalChain([this, 'access', _57 => _57.driver, 'access', _58 => _58.get_node, 'call', _59 => _59(node.parentId), 'optionalAccess', _60 => _60.type]) !== _core.CrdtType.LIST) {
|
|
1771
1818
|
return;
|
|
1772
1819
|
}
|
|
1773
1820
|
if (node.parentKey === targetKey) {
|
|
@@ -1775,7 +1822,7 @@ var Storage = class {
|
|
|
1775
1822
|
}
|
|
1776
1823
|
const key = this.findFreeListPosition(node.parentId, _core.asPos.call(void 0, targetKey));
|
|
1777
1824
|
if (key !== node.parentKey) {
|
|
1778
|
-
|
|
1825
|
+
this.driver.move_sibling(id, key);
|
|
1779
1826
|
}
|
|
1780
1827
|
return key;
|
|
1781
1828
|
}
|
|
@@ -1787,11 +1834,11 @@ var Storage = class {
|
|
|
1787
1834
|
* to use as parentKey.
|
|
1788
1835
|
*/
|
|
1789
1836
|
findFreeListPosition(parentId, parentPos) {
|
|
1790
|
-
if (!this.
|
|
1837
|
+
if (!this.driver.has_child_at(parentId, parentPos)) {
|
|
1791
1838
|
return parentPos;
|
|
1792
1839
|
}
|
|
1793
1840
|
const currPos = parentPos;
|
|
1794
|
-
const nextPos = this.
|
|
1841
|
+
const nextPos = this.driver.get_next_sibling(parentId, currPos);
|
|
1795
1842
|
if (nextPos !== void 0) {
|
|
1796
1843
|
return _core.makePosition.call(void 0, currPos, nextPos);
|
|
1797
1844
|
} else {
|
|
@@ -1801,6 +1848,7 @@ var Storage = class {
|
|
|
1801
1848
|
};
|
|
1802
1849
|
|
|
1803
1850
|
// src/YjsStorage.ts
|
|
1851
|
+
var _crypto = require('crypto');
|
|
1804
1852
|
var _jsbase64 = require('js-base64');
|
|
1805
1853
|
|
|
1806
1854
|
var _yjs = require('yjs'); var Y = _interopRequireWildcard(_yjs);
|
|
@@ -1810,30 +1858,33 @@ var YjsStorage = class {
|
|
|
1810
1858
|
constructor(driver, updateCountThreshold = UPDATE_COUNT_THRESHOLD) {
|
|
1811
1859
|
__publicField(this, "driver");
|
|
1812
1860
|
__publicField(this, "updateCountThreshold");
|
|
1813
|
-
|
|
1814
|
-
|
|
1861
|
+
/**
|
|
1862
|
+
* The root Y.Doc instance, which may not have been hydrated from storage
|
|
1863
|
+
* yet. Use getRootDoc() instead, which lazily hydrates it on first access —
|
|
1864
|
+
* only touch this field directly when the unhydrated instance is fine
|
|
1865
|
+
* (e.g. identity checks, subdoc traversal).
|
|
1866
|
+
*/
|
|
1867
|
+
__publicField(this, "rawRootDoc", new Y.Doc());
|
|
1815
1868
|
__publicField(this, "lastSnapshotById", /* @__PURE__ */ new Map());
|
|
1816
|
-
__publicField(this, "
|
|
1869
|
+
__publicField(this, "docsById", /* @__PURE__ */ new Map());
|
|
1817
1870
|
__publicField(this, "storedKeysById", /* @__PURE__ */ new Map());
|
|
1818
1871
|
// compact the updates into a single update and write it to the durable storage
|
|
1819
|
-
__publicField(this, "_compactYJSUpdates",
|
|
1872
|
+
__publicField(this, "_compactYJSUpdates", (doc, docId, storedKeys) => {
|
|
1820
1873
|
const compactedUpdate = Y.encodeStateAsUpdate(doc);
|
|
1821
1874
|
const newKey = _nanoid.nanoid.call(void 0, );
|
|
1822
|
-
|
|
1823
|
-
|
|
1875
|
+
this.driver.write_y_updates(docId, newKey, compactedUpdate);
|
|
1876
|
+
this.driver.delete_y_updates(docId, storedKeys);
|
|
1824
1877
|
this.storedKeysById.set(docId, [newKey]);
|
|
1825
1878
|
});
|
|
1826
|
-
__publicField(this, "_loadYDocFromDurableStorage",
|
|
1827
|
-
const docUpdates = Object.fromEntries(
|
|
1828
|
-
await this.driver.iter_y_updates(docId)
|
|
1829
|
-
);
|
|
1879
|
+
__publicField(this, "_loadYDocFromDurableStorage", (doc, docId) => {
|
|
1880
|
+
const docUpdates = Object.fromEntries(this.driver.iter_y_updates(docId));
|
|
1830
1881
|
const updates = Object.values(docUpdates);
|
|
1831
1882
|
const beforeSize = updates.reduce((acc, update) => acc + update.length, 0);
|
|
1832
1883
|
const newupdate = Y.mergeUpdates(updates);
|
|
1833
1884
|
const storedKeys = Object.keys(docUpdates);
|
|
1834
1885
|
Y.applyUpdate(doc, newupdate);
|
|
1835
1886
|
if (this.shouldCompactByKeyCount(storedKeys) || this.shouldCompactBySize(beforeSize, newupdate.length)) {
|
|
1836
|
-
|
|
1887
|
+
this._compactYJSUpdates(doc, docId, storedKeys);
|
|
1837
1888
|
} else {
|
|
1838
1889
|
this.storedKeysById.set(docId, storedKeys);
|
|
1839
1890
|
}
|
|
@@ -1842,7 +1893,7 @@ var YjsStorage = class {
|
|
|
1842
1893
|
});
|
|
1843
1894
|
this.driver = driver;
|
|
1844
1895
|
this.updateCountThreshold = updateCountThreshold;
|
|
1845
|
-
this.
|
|
1896
|
+
this.rawRootDoc.on("subdocs", ({ removed }) => {
|
|
1846
1897
|
removed.forEach((subdoc) => {
|
|
1847
1898
|
subdoc.destroy();
|
|
1848
1899
|
});
|
|
@@ -1851,9 +1902,27 @@ var YjsStorage = class {
|
|
|
1851
1902
|
// ------------------------------------------------------------------------------------
|
|
1852
1903
|
// Public API
|
|
1853
1904
|
// ------------------------------------------------------------------------------------
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1905
|
+
getYDoc(logger, docId) {
|
|
1906
|
+
if (docId !== ROOT_YDOC_ID) {
|
|
1907
|
+
this.getRootDoc(logger);
|
|
1908
|
+
}
|
|
1909
|
+
let loaded = this.docsById.get(docId);
|
|
1910
|
+
let doc = docId === ROOT_YDOC_ID ? this.rawRootDoc : this.findYSubdocByGuid(docId);
|
|
1911
|
+
if (!doc) {
|
|
1912
|
+
doc = new Y.Doc();
|
|
1913
|
+
}
|
|
1914
|
+
if (loaded === void 0) {
|
|
1915
|
+
loaded = this._loadYDocFromDurableStorage(doc, docId);
|
|
1916
|
+
this.docsById.set(docId, loaded);
|
|
1917
|
+
}
|
|
1918
|
+
return loaded;
|
|
1919
|
+
}
|
|
1920
|
+
/**
|
|
1921
|
+
* Returns the root Y.Doc, lazily loading it from storage first if it
|
|
1922
|
+
* hasn't been loaded yet during this instance's lifetime.
|
|
1923
|
+
*/
|
|
1924
|
+
getRootDoc(logger) {
|
|
1925
|
+
return this.getYDoc(logger, ROOT_YDOC_ID);
|
|
1857
1926
|
}
|
|
1858
1927
|
/**
|
|
1859
1928
|
* If passed a state vector, an update with diff will be returned, if not the entire doc is returned.
|
|
@@ -1861,18 +1930,13 @@ var YjsStorage = class {
|
|
|
1861
1930
|
* @param stateVector a base64 encoded target state vector created by running Y.encodeStateVector(Doc) on the client
|
|
1862
1931
|
* @returns a base64 encoded array of YJS updates
|
|
1863
1932
|
*/
|
|
1864
|
-
|
|
1865
|
-
const update =
|
|
1866
|
-
logger,
|
|
1867
|
-
stateVector,
|
|
1868
|
-
guid,
|
|
1869
|
-
isV2
|
|
1870
|
-
);
|
|
1933
|
+
getYDocUpdate(logger, stateVector = "", guid, isV2 = false) {
|
|
1934
|
+
const update = this.getYDocUpdateBinary(logger, stateVector, guid, isV2);
|
|
1871
1935
|
if (!update) return null;
|
|
1872
1936
|
return _jsbase64.Base64.fromUint8Array(update);
|
|
1873
1937
|
}
|
|
1874
|
-
|
|
1875
|
-
const doc = guid !== void 0 ?
|
|
1938
|
+
getYDocUpdateBinary(logger, stateVector = "", guid, isV2 = false) {
|
|
1939
|
+
const doc = guid !== void 0 ? this.getYSubdoc(logger, guid) : this.getRootDoc(logger);
|
|
1876
1940
|
if (!doc) {
|
|
1877
1941
|
return null;
|
|
1878
1942
|
}
|
|
@@ -1889,15 +1953,15 @@ var YjsStorage = class {
|
|
|
1889
1953
|
}
|
|
1890
1954
|
return Y.encodeStateAsUpdate(doc, encodedTargetVector);
|
|
1891
1955
|
}
|
|
1892
|
-
|
|
1893
|
-
const doc = guid !== void 0 ?
|
|
1956
|
+
getYStateVector(logger, guid) {
|
|
1957
|
+
const doc = guid !== void 0 ? this.getYSubdoc(logger, guid) : this.getRootDoc(logger);
|
|
1894
1958
|
if (!doc) {
|
|
1895
1959
|
return null;
|
|
1896
1960
|
}
|
|
1897
1961
|
return _jsbase64.Base64.fromUint8Array(Y.encodeStateVector(doc));
|
|
1898
1962
|
}
|
|
1899
|
-
|
|
1900
|
-
const doc = options.guid !== void 0 ?
|
|
1963
|
+
getSnapshotHash(logger, options) {
|
|
1964
|
+
const doc = options.guid !== void 0 ? this.getYSubdoc(logger, options.guid) : this.getRootDoc(logger);
|
|
1901
1965
|
if (!doc) {
|
|
1902
1966
|
return null;
|
|
1903
1967
|
}
|
|
@@ -1910,8 +1974,8 @@ var YjsStorage = class {
|
|
|
1910
1974
|
* isUpdated: true if the update had an effect on the YDoc
|
|
1911
1975
|
* snapshotHash: the hash of the new snapshot
|
|
1912
1976
|
*/
|
|
1913
|
-
|
|
1914
|
-
const doc = guid !== void 0 ?
|
|
1977
|
+
addYDocUpdate(logger, update, guid, isV2) {
|
|
1978
|
+
const doc = guid !== void 0 ? this.getYSubdoc(logger, guid) : this.getRootDoc(logger);
|
|
1915
1979
|
if (!doc) {
|
|
1916
1980
|
throw new Error(`YDoc with guid ${guid} not found`);
|
|
1917
1981
|
}
|
|
@@ -1923,11 +1987,11 @@ var YjsStorage = class {
|
|
|
1923
1987
|
const afterSnapshot = this._putLastSnapshot(doc);
|
|
1924
1988
|
const updated = !Y.equalSnapshots(beforeSnapshot, afterSnapshot);
|
|
1925
1989
|
if (updated) {
|
|
1926
|
-
|
|
1990
|
+
this.handleYDocUpdate(doc, updateAsU8, isV2);
|
|
1927
1991
|
}
|
|
1928
1992
|
return {
|
|
1929
1993
|
isUpdated: updated,
|
|
1930
|
-
snapshotHash:
|
|
1994
|
+
snapshotHash: this.calculateSnapshotHash(afterSnapshot, { isV2 })
|
|
1931
1995
|
};
|
|
1932
1996
|
} catch (e) {
|
|
1933
1997
|
logger.warn(`Ignored bad YDoc update: ${String(e)}`);
|
|
@@ -1936,32 +2000,12 @@ var YjsStorage = class {
|
|
|
1936
2000
|
);
|
|
1937
2001
|
}
|
|
1938
2002
|
}
|
|
1939
|
-
loadDocByIdIfNotAlreadyLoaded(logger, docId) {
|
|
1940
|
-
let loaded$ = this.initPromisesById.get(docId);
|
|
1941
|
-
let doc = docId === ROOT_YDOC_ID ? this.doc : this.findYSubdocByGuid(docId);
|
|
1942
|
-
if (!doc) {
|
|
1943
|
-
doc = new Y.Doc();
|
|
1944
|
-
}
|
|
1945
|
-
if (loaded$ === void 0) {
|
|
1946
|
-
loaded$ = this._loadYDocFromDurableStorage(logger, doc, docId);
|
|
1947
|
-
this.initPromisesById.set(docId, loaded$);
|
|
1948
|
-
}
|
|
1949
|
-
return loaded$;
|
|
1950
|
-
}
|
|
1951
|
-
async load(logger) {
|
|
1952
|
-
await this.loadDocByIdIfNotAlreadyLoaded(logger, ROOT_YDOC_ID);
|
|
1953
|
-
}
|
|
1954
|
-
/**
|
|
1955
|
-
* Unloads the Yjs documents from memory.
|
|
1956
|
-
*/
|
|
1957
|
-
unload() {
|
|
1958
|
-
}
|
|
1959
2003
|
// ------------------------------------------------------------------------------------
|
|
1960
2004
|
// Private APIs
|
|
1961
2005
|
// ------------------------------------------------------------------------------------
|
|
1962
2006
|
// NOTE: We could instead store the hash of snapshot instead of the whole snapshot to optimize memory usage.
|
|
1963
2007
|
_getOrPutLastSnapshot(doc) {
|
|
1964
|
-
const docId = doc.guid === this.
|
|
2008
|
+
const docId = doc.guid === this.rawRootDoc.guid ? ROOT_YDOC_ID : doc.guid;
|
|
1965
2009
|
const snapshot2 = this.lastSnapshotById.get(docId);
|
|
1966
2010
|
if (snapshot2) {
|
|
1967
2011
|
return snapshot2;
|
|
@@ -1970,47 +2014,44 @@ var YjsStorage = class {
|
|
|
1970
2014
|
}
|
|
1971
2015
|
// NOTE: We could instead store the hash of snapshot instead of the whole snapshot to optimize memory usage.
|
|
1972
2016
|
_putLastSnapshot(doc) {
|
|
1973
|
-
const docId = doc.guid === this.
|
|
2017
|
+
const docId = doc.guid === this.rawRootDoc.guid ? ROOT_YDOC_ID : doc.guid;
|
|
1974
2018
|
const snapshot2 = Y.snapshot(doc);
|
|
1975
2019
|
this.lastSnapshotById.set(docId, snapshot2);
|
|
1976
2020
|
return snapshot2;
|
|
1977
2021
|
}
|
|
1978
2022
|
findYSubdocByGuid(guid) {
|
|
1979
|
-
for (const subdoc of this.
|
|
2023
|
+
for (const subdoc of this.rawRootDoc.getSubdocs()) {
|
|
1980
2024
|
if (subdoc.guid === guid) {
|
|
1981
2025
|
return subdoc;
|
|
1982
2026
|
}
|
|
1983
2027
|
}
|
|
1984
2028
|
return null;
|
|
1985
2029
|
}
|
|
1986
|
-
|
|
2030
|
+
calculateSnapshotHash(snapshot2, { isV2 }) {
|
|
1987
2031
|
const encodedSnapshot = isV2 ? Y.encodeSnapshotV2(snapshot2) : Y.encodeSnapshot(snapshot2);
|
|
1988
|
-
return
|
|
1989
|
-
new Uint8Array(
|
|
1990
|
-
await crypto.subtle.digest("SHA-256", new Uint8Array(encodedSnapshot))
|
|
1991
|
-
)
|
|
1992
|
-
);
|
|
2032
|
+
return _crypto.createHash.call(void 0, "sha256").update(encodedSnapshot).digest("base64");
|
|
1993
2033
|
}
|
|
1994
2034
|
// gets a subdoc, it will be loaded if not already loaded
|
|
1995
|
-
|
|
2035
|
+
getYSubdoc(logger, guid) {
|
|
2036
|
+
this.getRootDoc(logger);
|
|
1996
2037
|
const subdoc = this.findYSubdocByGuid(guid);
|
|
1997
2038
|
if (!subdoc) {
|
|
1998
2039
|
return null;
|
|
1999
2040
|
}
|
|
2000
|
-
|
|
2041
|
+
this.getYDoc(logger, guid);
|
|
2001
2042
|
return subdoc;
|
|
2002
2043
|
}
|
|
2003
2044
|
// When the YJS doc changes, update it in durable storage
|
|
2004
|
-
|
|
2045
|
+
handleYDocUpdate(doc, update, isV2) {
|
|
2005
2046
|
const v1update = isV2 ? Y.convertUpdateFormatV2ToV1(update) : update;
|
|
2006
|
-
const docId = doc.guid === this.
|
|
2047
|
+
const docId = doc.guid === this.rawRootDoc.guid ? ROOT_YDOC_ID : doc.guid;
|
|
2007
2048
|
const storedKeys = this.storedKeysById.get(docId);
|
|
2008
2049
|
if (this.shouldCompactByKeyCount(storedKeys)) {
|
|
2009
|
-
|
|
2050
|
+
this._compactYJSUpdates(doc, docId, storedKeys || []);
|
|
2010
2051
|
return;
|
|
2011
2052
|
}
|
|
2012
2053
|
const newKey = _nanoid.nanoid.call(void 0, );
|
|
2013
|
-
|
|
2054
|
+
this.driver.write_y_updates(docId, newKey, v1update);
|
|
2014
2055
|
if (!storedKeys) {
|
|
2015
2056
|
this.storedKeysById.set(docId, [newKey]);
|
|
2016
2057
|
} else {
|
|
@@ -2029,10 +2070,19 @@ var YjsStorage = class {
|
|
|
2029
2070
|
};
|
|
2030
2071
|
|
|
2031
2072
|
// src/lib/tryCatch.ts
|
|
2032
|
-
|
|
2073
|
+
function isThenable(value) {
|
|
2074
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
2075
|
+
}
|
|
2076
|
+
function tryCatch(promise) {
|
|
2033
2077
|
try {
|
|
2034
|
-
const
|
|
2035
|
-
|
|
2078
|
+
const result = typeof promise === "function" ? promise() : promise;
|
|
2079
|
+
if (isThenable(result)) {
|
|
2080
|
+
return Promise.resolve(result).then(
|
|
2081
|
+
(data) => [data, void 0],
|
|
2082
|
+
(error) => [void 0, error]
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
return [result, void 0];
|
|
2036
2086
|
} catch (error) {
|
|
2037
2087
|
return [void 0, error];
|
|
2038
2088
|
}
|
|
@@ -2175,13 +2225,13 @@ var BrowserSession = class {
|
|
|
2175
2225
|
this.publicMeta = ticket.publicMeta;
|
|
2176
2226
|
__privateSet(this, __socket, socket);
|
|
2177
2227
|
__privateSet(this, __debug, debug);
|
|
2178
|
-
const now = _nullishCoalesce(_optionalChain([createdAt, 'optionalAccess',
|
|
2228
|
+
const now = _nullishCoalesce(_optionalChain([createdAt, 'optionalAccess', _61 => _61.getTime, 'call', _62 => _62()]), () => ( Date.now()));
|
|
2179
2229
|
this.createdAt = now;
|
|
2180
2230
|
__privateSet(this, __lastActiveAt, now);
|
|
2181
2231
|
__privateSet(this, __hasNotifiedClientStorageUpdateError, false);
|
|
2182
2232
|
}
|
|
2183
2233
|
get lastActiveAt() {
|
|
2184
|
-
const lastPing = _optionalChain([__privateGet, 'call',
|
|
2234
|
+
const lastPing = _optionalChain([__privateGet, 'call', _63 => _63(this, __socket), 'access', _64 => _64.getLastPongTimestamp, 'optionalCall', _65 => _65()]);
|
|
2185
2235
|
if (lastPing && lastPing.getTime() > __privateGet(this, __lastActiveAt)) {
|
|
2186
2236
|
return lastPing.getTime();
|
|
2187
2237
|
} else {
|
|
@@ -2258,13 +2308,13 @@ var BackendSession = class extends BrowserSession {
|
|
|
2258
2308
|
super(ticket, socket, debug);
|
|
2259
2309
|
}
|
|
2260
2310
|
};
|
|
2261
|
-
var __debug2;
|
|
2311
|
+
var _logger, __debug2;
|
|
2262
2312
|
var Room = class {
|
|
2263
2313
|
constructor(meta, options) {
|
|
2264
2314
|
// ^^^^^^^^^^ User-defined Room Metadata, Session Metadata, and Client Metadata
|
|
2265
2315
|
__publicField(this, "meta");
|
|
2266
2316
|
__publicField(this, "driver");
|
|
2267
|
-
|
|
2317
|
+
__privateAdd(this, _logger);
|
|
2268
2318
|
/**
|
|
2269
2319
|
* While a room is in "maintenance mode", all WebSocket connections to the
|
|
2270
2320
|
* room should be rejected until it's pulled out of maintenance mode again.
|
|
@@ -2274,61 +2324,49 @@ var Room = class {
|
|
|
2274
2324
|
* init-storage, storage-reset, etc.) cannot interfere with one another.
|
|
2275
2325
|
*/
|
|
2276
2326
|
__publicField(this, "_maintenanceMode", new (0, _asyncmutex.Mutex)());
|
|
2277
|
-
__publicField(this, "
|
|
2278
|
-
__publicField(this, "
|
|
2327
|
+
__publicField(this, "_storage");
|
|
2328
|
+
__publicField(this, "_yjsStorage");
|
|
2329
|
+
__publicField(this, "mutex", new (0, _asyncmutex.Mutex)());
|
|
2330
|
+
// prettier-ignore
|
|
2279
2331
|
__publicField(this, "_qsize", 0);
|
|
2280
2332
|
__publicField(this, "sessions", new UniqueMap((s) => s.actor));
|
|
2281
2333
|
__publicField(this, "hooks");
|
|
2282
2334
|
__privateAdd(this, __debug2);
|
|
2283
|
-
const driver = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
2335
|
+
const driver = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _66 => _66.storage]), () => ( makeNewInMemoryDriver()));
|
|
2284
2336
|
this.meta = meta;
|
|
2285
2337
|
this.driver = driver;
|
|
2286
|
-
this.
|
|
2338
|
+
this._storage = new Storage(this.driver);
|
|
2339
|
+
this._yjsStorage = new YjsStorage(this.driver);
|
|
2340
|
+
__privateSet(this, _logger, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _67 => _67.logger]), () => ( BLACK_HOLE)));
|
|
2287
2341
|
this.hooks = {
|
|
2288
|
-
isClientMsgAllowed: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
2342
|
+
isClientMsgAllowed: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _68 => _68.hooks, 'optionalAccess', _69 => _69.isClientMsgAllowed]), () => ( (() => {
|
|
2289
2343
|
return {
|
|
2290
2344
|
allowed: true
|
|
2291
2345
|
};
|
|
2292
2346
|
}))),
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
onSessionDidStart: _optionalChain([options, 'optionalAccess', _73 => _73.hooks, 'optionalAccess', _74 => _74.onSessionDidStart]),
|
|
2299
|
-
onSessionDidEnd: _optionalChain([options, 'optionalAccess', _75 => _75.hooks, 'optionalAccess', _76 => _76.onSessionDidEnd]),
|
|
2300
|
-
postClientMsgStorageDidUpdate: _optionalChain([options, 'optionalAccess', _77 => _77.hooks, 'optionalAccess', _78 => _78.postClientMsgStorageDidUpdate]),
|
|
2301
|
-
postClientMsgYdocDidUpdate: _optionalChain([options, 'optionalAccess', _79 => _79.hooks, 'optionalAccess', _80 => _80.postClientMsgYdocDidUpdate])
|
|
2347
|
+
onRoomWillUnload: _optionalChain([options, 'optionalAccess', _70 => _70.hooks, 'optionalAccess', _71 => _71.onRoomWillUnload]),
|
|
2348
|
+
onSessionDidStart: _optionalChain([options, 'optionalAccess', _72 => _72.hooks, 'optionalAccess', _73 => _73.onSessionDidStart]),
|
|
2349
|
+
onSessionDidEnd: _optionalChain([options, 'optionalAccess', _74 => _74.hooks, 'optionalAccess', _75 => _75.onSessionDidEnd]),
|
|
2350
|
+
postClientMsgStorageDidUpdate: _optionalChain([options, 'optionalAccess', _76 => _76.hooks, 'optionalAccess', _77 => _77.postClientMsgStorageDidUpdate]),
|
|
2351
|
+
postClientMsgYdocDidUpdate: _optionalChain([options, 'optionalAccess', _78 => _78.hooks, 'optionalAccess', _79 => _79.postClientMsgYdocDidUpdate])
|
|
2302
2352
|
};
|
|
2303
|
-
__privateSet(this, __debug2, _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
2304
|
-
}
|
|
2305
|
-
get loadingState() {
|
|
2306
|
-
if (this._loadData$ === null) {
|
|
2307
|
-
return "initial";
|
|
2308
|
-
} else if (this._data === null) {
|
|
2309
|
-
return "loading";
|
|
2310
|
-
} else {
|
|
2311
|
-
return "loaded";
|
|
2312
|
-
}
|
|
2353
|
+
__privateSet(this, __debug2, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _80 => _80.enableDebugLogging]), () => ( false)));
|
|
2313
2354
|
}
|
|
2314
|
-
get numSessions() {
|
|
2315
|
-
return this.sessions.size;
|
|
2316
|
-
}
|
|
2317
|
-
// prettier-ignore
|
|
2318
2355
|
get storage() {
|
|
2319
|
-
return this.
|
|
2356
|
+
return this._storage;
|
|
2320
2357
|
}
|
|
2321
2358
|
// prettier-ignore
|
|
2322
2359
|
get yjsStorage() {
|
|
2323
|
-
return this.
|
|
2360
|
+
return this._yjsStorage;
|
|
2324
2361
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
return this.data.mutex;
|
|
2362
|
+
get logger() {
|
|
2363
|
+
return __privateGet(this, _logger);
|
|
2328
2364
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2365
|
+
addLoggerContext(attrs) {
|
|
2366
|
+
__privateSet(this, _logger, __privateGet(this, _logger).withContext(attrs));
|
|
2367
|
+
}
|
|
2368
|
+
get numSessions() {
|
|
2369
|
+
return this.sessions.size;
|
|
2332
2370
|
}
|
|
2333
2371
|
// prettier-ignore
|
|
2334
2372
|
// ------------------------------------------------------------------------------------
|
|
@@ -2353,34 +2391,15 @@ var Room = class {
|
|
|
2353
2391
|
// Public API
|
|
2354
2392
|
// ------------------------------------------------------------------------------------
|
|
2355
2393
|
/**
|
|
2356
|
-
*
|
|
2357
|
-
*
|
|
2358
|
-
* be
|
|
2359
|
-
*/
|
|
2360
|
-
async load(ctx) {
|
|
2361
|
-
if (this._loadData$ === null) {
|
|
2362
|
-
this._data = null;
|
|
2363
|
-
this._loadData$ = this._load(ctx).catch((e) => {
|
|
2364
|
-
this._data = null;
|
|
2365
|
-
this._loadData$ = null;
|
|
2366
|
-
throw e;
|
|
2367
|
-
});
|
|
2368
|
-
}
|
|
2369
|
-
return this._loadData$;
|
|
2370
|
-
}
|
|
2371
|
-
/**
|
|
2372
|
-
* Releases the currently-loaded storage tree from worker memory, freeing it
|
|
2373
|
-
* up to be garbage collected. The next time a user will join the room, the
|
|
2374
|
-
* room will be reloaded from storage.
|
|
2394
|
+
* Releases the currently-loaded storage tree and Yjs documents from worker
|
|
2395
|
+
* memory, freeing them up to be garbage collected. The next time the room
|
|
2396
|
+
* is used, fresh instances will lazily be created.
|
|
2375
2397
|
*/
|
|
2376
2398
|
unload(ctx) {
|
|
2377
|
-
_optionalChain([this, 'access',
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
}
|
|
2382
|
-
this._loadData$ = null;
|
|
2383
|
-
_optionalChain([this, 'access', _85 => _85.hooks, 'access', _86 => _86.onRoomDidUnload, 'optionalCall', _87 => _87(ctx)]);
|
|
2399
|
+
_optionalChain([this, 'access', _81 => _81.hooks, 'access', _82 => _82.onRoomWillUnload, 'optionalCall', _83 => _83(ctx)]);
|
|
2400
|
+
this.driver.reinitialize();
|
|
2401
|
+
this._storage = new Storage(this.driver);
|
|
2402
|
+
this._yjsStorage = new YjsStorage(this.driver);
|
|
2384
2403
|
}
|
|
2385
2404
|
/**
|
|
2386
2405
|
* Issues a Ticket with a new/unique actor ID
|
|
@@ -2393,26 +2412,26 @@ var Room = class {
|
|
|
2393
2412
|
* connection is established. If the socket is never established, this
|
|
2394
2413
|
* unused Ticket will simply get garbage collected.
|
|
2395
2414
|
*/
|
|
2396
|
-
|
|
2397
|
-
const actor
|
|
2415
|
+
createTicket(options) {
|
|
2416
|
+
const actor = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _84 => _84.actor]), () => ( this.getNextActor()));
|
|
2398
2417
|
const sessionKey = _nanoid.nanoid.call(void 0, );
|
|
2399
|
-
const info = _optionalChain([options, 'optionalAccess',
|
|
2418
|
+
const info = _optionalChain([options, 'optionalAccess', _85 => _85.info]);
|
|
2400
2419
|
const ticket = {
|
|
2401
|
-
version: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
2402
|
-
actor
|
|
2420
|
+
version: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _86 => _86.version]), () => ( HIGHEST_PROTOCOL_VERSION)),
|
|
2421
|
+
actor,
|
|
2403
2422
|
sessionKey,
|
|
2404
|
-
meta: _optionalChain([options, 'optionalAccess',
|
|
2405
|
-
publicMeta: _optionalChain([options, 'optionalAccess',
|
|
2406
|
-
user: _optionalChain([options, 'optionalAccess',
|
|
2407
|
-
scopes: _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
2423
|
+
meta: _optionalChain([options, 'optionalAccess', _87 => _87.meta]),
|
|
2424
|
+
publicMeta: _optionalChain([options, 'optionalAccess', _88 => _88.publicMeta]),
|
|
2425
|
+
user: _optionalChain([options, 'optionalAccess', _89 => _89.id]) ? { id: options.id, info } : { anonymousId: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _90 => _90.anonymousId]), () => ( _nanoid.nanoid.call(void 0, ))), info },
|
|
2426
|
+
scopes: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _91 => _91.scopes]), () => ( ["room:write"]))
|
|
2408
2427
|
};
|
|
2409
2428
|
if (__privateGet(this, __debug2)) {
|
|
2410
2429
|
console.log(`new ticket created: ${JSON.stringify(ticket)}`);
|
|
2411
2430
|
}
|
|
2412
2431
|
return ticket;
|
|
2413
2432
|
}
|
|
2414
|
-
|
|
2415
|
-
const ticket =
|
|
2433
|
+
createBackendSession_experimental() {
|
|
2434
|
+
const ticket = this.createTicket();
|
|
2416
2435
|
const capturedServerMsgs = [];
|
|
2417
2436
|
const stub = {
|
|
2418
2437
|
send: (data) => {
|
|
@@ -2453,7 +2472,7 @@ var Room = class {
|
|
|
2453
2472
|
newSession.markActive(lastActivity);
|
|
2454
2473
|
}
|
|
2455
2474
|
}
|
|
2456
|
-
|
|
2475
|
+
sendSessionStartMessages(newSession, ticket, ctx, defer = () => {
|
|
2457
2476
|
throw new Error(
|
|
2458
2477
|
"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."
|
|
2459
2478
|
);
|
|
@@ -2466,10 +2485,7 @@ var Room = class {
|
|
|
2466
2485
|
scopes: session.scopes
|
|
2467
2486
|
};
|
|
2468
2487
|
}
|
|
2469
|
-
const leasedSessions =
|
|
2470
|
-
ctx,
|
|
2471
|
-
defer
|
|
2472
|
-
);
|
|
2488
|
+
const leasedSessions = this.listLeasedSessions(ctx, defer);
|
|
2473
2489
|
for (const leasedSession of leasedSessions) {
|
|
2474
2490
|
users[leasedSession.actorId] = {
|
|
2475
2491
|
id: leasedSession.sessionId,
|
|
@@ -2505,7 +2521,7 @@ var Room = class {
|
|
|
2505
2521
|
* - Sends a ROOM_STATE message to the socket.
|
|
2506
2522
|
* - Broadcasts a USER_JOINED message to all other sessions in the room.
|
|
2507
2523
|
*/
|
|
2508
|
-
|
|
2524
|
+
startBrowserSession(ticket, socket, ctx, defer = () => {
|
|
2509
2525
|
throw new Error(
|
|
2510
2526
|
"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."
|
|
2511
2527
|
);
|
|
@@ -2519,13 +2535,13 @@ var Room = class {
|
|
|
2519
2535
|
ctx,
|
|
2520
2536
|
defer
|
|
2521
2537
|
);
|
|
2522
|
-
this.
|
|
2538
|
+
__privateGet(this, _logger).warn(
|
|
2523
2539
|
`Previous session for actor ${ticket.actor} killed in favor of new session`
|
|
2524
2540
|
);
|
|
2525
2541
|
}
|
|
2526
2542
|
const newSession = new BrowserSession(ticket, socket, __privateGet(this, __debug2));
|
|
2527
2543
|
this.sessions.set(ticket.sessionKey, newSession);
|
|
2528
|
-
|
|
2544
|
+
this.sendSessionStartMessages(newSession, ticket, ctx, defer);
|
|
2529
2545
|
this.sendToOthers(
|
|
2530
2546
|
ticket.sessionKey,
|
|
2531
2547
|
{
|
|
@@ -2538,7 +2554,7 @@ var Room = class {
|
|
|
2538
2554
|
ctx,
|
|
2539
2555
|
defer
|
|
2540
2556
|
);
|
|
2541
|
-
const p$ = _optionalChain([this, 'access',
|
|
2557
|
+
const p$ = _optionalChain([this, 'access', _92 => _92.hooks, 'access', _93 => _93.onSessionDidStart, 'optionalCall', _94 => _94(newSession, ctx)]);
|
|
2542
2558
|
if (p$) defer(p$);
|
|
2543
2559
|
}
|
|
2544
2560
|
/**
|
|
@@ -2562,7 +2578,7 @@ var Room = class {
|
|
|
2562
2578
|
for (const other of this.otherSessions(key)) {
|
|
2563
2579
|
other.send({ type: ServerMsgCode2.USER_LEFT, actor: session.actor });
|
|
2564
2580
|
}
|
|
2565
|
-
const p$ = _optionalChain([this, 'access',
|
|
2581
|
+
const p$ = _optionalChain([this, 'access', _95 => _95.hooks, 'access', _96 => _96.onSessionDidEnd, 'optionalCall', _97 => _97(session, ctx)]);
|
|
2566
2582
|
if (p$) defer(p$);
|
|
2567
2583
|
}
|
|
2568
2584
|
}
|
|
@@ -2593,7 +2609,7 @@ var Room = class {
|
|
|
2593
2609
|
);
|
|
2594
2610
|
}) {
|
|
2595
2611
|
const text = typeof data === "string" ? data : _core.raise.call(void 0, "Unsupported message format");
|
|
2596
|
-
_optionalChain([this, 'access',
|
|
2612
|
+
_optionalChain([this, 'access', _98 => _98.sessions, 'access', _99 => _99.get, 'call', _100 => _100(key), 'optionalAccess', _101 => _101.markActive, 'call', _102 => _102()]);
|
|
2597
2613
|
if (text === "ping") {
|
|
2598
2614
|
await this.handlePing(key, ctx);
|
|
2599
2615
|
} else {
|
|
@@ -2633,7 +2649,6 @@ var Room = class {
|
|
|
2633
2649
|
* 2. The ClientMsg payload has been validated to be correct.
|
|
2634
2650
|
*/
|
|
2635
2651
|
async processClientMsg(key, messages, ctx) {
|
|
2636
|
-
await this.load(ctx);
|
|
2637
2652
|
const { defer, waitAll } = collectSideEffects();
|
|
2638
2653
|
await this.mutex.runExclusive(
|
|
2639
2654
|
() => this._processClientMsg_withExclusiveAccess(key, messages, ctx, defer)
|
|
@@ -2660,7 +2675,6 @@ var Room = class {
|
|
|
2660
2675
|
* a session.
|
|
2661
2676
|
*/
|
|
2662
2677
|
async processClientMsgFromBackendSession(session, messages, ctx) {
|
|
2663
|
-
await this.load(ctx);
|
|
2664
2678
|
const { defer, waitAll } = collectSideEffects();
|
|
2665
2679
|
await this.mutex.runExclusive(
|
|
2666
2680
|
() => this._processClientMsgFromBackendSession_withExclusiveAccess(
|
|
@@ -2682,18 +2696,18 @@ var Room = class {
|
|
|
2682
2696
|
* Upsert a leased session. Creates a new session if it doesn't exist (or is expired),
|
|
2683
2697
|
* or updates an existing session with merged presence.
|
|
2684
2698
|
*/
|
|
2685
|
-
|
|
2699
|
+
upsertLeasedSession(sessionId, presence, ttl, info, ctx, defer = () => {
|
|
2686
2700
|
throw new Error(
|
|
2687
2701
|
"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."
|
|
2688
2702
|
);
|
|
2689
2703
|
}) {
|
|
2690
|
-
const existingSession =
|
|
2704
|
+
const existingSession = this.driver.get_leased_session(sessionId);
|
|
2691
2705
|
const isExpired = existingSession !== void 0 && isLeasedSessionExpired(existingSession);
|
|
2692
2706
|
if (isExpired) {
|
|
2693
|
-
|
|
2707
|
+
this.deleteLeasedSession(existingSession, ctx, defer);
|
|
2694
2708
|
}
|
|
2695
2709
|
if (existingSession === void 0 || isExpired) {
|
|
2696
|
-
const actorId =
|
|
2710
|
+
const actorId = this.getNextActor();
|
|
2697
2711
|
const now = Date.now();
|
|
2698
2712
|
const session = {
|
|
2699
2713
|
sessionId,
|
|
@@ -2703,7 +2717,7 @@ var Room = class {
|
|
|
2703
2717
|
ttl,
|
|
2704
2718
|
actorId
|
|
2705
2719
|
};
|
|
2706
|
-
|
|
2720
|
+
this.driver.put_leased_session(session);
|
|
2707
2721
|
this.sendToAll(
|
|
2708
2722
|
{
|
|
2709
2723
|
type: ServerMsgCode2.USER_JOINED,
|
|
@@ -2737,7 +2751,7 @@ var Room = class {
|
|
|
2737
2751
|
updatedAt: Date.now(),
|
|
2738
2752
|
ttl
|
|
2739
2753
|
};
|
|
2740
|
-
|
|
2754
|
+
this.driver.put_leased_session(updatedSession);
|
|
2741
2755
|
this.sendToAll(
|
|
2742
2756
|
{
|
|
2743
2757
|
type: ServerMsgCode2.UPDATE_PRESENCE,
|
|
@@ -2754,13 +2768,12 @@ var Room = class {
|
|
|
2754
2768
|
/**
|
|
2755
2769
|
* List all server sessions. As a side effect, it will delete expired sessions.
|
|
2756
2770
|
*/
|
|
2757
|
-
|
|
2771
|
+
listLeasedSessions(ctx, defer = () => {
|
|
2758
2772
|
throw new Error(
|
|
2759
2773
|
"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."
|
|
2760
2774
|
);
|
|
2761
2775
|
}) {
|
|
2762
|
-
|
|
2763
|
-
const sessions = await this.driver.list_leased_sessions();
|
|
2776
|
+
const sessions = this.driver.list_leased_sessions();
|
|
2764
2777
|
const validSessions = [];
|
|
2765
2778
|
const toDelete = [];
|
|
2766
2779
|
for (const [_, session] of sessions) {
|
|
@@ -2771,14 +2784,14 @@ var Room = class {
|
|
|
2771
2784
|
}
|
|
2772
2785
|
}
|
|
2773
2786
|
for (const session of toDelete) {
|
|
2774
|
-
|
|
2787
|
+
this.deleteLeasedSession(session, ctx, defer);
|
|
2775
2788
|
}
|
|
2776
2789
|
return validSessions;
|
|
2777
2790
|
}
|
|
2778
2791
|
/**
|
|
2779
2792
|
* Delete a server session and broadcast USER_LEFT to all sessions.
|
|
2780
2793
|
*/
|
|
2781
|
-
|
|
2794
|
+
deleteLeasedSession(session, ctx, defer = () => {
|
|
2782
2795
|
throw new Error(
|
|
2783
2796
|
"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."
|
|
2784
2797
|
);
|
|
@@ -2791,20 +2804,19 @@ var Room = class {
|
|
|
2791
2804
|
ctx,
|
|
2792
2805
|
defer
|
|
2793
2806
|
);
|
|
2794
|
-
|
|
2807
|
+
this.driver.delete_leased_session(session.sessionId);
|
|
2795
2808
|
}
|
|
2796
2809
|
/**
|
|
2797
2810
|
* Delete all server sessions and broadcast USER_LEFT to all sessions.
|
|
2798
2811
|
*/
|
|
2799
|
-
|
|
2812
|
+
deleteAllLeasedSessions(ctx, defer = () => {
|
|
2800
2813
|
throw new Error(
|
|
2801
2814
|
"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."
|
|
2802
2815
|
);
|
|
2803
2816
|
}) {
|
|
2804
|
-
|
|
2805
|
-
const sessions = await this.driver.list_leased_sessions();
|
|
2817
|
+
const sessions = this.driver.list_leased_sessions();
|
|
2806
2818
|
for (const [_, session] of sessions) {
|
|
2807
|
-
|
|
2819
|
+
this.deleteLeasedSession(session, ctx, defer);
|
|
2808
2820
|
}
|
|
2809
2821
|
}
|
|
2810
2822
|
// ---------------------------------------------------------------------------
|
|
@@ -2813,53 +2825,53 @@ var Room = class {
|
|
|
2813
2825
|
/**
|
|
2814
2826
|
* List feeds with pagination and filtering.
|
|
2815
2827
|
*/
|
|
2816
|
-
|
|
2817
|
-
return
|
|
2828
|
+
listFeeds(options) {
|
|
2829
|
+
return this.driver.list_feeds(options);
|
|
2818
2830
|
}
|
|
2819
2831
|
/**
|
|
2820
2832
|
* Get a specific feed by feed ID.
|
|
2821
2833
|
*/
|
|
2822
|
-
|
|
2823
|
-
return
|
|
2834
|
+
getFeed(feedId) {
|
|
2835
|
+
return this.driver.get_feed(feedId);
|
|
2824
2836
|
}
|
|
2825
2837
|
/**
|
|
2826
2838
|
* Create a new feed.
|
|
2827
2839
|
* If timestamp is not provided, current server time is used.
|
|
2828
2840
|
*/
|
|
2829
|
-
|
|
2841
|
+
createFeed(feed) {
|
|
2830
2842
|
const now = _nullishCoalesce(feed.timestamp, () => ( Date.now()));
|
|
2831
2843
|
const fullFeed = {
|
|
2832
2844
|
...feed,
|
|
2833
2845
|
createdAt: now,
|
|
2834
2846
|
updatedAt: now
|
|
2835
2847
|
};
|
|
2836
|
-
|
|
2848
|
+
this.driver.create_feed(fullFeed);
|
|
2837
2849
|
return fullFeed;
|
|
2838
2850
|
}
|
|
2839
2851
|
/**
|
|
2840
2852
|
* Update a feed's metadata.
|
|
2841
2853
|
*/
|
|
2842
|
-
|
|
2843
|
-
|
|
2854
|
+
updateFeedMetadata(feedId, metadata) {
|
|
2855
|
+
this.driver.update_feed_metadata(feedId, metadata);
|
|
2844
2856
|
}
|
|
2845
2857
|
/**
|
|
2846
2858
|
* Delete a feed.
|
|
2847
2859
|
*/
|
|
2848
|
-
|
|
2849
|
-
|
|
2860
|
+
deleteFeed(feedId) {
|
|
2861
|
+
this.driver.delete_feed(feedId);
|
|
2850
2862
|
}
|
|
2851
2863
|
/**
|
|
2852
2864
|
* List feed messages for a feed with pagination.
|
|
2853
2865
|
*/
|
|
2854
|
-
|
|
2855
|
-
return
|
|
2866
|
+
listFeedMessages(feedId, options) {
|
|
2867
|
+
return this.driver.list_feed_messages(feedId, options);
|
|
2856
2868
|
}
|
|
2857
2869
|
/**
|
|
2858
2870
|
* Add a message to a feed.
|
|
2859
2871
|
* If message id is not provided, a unique ID is automatically generated.
|
|
2860
2872
|
* If timestamp is not provided, current server time is used.
|
|
2861
2873
|
*/
|
|
2862
|
-
|
|
2874
|
+
addFeedMessage(feedId, message) {
|
|
2863
2875
|
const now = _nullishCoalesce(message.timestamp, () => ( Date.now()));
|
|
2864
2876
|
const fullMessage = {
|
|
2865
2877
|
id: _nullishCoalesce(message.id, () => ( _nanoid.nanoid.call(void 0, ))),
|
|
@@ -2867,15 +2879,15 @@ var Room = class {
|
|
|
2867
2879
|
updatedAt: now,
|
|
2868
2880
|
data: message.data
|
|
2869
2881
|
};
|
|
2870
|
-
|
|
2882
|
+
this.driver.add_feed_message(feedId, fullMessage);
|
|
2871
2883
|
return fullMessage;
|
|
2872
2884
|
}
|
|
2873
2885
|
/**
|
|
2874
2886
|
* Update a feed message's data.
|
|
2875
2887
|
* Returns the updated message.
|
|
2876
2888
|
*/
|
|
2877
|
-
|
|
2878
|
-
return
|
|
2889
|
+
updateFeedMessage(feedId, messageId, data, timestamp) {
|
|
2890
|
+
return this.driver.update_feed_message(
|
|
2879
2891
|
feedId,
|
|
2880
2892
|
messageId,
|
|
2881
2893
|
data,
|
|
@@ -2885,8 +2897,8 @@ var Room = class {
|
|
|
2885
2897
|
/**
|
|
2886
2898
|
* Delete a feed message.
|
|
2887
2899
|
*/
|
|
2888
|
-
|
|
2889
|
-
|
|
2900
|
+
deleteFeedMessage(feedId, messageId) {
|
|
2901
|
+
this.driver.delete_feed_message(feedId, messageId);
|
|
2890
2902
|
}
|
|
2891
2903
|
/**
|
|
2892
2904
|
* Will send the given ServerMsg through all Session, except the Session
|
|
@@ -2933,37 +2945,15 @@ var Room = class {
|
|
|
2933
2945
|
}
|
|
2934
2946
|
}
|
|
2935
2947
|
}
|
|
2936
|
-
// ------------------------------------------------------------------------------------
|
|
2937
|
-
// Private APIs
|
|
2938
|
-
// ------------------------------------------------------------------------------------
|
|
2939
|
-
async _loadStorage() {
|
|
2940
|
-
const storage = new Storage(this.driver);
|
|
2941
|
-
await storage.load(this.logger);
|
|
2942
|
-
return storage;
|
|
2943
|
-
}
|
|
2944
|
-
async _loadYjsStorage() {
|
|
2945
|
-
const yjsStorage = new YjsStorage(this.driver);
|
|
2946
|
-
await yjsStorage.load(this.logger);
|
|
2947
|
-
return yjsStorage;
|
|
2948
|
-
}
|
|
2949
|
-
// Don't ever manually call this!
|
|
2950
|
-
async _load(ctx) {
|
|
2951
|
-
await _optionalChain([this, 'access', _107 => _107.hooks, 'access', _108 => _108.onRoomWillLoad, 'optionalCall', _109 => _109(ctx)]);
|
|
2952
|
-
const storage = await this._loadStorage();
|
|
2953
|
-
const yjsStorage = await this._loadYjsStorage();
|
|
2954
|
-
this._data = {
|
|
2955
|
-
mutex: new (0, _asyncmutex.Mutex)(),
|
|
2956
|
-
storage,
|
|
2957
|
-
yjsStorage
|
|
2958
|
-
};
|
|
2959
|
-
await _optionalChain([this, 'access', _110 => _110.hooks, 'access', _111 => _111.onRoomDidLoad, 'optionalCall', _112 => _112(ctx)]);
|
|
2960
|
-
}
|
|
2961
2948
|
/**
|
|
2962
2949
|
* Returns a new, unique, actor ID.
|
|
2963
2950
|
*/
|
|
2964
|
-
|
|
2965
|
-
return
|
|
2951
|
+
getNextActor() {
|
|
2952
|
+
return this.driver.next_actor();
|
|
2966
2953
|
}
|
|
2954
|
+
// ------------------------------------------------------------------------------------
|
|
2955
|
+
// Private APIs
|
|
2956
|
+
// ------------------------------------------------------------------------------------
|
|
2967
2957
|
/**
|
|
2968
2958
|
* Iterates over all *other* Sessions and their session keys.
|
|
2969
2959
|
*/
|
|
@@ -2991,18 +2981,18 @@ var Room = class {
|
|
|
2991
2981
|
async handlePing(sessionKey, ctx) {
|
|
2992
2982
|
const session = this.sessions.get(sessionKey);
|
|
2993
2983
|
if (session === void 0) {
|
|
2994
|
-
this.
|
|
2984
|
+
__privateGet(this, _logger).withContext({ sessionKey }).warn("[probe] in handlePing, no such session exists");
|
|
2995
2985
|
return;
|
|
2996
2986
|
}
|
|
2997
2987
|
const sent = session.sendPong();
|
|
2998
2988
|
if (sent !== 0) {
|
|
2999
|
-
await _optionalChain([this, 'access',
|
|
2989
|
+
await _optionalChain([this, 'access', _103 => _103.hooks, 'access', _104 => _104.onDidPong, 'optionalCall', _105 => _105(ctx)]);
|
|
3000
2990
|
}
|
|
3001
2991
|
}
|
|
3002
|
-
|
|
2992
|
+
_processClientMsg_withExclusiveAccess(sessionKey, messages, ctx, defer) {
|
|
3003
2993
|
const session = this.sessions.get(sessionKey);
|
|
3004
2994
|
if (!session) {
|
|
3005
|
-
this.
|
|
2995
|
+
__privateGet(this, _logger).withContext({ sessionKey }).warn("[probe] in handleClientMsgs, no such session exists");
|
|
3006
2996
|
return;
|
|
3007
2997
|
}
|
|
3008
2998
|
const toFanOut = [];
|
|
@@ -3013,7 +3003,7 @@ var Room = class {
|
|
|
3013
3003
|
for (const msg of messages) {
|
|
3014
3004
|
const isMsgAllowed = this.hooks.isClientMsgAllowed(msg, session);
|
|
3015
3005
|
if (isMsgAllowed.allowed) {
|
|
3016
|
-
|
|
3006
|
+
this.handleOne(
|
|
3017
3007
|
session,
|
|
3018
3008
|
msg,
|
|
3019
3009
|
replyImmediately,
|
|
@@ -3043,7 +3033,7 @@ var Room = class {
|
|
|
3043
3033
|
// TODO It's a bit bothering how much duplication there is between this method
|
|
3044
3034
|
// and the _processClientMsg_withExclusiveAccess version. A better
|
|
3045
3035
|
// abstraction is needed.
|
|
3046
|
-
|
|
3036
|
+
_processClientMsgFromBackendSession_withExclusiveAccess(session, messages, ctx, defer) {
|
|
3047
3037
|
const toFanOut = [];
|
|
3048
3038
|
const toReplyImmediately = [];
|
|
3049
3039
|
const toReplyAfter = [];
|
|
@@ -3065,7 +3055,7 @@ var Room = class {
|
|
|
3065
3055
|
const scheduleFanOut = (msg) => void toFanOut.push(msg);
|
|
3066
3056
|
const scheduleReply = (msg) => void toReplyAfter.push(msg);
|
|
3067
3057
|
for (const msg of messages) {
|
|
3068
|
-
|
|
3058
|
+
this.handleOne(
|
|
3069
3059
|
session,
|
|
3070
3060
|
msg,
|
|
3071
3061
|
replyImmediately,
|
|
@@ -3088,7 +3078,7 @@ var Room = class {
|
|
|
3088
3078
|
toReplyAfter.length = 0;
|
|
3089
3079
|
}
|
|
3090
3080
|
}
|
|
3091
|
-
|
|
3081
|
+
handleOne(session, msg, replyImmediately, scheduleFanOut, scheduleReply, ctx, defer) {
|
|
3092
3082
|
if (!this.mutex.isLocked()) {
|
|
3093
3083
|
throw new Error("Handling messages requires exclusive access");
|
|
3094
3084
|
}
|
|
@@ -3112,7 +3102,7 @@ var Room = class {
|
|
|
3112
3102
|
}
|
|
3113
3103
|
case _core.ClientMsgCode.FETCH_STORAGE: {
|
|
3114
3104
|
if (session.version >= 8 /* V8 */) {
|
|
3115
|
-
const rawStream = this.storage.
|
|
3105
|
+
const rawStream = this.storage.driver.iter_nodes_optimized();
|
|
3116
3106
|
for (const chunk of groupNodesForWebSocketMessages(rawStream)) {
|
|
3117
3107
|
const frame = `{"type":${ServerMsgCode2.STORAGE_CHUNK},"nodes":[${chunk.join(",")}]}`;
|
|
3118
3108
|
replyImmediately(frame);
|
|
@@ -3121,28 +3111,32 @@ var Room = class {
|
|
|
3121
3111
|
} else {
|
|
3122
3112
|
replyImmediately({
|
|
3123
3113
|
type: ServerMsgCode2.STORAGE_STATE_V7,
|
|
3124
|
-
items: Array.from(this.storage.
|
|
3114
|
+
items: Array.from(this.storage.driver.iter_nodes())
|
|
3125
3115
|
});
|
|
3126
3116
|
}
|
|
3127
3117
|
break;
|
|
3128
3118
|
}
|
|
3129
3119
|
case _core.ClientMsgCode.UPDATE_STORAGE: {
|
|
3130
|
-
_optionalChain([this, 'access',
|
|
3131
|
-
const result =
|
|
3120
|
+
_optionalChain([this, 'access', _106 => _106.driver, 'access', _107 => _107.bump_storage_version, 'optionalCall', _108 => _108()]);
|
|
3121
|
+
const result = this.storage.applyOps(msg.ops);
|
|
3132
3122
|
const opsToForward = result.flatMap(
|
|
3133
3123
|
(r) => r.action === "accepted" ? [r.op] : []
|
|
3134
3124
|
);
|
|
3135
|
-
const opsToSendBack = result.flatMap(
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3125
|
+
const opsToSendBack = result.flatMap(
|
|
3126
|
+
(r) => {
|
|
3127
|
+
switch (r.action) {
|
|
3128
|
+
case "ignored":
|
|
3129
|
+
return r.ignoredOpId !== void 0 ? [ackIgnoredOp(r.ignoredOpId)] : [];
|
|
3130
|
+
case "rectified":
|
|
3131
|
+
return [r.ackOp, r.fix];
|
|
3132
|
+
case "accepted":
|
|
3133
|
+
return r.fix !== void 0 ? [r.fix] : [];
|
|
3134
|
+
// istanbul ignore next
|
|
3135
|
+
default:
|
|
3136
|
+
return _core.assertNever.call(void 0, r, "Unhandled case");
|
|
3137
|
+
}
|
|
3144
3138
|
}
|
|
3145
|
-
|
|
3139
|
+
);
|
|
3146
3140
|
if (opsToForward.length > 0) {
|
|
3147
3141
|
scheduleFanOut({
|
|
3148
3142
|
type: ServerMsgCode2.UPDATE_STORAGE,
|
|
@@ -3160,7 +3154,7 @@ var Room = class {
|
|
|
3160
3154
|
});
|
|
3161
3155
|
}
|
|
3162
3156
|
if (opsToForward.length > 0) {
|
|
3163
|
-
const p$ = _optionalChain([this, 'access',
|
|
3157
|
+
const p$ = _optionalChain([this, 'access', _109 => _109.hooks, 'access', _110 => _110.postClientMsgStorageDidUpdate, 'optionalCall', _111 => _111(ctx)]);
|
|
3164
3158
|
if (p$) defer(p$);
|
|
3165
3159
|
}
|
|
3166
3160
|
break;
|
|
@@ -3169,11 +3163,17 @@ var Room = class {
|
|
|
3169
3163
|
const vector = msg.vector;
|
|
3170
3164
|
const guid = msg.guid;
|
|
3171
3165
|
const isV2 = msg.v2;
|
|
3172
|
-
const
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3166
|
+
const update = this.yjsStorage.getYDocUpdate(
|
|
3167
|
+
__privateGet(this, _logger),
|
|
3168
|
+
vector,
|
|
3169
|
+
guid,
|
|
3170
|
+
isV2
|
|
3171
|
+
);
|
|
3172
|
+
const stateVector = this.yjsStorage.getYStateVector(__privateGet(this, _logger), guid);
|
|
3173
|
+
const snapshotHash = this.yjsStorage.getSnapshotHash(__privateGet(this, _logger), {
|
|
3174
|
+
guid,
|
|
3175
|
+
isV2
|
|
3176
|
+
});
|
|
3177
3177
|
if (update !== null && snapshotHash !== null) {
|
|
3178
3178
|
replyImmediately({
|
|
3179
3179
|
type: ServerMsgCode2.UPDATE_YDOC,
|
|
@@ -3192,8 +3192,8 @@ var Room = class {
|
|
|
3192
3192
|
const update = msg.update;
|
|
3193
3193
|
const guid = msg.guid;
|
|
3194
3194
|
const isV2 = msg.v2;
|
|
3195
|
-
const [result, error] =
|
|
3196
|
-
this.yjsStorage.addYDocUpdate(this
|
|
3195
|
+
const [result, error] = tryCatch(
|
|
3196
|
+
() => this.yjsStorage.addYDocUpdate(__privateGet(this, _logger), update, guid, isV2)
|
|
3197
3197
|
);
|
|
3198
3198
|
if (error)
|
|
3199
3199
|
break;
|
|
@@ -3211,7 +3211,7 @@ var Room = class {
|
|
|
3211
3211
|
defer
|
|
3212
3212
|
);
|
|
3213
3213
|
if (result.isUpdated) {
|
|
3214
|
-
const p$ = _optionalChain([this, 'access',
|
|
3214
|
+
const p$ = _optionalChain([this, 'access', _112 => _112.hooks, 'access', _113 => _113.postClientMsgYdocDidUpdate, 'optionalCall', _114 => _114(ctx, session)]);
|
|
3215
3215
|
if (p$) defer(p$);
|
|
3216
3216
|
}
|
|
3217
3217
|
break;
|
|
@@ -3219,8 +3219,8 @@ var Room = class {
|
|
|
3219
3219
|
// Feed messages
|
|
3220
3220
|
case FeedMsgCode.FETCH_FEEDS: {
|
|
3221
3221
|
const fetchMsg = msg;
|
|
3222
|
-
const [result, err] =
|
|
3223
|
-
this.listFeeds({
|
|
3222
|
+
const [result, err] = tryCatch(
|
|
3223
|
+
() => this.listFeeds({
|
|
3224
3224
|
cursor: fetchMsg.cursor,
|
|
3225
3225
|
since: fetchMsg.since,
|
|
3226
3226
|
limit: fetchMsg.limit,
|
|
@@ -3241,8 +3241,8 @@ var Room = class {
|
|
|
3241
3241
|
}
|
|
3242
3242
|
case FeedMsgCode.FETCH_FEED_MESSAGES: {
|
|
3243
3243
|
const fetchMsg = msg;
|
|
3244
|
-
const [result, err] =
|
|
3245
|
-
this.listFeedMessages(fetchMsg.feedId, {
|
|
3244
|
+
const [result, err] = tryCatch(
|
|
3245
|
+
() => this.listFeedMessages(fetchMsg.feedId, {
|
|
3246
3246
|
cursor: fetchMsg.cursor,
|
|
3247
3247
|
since: fetchMsg.since,
|
|
3248
3248
|
limit: fetchMsg.limit
|
|
@@ -3263,8 +3263,8 @@ var Room = class {
|
|
|
3263
3263
|
}
|
|
3264
3264
|
case FeedMsgCode.ADD_FEED: {
|
|
3265
3265
|
const addMsg = msg;
|
|
3266
|
-
const [feed, err] =
|
|
3267
|
-
this.createFeed({
|
|
3266
|
+
const [feed, err] = tryCatch(
|
|
3267
|
+
() => this.createFeed({
|
|
3268
3268
|
feedId: addMsg.feedId,
|
|
3269
3269
|
metadata: _nullishCoalesce(addMsg.metadata, () => ( {}))
|
|
3270
3270
|
})
|
|
@@ -3283,14 +3283,14 @@ var Room = class {
|
|
|
3283
3283
|
}
|
|
3284
3284
|
case FeedMsgCode.UPDATE_FEED: {
|
|
3285
3285
|
const updateMsg = msg;
|
|
3286
|
-
const [, metaErr] =
|
|
3287
|
-
this.updateFeedMetadata(updateMsg.feedId, updateMsg.metadata)
|
|
3286
|
+
const [, metaErr] = tryCatch(
|
|
3287
|
+
() => this.updateFeedMetadata(updateMsg.feedId, updateMsg.metadata)
|
|
3288
3288
|
);
|
|
3289
3289
|
if (metaErr) {
|
|
3290
3290
|
replyImmediately(feedFailureServerMsg(updateMsg.requestId, metaErr));
|
|
3291
3291
|
break;
|
|
3292
3292
|
}
|
|
3293
|
-
const feed =
|
|
3293
|
+
const feed = this.getFeed(updateMsg.feedId);
|
|
3294
3294
|
if (!feed) {
|
|
3295
3295
|
replyImmediately(
|
|
3296
3296
|
feedRequestFailed(
|
|
@@ -3310,7 +3310,7 @@ var Room = class {
|
|
|
3310
3310
|
}
|
|
3311
3311
|
case FeedMsgCode.DELETE_FEED: {
|
|
3312
3312
|
const deleteMsg = msg;
|
|
3313
|
-
const [, err] =
|
|
3313
|
+
const [, err] = tryCatch(() => this.deleteFeed(deleteMsg.feedId));
|
|
3314
3314
|
if (err) {
|
|
3315
3315
|
replyImmediately(feedFailureServerMsg(deleteMsg.requestId, err));
|
|
3316
3316
|
break;
|
|
@@ -3325,8 +3325,8 @@ var Room = class {
|
|
|
3325
3325
|
}
|
|
3326
3326
|
case FeedMsgCode.ADD_FEED_MESSAGE: {
|
|
3327
3327
|
const addMsg = msg;
|
|
3328
|
-
const [message, err] =
|
|
3329
|
-
this.addFeedMessage(addMsg.feedId, {
|
|
3328
|
+
const [message, err] = tryCatch(
|
|
3329
|
+
() => this.addFeedMessage(addMsg.feedId, {
|
|
3330
3330
|
data: addMsg.data,
|
|
3331
3331
|
id: addMsg.id
|
|
3332
3332
|
})
|
|
@@ -3346,8 +3346,8 @@ var Room = class {
|
|
|
3346
3346
|
}
|
|
3347
3347
|
case FeedMsgCode.UPDATE_FEED_MESSAGE: {
|
|
3348
3348
|
const updateMsg = msg;
|
|
3349
|
-
const [message, err] =
|
|
3350
|
-
this.updateFeedMessage(
|
|
3349
|
+
const [message, err] = tryCatch(
|
|
3350
|
+
() => this.updateFeedMessage(
|
|
3351
3351
|
updateMsg.feedId,
|
|
3352
3352
|
updateMsg.messageId,
|
|
3353
3353
|
updateMsg.data
|
|
@@ -3368,8 +3368,8 @@ var Room = class {
|
|
|
3368
3368
|
}
|
|
3369
3369
|
case FeedMsgCode.DELETE_FEED_MESSAGE: {
|
|
3370
3370
|
const deleteMsg = msg;
|
|
3371
|
-
const [, err] =
|
|
3372
|
-
this.deleteFeedMessage(deleteMsg.feedId, deleteMsg.messageId)
|
|
3371
|
+
const [, err] = tryCatch(
|
|
3372
|
+
() => this.deleteFeedMessage(deleteMsg.feedId, deleteMsg.messageId)
|
|
3373
3373
|
);
|
|
3374
3374
|
if (err) {
|
|
3375
3375
|
replyImmediately(feedFailureServerMsg(deleteMsg.requestId, err));
|
|
@@ -3393,6 +3393,7 @@ var Room = class {
|
|
|
3393
3393
|
}
|
|
3394
3394
|
}
|
|
3395
3395
|
};
|
|
3396
|
+
_logger = new WeakMap();
|
|
3396
3397
|
__debug2 = new WeakMap();
|
|
3397
3398
|
|
|
3398
3399
|
|