@liveblocks/client 0.13.2 → 0.13.3
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/LiveList.d.ts +1 -0
- package/lib/cjs/LiveList.js +22 -0
- package/lib/cjs/immutable.d.ts +8 -0
- package/lib/cjs/immutable.js +233 -0
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/room.d.ts +2 -2
- package/lib/cjs/room.js +19 -6
- package/lib/cjs/types.d.ts +13 -1
- package/lib/esm/LiveList.d.ts +1 -0
- package/lib/esm/LiveList.js +22 -0
- package/lib/esm/immutable.d.ts +8 -0
- package/lib/esm/immutable.js +225 -0
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/room.d.ts +2 -2
- package/lib/esm/room.js +19 -6
- package/lib/esm/types.d.ts +13 -1
- package/package.json +1 -1
package/lib/cjs/LiveList.d.ts
CHANGED
package/lib/cjs/LiveList.js
CHANGED
|
@@ -256,6 +256,28 @@ class LiveList extends AbstractCrdt_1.AbstractCrdt {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
+
clear() {
|
|
260
|
+
if (this._doc) {
|
|
261
|
+
let ops = [];
|
|
262
|
+
let reverseOps = [];
|
|
263
|
+
for (const item of __classPrivateFieldGet(this, _LiveList_items, "f")) {
|
|
264
|
+
item[0]._detach();
|
|
265
|
+
const childId = item[0]._id;
|
|
266
|
+
if (childId) {
|
|
267
|
+
ops.push({ id: childId, type: live_1.OpType.DeleteCrdt });
|
|
268
|
+
reverseOps.push(...item[0]._serialize(this._id, item[1]));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
__classPrivateFieldSet(this, _LiveList_items, [], "f");
|
|
272
|
+
this._doc.dispatch(ops, reverseOps, [this]);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
for (const item of __classPrivateFieldGet(this, _LiveList_items, "f")) {
|
|
276
|
+
item[0]._detach();
|
|
277
|
+
}
|
|
278
|
+
__classPrivateFieldSet(this, _LiveList_items, [], "f");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
259
281
|
/**
|
|
260
282
|
* Returns an Array of all the elements in the LiveList.
|
|
261
283
|
*/
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LiveList } from "./LiveList";
|
|
2
|
+
import { LiveObject } from "./LiveObject";
|
|
3
|
+
import { StorageUpdate } from "./types";
|
|
4
|
+
export declare function liveObjectToJson(liveObject: LiveObject<any>): any;
|
|
5
|
+
export declare function patchLiveList<T>(liveList: LiveList<T>, prev: Array<T>, next: Array<T>): void;
|
|
6
|
+
export declare function patchLiveObjectKey<T>(liveObject: LiveObject<T>, key: keyof T, prev: any, next: any): void;
|
|
7
|
+
export declare function patchLiveObject<T extends Record<string, any>>(root: LiveObject<T>, prev: T, next: T): void;
|
|
8
|
+
export declare function patchImmutableObject<T>(state: T, updates: StorageUpdate[]): T;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.patchImmutableObject = exports.patchLiveObject = exports.patchLiveObjectKey = exports.patchLiveList = exports.liveObjectToJson = void 0;
|
|
4
|
+
const LiveList_1 = require("./LiveList");
|
|
5
|
+
const LiveMap_1 = require("./LiveMap");
|
|
6
|
+
const LiveObject_1 = require("./LiveObject");
|
|
7
|
+
const LiveRegister_1 = require("./LiveRegister");
|
|
8
|
+
function liveObjectToJson(liveObject) {
|
|
9
|
+
const result = {};
|
|
10
|
+
const obj = liveObject.toObject();
|
|
11
|
+
for (const key in obj) {
|
|
12
|
+
result[key] = liveNodeToJson(obj[key]);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
exports.liveObjectToJson = liveObjectToJson;
|
|
17
|
+
function liveMapToJson(map) {
|
|
18
|
+
const result = {};
|
|
19
|
+
const obj = Object.fromEntries(map);
|
|
20
|
+
for (const key in obj) {
|
|
21
|
+
result[key] = liveNodeToJson(obj[key]);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
function liveListToJson(value) {
|
|
26
|
+
return value.toArray().map(liveNodeToJson);
|
|
27
|
+
}
|
|
28
|
+
function liveNodeToJson(value) {
|
|
29
|
+
if (value instanceof LiveObject_1.LiveObject) {
|
|
30
|
+
return liveObjectToJson(value);
|
|
31
|
+
}
|
|
32
|
+
else if (value instanceof LiveList_1.LiveList) {
|
|
33
|
+
return liveListToJson(value);
|
|
34
|
+
}
|
|
35
|
+
else if (value instanceof LiveMap_1.LiveMap) {
|
|
36
|
+
return liveMapToJson(value);
|
|
37
|
+
}
|
|
38
|
+
else if (value instanceof LiveRegister_1.LiveRegister) {
|
|
39
|
+
return value.data;
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
function isPlainObject(obj) {
|
|
44
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
45
|
+
}
|
|
46
|
+
function anyToCrdt(obj) {
|
|
47
|
+
if (obj == null) {
|
|
48
|
+
return obj;
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(obj)) {
|
|
51
|
+
return new LiveList_1.LiveList(obj.map(anyToCrdt));
|
|
52
|
+
}
|
|
53
|
+
if (isPlainObject(obj)) {
|
|
54
|
+
const init = {};
|
|
55
|
+
for (const key in obj) {
|
|
56
|
+
init[key] = anyToCrdt(obj[key]);
|
|
57
|
+
}
|
|
58
|
+
return new LiveObject_1.LiveObject(init);
|
|
59
|
+
}
|
|
60
|
+
return obj;
|
|
61
|
+
}
|
|
62
|
+
function patchLiveList(liveList, prev, next) {
|
|
63
|
+
let i = 0;
|
|
64
|
+
let prevEnd = prev.length - 1;
|
|
65
|
+
let nextEnd = next.length - 1;
|
|
66
|
+
let prevNode = prev[0];
|
|
67
|
+
let nextNode = next[0];
|
|
68
|
+
/**
|
|
69
|
+
* For A,B,C => A,B,C,D
|
|
70
|
+
* i = 3, prevEnd = 2, nextEnd = 3
|
|
71
|
+
*
|
|
72
|
+
* For A,B,C => B,C
|
|
73
|
+
* i = 2, prevEnd = 2, nextEnd = 1
|
|
74
|
+
*
|
|
75
|
+
* For B,C => A,B,C
|
|
76
|
+
* i = 0, pre
|
|
77
|
+
*/
|
|
78
|
+
outer: {
|
|
79
|
+
while (prevNode === nextNode) {
|
|
80
|
+
++i;
|
|
81
|
+
if (i > prevEnd || i > nextEnd) {
|
|
82
|
+
break outer;
|
|
83
|
+
}
|
|
84
|
+
prevNode = prev[i];
|
|
85
|
+
nextNode = next[i];
|
|
86
|
+
}
|
|
87
|
+
prevNode = prev[prevEnd];
|
|
88
|
+
nextNode = next[nextEnd];
|
|
89
|
+
while (prevNode === nextNode) {
|
|
90
|
+
prevEnd--;
|
|
91
|
+
nextEnd--;
|
|
92
|
+
if (i > prevEnd || i > nextEnd) {
|
|
93
|
+
break outer;
|
|
94
|
+
}
|
|
95
|
+
prevNode = prev[prevEnd];
|
|
96
|
+
nextNode = next[nextEnd];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (i > prevEnd) {
|
|
100
|
+
if (i <= nextEnd) {
|
|
101
|
+
while (i <= nextEnd) {
|
|
102
|
+
liveList.insert(anyToCrdt(next[i]), i);
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (i > nextEnd) {
|
|
108
|
+
while (i <= prevEnd) {
|
|
109
|
+
liveList.delete(i++);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
while (i <= prevEnd && i <= nextEnd) {
|
|
114
|
+
prevNode = prev[i];
|
|
115
|
+
nextNode = next[i];
|
|
116
|
+
const liveListNode = liveList.get(i);
|
|
117
|
+
if (liveListNode instanceof LiveObject_1.LiveObject &&
|
|
118
|
+
isPlainObject(prevNode) &&
|
|
119
|
+
isPlainObject(nextNode)) {
|
|
120
|
+
patchLiveObject(liveListNode, prevNode, nextNode);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
liveList.delete(i);
|
|
124
|
+
liveList.insert(anyToCrdt(nextNode), i);
|
|
125
|
+
}
|
|
126
|
+
i++;
|
|
127
|
+
}
|
|
128
|
+
while (i <= nextEnd) {
|
|
129
|
+
liveList.insert(anyToCrdt(next[i]), i);
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
while (i <= prevEnd) {
|
|
133
|
+
liveList.delete(i);
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.patchLiveList = patchLiveList;
|
|
139
|
+
function patchLiveObjectKey(liveObject, key, prev, next) {
|
|
140
|
+
const value = liveObject.get(key);
|
|
141
|
+
if (next === undefined) {
|
|
142
|
+
liveObject.delete(key);
|
|
143
|
+
}
|
|
144
|
+
else if (value === undefined) {
|
|
145
|
+
liveObject.set(key, anyToCrdt(next));
|
|
146
|
+
}
|
|
147
|
+
else if (prev === next) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
else if (value instanceof LiveList_1.LiveList &&
|
|
151
|
+
Array.isArray(prev) &&
|
|
152
|
+
Array.isArray(next)) {
|
|
153
|
+
patchLiveList(value, prev, next);
|
|
154
|
+
}
|
|
155
|
+
else if (value instanceof LiveObject_1.LiveObject &&
|
|
156
|
+
isPlainObject(prev) &&
|
|
157
|
+
isPlainObject(next)) {
|
|
158
|
+
patchLiveObject(value, prev, next);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
liveObject.set(key, anyToCrdt(next));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.patchLiveObjectKey = patchLiveObjectKey;
|
|
165
|
+
function patchLiveObject(root, prev, next) {
|
|
166
|
+
const updates = {};
|
|
167
|
+
for (const key in next) {
|
|
168
|
+
patchLiveObjectKey(root, key, prev[key], next[key]);
|
|
169
|
+
}
|
|
170
|
+
for (const key in prev) {
|
|
171
|
+
if (next[key] === undefined) {
|
|
172
|
+
root.delete(key);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (Object.keys(updates).length > 0) {
|
|
176
|
+
root.update(updates);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.patchLiveObject = patchLiveObject;
|
|
180
|
+
function getParentsPath(node) {
|
|
181
|
+
const path = [];
|
|
182
|
+
while (node._parentKey != null && node._parent != null) {
|
|
183
|
+
if (node._parent instanceof LiveList_1.LiveList) {
|
|
184
|
+
path.push(node._parent._indexOfPosition(node._parentKey));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
path.push(node._parentKey);
|
|
188
|
+
}
|
|
189
|
+
node = node._parent;
|
|
190
|
+
}
|
|
191
|
+
return path;
|
|
192
|
+
}
|
|
193
|
+
function patchImmutableObject(state, updates) {
|
|
194
|
+
return updates.reduce((state, update) => patchImmutableObjectWithUpdate(state, update), state);
|
|
195
|
+
}
|
|
196
|
+
exports.patchImmutableObject = patchImmutableObject;
|
|
197
|
+
function patchImmutableObjectWithUpdate(state, update) {
|
|
198
|
+
const path = getParentsPath(update.node);
|
|
199
|
+
return patchImmutableNode(state, path, update);
|
|
200
|
+
}
|
|
201
|
+
function patchImmutableNode(state, path, update) {
|
|
202
|
+
const pathItem = path.pop();
|
|
203
|
+
if (pathItem === undefined) {
|
|
204
|
+
switch (update.type) {
|
|
205
|
+
case "LiveObject": {
|
|
206
|
+
if (typeof state !== "object") {
|
|
207
|
+
throw new Error("Internal: received update on LiveObject but state was not an object");
|
|
208
|
+
}
|
|
209
|
+
return liveObjectToJson(update.node);
|
|
210
|
+
}
|
|
211
|
+
case "LiveList": {
|
|
212
|
+
if (Array.isArray(state) === false) {
|
|
213
|
+
throw new Error("Internal: received update on LiveList but state was not an array");
|
|
214
|
+
}
|
|
215
|
+
return liveListToJson(update.node);
|
|
216
|
+
}
|
|
217
|
+
case "LiveMap": {
|
|
218
|
+
if (typeof state !== "object") {
|
|
219
|
+
throw new Error("Internal: received update on LiveMap but state was not an object");
|
|
220
|
+
}
|
|
221
|
+
return liveMapToJson(update.node);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(state)) {
|
|
226
|
+
const newArray = [...state];
|
|
227
|
+
newArray[pathItem] = patchImmutableNode(state[pathItem], path, update);
|
|
228
|
+
return newArray;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
return Object.assign(Object.assign({}, state), { [pathItem]: patchImmutableNode(state[pathItem], path, update) });
|
|
232
|
+
}
|
|
233
|
+
}
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { LiveObject } from "./LiveObject";
|
|
2
2
|
export { LiveMap } from "./LiveMap";
|
|
3
3
|
export { LiveList } from "./LiveList";
|
|
4
|
-
export type { Others, Presence, Room, Client, User } from "./types";
|
|
4
|
+
export type { Others, Presence, Room, Client, User, BroadcastOptions, } from "./types";
|
|
5
5
|
export { createClient } from "./client";
|
package/lib/cjs/room.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate } from "./types";
|
|
1
|
+
import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions } from "./types";
|
|
2
2
|
import { ClientMessage, Op } from "./live";
|
|
3
3
|
import { LiveMap } from "./LiveMap";
|
|
4
4
|
import { LiveObject } from "./LiveObject";
|
|
@@ -118,7 +118,7 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
|
|
|
118
118
|
updatePresence: <T_4 extends Presence>(overrides: Partial<T_4>, options?: {
|
|
119
119
|
addToHistory: boolean;
|
|
120
120
|
} | undefined) => void;
|
|
121
|
-
broadcastEvent: (event: any) => void;
|
|
121
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
122
122
|
batch: (callback: () => void) => void;
|
|
123
123
|
undo: () => void;
|
|
124
124
|
redo: () => void;
|
package/lib/cjs/room.js
CHANGED
|
@@ -58,6 +58,9 @@ function makeOthers(presenceMap) {
|
|
|
58
58
|
get count() {
|
|
59
59
|
return array.length;
|
|
60
60
|
},
|
|
61
|
+
[Symbol.iterator]() {
|
|
62
|
+
return array[Symbol.iterator]();
|
|
63
|
+
},
|
|
61
64
|
map(callback) {
|
|
62
65
|
return array.map(callback);
|
|
63
66
|
},
|
|
@@ -443,7 +446,9 @@ See v0.13 release notes for more information.
|
|
|
443
446
|
state.socket = socket;
|
|
444
447
|
}
|
|
445
448
|
function authenticationFailure(error) {
|
|
446
|
-
|
|
449
|
+
if (process.env.NODE_ENV !== "production") {
|
|
450
|
+
console.error("Call to authentication endpoint failed", error);
|
|
451
|
+
}
|
|
447
452
|
updateConnection({ state: "unavailable" });
|
|
448
453
|
state.numberOfRetry++;
|
|
449
454
|
state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
|
|
@@ -609,14 +614,20 @@ See v0.13 release notes for more information.
|
|
|
609
614
|
updateConnection({ state: "failed" });
|
|
610
615
|
const error = new LiveblocksError(event.reason, event.code);
|
|
611
616
|
for (const listener of state.listeners.error) {
|
|
612
|
-
|
|
617
|
+
if (process.env.NODE_ENV !== "production") {
|
|
618
|
+
console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
|
|
619
|
+
}
|
|
613
620
|
listener(error);
|
|
614
621
|
}
|
|
615
622
|
}
|
|
616
623
|
else if (event.wasClean === false) {
|
|
617
|
-
updateConnection({ state: "unavailable" });
|
|
618
624
|
state.numberOfRetry++;
|
|
619
|
-
|
|
625
|
+
const delay = getRetryDelay();
|
|
626
|
+
if (process.env.NODE_ENV !== "production") {
|
|
627
|
+
console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
|
|
628
|
+
}
|
|
629
|
+
updateConnection({ state: "unavailable" });
|
|
630
|
+
state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
|
|
620
631
|
}
|
|
621
632
|
else {
|
|
622
633
|
updateConnection({ state: "closed" });
|
|
@@ -758,8 +769,10 @@ See v0.13 release notes for more information.
|
|
|
758
769
|
function getOthers() {
|
|
759
770
|
return state.others;
|
|
760
771
|
}
|
|
761
|
-
function broadcastEvent(event
|
|
762
|
-
|
|
772
|
+
function broadcastEvent(event, options = {
|
|
773
|
+
shouldQueueEventIfNotReady: false,
|
|
774
|
+
}) {
|
|
775
|
+
if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
|
|
763
776
|
return;
|
|
764
777
|
}
|
|
765
778
|
state.buffer.messages.push({
|
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
|
*/
|
|
@@ -423,7 +435,7 @@ export declare type Room = {
|
|
|
423
435
|
* }
|
|
424
436
|
* });
|
|
425
437
|
*/
|
|
426
|
-
broadcastEvent: (event: any) => void;
|
|
438
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
427
439
|
/**
|
|
428
440
|
* Get the room's storage asynchronously.
|
|
429
441
|
* The storage's root is a {@link LiveObject}.
|
package/lib/esm/LiveList.d.ts
CHANGED
package/lib/esm/LiveList.js
CHANGED
|
@@ -253,6 +253,28 @@ export class LiveList extends AbstractCrdt {
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
|
+
clear() {
|
|
257
|
+
if (this._doc) {
|
|
258
|
+
let ops = [];
|
|
259
|
+
let reverseOps = [];
|
|
260
|
+
for (const item of __classPrivateFieldGet(this, _LiveList_items, "f")) {
|
|
261
|
+
item[0]._detach();
|
|
262
|
+
const childId = item[0]._id;
|
|
263
|
+
if (childId) {
|
|
264
|
+
ops.push({ id: childId, type: OpType.DeleteCrdt });
|
|
265
|
+
reverseOps.push(...item[0]._serialize(this._id, item[1]));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
__classPrivateFieldSet(this, _LiveList_items, [], "f");
|
|
269
|
+
this._doc.dispatch(ops, reverseOps, [this]);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
for (const item of __classPrivateFieldGet(this, _LiveList_items, "f")) {
|
|
273
|
+
item[0]._detach();
|
|
274
|
+
}
|
|
275
|
+
__classPrivateFieldSet(this, _LiveList_items, [], "f");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
256
278
|
/**
|
|
257
279
|
* Returns an Array of all the elements in the LiveList.
|
|
258
280
|
*/
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LiveList } from "./LiveList";
|
|
2
|
+
import { LiveObject } from "./LiveObject";
|
|
3
|
+
import { StorageUpdate } from "./types";
|
|
4
|
+
export declare function liveObjectToJson(liveObject: LiveObject<any>): any;
|
|
5
|
+
export declare function patchLiveList<T>(liveList: LiveList<T>, prev: Array<T>, next: Array<T>): void;
|
|
6
|
+
export declare function patchLiveObjectKey<T>(liveObject: LiveObject<T>, key: keyof T, prev: any, next: any): void;
|
|
7
|
+
export declare function patchLiveObject<T extends Record<string, any>>(root: LiveObject<T>, prev: T, next: T): void;
|
|
8
|
+
export declare function patchImmutableObject<T>(state: T, updates: StorageUpdate[]): T;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { LiveList } from "./LiveList";
|
|
2
|
+
import { LiveMap } from "./LiveMap";
|
|
3
|
+
import { LiveObject } from "./LiveObject";
|
|
4
|
+
import { LiveRegister } from "./LiveRegister";
|
|
5
|
+
export function liveObjectToJson(liveObject) {
|
|
6
|
+
const result = {};
|
|
7
|
+
const obj = liveObject.toObject();
|
|
8
|
+
for (const key in obj) {
|
|
9
|
+
result[key] = liveNodeToJson(obj[key]);
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
function liveMapToJson(map) {
|
|
14
|
+
const result = {};
|
|
15
|
+
const obj = Object.fromEntries(map);
|
|
16
|
+
for (const key in obj) {
|
|
17
|
+
result[key] = liveNodeToJson(obj[key]);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
function liveListToJson(value) {
|
|
22
|
+
return value.toArray().map(liveNodeToJson);
|
|
23
|
+
}
|
|
24
|
+
function liveNodeToJson(value) {
|
|
25
|
+
if (value instanceof LiveObject) {
|
|
26
|
+
return liveObjectToJson(value);
|
|
27
|
+
}
|
|
28
|
+
else if (value instanceof LiveList) {
|
|
29
|
+
return liveListToJson(value);
|
|
30
|
+
}
|
|
31
|
+
else if (value instanceof LiveMap) {
|
|
32
|
+
return liveMapToJson(value);
|
|
33
|
+
}
|
|
34
|
+
else if (value instanceof LiveRegister) {
|
|
35
|
+
return value.data;
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function isPlainObject(obj) {
|
|
40
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
41
|
+
}
|
|
42
|
+
function anyToCrdt(obj) {
|
|
43
|
+
if (obj == null) {
|
|
44
|
+
return obj;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(obj)) {
|
|
47
|
+
return new LiveList(obj.map(anyToCrdt));
|
|
48
|
+
}
|
|
49
|
+
if (isPlainObject(obj)) {
|
|
50
|
+
const init = {};
|
|
51
|
+
for (const key in obj) {
|
|
52
|
+
init[key] = anyToCrdt(obj[key]);
|
|
53
|
+
}
|
|
54
|
+
return new LiveObject(init);
|
|
55
|
+
}
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
58
|
+
export function patchLiveList(liveList, prev, next) {
|
|
59
|
+
let i = 0;
|
|
60
|
+
let prevEnd = prev.length - 1;
|
|
61
|
+
let nextEnd = next.length - 1;
|
|
62
|
+
let prevNode = prev[0];
|
|
63
|
+
let nextNode = next[0];
|
|
64
|
+
/**
|
|
65
|
+
* For A,B,C => A,B,C,D
|
|
66
|
+
* i = 3, prevEnd = 2, nextEnd = 3
|
|
67
|
+
*
|
|
68
|
+
* For A,B,C => B,C
|
|
69
|
+
* i = 2, prevEnd = 2, nextEnd = 1
|
|
70
|
+
*
|
|
71
|
+
* For B,C => A,B,C
|
|
72
|
+
* i = 0, pre
|
|
73
|
+
*/
|
|
74
|
+
outer: {
|
|
75
|
+
while (prevNode === nextNode) {
|
|
76
|
+
++i;
|
|
77
|
+
if (i > prevEnd || i > nextEnd) {
|
|
78
|
+
break outer;
|
|
79
|
+
}
|
|
80
|
+
prevNode = prev[i];
|
|
81
|
+
nextNode = next[i];
|
|
82
|
+
}
|
|
83
|
+
prevNode = prev[prevEnd];
|
|
84
|
+
nextNode = next[nextEnd];
|
|
85
|
+
while (prevNode === nextNode) {
|
|
86
|
+
prevEnd--;
|
|
87
|
+
nextEnd--;
|
|
88
|
+
if (i > prevEnd || i > nextEnd) {
|
|
89
|
+
break outer;
|
|
90
|
+
}
|
|
91
|
+
prevNode = prev[prevEnd];
|
|
92
|
+
nextNode = next[nextEnd];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (i > prevEnd) {
|
|
96
|
+
if (i <= nextEnd) {
|
|
97
|
+
while (i <= nextEnd) {
|
|
98
|
+
liveList.insert(anyToCrdt(next[i]), i);
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (i > nextEnd) {
|
|
104
|
+
while (i <= prevEnd) {
|
|
105
|
+
liveList.delete(i++);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
while (i <= prevEnd && i <= nextEnd) {
|
|
110
|
+
prevNode = prev[i];
|
|
111
|
+
nextNode = next[i];
|
|
112
|
+
const liveListNode = liveList.get(i);
|
|
113
|
+
if (liveListNode instanceof LiveObject &&
|
|
114
|
+
isPlainObject(prevNode) &&
|
|
115
|
+
isPlainObject(nextNode)) {
|
|
116
|
+
patchLiveObject(liveListNode, prevNode, nextNode);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
liveList.delete(i);
|
|
120
|
+
liveList.insert(anyToCrdt(nextNode), i);
|
|
121
|
+
}
|
|
122
|
+
i++;
|
|
123
|
+
}
|
|
124
|
+
while (i <= nextEnd) {
|
|
125
|
+
liveList.insert(anyToCrdt(next[i]), i);
|
|
126
|
+
i++;
|
|
127
|
+
}
|
|
128
|
+
while (i <= prevEnd) {
|
|
129
|
+
liveList.delete(i);
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function patchLiveObjectKey(liveObject, key, prev, next) {
|
|
135
|
+
const value = liveObject.get(key);
|
|
136
|
+
if (next === undefined) {
|
|
137
|
+
liveObject.delete(key);
|
|
138
|
+
}
|
|
139
|
+
else if (value === undefined) {
|
|
140
|
+
liveObject.set(key, anyToCrdt(next));
|
|
141
|
+
}
|
|
142
|
+
else if (prev === next) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
else if (value instanceof LiveList &&
|
|
146
|
+
Array.isArray(prev) &&
|
|
147
|
+
Array.isArray(next)) {
|
|
148
|
+
patchLiveList(value, prev, next);
|
|
149
|
+
}
|
|
150
|
+
else if (value instanceof LiveObject &&
|
|
151
|
+
isPlainObject(prev) &&
|
|
152
|
+
isPlainObject(next)) {
|
|
153
|
+
patchLiveObject(value, prev, next);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
liveObject.set(key, anyToCrdt(next));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export function patchLiveObject(root, prev, next) {
|
|
160
|
+
const updates = {};
|
|
161
|
+
for (const key in next) {
|
|
162
|
+
patchLiveObjectKey(root, key, prev[key], next[key]);
|
|
163
|
+
}
|
|
164
|
+
for (const key in prev) {
|
|
165
|
+
if (next[key] === undefined) {
|
|
166
|
+
root.delete(key);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (Object.keys(updates).length > 0) {
|
|
170
|
+
root.update(updates);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function getParentsPath(node) {
|
|
174
|
+
const path = [];
|
|
175
|
+
while (node._parentKey != null && node._parent != null) {
|
|
176
|
+
if (node._parent instanceof LiveList) {
|
|
177
|
+
path.push(node._parent._indexOfPosition(node._parentKey));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
path.push(node._parentKey);
|
|
181
|
+
}
|
|
182
|
+
node = node._parent;
|
|
183
|
+
}
|
|
184
|
+
return path;
|
|
185
|
+
}
|
|
186
|
+
export function patchImmutableObject(state, updates) {
|
|
187
|
+
return updates.reduce((state, update) => patchImmutableObjectWithUpdate(state, update), state);
|
|
188
|
+
}
|
|
189
|
+
function patchImmutableObjectWithUpdate(state, update) {
|
|
190
|
+
const path = getParentsPath(update.node);
|
|
191
|
+
return patchImmutableNode(state, path, update);
|
|
192
|
+
}
|
|
193
|
+
function patchImmutableNode(state, path, update) {
|
|
194
|
+
const pathItem = path.pop();
|
|
195
|
+
if (pathItem === undefined) {
|
|
196
|
+
switch (update.type) {
|
|
197
|
+
case "LiveObject": {
|
|
198
|
+
if (typeof state !== "object") {
|
|
199
|
+
throw new Error("Internal: received update on LiveObject but state was not an object");
|
|
200
|
+
}
|
|
201
|
+
return liveObjectToJson(update.node);
|
|
202
|
+
}
|
|
203
|
+
case "LiveList": {
|
|
204
|
+
if (Array.isArray(state) === false) {
|
|
205
|
+
throw new Error("Internal: received update on LiveList but state was not an array");
|
|
206
|
+
}
|
|
207
|
+
return liveListToJson(update.node);
|
|
208
|
+
}
|
|
209
|
+
case "LiveMap": {
|
|
210
|
+
if (typeof state !== "object") {
|
|
211
|
+
throw new Error("Internal: received update on LiveMap but state was not an object");
|
|
212
|
+
}
|
|
213
|
+
return liveMapToJson(update.node);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (Array.isArray(state)) {
|
|
218
|
+
const newArray = [...state];
|
|
219
|
+
newArray[pathItem] = patchImmutableNode(state[pathItem], path, update);
|
|
220
|
+
return newArray;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
return Object.assign(Object.assign({}, state), { [pathItem]: patchImmutableNode(state[pathItem], path, update) });
|
|
224
|
+
}
|
|
225
|
+
}
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { LiveObject } from "./LiveObject";
|
|
2
2
|
export { LiveMap } from "./LiveMap";
|
|
3
3
|
export { LiveList } from "./LiveList";
|
|
4
|
-
export type { Others, Presence, Room, Client, User } from "./types";
|
|
4
|
+
export type { Others, Presence, Room, Client, User, BroadcastOptions, } from "./types";
|
|
5
5
|
export { createClient } from "./client";
|
package/lib/esm/room.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate } from "./types";
|
|
1
|
+
import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions } from "./types";
|
|
2
2
|
import { ClientMessage, Op } from "./live";
|
|
3
3
|
import { LiveMap } from "./LiveMap";
|
|
4
4
|
import { LiveObject } from "./LiveObject";
|
|
@@ -118,7 +118,7 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
|
|
|
118
118
|
updatePresence: <T_4 extends Presence>(overrides: Partial<T_4>, options?: {
|
|
119
119
|
addToHistory: boolean;
|
|
120
120
|
} | undefined) => void;
|
|
121
|
-
broadcastEvent: (event: any) => void;
|
|
121
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
122
122
|
batch: (callback: () => void) => void;
|
|
123
123
|
undo: () => void;
|
|
124
124
|
redo: () => void;
|
package/lib/esm/room.js
CHANGED
|
@@ -36,6 +36,9 @@ function makeOthers(presenceMap) {
|
|
|
36
36
|
get count() {
|
|
37
37
|
return array.length;
|
|
38
38
|
},
|
|
39
|
+
[Symbol.iterator]() {
|
|
40
|
+
return array[Symbol.iterator]();
|
|
41
|
+
},
|
|
39
42
|
map(callback) {
|
|
40
43
|
return array.map(callback);
|
|
41
44
|
},
|
|
@@ -421,7 +424,9 @@ See v0.13 release notes for more information.
|
|
|
421
424
|
state.socket = socket;
|
|
422
425
|
}
|
|
423
426
|
function authenticationFailure(error) {
|
|
424
|
-
|
|
427
|
+
if (process.env.NODE_ENV !== "production") {
|
|
428
|
+
console.error("Call to authentication endpoint failed", error);
|
|
429
|
+
}
|
|
425
430
|
updateConnection({ state: "unavailable" });
|
|
426
431
|
state.numberOfRetry++;
|
|
427
432
|
state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
|
|
@@ -587,14 +592,20 @@ See v0.13 release notes for more information.
|
|
|
587
592
|
updateConnection({ state: "failed" });
|
|
588
593
|
const error = new LiveblocksError(event.reason, event.code);
|
|
589
594
|
for (const listener of state.listeners.error) {
|
|
590
|
-
|
|
595
|
+
if (process.env.NODE_ENV !== "production") {
|
|
596
|
+
console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
|
|
597
|
+
}
|
|
591
598
|
listener(error);
|
|
592
599
|
}
|
|
593
600
|
}
|
|
594
601
|
else if (event.wasClean === false) {
|
|
595
|
-
updateConnection({ state: "unavailable" });
|
|
596
602
|
state.numberOfRetry++;
|
|
597
|
-
|
|
603
|
+
const delay = getRetryDelay();
|
|
604
|
+
if (process.env.NODE_ENV !== "production") {
|
|
605
|
+
console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
|
|
606
|
+
}
|
|
607
|
+
updateConnection({ state: "unavailable" });
|
|
608
|
+
state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
|
|
598
609
|
}
|
|
599
610
|
else {
|
|
600
611
|
updateConnection({ state: "closed" });
|
|
@@ -736,8 +747,10 @@ See v0.13 release notes for more information.
|
|
|
736
747
|
function getOthers() {
|
|
737
748
|
return state.others;
|
|
738
749
|
}
|
|
739
|
-
function broadcastEvent(event
|
|
740
|
-
|
|
750
|
+
function broadcastEvent(event, options = {
|
|
751
|
+
shouldQueueEventIfNotReady: false,
|
|
752
|
+
}) {
|
|
753
|
+
if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
|
|
741
754
|
return;
|
|
742
755
|
}
|
|
743
756
|
state.buffer.messages.push({
|
package/lib/esm/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
|
*/
|
|
@@ -423,7 +435,7 @@ export declare type Room = {
|
|
|
423
435
|
* }
|
|
424
436
|
* });
|
|
425
437
|
*/
|
|
426
|
-
broadcastEvent: (event: any) => void;
|
|
438
|
+
broadcastEvent: (event: any, options?: BroadcastOptions) => void;
|
|
427
439
|
/**
|
|
428
440
|
* Get the room's storage asynchronously.
|
|
429
441
|
* The storage's root is a {@link LiveObject}.
|