@liveblocks/client 0.17.8 → 0.17.10-debug2

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/index.mjs CHANGED
@@ -1,1133 +1 @@
1
- import {
2
- _ as __rest,
3
- r as remove,
4
- i as isLiveNode,
5
- a as isRoomEventName,
6
- S as ServerMsgCode,
7
- m as mergeStorageUpdates,
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
+ export { LiveList, LiveMap, LiveObject, createClient } from "./index.js";