@liveblocks/client 0.16.17 → 0.17.0-test1

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