@liveblocks/client 0.13.2 → 0.14.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/lib/cjs/AbstractCrdt.d.ts +8 -4
- package/lib/cjs/AbstractCrdt.js +2 -2
- package/lib/cjs/LiveList.d.ts +9 -4
- package/lib/cjs/LiveList.js +58 -9
- package/lib/cjs/LiveMap.d.ts +7 -3
- package/lib/cjs/LiveMap.js +23 -5
- package/lib/cjs/LiveObject.d.ts +17 -7
- package/lib/cjs/LiveObject.js +45 -15
- package/lib/cjs/LiveRegister.d.ts +8 -4
- package/lib/cjs/LiveRegister.js +17 -4
- package/lib/cjs/client.js +50 -7
- package/lib/cjs/{immutable/index.d.ts → immutable.d.ts} +5 -3
- package/lib/cjs/{immutable/index.js → immutable.js} +98 -21
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/live.d.ts +7 -0
- package/lib/cjs/room.d.ts +17 -9
- package/lib/cjs/room.js +203 -81
- package/lib/cjs/types.d.ts +26 -1
- package/lib/cjs/utils.d.ts +2 -1
- package/lib/cjs/utils.js +76 -1
- package/lib/esm/AbstractCrdt.d.ts +8 -4
- package/lib/esm/AbstractCrdt.js +2 -2
- package/lib/esm/LiveList.d.ts +9 -4
- package/lib/esm/LiveList.js +59 -10
- package/lib/esm/LiveMap.d.ts +7 -3
- package/lib/esm/LiveMap.js +23 -5
- package/lib/esm/LiveObject.d.ts +17 -7
- package/lib/esm/LiveObject.js +45 -15
- package/lib/esm/LiveRegister.d.ts +8 -4
- package/lib/esm/LiveRegister.js +18 -5
- package/lib/esm/client.js +50 -7
- package/lib/esm/{immutable/index.d.ts → immutable.d.ts} +5 -3
- package/lib/esm/{immutable/index.js → immutable.js} +96 -21
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/live.d.ts +7 -0
- package/lib/esm/room.d.ts +17 -9
- package/lib/esm/room.js +203 -62
- package/lib/esm/types.d.ts +26 -1
- package/lib/esm/utils.d.ts +2 -1
- package/lib/esm/utils.js +75 -1
- package/package.json +6 -2
package/lib/cjs/room.js
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
22
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -30,7 +11,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
30
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
12
|
exports.createRoom = exports.defaultState = exports.makeStateMachine = void 0;
|
|
32
13
|
const utils_1 = require("./utils");
|
|
33
|
-
const authentication_1 = __importStar(require("./authentication"));
|
|
34
14
|
const live_1 = require("./live");
|
|
35
15
|
const LiveMap_1 = require("./LiveMap");
|
|
36
16
|
const LiveObject_1 = require("./LiveObject");
|
|
@@ -58,6 +38,9 @@ function makeOthers(presenceMap) {
|
|
|
58
38
|
get count() {
|
|
59
39
|
return array.length;
|
|
60
40
|
},
|
|
41
|
+
[Symbol.iterator]() {
|
|
42
|
+
return array[Symbol.iterator]();
|
|
43
|
+
},
|
|
61
44
|
map(callback) {
|
|
62
45
|
return array.map(callback);
|
|
63
46
|
},
|
|
@@ -72,16 +55,12 @@ function log(...params) {
|
|
|
72
55
|
}
|
|
73
56
|
function makeStateMachine(state, context, mockedEffects) {
|
|
74
57
|
const effects = mockedEffects || {
|
|
75
|
-
authenticate() {
|
|
58
|
+
authenticate(auth, createWebSocket) {
|
|
76
59
|
return __awaiter(this, void 0, void 0, function* () {
|
|
77
60
|
try {
|
|
78
|
-
const token = yield (
|
|
79
|
-
const parsedToken =
|
|
80
|
-
const socket =
|
|
81
|
-
socket.addEventListener("message", onMessage);
|
|
82
|
-
socket.addEventListener("open", onOpen);
|
|
83
|
-
socket.addEventListener("close", onClose);
|
|
84
|
-
socket.addEventListener("error", onError);
|
|
61
|
+
const { token } = yield auth(context.room);
|
|
62
|
+
const parsedToken = parseToken(token);
|
|
63
|
+
const socket = createWebSocket(token);
|
|
85
64
|
authenticationSuccess(parsedToken, socket);
|
|
86
65
|
}
|
|
87
66
|
catch (er) {
|
|
@@ -129,18 +108,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
129
108
|
};
|
|
130
109
|
return genericSubscribe(cb);
|
|
131
110
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
111
|
+
function createOrUpdateRootFromMessage(message) {
|
|
112
|
+
if (message.items.length === 0) {
|
|
113
|
+
throw new Error("Internal error: cannot load storage without items");
|
|
114
|
+
}
|
|
115
|
+
if (state.root) {
|
|
116
|
+
updateRoot(message.items);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
state.root = load(message.items);
|
|
120
|
+
}
|
|
134
121
|
for (const key in state.defaultStorageRoot) {
|
|
135
122
|
if (state.root.get(key) == null) {
|
|
136
123
|
state.root.set(key, state.defaultStorageRoot[key]);
|
|
137
124
|
}
|
|
138
125
|
}
|
|
139
126
|
}
|
|
140
|
-
function
|
|
141
|
-
if (items.length === 0) {
|
|
142
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
143
|
-
}
|
|
127
|
+
function buildRootAndParentToChildren(items) {
|
|
144
128
|
const parentToChildren = new Map();
|
|
145
129
|
let root = null;
|
|
146
130
|
for (const tuple of items) {
|
|
@@ -161,6 +145,23 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
161
145
|
if (root == null) {
|
|
162
146
|
throw new Error("Root can't be null");
|
|
163
147
|
}
|
|
148
|
+
return [root, parentToChildren];
|
|
149
|
+
}
|
|
150
|
+
function updateRoot(items) {
|
|
151
|
+
if (!state.root) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const currentItems = new Map();
|
|
155
|
+
state.items.forEach((liveCrdt, id) => {
|
|
156
|
+
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
157
|
+
});
|
|
158
|
+
// Get operations that represent the diff between 2 states.
|
|
159
|
+
const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
|
|
160
|
+
const result = apply(ops, false);
|
|
161
|
+
notify(result.updates);
|
|
162
|
+
}
|
|
163
|
+
function load(items) {
|
|
164
|
+
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
164
165
|
return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
|
|
165
166
|
addItem,
|
|
166
167
|
deleteItem,
|
|
@@ -249,7 +250,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
249
250
|
state.connection.state === "connecting") {
|
|
250
251
|
return state.connection.id;
|
|
251
252
|
}
|
|
252
|
-
|
|
253
|
+
else if (state.lastConnectionId !== null) {
|
|
254
|
+
return state.lastConnectionId;
|
|
255
|
+
}
|
|
256
|
+
throw new Error("Internal. Tried to get connection id but connection was never open");
|
|
253
257
|
}
|
|
254
258
|
function generateId() {
|
|
255
259
|
return `${getConnectionId()}:${state.clock++}`;
|
|
@@ -257,7 +261,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
257
261
|
function generateOpId() {
|
|
258
262
|
return `${getConnectionId()}:${state.opClock++}`;
|
|
259
263
|
}
|
|
260
|
-
function apply(item) {
|
|
264
|
+
function apply(item, isLocal) {
|
|
261
265
|
const result = {
|
|
262
266
|
reverse: [],
|
|
263
267
|
updates: { nodes: new Set(), presence: false },
|
|
@@ -284,7 +288,11 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
284
288
|
result.updates.presence = true;
|
|
285
289
|
}
|
|
286
290
|
else {
|
|
287
|
-
|
|
291
|
+
// Ops applied after undo/redo don't have an opId.
|
|
292
|
+
if (isLocal && !op.opId) {
|
|
293
|
+
op.opId = generateOpId();
|
|
294
|
+
}
|
|
295
|
+
const applyOpResult = applyOp(op, isLocal);
|
|
288
296
|
if (applyOpResult.modified) {
|
|
289
297
|
result.updates.nodes.add(applyOpResult.modified);
|
|
290
298
|
result.reverse.unshift(...applyOpResult.reverse);
|
|
@@ -293,7 +301,10 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
293
301
|
}
|
|
294
302
|
return result;
|
|
295
303
|
}
|
|
296
|
-
function applyOp(op) {
|
|
304
|
+
function applyOp(op, isLocal) {
|
|
305
|
+
if (op.opId) {
|
|
306
|
+
state.offlineOperations.delete(op.opId);
|
|
307
|
+
}
|
|
297
308
|
switch (op.type) {
|
|
298
309
|
case live_1.OpType.DeleteObjectKey:
|
|
299
310
|
case live_1.OpType.UpdateObject:
|
|
@@ -302,7 +313,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
302
313
|
if (item == null) {
|
|
303
314
|
return { modified: false };
|
|
304
315
|
}
|
|
305
|
-
return item._apply(op);
|
|
316
|
+
return item._apply(op, isLocal);
|
|
306
317
|
}
|
|
307
318
|
case live_1.OpType.SetParentKey: {
|
|
308
319
|
const item = state.items.get(op.id);
|
|
@@ -330,28 +341,28 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
330
341
|
if (parent == null || getItem(op.id) != null) {
|
|
331
342
|
return { modified: false };
|
|
332
343
|
}
|
|
333
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
|
|
344
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
|
|
334
345
|
}
|
|
335
346
|
case live_1.OpType.CreateList: {
|
|
336
347
|
const parent = state.items.get(op.parentId);
|
|
337
348
|
if (parent == null || getItem(op.id) != null) {
|
|
338
349
|
return { modified: false };
|
|
339
350
|
}
|
|
340
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
|
|
351
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
|
|
341
352
|
}
|
|
342
353
|
case live_1.OpType.CreateRegister: {
|
|
343
354
|
const parent = state.items.get(op.parentId);
|
|
344
355
|
if (parent == null || getItem(op.id) != null) {
|
|
345
356
|
return { modified: false };
|
|
346
357
|
}
|
|
347
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
|
|
358
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
|
|
348
359
|
}
|
|
349
360
|
case live_1.OpType.CreateMap: {
|
|
350
361
|
const parent = state.items.get(op.parentId);
|
|
351
362
|
if (parent == null || getItem(op.id) != null) {
|
|
352
363
|
return { modified: false };
|
|
353
364
|
}
|
|
354
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
|
|
365
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
|
|
355
366
|
}
|
|
356
367
|
}
|
|
357
368
|
return { modified: false };
|
|
@@ -398,15 +409,14 @@ See v0.13 release notes for more information.
|
|
|
398
409
|
: null;
|
|
399
410
|
}
|
|
400
411
|
function connect() {
|
|
401
|
-
if (typeof window === "undefined") {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
412
|
if (state.connection.state !== "closed" &&
|
|
405
413
|
state.connection.state !== "unavailable") {
|
|
406
414
|
return null;
|
|
407
415
|
}
|
|
416
|
+
const auth = prepareAuthEndpoint(context.authentication, context.fetchPolyfill);
|
|
417
|
+
const createWebSocket = prepareCreateWebSocket(context.liveblocksServer, context.WebSocketPolyfill);
|
|
408
418
|
updateConnection({ state: "authenticating" });
|
|
409
|
-
effects.authenticate();
|
|
419
|
+
effects.authenticate(auth, createWebSocket);
|
|
410
420
|
}
|
|
411
421
|
function updatePresence(overrides, options) {
|
|
412
422
|
const oldValues = {};
|
|
@@ -433,6 +443,10 @@ See v0.13 release notes for more information.
|
|
|
433
443
|
}
|
|
434
444
|
}
|
|
435
445
|
function authenticationSuccess(token, socket) {
|
|
446
|
+
socket.addEventListener("message", onMessage);
|
|
447
|
+
socket.addEventListener("open", onOpen);
|
|
448
|
+
socket.addEventListener("close", onClose);
|
|
449
|
+
socket.addEventListener("error", onError);
|
|
436
450
|
updateConnection({
|
|
437
451
|
state: "connecting",
|
|
438
452
|
id: token.actor,
|
|
@@ -443,7 +457,9 @@ See v0.13 release notes for more information.
|
|
|
443
457
|
state.socket = socket;
|
|
444
458
|
}
|
|
445
459
|
function authenticationFailure(error) {
|
|
446
|
-
|
|
460
|
+
if (process.env.NODE_ENV !== "production") {
|
|
461
|
+
console.error("Call to authentication endpoint failed", error);
|
|
462
|
+
}
|
|
447
463
|
updateConnection({ state: "unavailable" });
|
|
448
464
|
state.numberOfRetry++;
|
|
449
465
|
state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
|
|
@@ -571,12 +587,13 @@ See v0.13 release notes for more information.
|
|
|
571
587
|
break;
|
|
572
588
|
}
|
|
573
589
|
case live_1.ServerMessageType.InitialStorageState: {
|
|
574
|
-
|
|
590
|
+
createOrUpdateRootFromMessage(subMessage);
|
|
591
|
+
applyAndSendOfflineOps();
|
|
575
592
|
_getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
|
|
576
593
|
break;
|
|
577
594
|
}
|
|
578
595
|
case live_1.ServerMessageType.UpdateStorage: {
|
|
579
|
-
const applyResult = apply(subMessage.ops);
|
|
596
|
+
const applyResult = apply(subMessage.ops, false);
|
|
580
597
|
for (const node of applyResult.updates.nodes) {
|
|
581
598
|
updates.nodes.add(node);
|
|
582
599
|
}
|
|
@@ -609,14 +626,20 @@ See v0.13 release notes for more information.
|
|
|
609
626
|
updateConnection({ state: "failed" });
|
|
610
627
|
const error = new LiveblocksError(event.reason, event.code);
|
|
611
628
|
for (const listener of state.listeners.error) {
|
|
612
|
-
|
|
629
|
+
if (process.env.NODE_ENV !== "production") {
|
|
630
|
+
console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
|
|
631
|
+
}
|
|
613
632
|
listener(error);
|
|
614
633
|
}
|
|
615
634
|
}
|
|
616
635
|
else if (event.wasClean === false) {
|
|
617
|
-
updateConnection({ state: "unavailable" });
|
|
618
636
|
state.numberOfRetry++;
|
|
619
|
-
|
|
637
|
+
const delay = getRetryDelay();
|
|
638
|
+
if (process.env.NODE_ENV !== "production") {
|
|
639
|
+
console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
|
|
640
|
+
}
|
|
641
|
+
updateConnection({ state: "unavailable" });
|
|
642
|
+
state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
|
|
620
643
|
}
|
|
621
644
|
else {
|
|
622
645
|
updateConnection({ state: "closed" });
|
|
@@ -640,6 +663,10 @@ See v0.13 release notes for more information.
|
|
|
640
663
|
if (state.connection.state === "connecting") {
|
|
641
664
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
642
665
|
state.numberOfRetry = 0;
|
|
666
|
+
state.lastConnectionId = state.connection.id;
|
|
667
|
+
if (state.root) {
|
|
668
|
+
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
669
|
+
}
|
|
643
670
|
tryFlushing();
|
|
644
671
|
}
|
|
645
672
|
else {
|
|
@@ -653,7 +680,7 @@ See v0.13 release notes for more information.
|
|
|
653
680
|
}
|
|
654
681
|
clearTimeout(state.timeoutHandles.pongTimeout);
|
|
655
682
|
state.timeoutHandles.pongTimeout = effects.schedulePongTimeout();
|
|
656
|
-
if (state.socket.readyState ===
|
|
683
|
+
if (state.socket.readyState === state.socket.OPEN) {
|
|
657
684
|
state.socket.send("ping");
|
|
658
685
|
}
|
|
659
686
|
}
|
|
@@ -679,11 +706,29 @@ See v0.13 release notes for more information.
|
|
|
679
706
|
clearInterval(state.intervalHandles.heartbeat);
|
|
680
707
|
connect();
|
|
681
708
|
}
|
|
682
|
-
function
|
|
683
|
-
if (state.
|
|
709
|
+
function applyAndSendOfflineOps() {
|
|
710
|
+
if (state.offlineOperations.size === 0) {
|
|
684
711
|
return;
|
|
685
712
|
}
|
|
686
|
-
|
|
713
|
+
const messages = [];
|
|
714
|
+
const ops = Array.from(state.offlineOperations.values());
|
|
715
|
+
const result = apply(ops, true);
|
|
716
|
+
messages.push({
|
|
717
|
+
type: live_1.ClientMessageType.UpdateStorage,
|
|
718
|
+
ops: ops,
|
|
719
|
+
});
|
|
720
|
+
notify(result.updates);
|
|
721
|
+
effects.send(messages);
|
|
722
|
+
}
|
|
723
|
+
function tryFlushing() {
|
|
724
|
+
const storageOps = state.buffer.storageOperations;
|
|
725
|
+
if (storageOps.length > 0) {
|
|
726
|
+
storageOps.forEach((op) => {
|
|
727
|
+
state.offlineOperations.set(op.opId, op);
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
if (state.socket == null || state.socket.readyState !== state.socket.OPEN) {
|
|
731
|
+
state.buffer.storageOperations = [];
|
|
687
732
|
return;
|
|
688
733
|
}
|
|
689
734
|
const now = Date.now();
|
|
@@ -758,8 +803,10 @@ See v0.13 release notes for more information.
|
|
|
758
803
|
function getOthers() {
|
|
759
804
|
return state.others;
|
|
760
805
|
}
|
|
761
|
-
function broadcastEvent(event
|
|
762
|
-
|
|
806
|
+
function broadcastEvent(event, options = {
|
|
807
|
+
shouldQueueEventIfNotReady: false,
|
|
808
|
+
}) {
|
|
809
|
+
if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
|
|
763
810
|
return;
|
|
764
811
|
}
|
|
765
812
|
state.buffer.messages.push({
|
|
@@ -801,7 +848,7 @@ See v0.13 release notes for more information.
|
|
|
801
848
|
return;
|
|
802
849
|
}
|
|
803
850
|
state.isHistoryPaused = false;
|
|
804
|
-
const result = apply(historyItem);
|
|
851
|
+
const result = apply(historyItem, true);
|
|
805
852
|
notify(result.updates);
|
|
806
853
|
state.redoStack.push(result.reverse);
|
|
807
854
|
for (const op of historyItem) {
|
|
@@ -820,7 +867,7 @@ See v0.13 release notes for more information.
|
|
|
820
867
|
return;
|
|
821
868
|
}
|
|
822
869
|
state.isHistoryPaused = false;
|
|
823
|
-
const result = apply(historyItem);
|
|
870
|
+
const result = apply(historyItem, true);
|
|
824
871
|
notify(result.updates);
|
|
825
872
|
state.undoStack.push(result.reverse);
|
|
826
873
|
for (const op of historyItem) {
|
|
@@ -872,14 +919,26 @@ See v0.13 release notes for more information.
|
|
|
872
919
|
}
|
|
873
920
|
state.pausedHistory = [];
|
|
874
921
|
}
|
|
922
|
+
function simulateSocketClose() {
|
|
923
|
+
if (state.socket) {
|
|
924
|
+
state.socket.close();
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
function simulateSendCloseEvent(event) {
|
|
928
|
+
if (state.socket) {
|
|
929
|
+
onClose(event);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
875
932
|
return {
|
|
876
933
|
// Internal
|
|
877
|
-
onOpen,
|
|
878
934
|
onClose,
|
|
879
935
|
onMessage,
|
|
880
936
|
authenticationSuccess,
|
|
881
937
|
heartbeat,
|
|
882
938
|
onNavigatorOnline,
|
|
939
|
+
// Internal dev tools
|
|
940
|
+
simulateSocketClose,
|
|
941
|
+
simulateSendCloseEvent,
|
|
883
942
|
// onWakeUp,
|
|
884
943
|
onVisibilityChange,
|
|
885
944
|
getUndoStack: () => state.undoStack,
|
|
@@ -912,6 +971,7 @@ exports.makeStateMachine = makeStateMachine;
|
|
|
912
971
|
function defaultState(me, defaultStorageRoot) {
|
|
913
972
|
return {
|
|
914
973
|
connection: { state: "closed" },
|
|
974
|
+
lastConnectionId: null,
|
|
915
975
|
socket: null,
|
|
916
976
|
listeners: {
|
|
917
977
|
event: [],
|
|
@@ -956,29 +1016,13 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
956
1016
|
updates: { nodes: new Set(), presence: false, others: [] },
|
|
957
1017
|
reverseOps: [],
|
|
958
1018
|
},
|
|
1019
|
+
offlineOperations: new Map(),
|
|
959
1020
|
};
|
|
960
1021
|
}
|
|
961
1022
|
exports.defaultState = defaultState;
|
|
962
|
-
function createRoom(
|
|
963
|
-
const throttleDelay = options.throttle || 100;
|
|
964
|
-
const liveblocksServer = options.liveblocksServer || "wss://liveblocks.net/v5";
|
|
965
|
-
let authEndpoint;
|
|
966
|
-
if (options.authEndpoint) {
|
|
967
|
-
authEndpoint = options.authEndpoint;
|
|
968
|
-
}
|
|
969
|
-
else {
|
|
970
|
-
const publicAuthorizeEndpoint = options.publicAuthorizeEndpoint ||
|
|
971
|
-
"https://liveblocks.io/api/public/authorize";
|
|
972
|
-
authEndpoint = publicAuthorizeEndpoint;
|
|
973
|
-
}
|
|
1023
|
+
function createRoom(options, context) {
|
|
974
1024
|
const state = defaultState(options.defaultPresence, options.defaultStorageRoot);
|
|
975
|
-
const machine = makeStateMachine(state,
|
|
976
|
-
throttleDelay,
|
|
977
|
-
liveblocksServer,
|
|
978
|
-
authEndpoint,
|
|
979
|
-
room: name,
|
|
980
|
-
publicApiKey: options.publicApiKey,
|
|
981
|
-
});
|
|
1025
|
+
const machine = makeStateMachine(state, context);
|
|
982
1026
|
const room = {
|
|
983
1027
|
/////////////
|
|
984
1028
|
// Core //
|
|
@@ -1002,6 +1046,11 @@ function createRoom(name, options) {
|
|
|
1002
1046
|
pause: machine.pauseHistory,
|
|
1003
1047
|
resume: machine.resumeHistory,
|
|
1004
1048
|
},
|
|
1049
|
+
// @ts-ignore
|
|
1050
|
+
internalDevTools: {
|
|
1051
|
+
closeWebsocket: machine.simulateSocketClose,
|
|
1052
|
+
sendCloseEvent: machine.simulateSendCloseEvent,
|
|
1053
|
+
},
|
|
1005
1054
|
};
|
|
1006
1055
|
return {
|
|
1007
1056
|
connect: machine.connect,
|
|
@@ -1018,3 +1067,76 @@ class LiveblocksError extends Error {
|
|
|
1018
1067
|
this.code = code;
|
|
1019
1068
|
}
|
|
1020
1069
|
}
|
|
1070
|
+
function parseToken(token) {
|
|
1071
|
+
const tokenParts = token.split(".");
|
|
1072
|
+
if (tokenParts.length !== 3) {
|
|
1073
|
+
throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
1074
|
+
}
|
|
1075
|
+
const data = JSON.parse(atob(tokenParts[1]));
|
|
1076
|
+
if (typeof data.actor !== "number") {
|
|
1077
|
+
throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
1078
|
+
}
|
|
1079
|
+
return data;
|
|
1080
|
+
}
|
|
1081
|
+
function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
|
|
1082
|
+
if (typeof window === "undefined" && WebSocketPolyfill == null) {
|
|
1083
|
+
throw new Error("To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill.");
|
|
1084
|
+
}
|
|
1085
|
+
const ws = WebSocketPolyfill || WebSocket;
|
|
1086
|
+
return (token) => {
|
|
1087
|
+
return new ws(`${liveblocksServer}/?token=${token}`);
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function prepareAuthEndpoint(authentication, fetchPolyfill) {
|
|
1091
|
+
if (authentication.type === "public") {
|
|
1092
|
+
if (typeof window === "undefined" && fetchPolyfill == null) {
|
|
1093
|
+
throw new Error("To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill.");
|
|
1094
|
+
}
|
|
1095
|
+
return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
1096
|
+
room,
|
|
1097
|
+
publicApiKey: authentication.publicApiKey,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
if (authentication.type === "private") {
|
|
1101
|
+
if (typeof window === "undefined" && fetchPolyfill == null) {
|
|
1102
|
+
throw new Error("To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill.");
|
|
1103
|
+
}
|
|
1104
|
+
return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
1105
|
+
room,
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
if (authentication.type === "custom") {
|
|
1109
|
+
return authentication.callback;
|
|
1110
|
+
}
|
|
1111
|
+
throw new Error("Internal error. Unexpected authentication type");
|
|
1112
|
+
}
|
|
1113
|
+
function fetchAuthEndpoint(fetch, endpoint, body) {
|
|
1114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1115
|
+
const res = yield fetch(endpoint, {
|
|
1116
|
+
method: "POST",
|
|
1117
|
+
headers: {
|
|
1118
|
+
"Content-Type": "application/json",
|
|
1119
|
+
},
|
|
1120
|
+
body: JSON.stringify(body),
|
|
1121
|
+
});
|
|
1122
|
+
if (!res.ok) {
|
|
1123
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1124
|
+
}
|
|
1125
|
+
let authResponse = null;
|
|
1126
|
+
try {
|
|
1127
|
+
authResponse = yield res.json();
|
|
1128
|
+
}
|
|
1129
|
+
catch (er) {
|
|
1130
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1131
|
+
}
|
|
1132
|
+
if (typeof authResponse.token !== "string") {
|
|
1133
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1134
|
+
}
|
|
1135
|
+
return authResponse;
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
class AuthenticationError extends Error {
|
|
1139
|
+
constructor(message) {
|
|
1140
|
+
super(message);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -28,6 +28,14 @@ export declare type LiveListUpdates<TItem = any> = {
|
|
|
28
28
|
type: "LiveList";
|
|
29
29
|
node: LiveList<TItem>;
|
|
30
30
|
};
|
|
31
|
+
export declare type BroadcastOptions = {
|
|
32
|
+
/**
|
|
33
|
+
* Whether or not event is queued if the connection is currently closed.
|
|
34
|
+
*
|
|
35
|
+
* ❗ We are not sure if we want to support this option in the future so it might be deprecated to be replaced by something else
|
|
36
|
+
*/
|
|
37
|
+
shouldQueueEventIfNotReady: boolean;
|
|
38
|
+
};
|
|
31
39
|
export declare type StorageUpdate = LiveMapUpdates | LiveObjectUpdates | LiveListUpdates;
|
|
32
40
|
export declare type StorageCallback = (updates: StorageUpdate[]) => void;
|
|
33
41
|
export declare type Client = {
|
|
@@ -65,6 +73,10 @@ export interface Others<TPresence extends Presence = Presence> {
|
|
|
65
73
|
* Number of other users in the room.
|
|
66
74
|
*/
|
|
67
75
|
readonly count: number;
|
|
76
|
+
/**
|
|
77
|
+
* Returns a new Iterator object that contains the users.
|
|
78
|
+
*/
|
|
79
|
+
[Symbol.iterator](): IterableIterator<User<TPresence>>;
|
|
68
80
|
/**
|
|
69
81
|
* Returns the array of connected users in room.
|
|
70
82
|
*/
|
|
@@ -109,6 +121,8 @@ export declare type AuthEndpoint = string | AuthEndpointCallback;
|
|
|
109
121
|
*/
|
|
110
122
|
export declare type ClientOptions = {
|
|
111
123
|
throttle?: number;
|
|
124
|
+
fetchPolyfill?: any;
|
|
125
|
+
WebSocketPolyfill?: any;
|
|
112
126
|
} & ({
|
|
113
127
|
publicApiKey: string;
|
|
114
128
|
authEndpoint?: never;
|
|
@@ -119,6 +133,17 @@ export declare type ClientOptions = {
|
|
|
119
133
|
export declare type AuthorizeResponse = {
|
|
120
134
|
token: string;
|
|
121
135
|
};
|
|
136
|
+
export declare type Authentication = {
|
|
137
|
+
type: "public";
|
|
138
|
+
publicApiKey: string;
|
|
139
|
+
url: string;
|
|
140
|
+
} | {
|
|
141
|
+
type: "private";
|
|
142
|
+
url: string;
|
|
143
|
+
} | {
|
|
144
|
+
type: "custom";
|
|
145
|
+
callback: (room: string) => Promise<AuthorizeResponse>;
|
|
146
|
+
};
|
|
122
147
|
declare type ConnectionState = "closed" | "authenticating" | "unavailable" | "failed" | "open" | "connecting";
|
|
123
148
|
export declare type Connection = {
|
|
124
149
|
state: "closed" | "authenticating" | "unavailable" | "failed";
|
|
@@ -423,7 +448,7 @@ export declare type Room = {
|
|
|
423
448
|
* }
|
|
424
449
|
* });
|
|
425
450
|
*/
|
|
426
|
-
broadcastEvent: (event: any) => void;
|
|
451
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
427
452
|
/**
|
|
428
453
|
* Get the room's storage asynchronously.
|
|
429
454
|
* The storage's root is a {@link LiveObject}.
|
package/lib/cjs/utils.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { AbstractCrdt, Doc } from "./AbstractCrdt";
|
|
2
|
-
import { SerializedCrdtWithId } from "./live";
|
|
2
|
+
import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
|
|
3
3
|
export declare function remove<T>(array: T[], item: T): void;
|
|
4
4
|
export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
|
|
5
5
|
export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
|
|
6
6
|
export declare function isCrdt(obj: any): obj is AbstractCrdt;
|
|
7
7
|
export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
|
|
8
8
|
export declare function selfOrRegister(obj: any): AbstractCrdt;
|
|
9
|
+
export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
|
package/lib/cjs/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
|
|
3
|
+
exports.getTreesDiffOperations = exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
|
|
4
4
|
const live_1 = require("./live");
|
|
5
5
|
const LiveList_1 = require("./LiveList");
|
|
6
6
|
const LiveMap_1 = require("./LiveMap");
|
|
@@ -73,3 +73,78 @@ function selfOrRegister(obj) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
exports.selfOrRegister = selfOrRegister;
|
|
76
|
+
function getTreesDiffOperations(currentItems, newItems) {
|
|
77
|
+
const ops = [];
|
|
78
|
+
currentItems.forEach((_, id) => {
|
|
79
|
+
if (!newItems.get(id)) {
|
|
80
|
+
// Delete crdt
|
|
81
|
+
ops.push({
|
|
82
|
+
type: live_1.OpType.DeleteCrdt,
|
|
83
|
+
id: id,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
newItems.forEach((crdt, id) => {
|
|
88
|
+
const currentCrdt = currentItems.get(id);
|
|
89
|
+
if (currentCrdt) {
|
|
90
|
+
if (crdt.type === live_1.CrdtType.Object) {
|
|
91
|
+
if (JSON.stringify(crdt.data) !==
|
|
92
|
+
JSON.stringify(currentCrdt.data)) {
|
|
93
|
+
ops.push({
|
|
94
|
+
type: live_1.OpType.UpdateObject,
|
|
95
|
+
id: id,
|
|
96
|
+
data: crdt.data,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (crdt.parentKey !== currentCrdt.parentKey) {
|
|
101
|
+
ops.push({
|
|
102
|
+
type: live_1.OpType.SetParentKey,
|
|
103
|
+
id: id,
|
|
104
|
+
parentKey: crdt.parentKey,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// new Crdt
|
|
110
|
+
switch (crdt.type) {
|
|
111
|
+
case live_1.CrdtType.Register:
|
|
112
|
+
ops.push({
|
|
113
|
+
type: live_1.OpType.CreateRegister,
|
|
114
|
+
id: id,
|
|
115
|
+
parentId: crdt.parentId,
|
|
116
|
+
parentKey: crdt.parentKey,
|
|
117
|
+
data: crdt.data,
|
|
118
|
+
});
|
|
119
|
+
break;
|
|
120
|
+
case live_1.CrdtType.List:
|
|
121
|
+
ops.push({
|
|
122
|
+
type: live_1.OpType.CreateList,
|
|
123
|
+
id: id,
|
|
124
|
+
parentId: crdt.parentId,
|
|
125
|
+
parentKey: crdt.parentKey,
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
case live_1.CrdtType.Object:
|
|
129
|
+
ops.push({
|
|
130
|
+
type: live_1.OpType.CreateObject,
|
|
131
|
+
id: id,
|
|
132
|
+
parentId: crdt.parentId,
|
|
133
|
+
parentKey: crdt.parentKey,
|
|
134
|
+
data: crdt.data,
|
|
135
|
+
});
|
|
136
|
+
break;
|
|
137
|
+
case live_1.CrdtType.Map:
|
|
138
|
+
ops.push({
|
|
139
|
+
type: live_1.OpType.CreateMap,
|
|
140
|
+
id: id,
|
|
141
|
+
parentId: crdt.parentId,
|
|
142
|
+
parentKey: crdt.parentKey,
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return ops;
|
|
149
|
+
}
|
|
150
|
+
exports.getTreesDiffOperations = getTreesDiffOperations;
|