@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/esm/client.js CHANGED
@@ -26,11 +26,7 @@ import { createRoom } from "./room";
26
26
  */
27
27
  export function createClient(options) {
28
28
  const clientOptions = options;
29
- if (typeof clientOptions.throttle === "number") {
30
- if (clientOptions.throttle < 80 || clientOptions.throttle > 1000) {
31
- throw new Error("Liveblocks client throttle should be between 80 and 1000 ms");
32
- }
33
- }
29
+ const throttleDelay = getThrottleDelayFromOptions(options);
34
30
  const rooms = new Map();
35
31
  function getRoom(roomId) {
36
32
  const internalRoom = rooms.get(roomId);
@@ -41,9 +37,21 @@ export function createClient(options) {
41
37
  if (internalRoom) {
42
38
  return internalRoom.room;
43
39
  }
44
- internalRoom = createRoom(roomId, Object.assign(Object.assign({}, clientOptions), options));
40
+ internalRoom = createRoom({
41
+ defaultPresence: options.defaultPresence,
42
+ defaultStorageRoot: options.defaultStorageRoot,
43
+ }, {
44
+ room: roomId,
45
+ throttleDelay,
46
+ WebSocketPolyfill: clientOptions.WebSocketPolyfill,
47
+ fetchPolyfill: clientOptions.fetchPolyfill,
48
+ liveblocksServer: clientOptions.liveblocksServer || "wss://liveblocks.net/v5",
49
+ authentication: prepareAuthentication(clientOptions),
50
+ });
45
51
  rooms.set(roomId, internalRoom);
46
- internalRoom.connect();
52
+ if (!options.DO_NOT_USE_withoutConnecting) {
53
+ internalRoom.connect();
54
+ }
47
55
  return internalRoom.room;
48
56
  }
49
57
  function leave(roomId) {
@@ -74,3 +82,38 @@ export function createClient(options) {
74
82
  leave,
75
83
  };
76
84
  }
85
+ function getThrottleDelayFromOptions(options) {
86
+ if (options.throttle === undefined) {
87
+ return 100;
88
+ }
89
+ if (typeof options.throttle !== "number" ||
90
+ options.throttle < 80 ||
91
+ options.throttle > 1000) {
92
+ throw new Error("throttle should be a number between 80 and 1000.");
93
+ }
94
+ return options.throttle;
95
+ }
96
+ function prepareAuthentication(clientOptions) {
97
+ // TODO: throw descriptive errors for invalid options
98
+ if (typeof clientOptions.publicApiKey === "string") {
99
+ return {
100
+ type: "public",
101
+ publicApiKey: clientOptions.publicApiKey,
102
+ url: clientOptions.publicAuthorizeEndpoint ||
103
+ "https://liveblocks.io/api/public/authorize",
104
+ };
105
+ }
106
+ else if (typeof clientOptions.authEndpoint === "string") {
107
+ return {
108
+ type: "private",
109
+ url: clientOptions.authEndpoint,
110
+ };
111
+ }
112
+ else if (typeof clientOptions.authEndpoint === "function") {
113
+ return {
114
+ type: "custom",
115
+ callback: clientOptions.authEndpoint,
116
+ };
117
+ }
118
+ throw new Error("Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient");
119
+ }
@@ -1,7 +1,9 @@
1
- import { LiveList } from "../LiveList";
2
- import { LiveObject } from "../LiveObject";
3
- import { StorageUpdate } from "../types";
1
+ import { LiveList } from "./LiveList";
2
+ import { LiveObject } from "./LiveObject";
3
+ import { StorageUpdate } from "./types";
4
4
  export declare function liveObjectToJson(liveObject: LiveObject<any>): any;
5
+ export declare function liveNodeToJson(value: any): any;
5
6
  export declare function patchLiveList<T>(liveList: LiveList<T>, prev: Array<T>, next: Array<T>): void;
7
+ export declare function patchLiveObjectKey<T>(liveObject: LiveObject<T>, key: keyof T, prev: any, next: any): void;
6
8
  export declare function patchLiveObject<T extends Record<string, any>>(root: LiveObject<T>, prev: T, next: T): void;
7
9
  export declare function patchImmutableObject<T>(state: T, updates: StorageUpdate[]): T;
@@ -1,6 +1,7 @@
1
- import { LiveList } from "../LiveList";
2
- import { LiveMap } from "../LiveMap";
3
- import { LiveObject } from "../LiveObject";
1
+ import { LiveList } from "./LiveList";
2
+ import { LiveMap } from "./LiveMap";
3
+ import { LiveObject } from "./LiveObject";
4
+ import { LiveRegister } from "./LiveRegister";
4
5
  export function liveObjectToJson(liveObject) {
5
6
  const result = {};
6
7
  const obj = liveObject.toObject();
@@ -20,7 +21,7 @@ function liveMapToJson(map) {
20
21
  function liveListToJson(value) {
21
22
  return value.toArray().map(liveNodeToJson);
22
23
  }
23
- function liveNodeToJson(value) {
24
+ export function liveNodeToJson(value) {
24
25
  if (value instanceof LiveObject) {
25
26
  return liveObjectToJson(value);
26
27
  }
@@ -30,6 +31,9 @@ function liveNodeToJson(value) {
30
31
  else if (value instanceof LiveMap) {
31
32
  return liveMapToJson(value);
32
33
  }
34
+ else if (value instanceof LiveRegister) {
35
+ return value.data;
36
+ }
33
37
  return value;
34
38
  }
35
39
  function isPlainObject(obj) {
@@ -97,8 +101,10 @@ export function patchLiveList(liveList, prev, next) {
97
101
  }
98
102
  }
99
103
  else if (i > nextEnd) {
100
- while (i <= prevEnd) {
101
- liveList.delete(i++);
104
+ let localI = i;
105
+ while (localI <= prevEnd) {
106
+ liveList.delete(i);
107
+ localI++;
102
108
  }
103
109
  }
104
110
  else {
@@ -127,21 +133,35 @@ export function patchLiveList(liveList, prev, next) {
127
133
  }
128
134
  }
129
135
  }
136
+ export function patchLiveObjectKey(liveObject, key, prev, next) {
137
+ const value = liveObject.get(key);
138
+ if (next === undefined) {
139
+ liveObject.delete(key);
140
+ }
141
+ else if (value === undefined) {
142
+ liveObject.set(key, anyToCrdt(next));
143
+ }
144
+ else if (prev === next) {
145
+ return;
146
+ }
147
+ else if (value instanceof LiveList &&
148
+ Array.isArray(prev) &&
149
+ Array.isArray(next)) {
150
+ patchLiveList(value, prev, next);
151
+ }
152
+ else if (value instanceof LiveObject &&
153
+ isPlainObject(prev) &&
154
+ isPlainObject(next)) {
155
+ patchLiveObject(value, prev, next);
156
+ }
157
+ else {
158
+ liveObject.set(key, anyToCrdt(next));
159
+ }
160
+ }
130
161
  export function patchLiveObject(root, prev, next) {
131
162
  const updates = {};
132
163
  for (const key in next) {
133
- if (prev[key] === next[key]) {
134
- continue;
135
- }
136
- else if (Array.isArray(prev[key]) && Array.isArray(next[key])) {
137
- patchLiveList(root.get(key), prev[key], next[key]);
138
- }
139
- else if (isPlainObject(prev[key]) && isPlainObject(next[key])) {
140
- patchLiveObject(root.get(key), prev[key], next[key]);
141
- }
142
- else {
143
- updates[key] = anyToCrdt(next[key]);
144
- }
164
+ patchLiveObjectKey(root, key, prev[key], next[key]);
145
165
  }
146
166
  for (const key in prev) {
147
167
  if (next[key] === undefined) {
@@ -173,6 +193,7 @@ function patchImmutableObjectWithUpdate(state, update) {
173
193
  return patchImmutableNode(state, path, update);
174
194
  }
175
195
  function patchImmutableNode(state, path, update) {
196
+ var _a, _b, _c, _d;
176
197
  const pathItem = path.pop();
177
198
  if (pathItem === undefined) {
178
199
  switch (update.type) {
@@ -180,19 +201,73 @@ function patchImmutableNode(state, path, update) {
180
201
  if (typeof state !== "object") {
181
202
  throw new Error("Internal: received update on LiveObject but state was not an object");
182
203
  }
183
- return liveObjectToJson(update.node);
204
+ let newState = Object.assign({}, state);
205
+ for (const key in update.updates) {
206
+ if (((_a = update.updates[key]) === null || _a === void 0 ? void 0 : _a.type) === "update") {
207
+ newState[key] = liveNodeToJson(update.node.get(key));
208
+ }
209
+ else if (((_b = update.updates[key]) === null || _b === void 0 ? void 0 : _b.type) === "delete") {
210
+ delete newState[key];
211
+ }
212
+ }
213
+ return newState;
184
214
  }
185
215
  case "LiveList": {
186
216
  if (Array.isArray(state) === false) {
187
217
  throw new Error("Internal: received update on LiveList but state was not an array");
188
218
  }
189
- return liveListToJson(update.node);
219
+ let newState = state.map((x) => x);
220
+ for (const listUpdate of update.updates) {
221
+ if (listUpdate.type === "insert") {
222
+ if (listUpdate.index === newState.length) {
223
+ newState.push(liveNodeToJson(listUpdate.item));
224
+ }
225
+ else {
226
+ newState = [
227
+ ...newState.slice(0, listUpdate.index),
228
+ liveNodeToJson(listUpdate.item),
229
+ ...newState.slice(listUpdate.index),
230
+ ];
231
+ }
232
+ }
233
+ else if (listUpdate.type === "delete") {
234
+ newState.splice(listUpdate.index, 1);
235
+ }
236
+ else if (listUpdate.type === "move") {
237
+ if (listUpdate.previousIndex > listUpdate.index) {
238
+ newState = [
239
+ ...newState.slice(0, listUpdate.index),
240
+ liveNodeToJson(listUpdate.item),
241
+ ...newState.slice(listUpdate.index, listUpdate.previousIndex),
242
+ ...newState.slice(listUpdate.previousIndex + 1),
243
+ ];
244
+ }
245
+ else {
246
+ newState = [
247
+ ...newState.slice(0, listUpdate.previousIndex),
248
+ ...newState.slice(listUpdate.previousIndex + 1, listUpdate.index + 1),
249
+ liveNodeToJson(listUpdate.item),
250
+ ...newState.slice(listUpdate.index + 1),
251
+ ];
252
+ }
253
+ }
254
+ }
255
+ return newState;
190
256
  }
191
257
  case "LiveMap": {
192
258
  if (typeof state !== "object") {
193
259
  throw new Error("Internal: received update on LiveMap but state was not an object");
194
260
  }
195
- return liveMapToJson(update.node);
261
+ let newState = Object.assign({}, state);
262
+ for (const key in update.updates) {
263
+ if (((_c = update.updates[key]) === null || _c === void 0 ? void 0 : _c.type) === "update") {
264
+ newState[key] = liveNodeToJson(update.node.get(key));
265
+ }
266
+ else if (((_d = update.updates[key]) === null || _d === void 0 ? void 0 : _d.type) === "delete") {
267
+ delete newState[key];
268
+ }
269
+ }
270
+ return newState;
196
271
  }
197
272
  }
198
273
  }
@@ -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/esm/live.d.ts CHANGED
@@ -122,6 +122,7 @@ export declare type UpdateObjectOp = {
122
122
  };
123
123
  };
124
124
  export declare type CreateObjectOp = {
125
+ opId?: string;
125
126
  id: string;
126
127
  type: OpType.CreateObject;
127
128
  parentId?: string;
@@ -131,18 +132,21 @@ export declare type CreateObjectOp = {
131
132
  };
132
133
  };
133
134
  export declare type CreateListOp = {
135
+ opId?: string;
134
136
  id: string;
135
137
  type: OpType.CreateList;
136
138
  parentId: string;
137
139
  parentKey: string;
138
140
  };
139
141
  export declare type CreateMapOp = {
142
+ opId?: string;
140
143
  id: string;
141
144
  type: OpType.CreateMap;
142
145
  parentId: string;
143
146
  parentKey: string;
144
147
  };
145
148
  export declare type CreateRegisterOp = {
149
+ opId?: string;
146
150
  id: string;
147
151
  type: OpType.CreateRegister;
148
152
  parentId: string;
@@ -150,15 +154,18 @@ export declare type CreateRegisterOp = {
150
154
  data: any;
151
155
  };
152
156
  export declare type DeleteCrdtOp = {
157
+ opId?: string;
153
158
  id: string;
154
159
  type: OpType.DeleteCrdt;
155
160
  };
156
161
  export declare type SetParentKeyOp = {
162
+ opId?: string;
157
163
  id: string;
158
164
  type: OpType.SetParentKey;
159
165
  parentKey: string;
160
166
  };
161
167
  export declare type DeleteObjectKeyOp = {
168
+ opId?: string;
162
169
  id: string;
163
170
  type: OpType.DeleteObjectKey;
164
171
  key: string;
package/lib/esm/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";
@@ -11,6 +11,7 @@ declare type HistoryItem = Array<Op | {
11
11
  declare type IdFactory = () => string;
12
12
  export declare type State = {
13
13
  connection: Connection;
14
+ lastConnectionId: number | null;
14
15
  socket: WebSocket | null;
15
16
  lastFlushTime: number;
16
17
  buffer: {
@@ -62,9 +63,10 @@ export declare type State = {
62
63
  nodes: Set<AbstractCrdt>;
63
64
  };
64
65
  };
66
+ offlineOperations: Map<string, Op>;
65
67
  };
66
68
  export declare type Effects = {
67
- authenticate(): void;
69
+ authenticate(auth: (room: string) => Promise<AuthorizeResponse>, createWebSocket: (token: string) => WebSocket): void;
68
70
  send(messages: ClientMessage[]): void;
69
71
  delayFlush(delay: number): number;
70
72
  startHeartbeatInterval(): number;
@@ -73,13 +75,13 @@ export declare type Effects = {
73
75
  };
74
76
  declare type Context = {
75
77
  room: string;
76
- authEndpoint: AuthEndpoint;
77
- liveblocksServer: string;
78
78
  throttleDelay: number;
79
- publicApiKey?: string;
79
+ fetchPolyfill?: typeof fetch;
80
+ WebSocketPolyfill?: typeof WebSocket;
81
+ authentication: Authentication;
82
+ liveblocksServer: string;
80
83
  };
81
84
  export declare function makeStateMachine(state: State, context: Context, mockedEffects?: Effects): {
82
- onOpen: () => void;
83
85
  onClose: (event: {
84
86
  code: number;
85
87
  wasClean: boolean;
@@ -89,6 +91,12 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
89
91
  authenticationSuccess: (token: AuthenticationToken, socket: WebSocket) => void;
90
92
  heartbeat: () => void;
91
93
  onNavigatorOnline: () => void;
94
+ simulateSocketClose: () => void;
95
+ simulateSendCloseEvent: (event: {
96
+ code: number;
97
+ wasClean: boolean;
98
+ reason: any;
99
+ }) => void;
92
100
  onVisibilityChange: (visibilityState: VisibilityState) => void;
93
101
  getUndoStack: () => HistoryItem[];
94
102
  getItemsCount: () => number;
@@ -118,7 +126,7 @@ export declare function makeStateMachine(state: State, context: Context, mockedE
118
126
  updatePresence: <T_4 extends Presence>(overrides: Partial<T_4>, options?: {
119
127
  addToHistory: boolean;
120
128
  } | undefined) => void;
121
- broadcastEvent: (event: any) => void;
129
+ broadcastEvent: (event: any, options?: BroadcastOptions) => void;
122
130
  batch: (callback: () => void) => void;
123
131
  undo: () => void;
124
132
  redo: () => void;
@@ -144,8 +152,8 @@ export declare type InternalRoom = {
144
152
  onNavigatorOnline: () => void;
145
153
  onVisibilityChange: (visibilityState: VisibilityState) => void;
146
154
  };
147
- export declare function createRoom(name: string, options: ClientOptions & {
155
+ export declare function createRoom(options: {
148
156
  defaultPresence?: Presence;
149
157
  defaultStorageRoot?: Record<string, any>;
150
- }): InternalRoom;
158
+ }, context: Context): InternalRoom;
151
159
  export {};