@liveblocks/client 0.13.2 → 0.14.1

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 (41) hide show
  1. package/lib/cjs/AbstractCrdt.d.ts +8 -4
  2. package/lib/cjs/AbstractCrdt.js +2 -2
  3. package/lib/cjs/LiveList.d.ts +9 -4
  4. package/lib/cjs/LiveList.js +58 -9
  5. package/lib/cjs/LiveMap.d.ts +7 -3
  6. package/lib/cjs/LiveMap.js +23 -5
  7. package/lib/cjs/LiveObject.d.ts +17 -7
  8. package/lib/cjs/LiveObject.js +45 -15
  9. package/lib/cjs/LiveRegister.d.ts +8 -4
  10. package/lib/cjs/LiveRegister.js +17 -4
  11. package/lib/cjs/client.js +50 -7
  12. package/lib/cjs/{immutable/index.d.ts → immutable.d.ts} +5 -3
  13. package/lib/cjs/{immutable/index.js → immutable.js} +98 -21
  14. package/lib/cjs/index.d.ts +1 -1
  15. package/lib/cjs/live.d.ts +7 -0
  16. package/lib/cjs/room.d.ts +17 -9
  17. package/lib/cjs/room.js +203 -81
  18. package/lib/cjs/types.d.ts +26 -1
  19. package/lib/cjs/utils.d.ts +2 -1
  20. package/lib/cjs/utils.js +76 -1
  21. package/lib/esm/AbstractCrdt.d.ts +8 -4
  22. package/lib/esm/AbstractCrdt.js +2 -2
  23. package/lib/esm/LiveList.d.ts +9 -4
  24. package/lib/esm/LiveList.js +59 -10
  25. package/lib/esm/LiveMap.d.ts +7 -3
  26. package/lib/esm/LiveMap.js +23 -5
  27. package/lib/esm/LiveObject.d.ts +17 -7
  28. package/lib/esm/LiveObject.js +45 -15
  29. package/lib/esm/LiveRegister.d.ts +8 -4
  30. package/lib/esm/LiveRegister.js +18 -5
  31. package/lib/esm/client.js +50 -7
  32. package/lib/esm/{immutable/index.d.ts → immutable.d.ts} +5 -3
  33. package/lib/esm/{immutable/index.js → immutable.js} +96 -21
  34. package/lib/esm/index.d.ts +1 -1
  35. package/lib/esm/live.d.ts +7 -0
  36. package/lib/esm/room.d.ts +17 -9
  37. package/lib/esm/room.js +203 -62
  38. package/lib/esm/types.d.ts +26 -1
  39. package/lib/esm/utils.d.ts +2 -1
  40. package/lib/esm/utils.js +75 -1
  41. package/package.json +6 -2
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) {
@@ -129,18 +108,23 @@ function makeStateMachine(state, context, mockedEffects) {
129
108
  };
130
109
  return genericSubscribe(cb);
131
110
  }
132
- function createRootFromMessage(message) {
133
- state.root = load(message.items);
111
+ function createOrUpdateRootFromMessage(message) {
112
+ if (message.items.length === 0) {
113
+ throw new Error("Internal error: cannot load storage without items");
114
+ }
115
+ if (state.root) {
116
+ updateRoot(message.items);
117
+ }
118
+ else {
119
+ state.root = load(message.items);
120
+ }
134
121
  for (const key in state.defaultStorageRoot) {
135
122
  if (state.root.get(key) == null) {
136
123
  state.root.set(key, state.defaultStorageRoot[key]);
137
124
  }
138
125
  }
139
126
  }
140
- function load(items) {
141
- if (items.length === 0) {
142
- throw new Error("Internal error: cannot load storage without items");
143
- }
127
+ function buildRootAndParentToChildren(items) {
144
128
  const parentToChildren = new Map();
145
129
  let root = null;
146
130
  for (const tuple of items) {
@@ -161,6 +145,23 @@ function makeStateMachine(state, context, mockedEffects) {
161
145
  if (root == null) {
162
146
  throw new Error("Root can't be null");
163
147
  }
148
+ return [root, parentToChildren];
149
+ }
150
+ function updateRoot(items) {
151
+ if (!state.root) {
152
+ return;
153
+ }
154
+ const currentItems = new Map();
155
+ state.items.forEach((liveCrdt, id) => {
156
+ currentItems.set(id, liveCrdt._toSerializedCrdt());
157
+ });
158
+ // Get operations that represent the diff between 2 states.
159
+ const ops = (0, utils_1.getTreesDiffOperations)(currentItems, new Map(items));
160
+ const result = apply(ops, false);
161
+ notify(result.updates);
162
+ }
163
+ function load(items) {
164
+ const [root, parentToChildren] = buildRootAndParentToChildren(items);
164
165
  return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
165
166
  addItem,
166
167
  deleteItem,
@@ -249,7 +250,10 @@ function makeStateMachine(state, context, mockedEffects) {
249
250
  state.connection.state === "connecting") {
250
251
  return state.connection.id;
251
252
  }
252
- throw new Error("Internal. Tried to get connection id but connection is not open");
253
+ else if (state.lastConnectionId !== null) {
254
+ return state.lastConnectionId;
255
+ }
256
+ throw new Error("Internal. Tried to get connection id but connection was never open");
253
257
  }
254
258
  function generateId() {
255
259
  return `${getConnectionId()}:${state.clock++}`;
@@ -257,7 +261,7 @@ function makeStateMachine(state, context, mockedEffects) {
257
261
  function generateOpId() {
258
262
  return `${getConnectionId()}:${state.opClock++}`;
259
263
  }
260
- function apply(item) {
264
+ function apply(item, isLocal) {
261
265
  const result = {
262
266
  reverse: [],
263
267
  updates: { nodes: new Set(), presence: false },
@@ -284,7 +288,11 @@ function makeStateMachine(state, context, mockedEffects) {
284
288
  result.updates.presence = true;
285
289
  }
286
290
  else {
287
- const applyOpResult = applyOp(op);
291
+ // Ops applied after undo/redo don't have an opId.
292
+ if (isLocal && !op.opId) {
293
+ op.opId = generateOpId();
294
+ }
295
+ const applyOpResult = applyOp(op, isLocal);
288
296
  if (applyOpResult.modified) {
289
297
  result.updates.nodes.add(applyOpResult.modified);
290
298
  result.reverse.unshift(...applyOpResult.reverse);
@@ -293,7 +301,10 @@ function makeStateMachine(state, context, mockedEffects) {
293
301
  }
294
302
  return result;
295
303
  }
296
- function applyOp(op) {
304
+ function applyOp(op, isLocal) {
305
+ if (op.opId) {
306
+ state.offlineOperations.delete(op.opId);
307
+ }
297
308
  switch (op.type) {
298
309
  case live_1.OpType.DeleteObjectKey:
299
310
  case live_1.OpType.UpdateObject:
@@ -302,7 +313,7 @@ function makeStateMachine(state, context, mockedEffects) {
302
313
  if (item == null) {
303
314
  return { modified: false };
304
315
  }
305
- return item._apply(op);
316
+ return item._apply(op, isLocal);
306
317
  }
307
318
  case live_1.OpType.SetParentKey: {
308
319
  const item = state.items.get(op.id);
@@ -330,28 +341,28 @@ function makeStateMachine(state, context, mockedEffects) {
330
341
  if (parent == null || getItem(op.id) != null) {
331
342
  return { modified: false };
332
343
  }
333
- return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data));
344
+ return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
334
345
  }
335
346
  case live_1.OpType.CreateList: {
336
347
  const parent = state.items.get(op.parentId);
337
348
  if (parent == null || getItem(op.id) != null) {
338
349
  return { modified: false };
339
350
  }
340
- return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList());
351
+ return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
341
352
  }
342
353
  case live_1.OpType.CreateRegister: {
343
354
  const parent = state.items.get(op.parentId);
344
355
  if (parent == null || getItem(op.id) != null) {
345
356
  return { modified: false };
346
357
  }
347
- return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data));
358
+ return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
348
359
  }
349
360
  case live_1.OpType.CreateMap: {
350
361
  const parent = state.items.get(op.parentId);
351
362
  if (parent == null || getItem(op.id) != null) {
352
363
  return { modified: false };
353
364
  }
354
- return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap());
365
+ return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
355
366
  }
356
367
  }
357
368
  return { modified: false };
@@ -398,15 +409,14 @@ See v0.13 release notes for more information.
398
409
  : null;
399
410
  }
400
411
  function connect() {
401
- if (typeof window === "undefined") {
402
- return;
403
- }
404
412
  if (state.connection.state !== "closed" &&
405
413
  state.connection.state !== "unavailable") {
406
414
  return null;
407
415
  }
416
+ const auth = prepareAuthEndpoint(context.authentication, context.fetchPolyfill);
417
+ const createWebSocket = prepareCreateWebSocket(context.liveblocksServer, context.WebSocketPolyfill);
408
418
  updateConnection({ state: "authenticating" });
409
- effects.authenticate();
419
+ effects.authenticate(auth, createWebSocket);
410
420
  }
411
421
  function updatePresence(overrides, options) {
412
422
  const oldValues = {};
@@ -433,6 +443,10 @@ See v0.13 release notes for more information.
433
443
  }
434
444
  }
435
445
  function authenticationSuccess(token, socket) {
446
+ socket.addEventListener("message", onMessage);
447
+ socket.addEventListener("open", onOpen);
448
+ socket.addEventListener("close", onClose);
449
+ socket.addEventListener("error", onError);
436
450
  updateConnection({
437
451
  state: "connecting",
438
452
  id: token.actor,
@@ -443,7 +457,9 @@ See v0.13 release notes for more information.
443
457
  state.socket = socket;
444
458
  }
445
459
  function authenticationFailure(error) {
446
- console.error(error);
460
+ if (process.env.NODE_ENV !== "production") {
461
+ console.error("Call to authentication endpoint failed", error);
462
+ }
447
463
  updateConnection({ state: "unavailable" });
448
464
  state.numberOfRetry++;
449
465
  state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
@@ -571,12 +587,13 @@ See v0.13 release notes for more information.
571
587
  break;
572
588
  }
573
589
  case live_1.ServerMessageType.InitialStorageState: {
574
- createRootFromMessage(subMessage);
590
+ createOrUpdateRootFromMessage(subMessage);
591
+ applyAndSendOfflineOps();
575
592
  _getInitialStateResolver === null || _getInitialStateResolver === void 0 ? void 0 : _getInitialStateResolver();
576
593
  break;
577
594
  }
578
595
  case live_1.ServerMessageType.UpdateStorage: {
579
- const applyResult = apply(subMessage.ops);
596
+ const applyResult = apply(subMessage.ops, false);
580
597
  for (const node of applyResult.updates.nodes) {
581
598
  updates.nodes.add(node);
582
599
  }
@@ -609,14 +626,20 @@ See v0.13 release notes for more information.
609
626
  updateConnection({ state: "failed" });
610
627
  const error = new LiveblocksError(event.reason, event.code);
611
628
  for (const listener of state.listeners.error) {
612
- console.error(`Liveblocks WebSocket connection closed. Reason: ${error.message} (code: ${error.code})`);
629
+ if (process.env.NODE_ENV !== "production") {
630
+ console.error(`Connection to Liveblocks websocket server closed. Reason: ${error.message} (code: ${error.code})`);
631
+ }
613
632
  listener(error);
614
633
  }
615
634
  }
616
635
  else if (event.wasClean === false) {
617
- updateConnection({ state: "unavailable" });
618
636
  state.numberOfRetry++;
619
- state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
637
+ const delay = getRetryDelay();
638
+ if (process.env.NODE_ENV !== "production") {
639
+ console.warn(`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${delay}ms.`);
640
+ }
641
+ updateConnection({ state: "unavailable" });
642
+ state.timeoutHandles.reconnect = effects.scheduleReconnect(delay);
620
643
  }
621
644
  else {
622
645
  updateConnection({ state: "closed" });
@@ -640,6 +663,10 @@ See v0.13 release notes for more information.
640
663
  if (state.connection.state === "connecting") {
641
664
  updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
642
665
  state.numberOfRetry = 0;
666
+ state.lastConnectionId = state.connection.id;
667
+ if (state.root) {
668
+ state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
669
+ }
643
670
  tryFlushing();
644
671
  }
645
672
  else {
@@ -653,7 +680,7 @@ See v0.13 release notes for more information.
653
680
  }
654
681
  clearTimeout(state.timeoutHandles.pongTimeout);
655
682
  state.timeoutHandles.pongTimeout = effects.schedulePongTimeout();
656
- if (state.socket.readyState === WebSocket.OPEN) {
683
+ if (state.socket.readyState === state.socket.OPEN) {
657
684
  state.socket.send("ping");
658
685
  }
659
686
  }
@@ -679,11 +706,29 @@ See v0.13 release notes for more information.
679
706
  clearInterval(state.intervalHandles.heartbeat);
680
707
  connect();
681
708
  }
682
- function tryFlushing() {
683
- if (state.socket == null) {
709
+ function applyAndSendOfflineOps() {
710
+ if (state.offlineOperations.size === 0) {
684
711
  return;
685
712
  }
686
- if (state.socket.readyState !== WebSocket.OPEN) {
713
+ const messages = [];
714
+ const ops = Array.from(state.offlineOperations.values());
715
+ const result = apply(ops, true);
716
+ messages.push({
717
+ type: live_1.ClientMessageType.UpdateStorage,
718
+ ops: ops,
719
+ });
720
+ notify(result.updates);
721
+ effects.send(messages);
722
+ }
723
+ function tryFlushing() {
724
+ const storageOps = state.buffer.storageOperations;
725
+ if (storageOps.length > 0) {
726
+ storageOps.forEach((op) => {
727
+ state.offlineOperations.set(op.opId, op);
728
+ });
729
+ }
730
+ if (state.socket == null || state.socket.readyState !== state.socket.OPEN) {
731
+ state.buffer.storageOperations = [];
687
732
  return;
688
733
  }
689
734
  const now = Date.now();
@@ -758,8 +803,10 @@ See v0.13 release notes for more information.
758
803
  function getOthers() {
759
804
  return state.others;
760
805
  }
761
- function broadcastEvent(event) {
762
- if (state.socket == null) {
806
+ function broadcastEvent(event, options = {
807
+ shouldQueueEventIfNotReady: false,
808
+ }) {
809
+ if (state.socket == null && options.shouldQueueEventIfNotReady == false) {
763
810
  return;
764
811
  }
765
812
  state.buffer.messages.push({
@@ -801,7 +848,7 @@ See v0.13 release notes for more information.
801
848
  return;
802
849
  }
803
850
  state.isHistoryPaused = false;
804
- const result = apply(historyItem);
851
+ const result = apply(historyItem, true);
805
852
  notify(result.updates);
806
853
  state.redoStack.push(result.reverse);
807
854
  for (const op of historyItem) {
@@ -820,7 +867,7 @@ See v0.13 release notes for more information.
820
867
  return;
821
868
  }
822
869
  state.isHistoryPaused = false;
823
- const result = apply(historyItem);
870
+ const result = apply(historyItem, true);
824
871
  notify(result.updates);
825
872
  state.undoStack.push(result.reverse);
826
873
  for (const op of historyItem) {
@@ -872,14 +919,26 @@ See v0.13 release notes for more information.
872
919
  }
873
920
  state.pausedHistory = [];
874
921
  }
922
+ function simulateSocketClose() {
923
+ if (state.socket) {
924
+ state.socket.close();
925
+ }
926
+ }
927
+ function simulateSendCloseEvent(event) {
928
+ if (state.socket) {
929
+ onClose(event);
930
+ }
931
+ }
875
932
  return {
876
933
  // Internal
877
- onOpen,
878
934
  onClose,
879
935
  onMessage,
880
936
  authenticationSuccess,
881
937
  heartbeat,
882
938
  onNavigatorOnline,
939
+ // Internal dev tools
940
+ simulateSocketClose,
941
+ simulateSendCloseEvent,
883
942
  // onWakeUp,
884
943
  onVisibilityChange,
885
944
  getUndoStack: () => state.undoStack,
@@ -912,6 +971,7 @@ exports.makeStateMachine = makeStateMachine;
912
971
  function defaultState(me, defaultStorageRoot) {
913
972
  return {
914
973
  connection: { state: "closed" },
974
+ lastConnectionId: null,
915
975
  socket: null,
916
976
  listeners: {
917
977
  event: [],
@@ -956,29 +1016,13 @@ function defaultState(me, defaultStorageRoot) {
956
1016
  updates: { nodes: new Set(), presence: false, others: [] },
957
1017
  reverseOps: [],
958
1018
  },
1019
+ offlineOperations: new Map(),
959
1020
  };
960
1021
  }
961
1022
  exports.defaultState = defaultState;
962
- function createRoom(name, options) {
963
- const throttleDelay = options.throttle || 100;
964
- const liveblocksServer = options.liveblocksServer || "wss://liveblocks.net/v5";
965
- let authEndpoint;
966
- if (options.authEndpoint) {
967
- authEndpoint = options.authEndpoint;
968
- }
969
- else {
970
- const publicAuthorizeEndpoint = options.publicAuthorizeEndpoint ||
971
- "https://liveblocks.io/api/public/authorize";
972
- authEndpoint = publicAuthorizeEndpoint;
973
- }
1023
+ function createRoom(options, context) {
974
1024
  const state = defaultState(options.defaultPresence, options.defaultStorageRoot);
975
- const machine = makeStateMachine(state, {
976
- throttleDelay,
977
- liveblocksServer,
978
- authEndpoint,
979
- room: name,
980
- publicApiKey: options.publicApiKey,
981
- });
1025
+ const machine = makeStateMachine(state, context);
982
1026
  const room = {
983
1027
  /////////////
984
1028
  // Core //
@@ -1002,6 +1046,11 @@ function createRoom(name, options) {
1002
1046
  pause: machine.pauseHistory,
1003
1047
  resume: machine.resumeHistory,
1004
1048
  },
1049
+ // @ts-ignore
1050
+ internalDevTools: {
1051
+ closeWebsocket: machine.simulateSocketClose,
1052
+ sendCloseEvent: machine.simulateSendCloseEvent,
1053
+ },
1005
1054
  };
1006
1055
  return {
1007
1056
  connect: machine.connect,
@@ -1018,3 +1067,76 @@ class LiveblocksError extends Error {
1018
1067
  this.code = code;
1019
1068
  }
1020
1069
  }
1070
+ function parseToken(token) {
1071
+ const tokenParts = token.split(".");
1072
+ if (tokenParts.length !== 3) {
1073
+ throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
1074
+ }
1075
+ const data = JSON.parse(atob(tokenParts[1]));
1076
+ if (typeof data.actor !== "number") {
1077
+ throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
1078
+ }
1079
+ return data;
1080
+ }
1081
+ function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
1082
+ if (typeof window === "undefined" && WebSocketPolyfill == null) {
1083
+ throw new Error("To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill.");
1084
+ }
1085
+ const ws = WebSocketPolyfill || WebSocket;
1086
+ return (token) => {
1087
+ return new ws(`${liveblocksServer}/?token=${token}`);
1088
+ };
1089
+ }
1090
+ function prepareAuthEndpoint(authentication, fetchPolyfill) {
1091
+ if (authentication.type === "public") {
1092
+ if (typeof window === "undefined" && fetchPolyfill == null) {
1093
+ throw new Error("To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill.");
1094
+ }
1095
+ return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
1096
+ room,
1097
+ publicApiKey: authentication.publicApiKey,
1098
+ });
1099
+ }
1100
+ if (authentication.type === "private") {
1101
+ if (typeof window === "undefined" && fetchPolyfill == null) {
1102
+ 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.");
1103
+ }
1104
+ return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
1105
+ room,
1106
+ });
1107
+ }
1108
+ if (authentication.type === "custom") {
1109
+ return authentication.callback;
1110
+ }
1111
+ throw new Error("Internal error. Unexpected authentication type");
1112
+ }
1113
+ function fetchAuthEndpoint(fetch, endpoint, body) {
1114
+ return __awaiter(this, void 0, void 0, function* () {
1115
+ const res = yield fetch(endpoint, {
1116
+ method: "POST",
1117
+ headers: {
1118
+ "Content-Type": "application/json",
1119
+ },
1120
+ body: JSON.stringify(body),
1121
+ });
1122
+ if (!res.ok) {
1123
+ throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
1124
+ }
1125
+ let authResponse = null;
1126
+ try {
1127
+ authResponse = yield res.json();
1128
+ }
1129
+ catch (er) {
1130
+ throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
1131
+ }
1132
+ if (typeof authResponse.token !== "string") {
1133
+ throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
1134
+ }
1135
+ return authResponse;
1136
+ });
1137
+ }
1138
+ class AuthenticationError extends Error {
1139
+ constructor(message) {
1140
+ super(message);
1141
+ }
1142
+ }
@@ -28,6 +28,14 @@ export declare type LiveListUpdates<TItem = any> = {
28
28
  type: "LiveList";
29
29
  node: LiveList<TItem>;
30
30
  };
31
+ export declare type BroadcastOptions = {
32
+ /**
33
+ * Whether or not event is queued if the connection is currently closed.
34
+ *
35
+ * ❗ We are not sure if we want to support this option in the future so it might be deprecated to be replaced by something else
36
+ */
37
+ shouldQueueEventIfNotReady: boolean;
38
+ };
31
39
  export declare type StorageUpdate = LiveMapUpdates | LiveObjectUpdates | LiveListUpdates;
32
40
  export declare type StorageCallback = (updates: StorageUpdate[]) => void;
33
41
  export declare type Client = {
@@ -65,6 +73,10 @@ export interface Others<TPresence extends Presence = Presence> {
65
73
  * Number of other users in the room.
66
74
  */
67
75
  readonly count: number;
76
+ /**
77
+ * Returns a new Iterator object that contains the users.
78
+ */
79
+ [Symbol.iterator](): IterableIterator<User<TPresence>>;
68
80
  /**
69
81
  * Returns the array of connected users in room.
70
82
  */
@@ -109,6 +121,8 @@ export declare type AuthEndpoint = string | AuthEndpointCallback;
109
121
  */
110
122
  export declare type ClientOptions = {
111
123
  throttle?: number;
124
+ fetchPolyfill?: any;
125
+ WebSocketPolyfill?: any;
112
126
  } & ({
113
127
  publicApiKey: string;
114
128
  authEndpoint?: never;
@@ -119,6 +133,17 @@ export declare type ClientOptions = {
119
133
  export declare type AuthorizeResponse = {
120
134
  token: string;
121
135
  };
136
+ export declare type Authentication = {
137
+ type: "public";
138
+ publicApiKey: string;
139
+ url: string;
140
+ } | {
141
+ type: "private";
142
+ url: string;
143
+ } | {
144
+ type: "custom";
145
+ callback: (room: string) => Promise<AuthorizeResponse>;
146
+ };
122
147
  declare type ConnectionState = "closed" | "authenticating" | "unavailable" | "failed" | "open" | "connecting";
123
148
  export declare type Connection = {
124
149
  state: "closed" | "authenticating" | "unavailable" | "failed";
@@ -423,7 +448,7 @@ export declare type Room = {
423
448
  * }
424
449
  * });
425
450
  */
426
- broadcastEvent: (event: any) => void;
451
+ broadcastEvent: (event: any, options?: BroadcastOptions) => void;
427
452
  /**
428
453
  * Get the room's storage asynchronously.
429
454
  * The storage's root is a {@link LiveObject}.
@@ -1,8 +1,9 @@
1
1
  import { AbstractCrdt, Doc } from "./AbstractCrdt";
2
- import { SerializedCrdtWithId } from "./live";
2
+ import { SerializedCrdtWithId, Op, SerializedCrdt } from "./live";
3
3
  export declare function remove<T>(array: T[], item: T): void;
4
4
  export declare function isSameNodeOrChildOf(node: AbstractCrdt, parent: AbstractCrdt): boolean;
5
5
  export declare function deserialize(entry: SerializedCrdtWithId, parentToChildren: Map<string, SerializedCrdtWithId[]>, doc: Doc): AbstractCrdt;
6
6
  export declare function isCrdt(obj: any): obj is AbstractCrdt;
7
7
  export declare function selfOrRegisterValue(obj: AbstractCrdt): any;
8
8
  export declare function selfOrRegister(obj: any): AbstractCrdt;
9
+ export declare function getTreesDiffOperations(currentItems: Map<string, SerializedCrdt>, newItems: Map<string, SerializedCrdt>): Op[];
package/lib/cjs/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
3
+ exports.getTreesDiffOperations = exports.selfOrRegister = exports.selfOrRegisterValue = exports.isCrdt = exports.deserialize = exports.isSameNodeOrChildOf = exports.remove = void 0;
4
4
  const live_1 = require("./live");
5
5
  const LiveList_1 = require("./LiveList");
6
6
  const LiveMap_1 = require("./LiveMap");
@@ -73,3 +73,78 @@ function selfOrRegister(obj) {
73
73
  }
74
74
  }
75
75
  exports.selfOrRegister = selfOrRegister;
76
+ function getTreesDiffOperations(currentItems, newItems) {
77
+ const ops = [];
78
+ currentItems.forEach((_, id) => {
79
+ if (!newItems.get(id)) {
80
+ // Delete crdt
81
+ ops.push({
82
+ type: live_1.OpType.DeleteCrdt,
83
+ id: id,
84
+ });
85
+ }
86
+ });
87
+ newItems.forEach((crdt, id) => {
88
+ const currentCrdt = currentItems.get(id);
89
+ if (currentCrdt) {
90
+ if (crdt.type === live_1.CrdtType.Object) {
91
+ if (JSON.stringify(crdt.data) !==
92
+ JSON.stringify(currentCrdt.data)) {
93
+ ops.push({
94
+ type: live_1.OpType.UpdateObject,
95
+ id: id,
96
+ data: crdt.data,
97
+ });
98
+ }
99
+ }
100
+ if (crdt.parentKey !== currentCrdt.parentKey) {
101
+ ops.push({
102
+ type: live_1.OpType.SetParentKey,
103
+ id: id,
104
+ parentKey: crdt.parentKey,
105
+ });
106
+ }
107
+ }
108
+ else {
109
+ // new Crdt
110
+ switch (crdt.type) {
111
+ case live_1.CrdtType.Register:
112
+ ops.push({
113
+ type: live_1.OpType.CreateRegister,
114
+ id: id,
115
+ parentId: crdt.parentId,
116
+ parentKey: crdt.parentKey,
117
+ data: crdt.data,
118
+ });
119
+ break;
120
+ case live_1.CrdtType.List:
121
+ ops.push({
122
+ type: live_1.OpType.CreateList,
123
+ id: id,
124
+ parentId: crdt.parentId,
125
+ parentKey: crdt.parentKey,
126
+ });
127
+ break;
128
+ case live_1.CrdtType.Object:
129
+ ops.push({
130
+ type: live_1.OpType.CreateObject,
131
+ id: id,
132
+ parentId: crdt.parentId,
133
+ parentKey: crdt.parentKey,
134
+ data: crdt.data,
135
+ });
136
+ break;
137
+ case live_1.CrdtType.Map:
138
+ ops.push({
139
+ type: live_1.OpType.CreateMap,
140
+ id: id,
141
+ parentId: crdt.parentId,
142
+ parentKey: crdt.parentKey,
143
+ });
144
+ break;
145
+ }
146
+ }
147
+ });
148
+ return ops;
149
+ }
150
+ exports.getTreesDiffOperations = getTreesDiffOperations;