@liveblocks/client 0.17.8 → 0.17.9
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/.built-by-link-script +1 -0
- package/chunk-QLMVHHAZ.js +3592 -0
- package/index-0007883d.d.ts +793 -0
- package/index.d.ts +7 -27
- package/index.js +2007 -1296
- package/index.mjs +7 -1133
- package/internal.d.ts +198 -307
- package/internal.js +526 -309
- package/internal.mjs +30 -281
- package/package.json +1 -1
- package/shared.d.ts +0 -902
- package/shared.js +0 -2656
- package/shared.mjs +0 -2070
package/index.mjs
CHANGED
|
@@ -1,1133 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
W as WebsocketCloseCodes,
|
|
9
|
-
C as ClientMsgCode,
|
|
10
|
-
n as nn,
|
|
11
|
-
b as isPlainObject,
|
|
12
|
-
p as parseRoomAuthToken,
|
|
13
|
-
c as isTokenExpired,
|
|
14
|
-
d as isSameNodeOrChildOf,
|
|
15
|
-
L as LiveObject,
|
|
16
|
-
g as getTreesDiffOperations,
|
|
17
|
-
O as OpSource,
|
|
18
|
-
e as OpCode,
|
|
19
|
-
f as isLiveList,
|
|
20
|
-
t as tryParseJson,
|
|
21
|
-
h as isJsonArray,
|
|
22
|
-
j as compact,
|
|
23
|
-
k as isRootCrdt,
|
|
24
|
-
l as isJsonObject,
|
|
25
|
-
o as errorIf,
|
|
26
|
-
} from "./shared.mjs";
|
|
27
|
-
export { q as LiveList, s as LiveMap, L as LiveObject } from "./shared.mjs";
|
|
28
|
-
const BACKOFF_RETRY_DELAYS = [250, 500, 1e3, 2e3, 4e3, 8e3, 1e4],
|
|
29
|
-
BACKOFF_RETRY_DELAYS_SLOW = [2e3, 3e4, 6e4, 3e5];
|
|
30
|
-
function makeOthers(userMap) {
|
|
31
|
-
const users = Object.values(userMap).map((user) =>
|
|
32
|
-
__rest(user, ["_hasReceivedInitialPresence"])
|
|
33
|
-
);
|
|
34
|
-
return {
|
|
35
|
-
get count() {
|
|
36
|
-
return users.length;
|
|
37
|
-
},
|
|
38
|
-
[Symbol.iterator]: () => users[Symbol.iterator](),
|
|
39
|
-
map: (callback) => users.map(callback),
|
|
40
|
-
toArray: () => users,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function makeStateMachine(state, context, mockedEffects) {
|
|
44
|
-
const effects = mockedEffects || {
|
|
45
|
-
authenticate(auth, createWebSocket) {
|
|
46
|
-
const rawToken = state.token,
|
|
47
|
-
parsedToken = null !== rawToken && parseRoomAuthToken(rawToken);
|
|
48
|
-
if (!parsedToken || isTokenExpired(parsedToken))
|
|
49
|
-
return auth(context.roomId)
|
|
50
|
-
.then(({ token: token }) => {
|
|
51
|
-
if ("authenticating" !== state.connection.state) return;
|
|
52
|
-
authenticationSuccess(
|
|
53
|
-
parseRoomAuthToken(token),
|
|
54
|
-
createWebSocket(token)
|
|
55
|
-
),
|
|
56
|
-
(state.token = token);
|
|
57
|
-
})
|
|
58
|
-
.catch((er) =>
|
|
59
|
-
(function (error) {
|
|
60
|
-
"production" !== process.env.NODE_ENV &&
|
|
61
|
-
console.error("Call to authentication endpoint failed", error);
|
|
62
|
-
(state.token = null),
|
|
63
|
-
updateConnection({ state: "unavailable" }),
|
|
64
|
-
state.numberOfRetry++,
|
|
65
|
-
(state.timeoutHandles.reconnect = effects.scheduleReconnect(
|
|
66
|
-
getRetryDelay()
|
|
67
|
-
));
|
|
68
|
-
})(er instanceof Error ? er : new Error(String(er)))
|
|
69
|
-
);
|
|
70
|
-
authenticationSuccess(parsedToken, createWebSocket(rawToken));
|
|
71
|
-
},
|
|
72
|
-
send(messageOrMessages) {
|
|
73
|
-
if (null == state.socket)
|
|
74
|
-
throw new Error("Can't send message if socket is null");
|
|
75
|
-
state.socket.send(JSON.stringify(messageOrMessages));
|
|
76
|
-
},
|
|
77
|
-
delayFlush: (delay) => setTimeout(tryFlushing, delay),
|
|
78
|
-
startHeartbeatInterval: () => setInterval(heartbeat, 3e4),
|
|
79
|
-
schedulePongTimeout: () => setTimeout(pongTimeout, 2e3),
|
|
80
|
-
scheduleReconnect: (delay) => setTimeout(connect, delay),
|
|
81
|
-
};
|
|
82
|
-
function genericSubscribe(callback) {
|
|
83
|
-
return (
|
|
84
|
-
state.listeners.storage.push(callback),
|
|
85
|
-
() => remove(state.listeners.storage, callback)
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
function createOrUpdateRootFromMessage(message) {
|
|
89
|
-
if (0 === message.items.length)
|
|
90
|
-
throw new Error("Internal error: cannot load storage without items");
|
|
91
|
-
state.root
|
|
92
|
-
? (function (items) {
|
|
93
|
-
if (!state.root) return;
|
|
94
|
-
const currentItems = new Map();
|
|
95
|
-
state.items.forEach((liveCrdt, id) => {
|
|
96
|
-
currentItems.set(id, liveCrdt._toSerializedCrdt());
|
|
97
|
-
});
|
|
98
|
-
const ops = getTreesDiffOperations(currentItems, new Map(items));
|
|
99
|
-
notify(apply(ops, !1).updates);
|
|
100
|
-
})(message.items)
|
|
101
|
-
: (state.root = (function (items) {
|
|
102
|
-
const [root, parentToChildren] = (function (items) {
|
|
103
|
-
const parentToChildren = new Map();
|
|
104
|
-
let root = null;
|
|
105
|
-
for (const [id, crdt] of items)
|
|
106
|
-
if (isRootCrdt(crdt)) root = [id, crdt];
|
|
107
|
-
else {
|
|
108
|
-
const tuple = [id, crdt],
|
|
109
|
-
children = parentToChildren.get(crdt.parentId);
|
|
110
|
-
null != children
|
|
111
|
-
? children.push(tuple)
|
|
112
|
-
: parentToChildren.set(crdt.parentId, [tuple]);
|
|
113
|
-
}
|
|
114
|
-
if (null == root) throw new Error("Root can't be null");
|
|
115
|
-
return [root, parentToChildren];
|
|
116
|
-
})(items);
|
|
117
|
-
return LiveObject._deserialize(root, parentToChildren, {
|
|
118
|
-
getItem: getItem,
|
|
119
|
-
addItem: addItem,
|
|
120
|
-
deleteItem: deleteItem,
|
|
121
|
-
generateId: generateId,
|
|
122
|
-
generateOpId: generateOpId,
|
|
123
|
-
dispatch: storageDispatch,
|
|
124
|
-
roomId: context.roomId,
|
|
125
|
-
});
|
|
126
|
-
})(message.items));
|
|
127
|
-
for (const key in state.defaultStorageRoot)
|
|
128
|
-
null == state.root.get(key) &&
|
|
129
|
-
state.root.set(key, state.defaultStorageRoot[key]);
|
|
130
|
-
}
|
|
131
|
-
function addItem(id, liveItem) {
|
|
132
|
-
state.items.set(id, liveItem);
|
|
133
|
-
}
|
|
134
|
-
function deleteItem(id) {
|
|
135
|
-
state.items.delete(id);
|
|
136
|
-
}
|
|
137
|
-
function getItem(id) {
|
|
138
|
-
return state.items.get(id);
|
|
139
|
-
}
|
|
140
|
-
function addToUndoStack(historyItem) {
|
|
141
|
-
state.undoStack.length >= 50 && state.undoStack.shift(),
|
|
142
|
-
state.isHistoryPaused
|
|
143
|
-
? state.pausedHistory.unshift(...historyItem)
|
|
144
|
-
: (state.undoStack.push(historyItem), onHistoryChange());
|
|
145
|
-
}
|
|
146
|
-
function storageDispatch(ops, reverse, storageUpdates) {
|
|
147
|
-
state.isBatching
|
|
148
|
-
? (state.batch.ops.push(...ops),
|
|
149
|
-
storageUpdates.forEach((value, key) => {
|
|
150
|
-
state.batch.updates.storageUpdates.set(
|
|
151
|
-
key,
|
|
152
|
-
mergeStorageUpdates(
|
|
153
|
-
state.batch.updates.storageUpdates.get(key),
|
|
154
|
-
value
|
|
155
|
-
)
|
|
156
|
-
);
|
|
157
|
-
}),
|
|
158
|
-
state.batch.reverseOps.push(...reverse))
|
|
159
|
-
: (addToUndoStack(reverse),
|
|
160
|
-
(state.redoStack = []),
|
|
161
|
-
dispatch(ops),
|
|
162
|
-
notify({ storageUpdates: storageUpdates }));
|
|
163
|
-
}
|
|
164
|
-
function notify({
|
|
165
|
-
storageUpdates: storageUpdates = new Map(),
|
|
166
|
-
presence: presence = !1,
|
|
167
|
-
others: otherEvents = [],
|
|
168
|
-
}) {
|
|
169
|
-
if (otherEvents.length > 0) {
|
|
170
|
-
state.others = makeOthers(state.users);
|
|
171
|
-
for (const event of otherEvents)
|
|
172
|
-
for (const listener of state.listeners.others)
|
|
173
|
-
listener(state.others, event);
|
|
174
|
-
}
|
|
175
|
-
if (presence)
|
|
176
|
-
for (const listener of state.listeners["my-presence"]) listener(state.me);
|
|
177
|
-
if (storageUpdates.size > 0)
|
|
178
|
-
for (const subscriber of state.listeners.storage)
|
|
179
|
-
subscriber(Array.from(storageUpdates.values()));
|
|
180
|
-
}
|
|
181
|
-
function getConnectionId() {
|
|
182
|
-
if (
|
|
183
|
-
"open" === state.connection.state ||
|
|
184
|
-
"connecting" === state.connection.state
|
|
185
|
-
)
|
|
186
|
-
return state.connection.id;
|
|
187
|
-
if (null !== state.lastConnectionId) return state.lastConnectionId;
|
|
188
|
-
throw new Error(
|
|
189
|
-
"Internal. Tried to get connection id but connection was never open"
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
function generateId() {
|
|
193
|
-
return `${getConnectionId()}:${state.clock++}`;
|
|
194
|
-
}
|
|
195
|
-
function generateOpId() {
|
|
196
|
-
return `${getConnectionId()}:${state.opClock++}`;
|
|
197
|
-
}
|
|
198
|
-
function apply(item, isLocal) {
|
|
199
|
-
const result = {
|
|
200
|
-
reverse: [],
|
|
201
|
-
updates: { storageUpdates: new Map(), presence: !1 },
|
|
202
|
-
},
|
|
203
|
-
createdNodeIds = new Set();
|
|
204
|
-
for (const op of item)
|
|
205
|
-
if ("presence" === op.type) {
|
|
206
|
-
const reverse = { type: "presence", data: {} };
|
|
207
|
-
for (const key in op.data) reverse.data[key] = state.me[key];
|
|
208
|
-
if (
|
|
209
|
-
((state.me = Object.assign(Object.assign({}, state.me), op.data)),
|
|
210
|
-
null == state.buffer.presence)
|
|
211
|
-
)
|
|
212
|
-
state.buffer.presence = op.data;
|
|
213
|
-
else
|
|
214
|
-
for (const key in op.data) state.buffer.presence[key] = op.data[key];
|
|
215
|
-
result.reverse.unshift(reverse), (result.updates.presence = !0);
|
|
216
|
-
} else {
|
|
217
|
-
let source;
|
|
218
|
-
if ((op.opId || (op.opId = generateOpId()), isLocal))
|
|
219
|
-
source = OpSource.UNDOREDO_RECONNECT;
|
|
220
|
-
else {
|
|
221
|
-
source = state.offlineOperations.delete(nn(op.opId))
|
|
222
|
-
? OpSource.ACK
|
|
223
|
-
: OpSource.REMOTE;
|
|
224
|
-
}
|
|
225
|
-
const applyOpResult = applyOp(op, source);
|
|
226
|
-
if (applyOpResult.modified) {
|
|
227
|
-
const parentId =
|
|
228
|
-
"HasParent" === applyOpResult.modified.node.parent.type
|
|
229
|
-
? nn(
|
|
230
|
-
applyOpResult.modified.node.parent.node._id,
|
|
231
|
-
"Expected parent node to have an ID"
|
|
232
|
-
)
|
|
233
|
-
: void 0;
|
|
234
|
-
(parentId && createdNodeIds.has(parentId)) ||
|
|
235
|
-
(result.updates.storageUpdates.set(
|
|
236
|
-
nn(applyOpResult.modified.node._id),
|
|
237
|
-
mergeStorageUpdates(
|
|
238
|
-
result.updates.storageUpdates.get(
|
|
239
|
-
nn(applyOpResult.modified.node._id)
|
|
240
|
-
),
|
|
241
|
-
applyOpResult.modified
|
|
242
|
-
)
|
|
243
|
-
),
|
|
244
|
-
result.reverse.unshift(...applyOpResult.reverse)),
|
|
245
|
-
(op.type !== OpCode.CREATE_LIST &&
|
|
246
|
-
op.type !== OpCode.CREATE_MAP &&
|
|
247
|
-
op.type !== OpCode.CREATE_OBJECT) ||
|
|
248
|
-
createdNodeIds.add(nn(applyOpResult.modified.node._id));
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return result;
|
|
252
|
-
}
|
|
253
|
-
function applyOp(op, source) {
|
|
254
|
-
switch (op.type) {
|
|
255
|
-
case OpCode.DELETE_OBJECT_KEY:
|
|
256
|
-
case OpCode.UPDATE_OBJECT:
|
|
257
|
-
case OpCode.DELETE_CRDT: {
|
|
258
|
-
const item = state.items.get(op.id);
|
|
259
|
-
return null == item
|
|
260
|
-
? { modified: !1 }
|
|
261
|
-
: item._apply(op, source === OpSource.UNDOREDO_RECONNECT);
|
|
262
|
-
}
|
|
263
|
-
case OpCode.SET_PARENT_KEY: {
|
|
264
|
-
const item = state.items.get(op.id);
|
|
265
|
-
return null == item
|
|
266
|
-
? { modified: !1 }
|
|
267
|
-
: "HasParent" === item.parent.type && isLiveList(item.parent.node)
|
|
268
|
-
? item.parent.node._setChildKey(op.parentKey, item, source)
|
|
269
|
-
: { modified: !1 };
|
|
270
|
-
}
|
|
271
|
-
case OpCode.CREATE_OBJECT:
|
|
272
|
-
case OpCode.CREATE_LIST:
|
|
273
|
-
case OpCode.CREATE_MAP:
|
|
274
|
-
case OpCode.CREATE_REGISTER: {
|
|
275
|
-
if (void 0 === op.parentId) return { modified: !1 };
|
|
276
|
-
const parent = state.items.get(op.parentId);
|
|
277
|
-
return null == parent
|
|
278
|
-
? { modified: !1 }
|
|
279
|
-
: parent._attachChild(op, source);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
function connect() {
|
|
284
|
-
var _a, _b, _c, _d;
|
|
285
|
-
if (
|
|
286
|
-
"closed" !== state.connection.state &&
|
|
287
|
-
"unavailable" !== state.connection.state
|
|
288
|
-
)
|
|
289
|
-
return null;
|
|
290
|
-
const auth = (function (authentication, fetchPolyfill) {
|
|
291
|
-
if ("public" === authentication.type) {
|
|
292
|
-
if ("undefined" == typeof window && null == fetchPolyfill)
|
|
293
|
-
throw new Error(
|
|
294
|
-
"To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill."
|
|
295
|
-
);
|
|
296
|
-
return (room) =>
|
|
297
|
-
fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
298
|
-
room: room,
|
|
299
|
-
publicApiKey: authentication.publicApiKey,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
if ("private" === authentication.type) {
|
|
303
|
-
if ("undefined" == typeof window && null == fetchPolyfill)
|
|
304
|
-
throw new Error(
|
|
305
|
-
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
306
|
-
);
|
|
307
|
-
return (room) =>
|
|
308
|
-
fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
309
|
-
room: room,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
if ("custom" === authentication.type) {
|
|
313
|
-
return (room) =>
|
|
314
|
-
authentication.callback(room).then((response) => {
|
|
315
|
-
if (!response || !response.token)
|
|
316
|
-
throw new Error(
|
|
317
|
-
'Authentication error. We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
318
|
-
);
|
|
319
|
-
return response;
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
throw new Error("Internal error. Unexpected authentication type");
|
|
323
|
-
})(
|
|
324
|
-
context.authentication,
|
|
325
|
-
null !==
|
|
326
|
-
(_b =
|
|
327
|
-
null === (_a = context.polyfills) || void 0 === _a
|
|
328
|
-
? void 0
|
|
329
|
-
: _a.fetch) && void 0 !== _b
|
|
330
|
-
? _b
|
|
331
|
-
: context.fetchPolyfill
|
|
332
|
-
),
|
|
333
|
-
createWebSocket = (function (liveblocksServer, WebSocketPolyfill) {
|
|
334
|
-
if ("undefined" == typeof window && null == WebSocketPolyfill)
|
|
335
|
-
throw new Error(
|
|
336
|
-
"To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill."
|
|
337
|
-
);
|
|
338
|
-
const ws = WebSocketPolyfill || WebSocket;
|
|
339
|
-
return (token) =>
|
|
340
|
-
new ws(`${liveblocksServer}/?token=${token}&version=0.17.8`);
|
|
341
|
-
})(
|
|
342
|
-
context.liveblocksServer,
|
|
343
|
-
null !==
|
|
344
|
-
(_d =
|
|
345
|
-
null === (_c = context.polyfills) || void 0 === _c
|
|
346
|
-
? void 0
|
|
347
|
-
: _c.WebSocket) && void 0 !== _d
|
|
348
|
-
? _d
|
|
349
|
-
: context.WebSocketPolyfill
|
|
350
|
-
);
|
|
351
|
-
updateConnection({ state: "authenticating" }),
|
|
352
|
-
effects.authenticate(auth, createWebSocket);
|
|
353
|
-
}
|
|
354
|
-
function authenticationSuccess(token, socket) {
|
|
355
|
-
socket.addEventListener("message", onMessage),
|
|
356
|
-
socket.addEventListener("open", onOpen),
|
|
357
|
-
socket.addEventListener("close", onClose),
|
|
358
|
-
socket.addEventListener("error", onError),
|
|
359
|
-
updateConnection({
|
|
360
|
-
state: "connecting",
|
|
361
|
-
id: token.actor,
|
|
362
|
-
userInfo: token.info,
|
|
363
|
-
userId: token.id,
|
|
364
|
-
}),
|
|
365
|
-
(state.idFactory = (function (connectionId) {
|
|
366
|
-
let count = 0;
|
|
367
|
-
return () => `${connectionId}:${count++}`;
|
|
368
|
-
})(token.actor)),
|
|
369
|
-
(state.socket = socket);
|
|
370
|
-
}
|
|
371
|
-
function onUpdatePresenceMessage(message) {
|
|
372
|
-
const user = state.users[message.actor];
|
|
373
|
-
if (
|
|
374
|
-
void 0 !== message.targetActor ||
|
|
375
|
-
null == user ||
|
|
376
|
-
user._hasReceivedInitialPresence
|
|
377
|
-
)
|
|
378
|
-
return (
|
|
379
|
-
(state.users[message.actor] =
|
|
380
|
-
null == user
|
|
381
|
-
? {
|
|
382
|
-
connectionId: message.actor,
|
|
383
|
-
presence: message.data,
|
|
384
|
-
id: void 0,
|
|
385
|
-
info: void 0,
|
|
386
|
-
_hasReceivedInitialPresence: !0,
|
|
387
|
-
}
|
|
388
|
-
: {
|
|
389
|
-
id: user.id,
|
|
390
|
-
info: user.info,
|
|
391
|
-
connectionId: message.actor,
|
|
392
|
-
presence: Object.assign(
|
|
393
|
-
Object.assign({}, user.presence),
|
|
394
|
-
message.data
|
|
395
|
-
),
|
|
396
|
-
_hasReceivedInitialPresence: !0,
|
|
397
|
-
}),
|
|
398
|
-
{
|
|
399
|
-
type: "update",
|
|
400
|
-
updates: message.data,
|
|
401
|
-
user: state.users[message.actor],
|
|
402
|
-
}
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
function onUserLeftMessage(message) {
|
|
406
|
-
const userLeftMessage = message,
|
|
407
|
-
user = state.users[userLeftMessage.actor];
|
|
408
|
-
return user
|
|
409
|
-
? (delete state.users[userLeftMessage.actor],
|
|
410
|
-
{ type: "leave", user: user })
|
|
411
|
-
: null;
|
|
412
|
-
}
|
|
413
|
-
function onRoomStateMessage(message) {
|
|
414
|
-
const newUsers = {};
|
|
415
|
-
for (const key in message.users) {
|
|
416
|
-
const connectionId = Number.parseInt(key),
|
|
417
|
-
user = message.users[key];
|
|
418
|
-
newUsers[connectionId] = {
|
|
419
|
-
connectionId: connectionId,
|
|
420
|
-
info: user.info,
|
|
421
|
-
id: user.id,
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
return (state.users = newUsers), { type: "reset" };
|
|
425
|
-
}
|
|
426
|
-
function onEvent(message) {
|
|
427
|
-
for (const listener of state.listeners.event)
|
|
428
|
-
listener({ connectionId: message.actor, event: message.event });
|
|
429
|
-
}
|
|
430
|
-
function onHistoryChange() {
|
|
431
|
-
for (const listener of state.listeners.history)
|
|
432
|
-
listener({ canUndo: canUndo(), canRedo: canRedo() });
|
|
433
|
-
}
|
|
434
|
-
function onUserJoinedMessage(message) {
|
|
435
|
-
return (
|
|
436
|
-
(state.users[message.actor] = {
|
|
437
|
-
connectionId: message.actor,
|
|
438
|
-
info: message.info,
|
|
439
|
-
id: message.id,
|
|
440
|
-
_hasReceivedInitialPresence: !0,
|
|
441
|
-
}),
|
|
442
|
-
state.me &&
|
|
443
|
-
(state.buffer.messages.push({
|
|
444
|
-
type: ClientMsgCode.UPDATE_PRESENCE,
|
|
445
|
-
data: state.me,
|
|
446
|
-
targetActor: message.actor,
|
|
447
|
-
}),
|
|
448
|
-
tryFlushing()),
|
|
449
|
-
{ type: "enter", user: state.users[message.actor] }
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
function parseServerMessage(data) {
|
|
453
|
-
return isJsonObject(data) ? data : null;
|
|
454
|
-
}
|
|
455
|
-
function onMessage(event) {
|
|
456
|
-
if ("pong" === event.data)
|
|
457
|
-
return void clearTimeout(state.timeoutHandles.pongTimeout);
|
|
458
|
-
const messages = (function (text) {
|
|
459
|
-
const data = tryParseJson(text);
|
|
460
|
-
return void 0 === data
|
|
461
|
-
? null
|
|
462
|
-
: isJsonArray(data)
|
|
463
|
-
? compact(data.map((item) => parseServerMessage(item)))
|
|
464
|
-
: compact([parseServerMessage(data)]);
|
|
465
|
-
})(event.data);
|
|
466
|
-
if (null === messages || 0 === messages.length) return;
|
|
467
|
-
const updates = { storageUpdates: new Map(), others: [] };
|
|
468
|
-
for (const message of messages)
|
|
469
|
-
switch (message.type) {
|
|
470
|
-
case ServerMsgCode.USER_JOINED:
|
|
471
|
-
updates.others.push(onUserJoinedMessage(message));
|
|
472
|
-
break;
|
|
473
|
-
case ServerMsgCode.UPDATE_PRESENCE: {
|
|
474
|
-
const othersPresenceUpdate = onUpdatePresenceMessage(message);
|
|
475
|
-
othersPresenceUpdate && updates.others.push(othersPresenceUpdate);
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
case ServerMsgCode.BROADCASTED_EVENT:
|
|
479
|
-
onEvent(message);
|
|
480
|
-
break;
|
|
481
|
-
case ServerMsgCode.USER_LEFT: {
|
|
482
|
-
const event = onUserLeftMessage(message);
|
|
483
|
-
event && updates.others.push(event);
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
case ServerMsgCode.ROOM_STATE:
|
|
487
|
-
updates.others.push(onRoomStateMessage(message));
|
|
488
|
-
break;
|
|
489
|
-
case ServerMsgCode.INITIAL_STORAGE_STATE: {
|
|
490
|
-
const offlineOps = new Map(state.offlineOperations);
|
|
491
|
-
createOrUpdateRootFromMessage(message),
|
|
492
|
-
applyAndSendOfflineOps(offlineOps),
|
|
493
|
-
null == _getInitialStateResolver || _getInitialStateResolver();
|
|
494
|
-
break;
|
|
495
|
-
}
|
|
496
|
-
case ServerMsgCode.UPDATE_STORAGE:
|
|
497
|
-
apply(message.ops, !1).updates.storageUpdates.forEach(
|
|
498
|
-
(value, key) => {
|
|
499
|
-
updates.storageUpdates.set(
|
|
500
|
-
key,
|
|
501
|
-
mergeStorageUpdates(updates.storageUpdates.get(key), value)
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
);
|
|
505
|
-
break;
|
|
506
|
-
}
|
|
507
|
-
notify(updates);
|
|
508
|
-
}
|
|
509
|
-
function onClose(event) {
|
|
510
|
-
if (
|
|
511
|
-
((state.socket = null),
|
|
512
|
-
clearTimeout(state.timeoutHandles.pongTimeout),
|
|
513
|
-
clearInterval(state.intervalHandles.heartbeat),
|
|
514
|
-
state.timeoutHandles.flush && clearTimeout(state.timeoutHandles.flush),
|
|
515
|
-
clearTimeout(state.timeoutHandles.reconnect),
|
|
516
|
-
(state.users = {}),
|
|
517
|
-
notify({ others: [{ type: "reset" }] }),
|
|
518
|
-
event.code >= 4e3 && event.code <= 4100)
|
|
519
|
-
) {
|
|
520
|
-
updateConnection({ state: "failed" });
|
|
521
|
-
const error = new LiveblocksError(event.reason, event.code);
|
|
522
|
-
for (const listener of state.listeners.error) listener(error);
|
|
523
|
-
const delay = getRetryDelay(!0);
|
|
524
|
-
state.numberOfRetry++,
|
|
525
|
-
"production" !== process.env.NODE_ENV &&
|
|
526
|
-
console.error(
|
|
527
|
-
`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code}). Retrying in ${delay}ms.`
|
|
528
|
-
),
|
|
529
|
-
updateConnection({ state: "unavailable" }),
|
|
530
|
-
(state.timeoutHandles.reconnect = effects.scheduleReconnect(delay));
|
|
531
|
-
} else if (event.code === WebsocketCloseCodes.CLOSE_WITHOUT_RETRY)
|
|
532
|
-
updateConnection({ state: "closed" });
|
|
533
|
-
else {
|
|
534
|
-
const delay = getRetryDelay();
|
|
535
|
-
state.numberOfRetry++,
|
|
536
|
-
"production" !== process.env.NODE_ENV &&
|
|
537
|
-
console.warn(
|
|
538
|
-
`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`
|
|
539
|
-
),
|
|
540
|
-
updateConnection({ state: "unavailable" }),
|
|
541
|
-
(state.timeoutHandles.reconnect = effects.scheduleReconnect(delay));
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
function updateConnection(connection) {
|
|
545
|
-
state.connection = connection;
|
|
546
|
-
for (const listener of state.listeners.connection)
|
|
547
|
-
listener(connection.state);
|
|
548
|
-
}
|
|
549
|
-
function getRetryDelay(slow = !1) {
|
|
550
|
-
return slow
|
|
551
|
-
? BACKOFF_RETRY_DELAYS_SLOW[
|
|
552
|
-
state.numberOfRetry < BACKOFF_RETRY_DELAYS_SLOW.length
|
|
553
|
-
? state.numberOfRetry
|
|
554
|
-
: BACKOFF_RETRY_DELAYS_SLOW.length - 1
|
|
555
|
-
]
|
|
556
|
-
: BACKOFF_RETRY_DELAYS[
|
|
557
|
-
state.numberOfRetry < BACKOFF_RETRY_DELAYS.length
|
|
558
|
-
? state.numberOfRetry
|
|
559
|
-
: BACKOFF_RETRY_DELAYS.length - 1
|
|
560
|
-
];
|
|
561
|
-
}
|
|
562
|
-
function onError() {}
|
|
563
|
-
function onOpen() {
|
|
564
|
-
clearInterval(state.intervalHandles.heartbeat),
|
|
565
|
-
(state.intervalHandles.heartbeat = effects.startHeartbeatInterval()),
|
|
566
|
-
"connecting" === state.connection.state &&
|
|
567
|
-
(updateConnection(
|
|
568
|
-
Object.assign(Object.assign({}, state.connection), { state: "open" })
|
|
569
|
-
),
|
|
570
|
-
(state.numberOfRetry = 0),
|
|
571
|
-
void 0 !== state.lastConnectionId &&
|
|
572
|
-
((state.buffer.presence = state.me), tryFlushing()),
|
|
573
|
-
(state.lastConnectionId = state.connection.id),
|
|
574
|
-
state.root &&
|
|
575
|
-
state.buffer.messages.push({ type: ClientMsgCode.FETCH_STORAGE }),
|
|
576
|
-
tryFlushing());
|
|
577
|
-
}
|
|
578
|
-
function heartbeat() {
|
|
579
|
-
null != state.socket &&
|
|
580
|
-
(clearTimeout(state.timeoutHandles.pongTimeout),
|
|
581
|
-
(state.timeoutHandles.pongTimeout = effects.schedulePongTimeout()),
|
|
582
|
-
state.socket.readyState === state.socket.OPEN &&
|
|
583
|
-
state.socket.send("ping"));
|
|
584
|
-
}
|
|
585
|
-
function pongTimeout() {
|
|
586
|
-
reconnect();
|
|
587
|
-
}
|
|
588
|
-
function reconnect() {
|
|
589
|
-
state.socket &&
|
|
590
|
-
(state.socket.removeEventListener("open", onOpen),
|
|
591
|
-
state.socket.removeEventListener("message", onMessage),
|
|
592
|
-
state.socket.removeEventListener("close", onClose),
|
|
593
|
-
state.socket.removeEventListener("error", onError),
|
|
594
|
-
state.socket.close(),
|
|
595
|
-
(state.socket = null)),
|
|
596
|
-
updateConnection({ state: "unavailable" }),
|
|
597
|
-
clearTimeout(state.timeoutHandles.pongTimeout),
|
|
598
|
-
state.timeoutHandles.flush && clearTimeout(state.timeoutHandles.flush),
|
|
599
|
-
clearTimeout(state.timeoutHandles.reconnect),
|
|
600
|
-
clearInterval(state.intervalHandles.heartbeat),
|
|
601
|
-
connect();
|
|
602
|
-
}
|
|
603
|
-
function applyAndSendOfflineOps(offlineOps) {
|
|
604
|
-
if (0 === offlineOps.size) return;
|
|
605
|
-
const messages = [],
|
|
606
|
-
ops = Array.from(offlineOps.values()),
|
|
607
|
-
result = apply(ops, !0);
|
|
608
|
-
messages.push({ type: ClientMsgCode.UPDATE_STORAGE, ops: ops }),
|
|
609
|
-
notify(result.updates),
|
|
610
|
-
effects.send(messages);
|
|
611
|
-
}
|
|
612
|
-
function tryFlushing() {
|
|
613
|
-
const storageOps = state.buffer.storageOperations;
|
|
614
|
-
if (
|
|
615
|
-
(storageOps.length > 0 &&
|
|
616
|
-
storageOps.forEach((op) => {
|
|
617
|
-
state.offlineOperations.set(nn(op.opId), op);
|
|
618
|
-
}),
|
|
619
|
-
null == state.socket || state.socket.readyState !== state.socket.OPEN)
|
|
620
|
-
)
|
|
621
|
-
return void (state.buffer.storageOperations = []);
|
|
622
|
-
const now = Date.now();
|
|
623
|
-
if (now - state.lastFlushTime > context.throttleDelay) {
|
|
624
|
-
const messages = (function (state) {
|
|
625
|
-
const messages = [];
|
|
626
|
-
state.buffer.presence &&
|
|
627
|
-
messages.push({
|
|
628
|
-
type: ClientMsgCode.UPDATE_PRESENCE,
|
|
629
|
-
data: state.buffer.presence,
|
|
630
|
-
});
|
|
631
|
-
for (const event of state.buffer.messages) messages.push(event);
|
|
632
|
-
state.buffer.storageOperations.length > 0 &&
|
|
633
|
-
messages.push({
|
|
634
|
-
type: ClientMsgCode.UPDATE_STORAGE,
|
|
635
|
-
ops: state.buffer.storageOperations,
|
|
636
|
-
});
|
|
637
|
-
return messages;
|
|
638
|
-
})(state);
|
|
639
|
-
if (0 === messages.length) return;
|
|
640
|
-
effects.send(messages),
|
|
641
|
-
(state.buffer = {
|
|
642
|
-
messages: [],
|
|
643
|
-
storageOperations: [],
|
|
644
|
-
presence: null,
|
|
645
|
-
}),
|
|
646
|
-
(state.lastFlushTime = now);
|
|
647
|
-
} else
|
|
648
|
-
null != state.timeoutHandles.flush &&
|
|
649
|
-
clearTimeout(state.timeoutHandles.flush),
|
|
650
|
-
(state.timeoutHandles.flush = effects.delayFlush(
|
|
651
|
-
context.throttleDelay - (now - state.lastFlushTime)
|
|
652
|
-
));
|
|
653
|
-
}
|
|
654
|
-
function getPresence() {
|
|
655
|
-
return state.me;
|
|
656
|
-
}
|
|
657
|
-
function dispatch(ops) {
|
|
658
|
-
state.buffer.storageOperations.push(...ops), tryFlushing();
|
|
659
|
-
}
|
|
660
|
-
let _getInitialStatePromise = null,
|
|
661
|
-
_getInitialStateResolver = null;
|
|
662
|
-
function canUndo() {
|
|
663
|
-
return state.undoStack.length > 0;
|
|
664
|
-
}
|
|
665
|
-
function canRedo() {
|
|
666
|
-
return state.redoStack.length > 0;
|
|
667
|
-
}
|
|
668
|
-
return {
|
|
669
|
-
onClose: onClose,
|
|
670
|
-
onMessage: onMessage,
|
|
671
|
-
authenticationSuccess: authenticationSuccess,
|
|
672
|
-
heartbeat: heartbeat,
|
|
673
|
-
onNavigatorOnline: function () {
|
|
674
|
-
"unavailable" === state.connection.state && reconnect();
|
|
675
|
-
},
|
|
676
|
-
simulateSocketClose: function () {
|
|
677
|
-
state.socket && (state.socket = null);
|
|
678
|
-
},
|
|
679
|
-
simulateSendCloseEvent: function (event) {
|
|
680
|
-
onClose(event);
|
|
681
|
-
},
|
|
682
|
-
onVisibilityChange: function (visibilityState) {
|
|
683
|
-
"visible" === visibilityState &&
|
|
684
|
-
"open" === state.connection.state &&
|
|
685
|
-
heartbeat();
|
|
686
|
-
},
|
|
687
|
-
getUndoStack: () => state.undoStack,
|
|
688
|
-
getItemsCount: () => state.items.size,
|
|
689
|
-
connect: connect,
|
|
690
|
-
disconnect: function () {
|
|
691
|
-
state.socket &&
|
|
692
|
-
(state.socket.removeEventListener("open", onOpen),
|
|
693
|
-
state.socket.removeEventListener("message", onMessage),
|
|
694
|
-
state.socket.removeEventListener("close", onClose),
|
|
695
|
-
state.socket.removeEventListener("error", onError),
|
|
696
|
-
state.socket.close(),
|
|
697
|
-
(state.socket = null)),
|
|
698
|
-
updateConnection({ state: "closed" }),
|
|
699
|
-
state.timeoutHandles.flush && clearTimeout(state.timeoutHandles.flush),
|
|
700
|
-
clearTimeout(state.timeoutHandles.reconnect),
|
|
701
|
-
clearTimeout(state.timeoutHandles.pongTimeout),
|
|
702
|
-
clearInterval(state.intervalHandles.heartbeat),
|
|
703
|
-
(state.users = {}),
|
|
704
|
-
notify({ others: [{ type: "reset" }] }),
|
|
705
|
-
(function () {
|
|
706
|
-
for (const key in state.listeners) state.listeners[key] = [];
|
|
707
|
-
})();
|
|
708
|
-
},
|
|
709
|
-
subscribe: function (first, second, options) {
|
|
710
|
-
if (void 0 === second || "function" == typeof first) {
|
|
711
|
-
if ("function" == typeof first) {
|
|
712
|
-
return genericSubscribe(first);
|
|
713
|
-
}
|
|
714
|
-
throw new Error("Please specify a listener callback");
|
|
715
|
-
}
|
|
716
|
-
if (isLiveNode(first)) {
|
|
717
|
-
const node = first;
|
|
718
|
-
if (null == options ? void 0 : options.isDeep) {
|
|
719
|
-
return (function (node, callback) {
|
|
720
|
-
return genericSubscribe((updates) => {
|
|
721
|
-
const relatedUpdates = updates.filter((update) =>
|
|
722
|
-
isSameNodeOrChildOf(update.node, node)
|
|
723
|
-
);
|
|
724
|
-
relatedUpdates.length > 0 && callback(relatedUpdates);
|
|
725
|
-
});
|
|
726
|
-
})(node, second);
|
|
727
|
-
}
|
|
728
|
-
return (function (node, callback) {
|
|
729
|
-
return genericSubscribe((updates) => {
|
|
730
|
-
for (const update of updates)
|
|
731
|
-
update.node._id === node._id && callback(update.node);
|
|
732
|
-
});
|
|
733
|
-
})(node, second);
|
|
734
|
-
}
|
|
735
|
-
if (!isRoomEventName(first))
|
|
736
|
-
throw new Error(`"${first}" is not a valid event name`);
|
|
737
|
-
const eventName = first,
|
|
738
|
-
eventListener = second;
|
|
739
|
-
return (
|
|
740
|
-
state.listeners[eventName].push(eventListener),
|
|
741
|
-
() => {
|
|
742
|
-
const callbacks = state.listeners[eventName];
|
|
743
|
-
remove(callbacks, eventListener);
|
|
744
|
-
}
|
|
745
|
-
);
|
|
746
|
-
},
|
|
747
|
-
updatePresence: function (overrides, options) {
|
|
748
|
-
const oldValues = {};
|
|
749
|
-
null == state.buffer.presence && (state.buffer.presence = {});
|
|
750
|
-
for (const key in overrides) {
|
|
751
|
-
const overrideValue = overrides[key];
|
|
752
|
-
void 0 !== overrideValue &&
|
|
753
|
-
((state.buffer.presence[key] = overrideValue),
|
|
754
|
-
(oldValues[key] = state.me[key]));
|
|
755
|
-
}
|
|
756
|
-
(state.me = Object.assign(Object.assign({}, state.me), overrides)),
|
|
757
|
-
state.isBatching
|
|
758
|
-
? ((null == options ? void 0 : options.addToHistory) &&
|
|
759
|
-
state.batch.reverseOps.push({
|
|
760
|
-
type: "presence",
|
|
761
|
-
data: oldValues,
|
|
762
|
-
}),
|
|
763
|
-
(state.batch.updates.presence = !0))
|
|
764
|
-
: (tryFlushing(),
|
|
765
|
-
(null == options ? void 0 : options.addToHistory) &&
|
|
766
|
-
addToUndoStack([{ type: "presence", data: oldValues }]),
|
|
767
|
-
notify({ presence: !0 }));
|
|
768
|
-
},
|
|
769
|
-
broadcastEvent: function (
|
|
770
|
-
event,
|
|
771
|
-
options = { shouldQueueEventIfNotReady: !1 }
|
|
772
|
-
) {
|
|
773
|
-
(null == state.socket && 0 == options.shouldQueueEventIfNotReady) ||
|
|
774
|
-
(state.buffer.messages.push({
|
|
775
|
-
type: ClientMsgCode.BROADCAST_EVENT,
|
|
776
|
-
event: event,
|
|
777
|
-
}),
|
|
778
|
-
tryFlushing());
|
|
779
|
-
},
|
|
780
|
-
batch: function (callback) {
|
|
781
|
-
if (state.isBatching)
|
|
782
|
-
throw new Error("batch should not be called during a batch");
|
|
783
|
-
state.isBatching = !0;
|
|
784
|
-
try {
|
|
785
|
-
callback();
|
|
786
|
-
} finally {
|
|
787
|
-
(state.isBatching = !1),
|
|
788
|
-
state.batch.reverseOps.length > 0 &&
|
|
789
|
-
addToUndoStack(state.batch.reverseOps),
|
|
790
|
-
state.batch.ops.length > 0 && (state.redoStack = []),
|
|
791
|
-
state.batch.ops.length > 0 && dispatch(state.batch.ops),
|
|
792
|
-
notify(state.batch.updates),
|
|
793
|
-
(state.batch = {
|
|
794
|
-
ops: [],
|
|
795
|
-
reverseOps: [],
|
|
796
|
-
updates: { others: [], storageUpdates: new Map(), presence: !1 },
|
|
797
|
-
}),
|
|
798
|
-
tryFlushing();
|
|
799
|
-
}
|
|
800
|
-
},
|
|
801
|
-
undo: function () {
|
|
802
|
-
if (state.isBatching)
|
|
803
|
-
throw new Error("undo is not allowed during a batch");
|
|
804
|
-
const historyItem = state.undoStack.pop();
|
|
805
|
-
if (null == historyItem) return;
|
|
806
|
-
state.isHistoryPaused = !1;
|
|
807
|
-
const result = apply(historyItem, !0);
|
|
808
|
-
notify(result.updates),
|
|
809
|
-
state.redoStack.push(result.reverse),
|
|
810
|
-
onHistoryChange();
|
|
811
|
-
for (const op of historyItem)
|
|
812
|
-
"presence" !== op.type && state.buffer.storageOperations.push(op);
|
|
813
|
-
tryFlushing();
|
|
814
|
-
},
|
|
815
|
-
redo: function () {
|
|
816
|
-
if (state.isBatching)
|
|
817
|
-
throw new Error("redo is not allowed during a batch");
|
|
818
|
-
const historyItem = state.redoStack.pop();
|
|
819
|
-
if (null == historyItem) return;
|
|
820
|
-
state.isHistoryPaused = !1;
|
|
821
|
-
const result = apply(historyItem, !0);
|
|
822
|
-
notify(result.updates),
|
|
823
|
-
state.undoStack.push(result.reverse),
|
|
824
|
-
onHistoryChange();
|
|
825
|
-
for (const op of historyItem)
|
|
826
|
-
"presence" !== op.type && state.buffer.storageOperations.push(op);
|
|
827
|
-
tryFlushing();
|
|
828
|
-
},
|
|
829
|
-
canUndo: canUndo,
|
|
830
|
-
canRedo: canRedo,
|
|
831
|
-
pauseHistory: function () {
|
|
832
|
-
(state.pausedHistory = []), (state.isHistoryPaused = !0);
|
|
833
|
-
},
|
|
834
|
-
resumeHistory: function () {
|
|
835
|
-
(state.isHistoryPaused = !1),
|
|
836
|
-
state.pausedHistory.length > 0 && addToUndoStack(state.pausedHistory),
|
|
837
|
-
(state.pausedHistory = []);
|
|
838
|
-
},
|
|
839
|
-
getStorage: function () {
|
|
840
|
-
return state.root
|
|
841
|
-
? new Promise((resolve) => resolve({ root: state.root }))
|
|
842
|
-
: (null == _getInitialStatePromise &&
|
|
843
|
-
(state.buffer.messages.push({ type: ClientMsgCode.FETCH_STORAGE }),
|
|
844
|
-
tryFlushing(),
|
|
845
|
-
(_getInitialStatePromise = new Promise(
|
|
846
|
-
(resolve) => (_getInitialStateResolver = resolve)
|
|
847
|
-
))),
|
|
848
|
-
_getInitialStatePromise.then(() => ({ root: nn(state.root) })));
|
|
849
|
-
},
|
|
850
|
-
selectors: {
|
|
851
|
-
getConnectionState: function () {
|
|
852
|
-
return state.connection.state;
|
|
853
|
-
},
|
|
854
|
-
getSelf: function () {
|
|
855
|
-
return "open" === state.connection.state ||
|
|
856
|
-
"connecting" === state.connection.state
|
|
857
|
-
? {
|
|
858
|
-
connectionId: state.connection.id,
|
|
859
|
-
id: state.connection.userId,
|
|
860
|
-
info: state.connection.userInfo,
|
|
861
|
-
presence: getPresence(),
|
|
862
|
-
}
|
|
863
|
-
: null;
|
|
864
|
-
},
|
|
865
|
-
getPresence: getPresence,
|
|
866
|
-
getOthers: function () {
|
|
867
|
-
return state.others;
|
|
868
|
-
},
|
|
869
|
-
},
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
function createRoom(options, context) {
|
|
873
|
-
var _a, _b;
|
|
874
|
-
const initialPresence =
|
|
875
|
-
null !== (_a = options.initialPresence) && void 0 !== _a
|
|
876
|
-
? _a
|
|
877
|
-
: options.defaultPresence,
|
|
878
|
-
initialStorage =
|
|
879
|
-
null !== (_b = options.initialStorage) && void 0 !== _b
|
|
880
|
-
? _b
|
|
881
|
-
: options.defaultStorageRoot,
|
|
882
|
-
machine = makeStateMachine(
|
|
883
|
-
(function (initialPresence, initialStorage) {
|
|
884
|
-
return {
|
|
885
|
-
connection: { state: "closed" },
|
|
886
|
-
token: null,
|
|
887
|
-
lastConnectionId: null,
|
|
888
|
-
socket: null,
|
|
889
|
-
listeners: {
|
|
890
|
-
event: [],
|
|
891
|
-
others: [],
|
|
892
|
-
"my-presence": [],
|
|
893
|
-
error: [],
|
|
894
|
-
connection: [],
|
|
895
|
-
storage: [],
|
|
896
|
-
history: [],
|
|
897
|
-
},
|
|
898
|
-
numberOfRetry: 0,
|
|
899
|
-
lastFlushTime: 0,
|
|
900
|
-
timeoutHandles: { flush: null, reconnect: 0, pongTimeout: 0 },
|
|
901
|
-
buffer: {
|
|
902
|
-
presence: null == initialPresence ? {} : initialPresence,
|
|
903
|
-
messages: [],
|
|
904
|
-
storageOperations: [],
|
|
905
|
-
},
|
|
906
|
-
intervalHandles: { heartbeat: 0 },
|
|
907
|
-
me: null == initialPresence ? {} : initialPresence,
|
|
908
|
-
users: {},
|
|
909
|
-
others: makeOthers({}),
|
|
910
|
-
defaultStorageRoot: initialStorage,
|
|
911
|
-
idFactory: null,
|
|
912
|
-
clock: 0,
|
|
913
|
-
opClock: 0,
|
|
914
|
-
items: new Map(),
|
|
915
|
-
root: void 0,
|
|
916
|
-
undoStack: [],
|
|
917
|
-
redoStack: [],
|
|
918
|
-
isHistoryPaused: !1,
|
|
919
|
-
pausedHistory: [],
|
|
920
|
-
isBatching: !1,
|
|
921
|
-
batch: {
|
|
922
|
-
ops: [],
|
|
923
|
-
updates: { storageUpdates: new Map(), presence: !1, others: [] },
|
|
924
|
-
reverseOps: [],
|
|
925
|
-
},
|
|
926
|
-
offlineOperations: new Map(),
|
|
927
|
-
};
|
|
928
|
-
})(
|
|
929
|
-
"function" == typeof initialPresence
|
|
930
|
-
? initialPresence(context.roomId)
|
|
931
|
-
: initialPresence,
|
|
932
|
-
"function" == typeof initialStorage
|
|
933
|
-
? initialStorage(context.roomId)
|
|
934
|
-
: initialStorage
|
|
935
|
-
),
|
|
936
|
-
context
|
|
937
|
-
),
|
|
938
|
-
room = {
|
|
939
|
-
id: context.roomId,
|
|
940
|
-
getConnectionState: machine.selectors.getConnectionState,
|
|
941
|
-
getSelf: machine.selectors.getSelf,
|
|
942
|
-
subscribe: machine.subscribe,
|
|
943
|
-
getPresence: machine.selectors.getPresence,
|
|
944
|
-
updatePresence: machine.updatePresence,
|
|
945
|
-
getOthers: machine.selectors.getOthers,
|
|
946
|
-
broadcastEvent: machine.broadcastEvent,
|
|
947
|
-
getStorage: machine.getStorage,
|
|
948
|
-
batch: machine.batch,
|
|
949
|
-
history: {
|
|
950
|
-
undo: machine.undo,
|
|
951
|
-
redo: machine.redo,
|
|
952
|
-
canUndo: machine.canUndo,
|
|
953
|
-
canRedo: machine.canRedo,
|
|
954
|
-
pause: machine.pauseHistory,
|
|
955
|
-
resume: machine.resumeHistory,
|
|
956
|
-
},
|
|
957
|
-
__INTERNAL_DO_NOT_USE: {
|
|
958
|
-
simulateCloseWebsocket: machine.simulateSocketClose,
|
|
959
|
-
simulateSendCloseEvent: machine.simulateSendCloseEvent,
|
|
960
|
-
},
|
|
961
|
-
};
|
|
962
|
-
return {
|
|
963
|
-
connect: machine.connect,
|
|
964
|
-
disconnect: machine.disconnect,
|
|
965
|
-
onNavigatorOnline: machine.onNavigatorOnline,
|
|
966
|
-
onVisibilityChange: machine.onVisibilityChange,
|
|
967
|
-
room: room,
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
class LiveblocksError extends Error {
|
|
971
|
-
constructor(message, code) {
|
|
972
|
-
super(message), (this.code = code);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
function fetchAuthEndpoint(fetch, endpoint, body) {
|
|
976
|
-
return fetch(endpoint, {
|
|
977
|
-
method: "POST",
|
|
978
|
-
headers: { "Content-Type": "application/json" },
|
|
979
|
-
body: JSON.stringify(body),
|
|
980
|
-
})
|
|
981
|
-
.then((res) => {
|
|
982
|
-
if (!res.ok)
|
|
983
|
-
throw new AuthenticationError(
|
|
984
|
-
`Expected a status 200 but got ${res.status} when doing a POST request on "${endpoint}"`
|
|
985
|
-
);
|
|
986
|
-
return res.json().catch((er) => {
|
|
987
|
-
throw new AuthenticationError(
|
|
988
|
-
`Expected a JSON response when doing a POST request on "${endpoint}". ${er}`
|
|
989
|
-
);
|
|
990
|
-
});
|
|
991
|
-
})
|
|
992
|
-
.then((data) => {
|
|
993
|
-
if (!isPlainObject(data) || "string" != typeof data.token)
|
|
994
|
-
throw new AuthenticationError(
|
|
995
|
-
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
|
|
996
|
-
data
|
|
997
|
-
)}`
|
|
998
|
-
);
|
|
999
|
-
const { token: token } = data;
|
|
1000
|
-
return { token: token };
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
class AuthenticationError extends Error {
|
|
1004
|
-
constructor(message) {
|
|
1005
|
-
super(message);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
function createClient(options) {
|
|
1009
|
-
const clientOptions = options,
|
|
1010
|
-
throttleDelay = (function (options) {
|
|
1011
|
-
if (void 0 === options.throttle) return 100;
|
|
1012
|
-
if (
|
|
1013
|
-
"number" != typeof options.throttle ||
|
|
1014
|
-
options.throttle < 80 ||
|
|
1015
|
-
options.throttle > 1e3
|
|
1016
|
-
)
|
|
1017
|
-
throw new Error("throttle should be a number between 80 and 1000.");
|
|
1018
|
-
return options.throttle;
|
|
1019
|
-
})(options),
|
|
1020
|
-
rooms = new Map();
|
|
1021
|
-
return (
|
|
1022
|
-
"undefined" != typeof window &&
|
|
1023
|
-
void 0 !== window.addEventListener &&
|
|
1024
|
-
window.addEventListener("online", () => {
|
|
1025
|
-
for (const [, room] of rooms) room.onNavigatorOnline();
|
|
1026
|
-
}),
|
|
1027
|
-
"undefined" != typeof document &&
|
|
1028
|
-
document.addEventListener("visibilitychange", () => {
|
|
1029
|
-
for (const [, room] of rooms)
|
|
1030
|
-
room.onVisibilityChange(document.visibilityState);
|
|
1031
|
-
}),
|
|
1032
|
-
{
|
|
1033
|
-
getRoom: function (roomId) {
|
|
1034
|
-
const internalRoom = rooms.get(roomId);
|
|
1035
|
-
return internalRoom ? internalRoom.room : null;
|
|
1036
|
-
},
|
|
1037
|
-
enter: function (roomId, options = {}) {
|
|
1038
|
-
var _a;
|
|
1039
|
-
let internalRoom = rooms.get(roomId);
|
|
1040
|
-
if (internalRoom) return internalRoom.room;
|
|
1041
|
-
if (
|
|
1042
|
-
(errorIf(
|
|
1043
|
-
options.defaultPresence,
|
|
1044
|
-
"Argument `defaultPresence` will be removed in @liveblocks/client 0.18. Please use `initialPresence` instead. For more info, see https://bit.ly/3Niy5aP"
|
|
1045
|
-
),
|
|
1046
|
-
errorIf(
|
|
1047
|
-
options.defaultStorageRoot,
|
|
1048
|
-
"Argument `defaultStorageRoot` will be removed in @liveblocks/client 0.18. Please use `initialStorage` instead. For more info, see https://bit.ly/3Niy5aP"
|
|
1049
|
-
),
|
|
1050
|
-
(internalRoom = createRoom(
|
|
1051
|
-
{
|
|
1052
|
-
initialPresence: options.initialPresence,
|
|
1053
|
-
initialStorage: options.initialStorage,
|
|
1054
|
-
defaultPresence: options.defaultPresence,
|
|
1055
|
-
defaultStorageRoot: options.defaultStorageRoot,
|
|
1056
|
-
},
|
|
1057
|
-
{
|
|
1058
|
-
roomId: roomId,
|
|
1059
|
-
throttleDelay: throttleDelay,
|
|
1060
|
-
polyfills: clientOptions.polyfills,
|
|
1061
|
-
WebSocketPolyfill: clientOptions.WebSocketPolyfill,
|
|
1062
|
-
fetchPolyfill: clientOptions.fetchPolyfill,
|
|
1063
|
-
liveblocksServer:
|
|
1064
|
-
(null == clientOptions
|
|
1065
|
-
? void 0
|
|
1066
|
-
: clientOptions.liveblocksServer) ||
|
|
1067
|
-
"wss://liveblocks.net/v6",
|
|
1068
|
-
authentication: prepareAuthentication(clientOptions),
|
|
1069
|
-
}
|
|
1070
|
-
)),
|
|
1071
|
-
rooms.set(roomId, internalRoom),
|
|
1072
|
-
!options.DO_NOT_USE_withoutConnecting)
|
|
1073
|
-
) {
|
|
1074
|
-
if ("undefined" == typeof atob) {
|
|
1075
|
-
if (
|
|
1076
|
-
null ==
|
|
1077
|
-
(null === (_a = clientOptions.polyfills) || void 0 === _a
|
|
1078
|
-
? void 0
|
|
1079
|
-
: _a.atob)
|
|
1080
|
-
)
|
|
1081
|
-
throw new Error(
|
|
1082
|
-
"You need to polyfill atob to use the client in your environment. Please follow the instructions at https://liveblocks.io/docs/errors/liveblocks-client/atob-polyfill"
|
|
1083
|
-
);
|
|
1084
|
-
global.atob = clientOptions.polyfills.atob;
|
|
1085
|
-
}
|
|
1086
|
-
internalRoom.connect();
|
|
1087
|
-
}
|
|
1088
|
-
return internalRoom.room;
|
|
1089
|
-
},
|
|
1090
|
-
leave: function (roomId) {
|
|
1091
|
-
const room = rooms.get(roomId);
|
|
1092
|
-
room && (room.disconnect(), rooms.delete(roomId));
|
|
1093
|
-
},
|
|
1094
|
-
}
|
|
1095
|
-
);
|
|
1096
|
-
}
|
|
1097
|
-
function prepareAuthentication(clientOptions) {
|
|
1098
|
-
const { publicApiKey: publicApiKey, authEndpoint: authEndpoint } =
|
|
1099
|
-
clientOptions;
|
|
1100
|
-
if (void 0 !== authEndpoint && void 0 !== publicApiKey)
|
|
1101
|
-
throw new Error(
|
|
1102
|
-
"You cannot use both publicApiKey and authEndpoint. Please use either publicApiKey or authEndpoint, but not both. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
1103
|
-
);
|
|
1104
|
-
if ("string" == typeof publicApiKey) {
|
|
1105
|
-
if (publicApiKey.startsWith("sk_"))
|
|
1106
|
-
throw new Error(
|
|
1107
|
-
"Invalid publicApiKey. You are using the secret key which is not supported. Please use the public key instead. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
1108
|
-
);
|
|
1109
|
-
if (!publicApiKey.startsWith("pk_"))
|
|
1110
|
-
throw new Error(
|
|
1111
|
-
"Invalid key. Please use the public key format: pk_<public key>. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
1112
|
-
);
|
|
1113
|
-
return {
|
|
1114
|
-
type: "public",
|
|
1115
|
-
publicApiKey: publicApiKey,
|
|
1116
|
-
url:
|
|
1117
|
-
clientOptions.publicAuthorizeEndpoint ||
|
|
1118
|
-
"https://liveblocks.io/api/public/authorize",
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
if ("string" == typeof authEndpoint)
|
|
1122
|
-
return { type: "private", url: authEndpoint };
|
|
1123
|
-
if ("function" == typeof authEndpoint)
|
|
1124
|
-
return { type: "custom", callback: authEndpoint };
|
|
1125
|
-
if (void 0 !== authEndpoint)
|
|
1126
|
-
throw new Error(
|
|
1127
|
-
"authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint"
|
|
1128
|
-
);
|
|
1129
|
-
throw new Error(
|
|
1130
|
-
"Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
1131
|
-
);
|
|
1132
|
-
}
|
|
1133
|
-
export { createClient };
|
|
1
|
+
import mod from "./index.js";
|
|
2
|
+
|
|
3
|
+
export default mod;
|
|
4
|
+
export const LiveList = mod.LiveList;
|
|
5
|
+
export const LiveMap = mod.LiveMap;
|
|
6
|
+
export const LiveObject = mod.LiveObject;
|
|
7
|
+
export const createClient = mod.createClient;
|