@liveblocks/client 0.14.0-beta.1 → 0.14.2

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.
@@ -8,6 +8,7 @@ export declare type ApplyResult = {
8
8
  export interface Doc {
9
9
  generateId: () => string;
10
10
  generateOpId: () => string;
11
+ getItem: (id: string) => AbstractCrdt | undefined;
11
12
  addItem: (id: string, item: AbstractCrdt) => void;
12
13
  deleteItem: (id: string) => void;
13
14
  dispatch: (ops: Op[], reverseOps: Op[], modified: AbstractCrdt[]) => void;
@@ -45,7 +46,7 @@ export declare abstract class AbstractCrdt {
45
46
  /**
46
47
  * INTERNAL
47
48
  */
48
- abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
49
+ abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
49
50
  /**
50
51
  * INTERNAL
51
52
  */
@@ -25,7 +25,7 @@ export declare class LiveList<T> extends AbstractCrdt {
25
25
  /**
26
26
  * INTERNAL
27
27
  */
28
- _attachChild(id: string, key: string, child: AbstractCrdt, isLocal: boolean): ApplyResult;
28
+ _attachChild(id: string, key: string, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
29
29
  /**
30
30
  * INTERNAL
31
31
  */
@@ -68,6 +68,7 @@ export declare class LiveList<T> extends AbstractCrdt {
68
68
  * @param index The index of the element to delete
69
69
  */
70
70
  delete(index: number): void;
71
+ clear(): void;
71
72
  /**
72
73
  * Returns an Array of all the elements in the LiveList.
73
74
  */
@@ -96,11 +96,14 @@ class LiveList extends AbstractCrdt_1.AbstractCrdt {
96
96
  /**
97
97
  * INTERNAL
98
98
  */
99
- _attachChild(id, key, child, isLocal) {
99
+ _attachChild(id, key, child, opId, isLocal) {
100
100
  var _a;
101
101
  if (this._doc == null) {
102
102
  throw new Error("Can't attach child if doc is not present");
103
103
  }
104
+ if (this._doc.getItem(id) !== undefined) {
105
+ return { modified: false };
106
+ }
104
107
  child._attach(id, this._doc);
105
108
  child._setParentLink(this, key);
106
109
  const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key);
@@ -283,6 +286,28 @@ class LiveList extends AbstractCrdt_1.AbstractCrdt {
283
286
  }
284
287
  }
285
288
  }
289
+ clear() {
290
+ if (this._doc) {
291
+ let ops = [];
292
+ let reverseOps = [];
293
+ for (const item of __classPrivateFieldGet(this, _LiveList_items, "f")) {
294
+ item[0]._detach();
295
+ const childId = item[0]._id;
296
+ if (childId) {
297
+ ops.push({ id: childId, type: live_1.OpType.DeleteCrdt });
298
+ reverseOps.push(...item[0]._serialize(this._id, item[1]));
299
+ }
300
+ }
301
+ __classPrivateFieldSet(this, _LiveList_items, [], "f");
302
+ this._doc.dispatch(ops, reverseOps, [this]);
303
+ }
304
+ else {
305
+ for (const item of __classPrivateFieldGet(this, _LiveList_items, "f")) {
306
+ item[0]._detach();
307
+ }
308
+ __classPrivateFieldSet(this, _LiveList_items, [], "f");
309
+ }
310
+ }
286
311
  /**
287
312
  * Returns an Array of all the elements in the LiveList.
288
313
  */
@@ -23,7 +23,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
23
23
  /**
24
24
  * INTERNAL
25
25
  */
26
- _attachChild(id: string, key: TKey, child: AbstractCrdt, isLocal: boolean): ApplyResult;
26
+ _attachChild(id: string, key: TKey, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
27
27
  /**
28
28
  * INTERNAL
29
29
  */
@@ -100,10 +100,13 @@ class LiveMap extends AbstractCrdt_1.AbstractCrdt {
100
100
  /**
101
101
  * INTERNAL
102
102
  */
103
- _attachChild(id, key, child, isLocal) {
103
+ _attachChild(id, key, child, opId, isLocal) {
104
104
  if (this._doc == null) {
105
105
  throw new Error("Can't attach child if doc is not present");
106
106
  }
107
+ if (this._doc.getItem(id) !== undefined) {
108
+ return { modified: false };
109
+ }
107
110
  const previousValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key);
108
111
  let reverse;
109
112
  if (previousValue) {
@@ -27,7 +27,7 @@ export declare class LiveObject<T extends Record<string, any> = Record<string, a
27
27
  /**
28
28
  * INTERNAL
29
29
  */
30
- _attachChild(id: string, key: keyof T, child: AbstractCrdt, isLocal: boolean): ApplyResult;
30
+ _attachChild(id: string, key: keyof T, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
31
31
  /**
32
32
  * INTERNAL
33
33
  */
@@ -106,10 +106,32 @@ class LiveObject extends AbstractCrdt_1.AbstractCrdt {
106
106
  /**
107
107
  * INTERNAL
108
108
  */
109
- _attachChild(id, key, child, isLocal) {
109
+ _attachChild(id, key, child, opId, isLocal) {
110
110
  if (this._doc == null) {
111
111
  throw new Error("Can't attach child if doc is not present");
112
112
  }
113
+ if (this._doc.getItem(id) !== undefined) {
114
+ if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === opId) {
115
+ // Acknowlegment from local operation
116
+ __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key);
117
+ }
118
+ return { modified: false };
119
+ }
120
+ if (isLocal) {
121
+ __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
122
+ }
123
+ else if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === undefined) {
124
+ // Remote operation with no local change => apply operation
125
+ }
126
+ else if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === opId) {
127
+ // Acknowlegment from local operation
128
+ __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key);
129
+ return { modified: false };
130
+ }
131
+ else {
132
+ // Conflict, ignore remote operation
133
+ return { modified: false };
134
+ }
113
135
  const previousValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
114
136
  let reverse;
115
137
  if ((0, utils_1.isCrdt)(previousValue)) {
@@ -284,7 +306,6 @@ class LiveObject extends AbstractCrdt_1.AbstractCrdt {
284
306
  data: {},
285
307
  };
286
308
  for (const key in overrides) {
287
- __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
288
309
  const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
289
310
  if (oldValue instanceof AbstractCrdt_1.AbstractCrdt) {
290
311
  reverseOps.push(...oldValue._serialize(this._id, key));
@@ -300,10 +321,16 @@ class LiveObject extends AbstractCrdt_1.AbstractCrdt {
300
321
  if (newValue instanceof AbstractCrdt_1.AbstractCrdt) {
301
322
  newValue._setParentLink(this, key);
302
323
  newValue._attach(this._doc.generateId(), this._doc);
303
- ops.push(...newValue._serialize(this._id, key, this._doc));
324
+ const newAttachChildOps = newValue._serialize(this._id, key, this._doc);
325
+ const createCrdtOp = newAttachChildOps.find((op) => op.parentId === this._id);
326
+ if (createCrdtOp) {
327
+ __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, createCrdtOp.opId);
328
+ }
329
+ ops.push(...newAttachChildOps);
304
330
  }
305
331
  else {
306
332
  updatedProps[key] = newValue;
333
+ __classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
307
334
  }
308
335
  __classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue);
309
336
  }
@@ -378,6 +405,11 @@ _LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _
378
405
  if (__classPrivateFieldGet(this, _LiveObject_map, "f").has(key) === false) {
379
406
  return { modified: false };
380
407
  }
408
+ // If a local operation exists on the same key
409
+ // prevent flickering by not applying delete op.
410
+ if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) !== undefined) {
411
+ return { modified: false };
412
+ }
381
413
  const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
382
414
  let reverse = [];
383
415
  if ((0, utils_1.isCrdt)(oldValue)) {
@@ -19,7 +19,7 @@ export declare class LiveRegister<TValue = any> extends AbstractCrdt {
19
19
  * INTERNAL
20
20
  */
21
21
  _toSerializedCrdt(): SerializedCrdt;
22
- _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
22
+ _attachChild(id: string, key: string, crdt: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
23
23
  _detachChild(crdt: AbstractCrdt): void;
24
24
  _apply(op: Op, isLocal: boolean): ApplyResult;
25
25
  }
@@ -68,7 +68,7 @@ class LiveRegister extends AbstractCrdt_1.AbstractCrdt {
68
68
  data: this.data,
69
69
  };
70
70
  }
71
- _attachChild(id, key, crdt, isLocal) {
71
+ _attachChild(id, key, crdt, opId, isLocal) {
72
72
  throw new Error("Method not implemented.");
73
73
  }
74
74
  _detachChild(crdt) {
package/lib/cjs/client.js CHANGED
@@ -29,11 +29,7 @@ const room_1 = require("./room");
29
29
  */
30
30
  function createClient(options) {
31
31
  const clientOptions = options;
32
- if (typeof clientOptions.throttle === "number") {
33
- if (clientOptions.throttle < 80 || clientOptions.throttle > 1000) {
34
- throw new Error("Liveblocks client throttle should be between 80 and 1000 ms");
35
- }
36
- }
32
+ const throttleDelay = getThrottleDelayFromOptions(options);
37
33
  const rooms = new Map();
38
34
  function getRoom(roomId) {
39
35
  const internalRoom = rooms.get(roomId);
@@ -44,9 +40,21 @@ function createClient(options) {
44
40
  if (internalRoom) {
45
41
  return internalRoom.room;
46
42
  }
47
- internalRoom = (0, room_1.createRoom)(roomId, Object.assign(Object.assign({}, clientOptions), options));
43
+ internalRoom = (0, room_1.createRoom)({
44
+ defaultPresence: options.defaultPresence,
45
+ defaultStorageRoot: options.defaultStorageRoot,
46
+ }, {
47
+ room: roomId,
48
+ throttleDelay,
49
+ WebSocketPolyfill: clientOptions.WebSocketPolyfill,
50
+ fetchPolyfill: clientOptions.fetchPolyfill,
51
+ liveblocksServer: clientOptions.liveblocksServer || "wss://liveblocks.net/v5",
52
+ authentication: prepareAuthentication(clientOptions),
53
+ });
48
54
  rooms.set(roomId, internalRoom);
49
- internalRoom.connect();
55
+ if (!options.DO_NOT_USE_withoutConnecting) {
56
+ internalRoom.connect();
57
+ }
50
58
  return internalRoom.room;
51
59
  }
52
60
  function leave(roomId) {
@@ -78,3 +86,38 @@ function createClient(options) {
78
86
  };
79
87
  }
80
88
  exports.createClient = createClient;
89
+ function getThrottleDelayFromOptions(options) {
90
+ if (options.throttle === undefined) {
91
+ return 100;
92
+ }
93
+ if (typeof options.throttle !== "number" ||
94
+ options.throttle < 80 ||
95
+ options.throttle > 1000) {
96
+ throw new Error("throttle should be a number between 80 and 1000.");
97
+ }
98
+ return options.throttle;
99
+ }
100
+ function prepareAuthentication(clientOptions) {
101
+ // TODO: throw descriptive errors for invalid options
102
+ if (typeof clientOptions.publicApiKey === "string") {
103
+ return {
104
+ type: "public",
105
+ publicApiKey: clientOptions.publicApiKey,
106
+ url: clientOptions.publicAuthorizeEndpoint ||
107
+ "https://liveblocks.io/api/public/authorize",
108
+ };
109
+ }
110
+ else if (typeof clientOptions.authEndpoint === "string") {
111
+ return {
112
+ type: "private",
113
+ url: clientOptions.authEndpoint,
114
+ };
115
+ }
116
+ else if (typeof clientOptions.authEndpoint === "function") {
117
+ return {
118
+ type: "custom",
119
+ callback: clientOptions.authEndpoint,
120
+ };
121
+ }
122
+ throw new Error("Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient");
123
+ }
@@ -1,5 +1,5 @@
1
1
  export { LiveObject } from "./LiveObject";
2
2
  export { LiveMap } from "./LiveMap";
3
3
  export { LiveList } from "./LiveList";
4
- export type { Others, Presence, Room, Client, User } from "./types";
4
+ export type { Others, Presence, Room, Client, User, BroadcastOptions, } from "./types";
5
5
  export { createClient } from "./client";
package/lib/cjs/room.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate } from "./types";
1
+ import { Others, Presence, Room, MyPresenceCallback, OthersEventCallback, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions, AuthorizeResponse, Authentication } from "./types";
2
2
  import { ClientMessage, Op } from "./live";
3
3
  import { LiveMap } from "./LiveMap";
4
4
  import { LiveObject } from "./LiveObject";
@@ -66,7 +66,7 @@ export declare type State = {
66
66
  offlineOperations: Map<string, Op>;
67
67
  };
68
68
  export declare type Effects = {
69
- authenticate(): void;
69
+ authenticate(auth: (room: string) => Promise<AuthorizeResponse>, createWebSocket: (token: string) => WebSocket): void;
70
70
  send(messages: ClientMessage[]): void;
71
71
  delayFlush(delay: number): number;
72
72
  startHeartbeatInterval(): number;
@@ -75,13 +75,13 @@ export declare type Effects = {
75
75
  };
76
76
  declare type Context = {
77
77
  room: string;
78
- authEndpoint: AuthEndpoint;
79
- liveblocksServer: string;
80
78
  throttleDelay: number;
81
- publicApiKey?: string;
79
+ fetchPolyfill?: typeof fetch;
80
+ WebSocketPolyfill?: typeof WebSocket;
81
+ authentication: Authentication;
82
+ liveblocksServer: string;
82
83
  };
83
84
  export declare function makeStateMachine(state: State, context: Context, mockedEffects?: Effects): {
84
- onOpen: () => void;
85
85
  onClose: (event: {
86
86
  code: number;
87
87
  wasClean: boolean;
@@ -126,7 +126,7 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
126
126
  updatePresence: <T_4 extends Presence>(overrides: Partial<T_4>, options?: {
127
127
  addToHistory: boolean;
128
128
  } | undefined) => void;
129
- broadcastEvent: (event: any) => void;
129
+ broadcastEvent: (event: any, options?: BroadcastOptions) => void;
130
130
  batch: (callback: () => void) => void;
131
131
  undo: () => void;
132
132
  redo: () => void;
@@ -152,8 +152,8 @@ export declare type InternalRoom = {
152
152
  onNavigatorOnline: () => void;
153
153
  onVisibilityChange: (visibilityState: VisibilityState) => void;
154
154
  };
155
- export declare function createRoom(name: string, options: ClientOptions & {
155
+ export declare function createRoom(options: {
156
156
  defaultPresence?: Presence;
157
157
  defaultStorageRoot?: Record<string, any>;
158
- }): InternalRoom;
158
+ }, context: Context): InternalRoom;
159
159
  export {};
package/lib/cjs/room.js CHANGED
@@ -1,23 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
20
- };
21
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -30,7 +11,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
30
11
  Object.defineProperty(exports, "__esModule", { value: true });
31
12
  exports.createRoom = exports.defaultState = exports.makeStateMachine = void 0;
32
13
  const utils_1 = require("./utils");
33
- const authentication_1 = __importStar(require("./authentication"));
34
14
  const live_1 = require("./live");
35
15
  const LiveMap_1 = require("./LiveMap");
36
16
  const LiveObject_1 = require("./LiveObject");
@@ -58,6 +38,9 @@ function makeOthers(presenceMap) {
58
38
  get count() {
59
39
  return array.length;
60
40
  },
41
+ [Symbol.iterator]() {
42
+ return array[Symbol.iterator]();
43
+ },
61
44
  map(callback) {
62
45
  return array.map(callback);
63
46
  },
@@ -72,16 +55,12 @@ function log(...params) {
72
55
  }
73
56
  function makeStateMachine(state, context, mockedEffects) {
74
57
  const effects = mockedEffects || {
75
- authenticate() {
58
+ authenticate(auth, createWebSocket) {
76
59
  return __awaiter(this, void 0, void 0, function* () {
77
60
  try {
78
- const token = yield (0, authentication_1.default)(context.authEndpoint, context.room, context.publicApiKey);
79
- const parsedToken = (0, authentication_1.parseToken)(token);
80
- const socket = new WebSocket(`${context.liveblocksServer}/?token=${token}`);
81
- socket.addEventListener("message", onMessage);
82
- socket.addEventListener("open", onOpen);
83
- socket.addEventListener("close", onClose);
84
- socket.addEventListener("error", onError);
61
+ const { token } = yield auth(context.room);
62
+ const parsedToken = parseToken(token);
63
+ const socket = createWebSocket(token);
85
64
  authenticationSuccess(parsedToken, socket);
86
65
  }
87
66
  catch (er) {
@@ -184,6 +163,7 @@ function makeStateMachine(state, context, mockedEffects) {
184
163
  function load(items) {
185
164
  const [root, parentToChildren] = buildRootAndParentToChildren(items);
186
165
  return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
166
+ getItem,
187
167
  addItem,
188
168
  deleteItem,
189
169
  generateId,
@@ -359,31 +339,31 @@ function makeStateMachine(state, context, mockedEffects) {
359
339
  }
360
340
  case live_1.OpType.CreateObject: {
361
341
  const parent = state.items.get(op.parentId);
362
- if (parent == null || getItem(op.id) != null) {
342
+ if (parent == null) {
363
343
  return { modified: false };
364
344
  }
365
- return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
345
+ return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), op.opId, isLocal);
366
346
  }
367
347
  case live_1.OpType.CreateList: {
368
348
  const parent = state.items.get(op.parentId);
369
- if (parent == null || getItem(op.id) != null) {
349
+ if (parent == null) {
370
350
  return { modified: false };
371
351
  }
372
- return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
352
+ return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), op.opId, isLocal);
373
353
  }
374
354
  case live_1.OpType.CreateRegister: {
375
355
  const parent = state.items.get(op.parentId);
376
- if (parent == null || getItem(op.id) != null) {
356
+ if (parent == null) {
377
357
  return { modified: false };
378
358
  }
379
- return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
359
+ return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), op.opId, isLocal);
380
360
  }
381
361
  case live_1.OpType.CreateMap: {
382
362
  const parent = state.items.get(op.parentId);
383
- if (parent == null || getItem(op.id) != null) {
363
+ if (parent == null) {
384
364
  return { modified: false };
385
365
  }
386
- return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
366
+ return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), op.opId, isLocal);
387
367
  }
388
368
  }
389
369
  return { modified: false };
@@ -430,15 +410,14 @@ See v0.13 release notes for more information.
430
410
  : null;
431
411
  }
432
412
  function connect() {
433
- if (typeof window === "undefined") {
434
- return;
435
- }
436
413
  if (state.connection.state !== "closed" &&
437
414
  state.connection.state !== "unavailable") {
438
415
  return null;
439
416
  }
417
+ const auth = prepareAuthEndpoint(context.authentication, context.fetchPolyfill);
418
+ const createWebSocket = prepareCreateWebSocket(context.liveblocksServer, context.WebSocketPolyfill);
440
419
  updateConnection({ state: "authenticating" });
441
- effects.authenticate();
420
+ effects.authenticate(auth, createWebSocket);
442
421
  }
443
422
  function updatePresence(overrides, options) {
444
423
  const oldValues = {};
@@ -465,6 +444,10 @@ See v0.13 release notes for more information.
465
444
  }
466
445
  }
467
446
  function authenticationSuccess(token, socket) {
447
+ socket.addEventListener("message", onMessage);
448
+ socket.addEventListener("open", onOpen);
449
+ socket.addEventListener("close", onClose);
450
+ socket.addEventListener("error", onError);
468
451
  updateConnection({
469
452
  state: "connecting",
470
453
  id: token.actor,
@@ -475,7 +458,9 @@ See v0.13 release notes for more information.
475
458
  state.socket = socket;
476
459
  }
477
460
  function authenticationFailure(error) {
478
- console.error(error);
461
+ if (process.env.NODE_ENV !== "production") {
462
+ console.error("Call to authentication endpoint failed", error);
463
+ }
479
464
  updateConnection({ state: "unavailable" });
480
465
  state.numberOfRetry++;
481
466
  state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
@@ -642,14 +627,20 @@ See v0.13 release notes for more information.
642
627
  updateConnection({ state: "failed" });
643
628
  const error = new LiveblocksError(event.reason, event.code);
644
629
  for (const listener of state.listeners.error) {
645
- console.error(`Liveblocks WebSocket connection closed. Reason: ${error.message} (code: ${error.code})`);
630
+ if (process.env.NODE_ENV !== "production") {
631
+ console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
632
+ }
646
633
  listener(error);
647
634
  }
648
635
  }
649
636
  else if (event.wasClean === false) {
650
- updateConnection({ state: "unavailable" });
651
637
  state.numberOfRetry++;
652
- state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
638
+ const delay = getRetryDelay();
639
+ if (process.env.NODE_ENV !== "production") {
640
+ console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
641
+ }
642
+ updateConnection({ state: "unavailable" });
643
+ state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
653
644
  }
654
645
  else {
655
646
  updateConnection({ state: "closed" });
@@ -690,7 +681,7 @@ See v0.13 release notes for more information.
690
681
  }
691
682
  clearTimeout(state.timeoutHandles.pongTimeout);
692
683
  state.timeoutHandles.pongTimeout = effects.schedulePongTimeout();
693
- if (state.socket.readyState === WebSocket.OPEN) {
684
+ if (state.socket.readyState === state.socket.OPEN) {
694
685
  state.socket.send("ping");
695
686
  }
696
687
  }
@@ -737,7 +728,7 @@ See v0.13 release notes for more information.
737
728
  state.offlineOperations.set(op.opId, op);
738
729
  });
739
730
  }
740
- if (state.socket == null || state.socket.readyState !== WebSocket.OPEN) {
731
+ if (state.socket == null || state.socket.readyState !== state.socket.OPEN) {
741
732
  state.buffer.storageOperations = [];
742
733
  return;
743
734
  }
@@ -813,8 +804,10 @@ See v0.13 release notes for more information.
813
804
  function getOthers() {
814
805
  return state.others;
815
806
  }
816
- function broadcastEvent(event) {
817
- if (state.socket == null) {
807
+ function broadcastEvent(event, options = {
808
+ shouldQueueEventIfNotReady: false,
809
+ }) {
810
+ if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
818
811
  return;
819
812
  }
820
813
  state.buffer.messages.push({
@@ -939,7 +932,6 @@ See v0.13 release notes for more information.
939
932
  }
940
933
  return {
941
934
  // Internal
942
- onOpen,
943
935
  onClose,
944
936
  onMessage,
945
937
  authenticationSuccess,
@@ -1029,26 +1021,9 @@ function defaultState(me, defaultStorageRoot) {
1029
1021
  };
1030
1022
  }
1031
1023
  exports.defaultState = defaultState;
1032
- function createRoom(name, options) {
1033
- const throttleDelay = options.throttle || 100;
1034
- const liveblocksServer = options.liveblocksServer || "wss://liveblocks.net/v5";
1035
- let authEndpoint;
1036
- if (options.authEndpoint) {
1037
- authEndpoint = options.authEndpoint;
1038
- }
1039
- else {
1040
- const publicAuthorizeEndpoint = options.publicAuthorizeEndpoint ||
1041
- "https://liveblocks.io/api/public/authorize";
1042
- authEndpoint = publicAuthorizeEndpoint;
1043
- }
1024
+ function createRoom(options, context) {
1044
1025
  const state = defaultState(options.defaultPresence, options.defaultStorageRoot);
1045
- const machine = makeStateMachine(state, {
1046
- throttleDelay,
1047
- liveblocksServer,
1048
- authEndpoint,
1049
- room: name,
1050
- publicApiKey: options.publicApiKey,
1051
- });
1026
+ const machine = makeStateMachine(state, context);
1052
1027
  const room = {
1053
1028
  /////////////
1054
1029
  // Core //
@@ -1093,3 +1068,76 @@ class LiveblocksError extends Error {
1093
1068
  this.code = code;
1094
1069
  }
1095
1070
  }
1071
+ function parseToken(token) {
1072
+ const tokenParts = token.split(".");
1073
+ if (tokenParts.length !== 3) {
1074
+ throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
1075
+ }
1076
+ const data = JSON.parse(atob(tokenParts[1]));
1077
+ if (typeof data.actor !== "number") {
1078
+ throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
1079
+ }
1080
+ return data;
1081
+ }
1082
+ function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
1083
+ if (typeof window === "undefined" && WebSocketPolyfill == null) {
1084
+ throw new Error("To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill.");
1085
+ }
1086
+ const ws = WebSocketPolyfill || WebSocket;
1087
+ return (token) => {
1088
+ return new ws(`${liveblocksServer}/?token=${token}`);
1089
+ };
1090
+ }
1091
+ function prepareAuthEndpoint(authentication, fetchPolyfill) {
1092
+ if (authentication.type === "public") {
1093
+ if (typeof window === "undefined" && fetchPolyfill == null) {
1094
+ throw new Error("To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill.");
1095
+ }
1096
+ return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
1097
+ room,
1098
+ publicApiKey: authentication.publicApiKey,
1099
+ });
1100
+ }
1101
+ if (authentication.type === "private") {
1102
+ if (typeof window === "undefined" && fetchPolyfill == null) {
1103
+ 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.");
1104
+ }
1105
+ return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
1106
+ room,
1107
+ });
1108
+ }
1109
+ if (authentication.type === "custom") {
1110
+ return authentication.callback;
1111
+ }
1112
+ throw new Error("Internal error. Unexpected authentication type");
1113
+ }
1114
+ function fetchAuthEndpoint(fetch, endpoint, body) {
1115
+ return __awaiter(this, void 0, void 0, function* () {
1116
+ const res = yield fetch(endpoint, {
1117
+ method: "POST",
1118
+ headers: {
1119
+ "Content-Type": "application/json",
1120
+ },
1121
+ body: JSON.stringify(body),
1122
+ });
1123
+ if (!res.ok) {
1124
+ throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
1125
+ }
1126
+ let authResponse = null;
1127
+ try {
1128
+ authResponse = yield res.json();
1129
+ }
1130
+ catch (er) {
1131
+ throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
1132
+ }
1133
+ if (typeof authResponse.token !== "string") {
1134
+ throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
1135
+ }
1136
+ return authResponse;
1137
+ });
1138
+ }
1139
+ class AuthenticationError extends Error {
1140
+ constructor(message) {
1141
+ super(message);
1142
+ }
1143
+ }