@liveblocks/client 0.16.16 → 0.17.0-beta2

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