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