@liveblocks/react 0.9.0 → 0.12.0-beta.10

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.
package/lib/index.js CHANGED
@@ -1,1397 +1,59 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
+ var client = require('@liveblocks/client');
3
4
  var React = require('react');
4
5
 
5
- var ServerMessageType;
6
- (function (ServerMessageType) {
7
- ServerMessageType[ServerMessageType["UpdatePresence"] = 100] = "UpdatePresence";
8
- ServerMessageType[ServerMessageType["UserJoined"] = 101] = "UserJoined";
9
- ServerMessageType[ServerMessageType["UserLeft"] = 102] = "UserLeft";
10
- ServerMessageType[ServerMessageType["Event"] = 103] = "Event";
11
- ServerMessageType[ServerMessageType["RoomState"] = 104] = "RoomState";
12
- ServerMessageType[ServerMessageType["InitialStorageState"] = 200] = "InitialStorageState";
13
- ServerMessageType[ServerMessageType["UpdateStorage"] = 201] = "UpdateStorage";
14
- })(ServerMessageType || (ServerMessageType = {}));
15
- var ClientMessageType;
16
- (function (ClientMessageType) {
17
- ClientMessageType[ClientMessageType["UpdatePresence"] = 100] = "UpdatePresence";
18
- ClientMessageType[ClientMessageType["ClientEvent"] = 103] = "ClientEvent";
19
- ClientMessageType[ClientMessageType["FetchStorage"] = 200] = "FetchStorage";
20
- ClientMessageType[ClientMessageType["UpdateStorage"] = 201] = "UpdateStorage";
21
- })(ClientMessageType || (ClientMessageType = {}));
22
- var CrdtType;
23
- (function (CrdtType) {
24
- CrdtType[CrdtType["Record"] = 0] = "Record";
25
- CrdtType[CrdtType["List"] = 1] = "List";
26
- CrdtType[CrdtType["Register"] = 2] = "Register";
27
- })(CrdtType || (CrdtType = {}));
28
- var OpType;
29
- (function (OpType) {
30
- OpType[OpType["Init"] = 100] = "Init";
31
- OpType[OpType["ListInsert"] = 200] = "ListInsert";
32
- OpType[OpType["ListMove"] = 201] = "ListMove";
33
- OpType[OpType["ListRemove"] = 202] = "ListRemove";
34
- OpType[OpType["RecordUpdate"] = 300] = "RecordUpdate";
35
- })(OpType || (OpType = {}));
36
- var WebsocketCloseCodes;
37
- (function (WebsocketCloseCodes) {
38
- WebsocketCloseCodes[WebsocketCloseCodes["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
39
- WebsocketCloseCodes[WebsocketCloseCodes["INVALID_MESSAGE_FORMAT"] = 4000] = "INVALID_MESSAGE_FORMAT";
40
- WebsocketCloseCodes[WebsocketCloseCodes["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
41
- WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
42
- WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
43
- WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
44
- WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
45
- })(WebsocketCloseCodes || (WebsocketCloseCodes = {}));
46
-
47
- const min = 32;
48
- const max = 127;
49
- function makePosition(before, after) {
50
- // No children
51
- if (before == null && after == null) {
52
- return pos([min + 1]);
53
- }
54
- // Insert at the end
55
- if (before != null && after == null) {
56
- return getNextPosition(before);
57
- }
58
- // Insert at the start
59
- if (before == null && after != null) {
60
- return getPreviousPosition(after);
61
- }
62
- return pos(makePositionFromCodes(posCodes(before), posCodes(after)));
63
- }
64
- function getPreviousPosition(after) {
65
- const result = [];
66
- const afterCodes = posCodes(after);
67
- for (let i = 0; i < afterCodes.length; i++) {
68
- const code = afterCodes[i];
69
- if (code <= min + 1) {
70
- result.push(min);
71
- if (afterCodes.length - 1 === i) {
72
- result.push(max - 1);
73
- break;
74
- }
75
- }
76
- else {
77
- result.push(code - 1);
78
- break;
79
- }
80
- }
81
- return pos(result);
82
- }
83
- function getNextPosition(before) {
84
- const result = [];
85
- const beforeCodes = posCodes(before);
86
- for (let i = 0; i < beforeCodes.length; i++) {
87
- const code = beforeCodes[i];
88
- if (code === max - 1) {
89
- result.push(code);
90
- if (beforeCodes.length - 1 === i) {
91
- result.push(min + 1);
92
- break;
93
- }
94
- }
95
- else {
96
- result.push(code + 1);
97
- break;
98
- }
99
- }
100
- return pos(result);
101
- }
102
- function makePositionFromCodes(before, after) {
103
- let index = 0;
104
- const result = [];
105
- while (true) {
106
- const beforeDigit = before[index] || min;
107
- const afterDigit = after[index] || max;
108
- if (beforeDigit > afterDigit) {
109
- throw new Error(`Impossible to generate position between ${before} and ${after}`);
110
- }
111
- if (beforeDigit === afterDigit) {
112
- result.push(beforeDigit);
113
- index++;
114
- continue;
115
- }
116
- if (afterDigit - beforeDigit === 1) {
117
- result.push(beforeDigit);
118
- result.push(...makePositionFromCodes(before.slice(index + 1), []));
119
- break;
120
- }
121
- const mid = beforeDigit + Math.floor((afterDigit - beforeDigit) / 2);
122
- result.push(mid);
123
- break;
124
- }
125
- return result;
126
- }
127
- function posCodes(str) {
128
- const codes = [];
129
- for (let i = 0; i < str.length; i++) {
130
- codes.push(str.charCodeAt(i));
131
- }
132
- return codes;
133
- }
134
- function pos(codes) {
135
- return String.fromCharCode(...codes);
136
- }
137
- function compare(itemA, itemB) {
138
- const aCodes = posCodes(itemA.position);
139
- const bCodes = posCodes(itemB.position);
140
- const maxLength = Math.max(aCodes.length, bCodes.length);
141
- for (let i = 0; i < maxLength; i++) {
142
- const a = aCodes[i] == null ? min : aCodes[i];
143
- const b = bCodes[i] == null ? min : bCodes[i];
144
- if (a === b) {
145
- continue;
146
- }
147
- else {
148
- return a - b;
149
- }
150
- }
151
- throw new Error(`Impossible to compare similar position "${itemA.position}" and "${itemB.position}"`);
152
- }
153
-
154
- const RECORD = Symbol("liveblocks.record");
155
- const LIST = Symbol("liveblocks.list");
156
- function createRecord(id, data) {
157
- return Object.assign({ id, $$type: RECORD }, data);
158
- }
159
- function createList(id, items = []) {
160
- return {
161
- id,
162
- $$type: LIST,
163
- length: items.length,
164
- toArray: () => items,
165
- map: (callback) => items.map(callback),
166
- };
167
- }
168
- function noop() { }
169
- class Doc {
170
- constructor(root, _cache, _emit) {
171
- this.root = root;
172
- this._cache = _cache;
173
- this._emit = _emit;
174
- }
175
- static empty(id = "root", emit = noop) {
176
- const root = {
177
- id,
178
- $$type: RECORD,
179
- };
180
- return new Doc(root, { links: new Map(), listCache: new Map() }, emit);
181
- }
182
- static createFromRoot(data, id = "root", emit = noop) {
183
- let doc = Doc.empty(id, emit);
184
- doc = doc.updateRecord(doc.root.id, data);
185
- return doc;
186
- }
187
- static load(root, emit = noop) {
188
- let doc = Doc.empty(root.id, emit);
189
- return doc.dispatch({
190
- type: OpType.RecordUpdate,
191
- id: root.id,
192
- data: root.data,
193
- });
194
- }
195
- get data() {
196
- return this.root;
197
- }
198
- dispatch(op, shouldEmit = false) {
199
- if (shouldEmit) {
200
- this._emit(op);
201
- }
202
- if (op.id === this.root.id) {
203
- const node = dispatch(this.root, op, this._cache, []);
204
- return new Doc(node, this._cache, this._emit);
205
- }
206
- else {
207
- const links = getAllLinks(op.id, this.root.id, this._cache.links);
208
- const node = dispatch(this.root, op, this._cache, links);
209
- return new Doc(node, this._cache, this._emit);
210
- }
211
- }
212
- getChild(id) {
213
- if (id === this.root.id) {
214
- return this.root;
215
- }
216
- const allLinks = getAllLinks(id, this.root.id, this._cache.links);
217
- return getChildDeep(this.root, id, allLinks, this._cache);
218
- }
219
- updateRecord(id, overrides) {
220
- const currentRecord = this.getChild(id);
221
- if (currentRecord == null) {
222
- throw new Error(`Record with id "${id}" does not exist`);
223
- }
224
- let data = {};
225
- for (const key in overrides) {
226
- const value = overrides[key];
227
- data[key] = serialize(value);
228
- }
229
- const op = {
230
- id: currentRecord.id,
231
- type: OpType.RecordUpdate,
232
- data,
233
- };
234
- return this.dispatch(op, true);
235
- }
236
- pushItem(id, item) {
237
- const list = this.getChild(id);
238
- if (list == null) {
239
- throw new Error(`List with id "${id}" does not exist`);
240
- }
241
- if (list.$$type !== LIST) {
242
- throw new Error(`Node with id "${id}" is not a list`);
243
- }
244
- if (!isRecord(item)) {
245
- throw new Error("List can't only have Record as children");
246
- }
247
- const data = serialize(item);
248
- if (list.length === 0) {
249
- return this.dispatch({
250
- type: OpType.ListInsert,
251
- id: list.id,
252
- position: makePosition(),
253
- data,
254
- }, true);
255
- }
256
- const items = sortedListItems(getListItems(this._cache, id));
257
- const [tailPosition] = items[items.length - 1];
258
- const position = makePosition(tailPosition);
259
- const operation = {
260
- type: OpType.ListInsert,
261
- id: list.id,
262
- position,
263
- data,
264
- };
265
- return this.dispatch(operation, true);
266
- }
267
- moveItem(id, index, targetIndex) {
268
- const list = this.getChild(id);
269
- if (list == null) {
270
- throw new Error(`List with id "${id}" does not exist`);
271
- }
272
- if (list.$$type !== LIST) {
273
- throw new Error(`Node with id "${id}" is not a list`);
274
- }
275
- const items = sortedListItems(getListItems(this._cache, id));
276
- if (targetIndex < 0) {
277
- throw new Error("targetIndex cannot be less than 0");
278
- }
279
- if (targetIndex >= items.length) {
280
- throw new Error("targetIndex cannot be greater or equal than the list length");
281
- }
282
- if (index < 0) {
283
- throw new Error("index cannot be less than 0");
284
- }
285
- if (index >= items.length) {
286
- throw new Error("index cannot be greater or equal than the list length");
287
- }
288
- if (index === targetIndex) {
289
- return this;
290
- }
291
- let beforePosition = null;
292
- let afterPosition = null;
293
- if (index < targetIndex) {
294
- afterPosition =
295
- targetIndex === items.length - 1
296
- ? undefined
297
- : items[targetIndex + 1][0];
298
- beforePosition = items[targetIndex][0];
299
- }
300
- else {
301
- afterPosition = items[targetIndex][0];
302
- beforePosition =
303
- targetIndex === 0 ? undefined : items[targetIndex - 1][0];
304
- }
305
- const position = makePosition(beforePosition, afterPosition);
306
- const [, item] = items[index];
307
- return this.dispatch({
308
- type: OpType.ListMove,
309
- id: list.id,
310
- itemId: item.id,
311
- position,
312
- }, true);
313
- }
314
- deleteItem(id, index) {
315
- const list = this.getChild(id);
316
- if (list == null) {
317
- throw new Error(`List with id "${id}" does not exist`);
318
- }
319
- if (list.$$type !== LIST) {
320
- throw new Error(`Node with id "${id}" is not a list`);
321
- }
322
- const items = sortedListItems(getListItems(this._cache, id));
323
- const [, item] = items[index];
324
- return this.dispatch({
325
- type: OpType.ListRemove,
326
- id: list.id,
327
- itemId: item.id,
328
- }, true);
329
- }
330
- deleteItemById(id, itemId) {
331
- const list = this.getChild(id);
332
- if (list == null) {
333
- throw new Error(`List with id "${id}" does not exist`);
334
- }
335
- if (list.$$type !== LIST) {
336
- throw new Error(`Node with id "${id}" is not a list`);
337
- }
338
- const itemsMap = getListItems(this._cache, id);
339
- let item = null;
340
- for (const [, crdt] of itemsMap) {
341
- if (crdt.id === itemId) {
342
- item = crdt;
343
- break;
344
- }
345
- }
346
- if (item == null) {
347
- throw new Error(`List with id "${id}" does not have an item with id "${itemId}"`);
348
- }
349
- return this.dispatch({
350
- type: OpType.ListRemove,
351
- id: list.id,
352
- itemId: item.id,
353
- }, true);
354
- }
355
- }
356
- function getAllLinks(id, rootId, links) {
357
- let currentId = id;
358
- const result = [];
359
- do {
360
- const link = links.get(currentId);
361
- if (link == null) {
362
- throw new Error(`Can't find link for id "${currentId}"`);
363
- }
364
- currentId = link.parentId;
365
- result.push(link);
366
- } while (currentId !== rootId);
367
- return result;
368
- }
369
- function deserializeList(serialized, cache) {
370
- const listItems = new Map();
371
- for (const position in serialized.data) {
372
- const item = deserialize(serialized.data[position], cache);
373
- if (!isRecord(item)) {
374
- throw new Error("TODO");
375
- }
376
- listItems.set(position, item);
377
- cache.links.set(item.id, { parentId: serialized.id, parentKey: position });
378
- }
379
- cache.listCache.set(serialized.id, listItems);
380
- return createList(serialized.id, listItemsToArray(listItems));
381
- }
382
- function getListItems(cache, listId) {
383
- const items = cache.listCache.get(listId);
384
- if (items == null) {
385
- throw new Error(`Can't find list cache for id "${listId}"`);
386
- }
387
- return items;
388
- }
389
- function deserializeRecord(serialized, cache) {
390
- const result = {
391
- id: serialized.id,
392
- $$type: RECORD,
393
- };
394
- for (const key in serialized.data) {
395
- const item = deserialize(serialized.data[key], cache);
396
- if (isCrdt(item)) {
397
- cache.links.set(item.id, {
398
- parentId: serialized.id,
399
- parentKey: key,
400
- });
401
- }
402
- result[key] = item;
403
- }
404
- return result;
405
- }
406
- function deserialize(serialized, cache) {
407
- switch (serialized.type) {
408
- case CrdtType.Register: {
409
- return serialized.data;
410
- }
411
- case CrdtType.Record: {
412
- return deserializeRecord(serialized, cache);
413
- }
414
- case CrdtType.List: {
415
- return deserializeList(serialized, cache);
416
- }
417
- default: {
418
- throw new Error("TODO");
419
- }
420
- }
421
- }
422
- function dispatchOnRecord(record, op, cache, links) {
423
- if (links.length === 0) {
424
- if (record.id !== op.id) {
425
- throw new Error("TODO");
426
- }
427
- switch (op.type) {
428
- case OpType.RecordUpdate: {
429
- return updateRecord(record, op, cache);
430
- }
431
- default: {
432
- console.warn("Unsupported operation");
433
- return record;
434
- }
435
- }
436
- }
437
- const currentLink = links.pop();
438
- const child = record[currentLink.parentKey];
439
- const newNode = dispatch(child, op, cache, links);
440
- return Object.assign(Object.assign({}, record), { [currentLink.parentKey]: newNode });
441
- }
442
- function dispatchOnList(list, op, cache, links) {
443
- if (links.length === 0) {
444
- if (list.id !== op.id) {
445
- throw new Error("TODO");
446
- }
447
- switch (op.type) {
448
- case OpType.ListInsert: {
449
- return listInsert(list, op, cache);
450
- }
451
- case OpType.ListMove: {
452
- return listMove(list, op, cache);
453
- }
454
- case OpType.ListRemove: {
455
- return listDelete(list, op, cache);
456
- }
457
- default: {
458
- console.warn("Unsupported operation");
459
- return list;
460
- }
461
- }
462
- }
463
- const currentLink = links.pop();
464
- const position = currentLink.parentKey;
465
- const items = getListItems(cache, list.id);
466
- const item = items.get(position);
467
- if (item == null) {
468
- throw new Error("TODO");
469
- }
470
- const newItem = dispatch(item, op, cache, links);
471
- items.set(position, newItem);
472
- return createList(list.id, listItemsToArray(items));
473
- }
474
- function dispatch(node, op, cache, links) {
475
- switch (node.$$type) {
476
- case RECORD:
477
- return dispatchOnRecord(node, op, cache, links);
478
- case LIST:
479
- return dispatchOnList(node, op, cache, links);
480
- default: {
481
- throw new Error("Unknown CRDT");
482
- }
483
- }
484
- }
485
- function updateRecord(node, op, cache) {
486
- const result = Object.assign({}, node);
487
- for (const key in op.data) {
488
- const value = op.data[key];
489
- const item = deserialize(value, cache);
490
- if (isCrdt(item)) {
491
- cache.links.set(item.id, { parentId: node.id, parentKey: key });
492
- }
493
- result[key] = item;
494
- }
495
- return result;
496
- }
497
- function listInsert(list, op, cache) {
498
- const items = getListItems(cache, list.id);
499
- const item = deserialize(op.data, cache);
500
- if (isCrdt(item)) {
501
- items.set(op.position, item);
502
- cache.links.set(item.id, { parentId: list.id, parentKey: op.position });
503
- }
504
- return createList(list.id, listItemsToArray(items));
505
- }
506
- function listMove(list, op, cache) {
507
- const items = getListItems(cache, list.id);
508
- const link = getLinkOrThrow(cache, op.itemId);
509
- const item = items.get(link.parentKey);
510
- if (item == null) {
511
- throw new Error("TODO");
512
- }
513
- // Delete old position cache entry
514
- items.delete(link.parentKey);
515
- // Insert new position in cache
516
- items.set(op.position, item);
517
- // Update link
518
- cache.links.set(op.itemId, { parentId: list.id, parentKey: op.position });
519
- return createList(list.id, listItemsToArray(items));
520
- }
521
- function getLinkOrThrow(cache, id) {
522
- const link = cache.links.get(id);
523
- if (link == null) {
524
- throw new Error(`Can't find link with id "${id}"`);
525
- }
526
- return link;
527
- }
528
- function listDelete(list, op, cache) {
529
- const items = getListItems(cache, list.id);
530
- const link = getLinkOrThrow(cache, op.itemId);
531
- items.delete(link.parentKey);
532
- cache.links.delete(op.itemId);
533
- return createList(list.id, listItemsToArray(items));
534
- }
535
- function listItemsToArray(items) {
536
- return sortedListItems(items).map((entry) => entry[1]);
537
- }
538
- function sortedListItems(items) {
539
- return Array.from(items.entries()).sort((entryA, entryB) => compare({ position: entryA[0] }, { position: entryB[0] }));
540
- }
541
- function getChildDeep(node, id, links, cache) {
542
- let currentNode = node;
543
- while (currentNode.id !== id) {
544
- const link = links.pop();
545
- if (link == null || link.parentId !== currentNode.id) {
546
- throw new Error("TODO");
547
- }
548
- if (currentNode.$$type === RECORD) {
549
- currentNode = currentNode[link.parentKey];
550
- }
551
- else {
552
- const listItems = getListItems(cache, currentNode.id);
553
- const item = listItems.get(link.parentKey);
554
- if (item == null) {
555
- throw new Error("TODO");
556
- }
557
- currentNode = item;
558
- }
559
- }
560
- return currentNode;
561
- }
562
- function isRecord(value) {
563
- return value != null && typeof value === "object" && value.$$type === RECORD;
564
- }
565
- function isList(value) {
566
- return value != null && typeof value === "object" && value.$$type === LIST;
567
- }
568
- function isCrdt(value) {
569
- return isRecord(value) || isList(value);
570
- }
571
- function serializeRecord(record) {
572
- const serializedData = {};
573
- for (const key in record) {
574
- if (key !== "id" && key !== "$$type") {
575
- const value = record[key]; // TODO: Find out why typescript does not like that
576
- serializedData[key] = serialize(value);
577
- }
578
- }
579
- return {
580
- id: record.id,
581
- type: CrdtType.Record,
582
- data: serializedData,
583
- };
584
- }
585
- function serializeList(list) {
586
- return {
587
- id: list.id,
588
- type: CrdtType.List,
589
- data: {},
590
- };
591
- }
592
- function serialize(value) {
593
- if (isRecord(value)) {
594
- return serializeRecord(value);
595
- }
596
- else if (isList(value)) {
597
- return serializeList(value);
598
- }
599
- else {
600
- return { type: CrdtType.Register, data: value };
601
- }
602
- }
603
-
604
- var LiveStorageState;
605
- (function (LiveStorageState) {
606
- LiveStorageState[LiveStorageState["NotInitialized"] = 0] = "NotInitialized";
607
- LiveStorageState[LiveStorageState["Loading"] = 1] = "Loading";
608
- LiveStorageState[LiveStorageState["Loaded"] = 2] = "Loaded";
609
- })(LiveStorageState || (LiveStorageState = {}));
610
-
611
- function remove(array, item) {
612
- for (let i = 0; i < array.length; i++) {
613
- if (array[i] === item) {
614
- array.splice(i, 1);
615
- break;
616
- }
617
- }
618
- }
619
-
620
- var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
621
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
622
- return new (P || (P = Promise))(function (resolve, reject) {
623
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
624
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
625
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
626
- step((generator = generator.apply(thisArg, _arguments || [])).next());
627
- });
628
- };
629
- function fetchAuthorize(endpoint, room) {
630
- return __awaiter(this, void 0, void 0, function* () {
631
- const res = yield fetch(endpoint, {
632
- method: "POST",
633
- headers: {
634
- "Content-Type": "application/json",
635
- },
636
- body: JSON.stringify({
637
- room,
638
- }),
639
- });
640
- if (!res.ok) {
641
- throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
642
- }
643
- let authResponse = null;
644
- try {
645
- authResponse = yield res.json();
646
- }
647
- catch (er) {
648
- throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
649
- }
650
- if (typeof authResponse.token !== "string") {
651
- throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
652
- }
653
- return authResponse.token;
654
- });
655
- }
656
- function auth(endpoint, room) {
657
- return __awaiter(this, void 0, void 0, function* () {
658
- if (typeof endpoint === "string") {
659
- return fetchAuthorize(endpoint, room);
660
- }
661
- if (typeof endpoint === "function") {
662
- return endpoint(room);
663
- }
664
- throw new Error("Authentication error. Liveblocks could not parse the response of your authentication endpoint");
665
- });
666
- }
667
- class AuthenticationError extends Error {
668
- constructor(message) {
669
- super(message);
670
- }
671
- }
672
- function parseToken(token) {
673
- const tokenParts = token.split(".");
674
- if (tokenParts.length !== 3) {
675
- throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
676
- }
677
- const data = JSON.parse(atob(tokenParts[1]));
678
- if (typeof data.actor !== "number") {
679
- throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
680
- }
681
- return data;
682
- }
683
-
684
- var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
685
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
686
- return new (P || (P = Promise))(function (resolve, reject) {
687
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
688
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
689
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
690
- step((generator = generator.apply(thisArg, _arguments || [])).next());
691
- });
692
- };
693
- const BACKOFF_RETRY_DELAYS = [250, 500, 1000, 2000, 4000, 8000, 10000];
694
- const HEARTBEAT_INTERVAL = 30000;
695
- // const WAKE_UP_CHECK_INTERVAL = 2000;
696
- const PONG_TIMEOUT = 2000;
697
- function isValidRoomEventType(value) {
698
- return (value === "storage" ||
699
- value === "my-presence" ||
700
- value === "others" ||
701
- value === "event" ||
702
- value === "error" ||
703
- value === "connection");
704
- }
705
- function makeIdFactory(connectionId) {
706
- let count = 0;
707
- return () => `${connectionId}:${count++}`;
708
- }
709
- function makeOthers(presenceMap) {
710
- const array = Object.values(presenceMap);
711
- return {
712
- get count() {
713
- return array.length;
714
- },
715
- map(callback) {
716
- return array.map(callback);
717
- },
718
- toArray() {
719
- return array;
720
- },
721
- };
722
- }
723
- function makeStateMachine(state, context, mockedEffects) {
724
- const effects = mockedEffects || {
725
- authenticate() {
726
- return __awaiter$1(this, void 0, void 0, function* () {
727
- try {
728
- const token = yield auth(context.authEndpoint, context.room);
729
- const parsedToken = parseToken(token);
730
- const socket = new WebSocket(`${context.liveblocksServer}/?token=${token}`);
731
- socket.addEventListener("message", onMessage);
732
- socket.addEventListener("open", onOpen);
733
- socket.addEventListener("close", onClose);
734
- socket.addEventListener("error", onError);
735
- authenticationSuccess(parsedToken, socket);
736
- }
737
- catch (er) {
738
- authenticationFailure(er);
739
- }
740
- });
741
- },
742
- send(messageOrMessages) {
743
- if (state.socket == null) {
744
- throw new Error("Can't send message if socket is null");
745
- }
746
- state.socket.send(JSON.stringify(messageOrMessages));
747
- },
748
- delayFlush(delay) {
749
- return setTimeout(tryFlushing, delay);
750
- },
751
- startHeartbeatInterval() {
752
- return setInterval(heartbeat, HEARTBEAT_INTERVAL);
753
- },
754
- schedulePongTimeout() {
755
- return setTimeout(pongTimeout, PONG_TIMEOUT);
756
- },
757
- scheduleReconnect(delay) {
758
- return setTimeout(connect, delay);
759
- },
760
- };
761
- function subscribe(type, listener) {
762
- if (!isValidRoomEventType(type)) {
763
- throw new Error(`"${type}" is not a valid event name`);
764
- }
765
- state.listeners[type].push(listener);
766
- }
767
- function unsubscribe(event, callback) {
768
- if (!isValidRoomEventType(event)) {
769
- throw new Error(`"${event}" is not a valid event name`);
770
- }
771
- const callbacks = state.listeners[event];
772
- remove(callbacks, callback);
773
- }
774
- function getConnectionState() {
775
- return state.connection.state;
776
- }
777
- function getCurrentUser() {
778
- return state.connection.state === "open" ||
779
- state.connection.state === "connecting"
780
- ? {
781
- connectionId: state.connection.id,
782
- id: state.connection.userId,
783
- info: state.connection.userInfo,
784
- presence: getPresence(),
785
- }
786
- : null;
787
- }
788
- function connect() {
789
- if (typeof window === "undefined") {
790
- return;
791
- }
792
- if (state.connection.state !== "closed" &&
793
- state.connection.state !== "unavailable") {
794
- return null;
795
- }
796
- updateConnection({ state: "authenticating" });
797
- effects.authenticate();
798
- }
799
- function updatePresence(overrides) {
800
- const newPresence = Object.assign(Object.assign({}, state.me), overrides);
801
- if (state.flushData.presence == null) {
802
- state.flushData.presence = overrides;
803
- }
804
- else {
805
- for (const key in overrides) {
806
- state.flushData.presence[key] = overrides[key];
807
- }
808
- }
809
- state.me = newPresence;
810
- tryFlushing();
811
- for (const listener of state.listeners["my-presence"]) {
812
- listener(state.me);
813
- }
814
- }
815
- function authenticationSuccess(token, socket) {
816
- updateConnection({
817
- state: "connecting",
818
- id: token.actor,
819
- userInfo: token.info,
820
- userId: token.id,
821
- });
822
- state.idFactory = makeIdFactory(token.actor);
823
- state.socket = socket;
824
- }
825
- function authenticationFailure(error) {
826
- console.error(error);
827
- updateConnection({ state: "unavailable" });
828
- state.numberOfRetry++;
829
- state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
830
- }
831
- function onVisibilityChange(visibilityState) {
832
- if (visibilityState === "visible" && state.connection.state === "open") {
833
- heartbeat();
834
- }
835
- }
836
- function onUpdatePresenceMessage(message) {
837
- const user = state.users[message.actor];
838
- if (user == null) {
839
- state.users[message.actor] = {
840
- connectionId: message.actor,
841
- presence: message.data,
842
- };
843
- }
844
- else {
845
- state.users[message.actor] = {
846
- id: user.id,
847
- info: user.info,
848
- connectionId: message.actor,
849
- presence: Object.assign(Object.assign({}, user.presence), message.data),
850
- };
851
- }
852
- updateUsers({
853
- type: "update",
854
- updates: message.data,
855
- user: state.users[message.actor],
856
- });
857
- }
858
- function updateUsers(event) {
859
- state.others = makeOthers(state.users);
860
- for (const listener of state.listeners["others"]) {
861
- listener(state.others, event);
862
- }
863
- }
864
- function onUserLeftMessage(message) {
865
- const userLeftMessage = message;
866
- const user = state.users[userLeftMessage.actor];
867
- if (user) {
868
- delete state.users[userLeftMessage.actor];
869
- updateUsers({ type: "leave", user });
870
- }
871
- }
872
- function onRoomStateMessage(message) {
873
- const newUsers = {};
874
- for (const key in message.users) {
875
- const connectionId = Number.parseInt(key);
876
- const user = message.users[key];
877
- newUsers[connectionId] = {
878
- connectionId,
879
- info: user.info,
880
- id: user.id,
881
- };
882
- }
883
- state.users = newUsers;
884
- updateUsers({ type: "reset" });
885
- }
886
- function onNavigatorOnline() {
887
- if (state.connection.state === "unavailable") {
888
- reconnect();
889
- }
890
- }
891
- function onEvent(message) {
892
- for (const listener of state.listeners.event) {
893
- listener({ connectionId: message.actor, event: message.event });
894
- }
895
- }
896
- function onUserJoinedMessage(message) {
897
- state.users[message.actor] = {
898
- connectionId: message.actor,
899
- info: message.info,
900
- id: message.id,
901
- };
902
- updateUsers({ type: "enter", user: state.users[message.actor] });
903
- if (state.me) {
904
- // Send current presence to new user
905
- // TODO: Consider storing it on the backend
906
- state.flushData.messages.push({
907
- type: ClientMessageType.UpdatePresence,
908
- data: state.me,
909
- targetActor: message.actor,
910
- });
911
- tryFlushing();
912
- }
913
- }
914
- function onMessage(event) {
915
- if (event.data === "pong") {
916
- clearTimeout(state.timeoutHandles.pongTimeout);
917
- return;
918
- }
919
- const message = JSON.parse(event.data);
920
- switch (message.type) {
921
- case ServerMessageType.InitialStorageState: {
922
- onInitialStorageState(message);
923
- break;
924
- }
925
- case ServerMessageType.UpdateStorage: {
926
- onStorageUpdates(message);
927
- break;
928
- }
929
- case ServerMessageType.UserJoined: {
930
- onUserJoinedMessage(message);
931
- break;
932
- }
933
- case ServerMessageType.UpdatePresence: {
934
- onUpdatePresenceMessage(message);
935
- break;
936
- }
937
- case ServerMessageType.Event: {
938
- onEvent(message);
939
- break;
940
- }
941
- case ServerMessageType.UserLeft: {
942
- onUserLeftMessage(message);
943
- break;
944
- }
945
- case ServerMessageType.RoomState: {
946
- onRoomStateMessage(message);
947
- break;
948
- }
949
- }
950
- }
951
- // function onWakeUp() {
952
- // // Sometimes, the browser can put the webpage on pause (computer is on sleep mode for example)
953
- // // The client will not know that the server has probably close the connection even if the readyState is Open
954
- // // One way to detect this kind of pause is to ensure that a setInterval is not taking more than the delay it was configured with
955
- // if (state.connection.state === "open") {
956
- // log("Try to reconnect after laptop wake up");
957
- // reconnect();
958
- // }
959
- // }
960
- function onClose(event) {
961
- state.socket = null;
962
- clearTimeout(state.timeoutHandles.pongTimeout);
963
- clearInterval(state.intervalHandles.heartbeat);
964
- if (state.timeoutHandles.flush) {
965
- clearTimeout(state.timeoutHandles.flush);
966
- }
967
- clearTimeout(state.timeoutHandles.reconnect);
968
- state.users = {};
969
- updateUsers({ type: "reset" });
970
- if (event.code >= 4000 && event.code <= 4100) {
971
- updateConnection({ state: "failed" });
972
- const error = new LiveblocksError(event.reason, event.code);
973
- for (const listener of state.listeners.error) {
974
- listener(error);
975
- }
976
- }
977
- else if (event.wasClean === false) {
978
- updateConnection({ state: "unavailable" });
979
- state.numberOfRetry++;
980
- state.timeoutHandles.reconnect = effects.scheduleReconnect(getRetryDelay());
981
- }
982
- else {
983
- updateConnection({ state: "closed" });
984
- }
985
- }
986
- function updateConnection(connection) {
987
- state.connection = connection;
988
- for (const listener of state.listeners.connection) {
989
- listener(connection.state);
990
- }
991
- }
992
- function getRetryDelay() {
993
- return BACKOFF_RETRY_DELAYS[state.numberOfRetry < BACKOFF_RETRY_DELAYS.length
994
- ? state.numberOfRetry
995
- : BACKOFF_RETRY_DELAYS.length - 1];
996
- }
997
- function onError() { }
998
- function onOpen() {
999
- clearInterval(state.intervalHandles.heartbeat);
1000
- state.intervalHandles.heartbeat = effects.startHeartbeatInterval();
1001
- if (state.connection.state === "connecting") {
1002
- updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
1003
- state.numberOfRetry = 0;
1004
- tryFlushing();
1005
- }
1006
- }
1007
- function heartbeat() {
1008
- if (state.socket == null) {
1009
- // Should never happen, because we clear the pong timeout when the connection is dropped explictly
1010
- return;
1011
- }
1012
- clearTimeout(state.timeoutHandles.pongTimeout);
1013
- state.timeoutHandles.pongTimeout = effects.schedulePongTimeout();
1014
- if (state.socket.readyState === WebSocket.OPEN) {
1015
- state.socket.send("ping");
1016
- }
1017
- }
1018
- function pongTimeout() {
1019
- reconnect();
1020
- }
1021
- function reconnect() {
1022
- if (state.socket) {
1023
- state.socket.removeEventListener("open", onOpen);
1024
- state.socket.removeEventListener("message", onMessage);
1025
- state.socket.removeEventListener("close", onClose);
1026
- state.socket.removeEventListener("error", onError);
1027
- state.socket.close();
1028
- state.socket = null;
1029
- }
1030
- updateConnection({ state: "unavailable" });
1031
- clearTimeout(state.timeoutHandles.pongTimeout);
1032
- if (state.timeoutHandles.flush) {
1033
- clearTimeout(state.timeoutHandles.flush);
1034
- }
1035
- clearTimeout(state.timeoutHandles.reconnect);
1036
- clearInterval(state.intervalHandles.heartbeat);
1037
- connect();
1038
- }
1039
- function tryFlushing() {
1040
- if (state.socket == null) {
1041
- return;
1042
- }
1043
- if (state.socket.readyState !== WebSocket.OPEN) {
1044
- return;
1045
- }
1046
- const now = Date.now();
1047
- const elapsedTime = now - state.lastFlushTime;
1048
- if (elapsedTime > context.throttleDelay) {
1049
- const messages = flushDataToMessages(state);
1050
- if (messages.length === 0) {
1051
- return;
1052
- }
1053
- effects.send(messages);
1054
- state.flushData = {
1055
- messages: [],
1056
- storageOperations: [],
1057
- presence: null,
1058
- };
1059
- state.lastFlushTime = now;
1060
- }
1061
- else {
1062
- if (state.timeoutHandles.flush != null) {
1063
- clearTimeout(state.timeoutHandles.flush);
1064
- }
1065
- state.timeoutHandles.flush = effects.delayFlush(context.throttleDelay - (now - state.lastFlushTime));
1066
- }
1067
- }
1068
- function flushDataToMessages(state) {
1069
- const messages = [];
1070
- if (state.flushData.presence) {
1071
- messages.push({
1072
- type: ClientMessageType.UpdatePresence,
1073
- data: state.flushData.presence,
1074
- });
1075
- }
1076
- for (const event of state.flushData.messages) {
1077
- messages.push(event);
1078
- }
1079
- if (state.flushData.storageOperations.length > 0) {
1080
- messages.push({
1081
- type: ClientMessageType.UpdateStorage,
1082
- ops: state.flushData.storageOperations,
1083
- });
1084
- }
1085
- return messages;
1086
- }
1087
- function disconnect() {
1088
- if (state.socket) {
1089
- state.socket.removeEventListener("open", onOpen);
1090
- state.socket.removeEventListener("message", onMessage);
1091
- state.socket.removeEventListener("close", onClose);
1092
- state.socket.removeEventListener("error", onError);
1093
- state.socket.close();
1094
- state.socket = null;
1095
- }
1096
- updateConnection({ state: "closed" });
1097
- if (state.timeoutHandles.flush) {
1098
- clearTimeout(state.timeoutHandles.flush);
1099
- }
1100
- clearTimeout(state.timeoutHandles.reconnect);
1101
- clearTimeout(state.timeoutHandles.pongTimeout);
1102
- clearInterval(state.intervalHandles.heartbeat);
1103
- state.users = {};
1104
- updateUsers({ type: "reset" });
1105
- clearListeners();
1106
- }
1107
- function clearListeners() {
1108
- for (const key in state.listeners) {
1109
- state.listeners[key] = [];
1110
- }
1111
- }
1112
- function getPresence() {
1113
- return state.me;
1114
- }
1115
- function getOthers() {
1116
- return state.others;
1117
- }
1118
- function broadcastEvent(event) {
1119
- if (state.socket == null) {
1120
- return;
1121
- }
1122
- state.flushData.messages.push({
1123
- type: ClientMessageType.ClientEvent,
1124
- event,
1125
- });
1126
- tryFlushing();
1127
- }
1128
- /**
1129
- * STORAGE
1130
- */
1131
- function onStorageUpdates(message) {
1132
- if (state.doc == null) {
1133
- // TODO: Cache updates in case they are coming while root is queried
1134
- return;
1135
- }
1136
- updateDoc(message.ops.reduce((doc, op) => doc.dispatch(op), state.doc));
1137
- }
1138
- function updateDoc(doc) {
1139
- state.doc = doc;
1140
- if (doc) {
1141
- for (const listener of state.listeners.storage) {
1142
- listener(getStorage());
1143
- }
1144
- }
1145
- }
1146
- function getStorage() {
1147
- if (state.storageState === LiveStorageState.Loaded) {
1148
- return {
1149
- state: state.storageState,
1150
- root: state.doc.root,
1151
- };
1152
- }
1153
- return {
1154
- state: state.storageState,
1155
- };
1156
- }
1157
- function onInitialStorageState(message) {
1158
- state.storageState = LiveStorageState.Loaded;
1159
- if (message.root == null) {
1160
- const rootId = makeId();
1161
- state.doc = Doc.empty(rootId, (op) => dispatch(op));
1162
- updateDoc(state.doc.updateRecord(rootId, state.initialStorageFactory({
1163
- createRecord: (data) => createRecord$1(data),
1164
- createList: () => createList$1(),
1165
- })));
1166
- }
1167
- else {
1168
- updateDoc(Doc.load(message.root, (op) => dispatch(op)));
1169
- }
1170
- }
1171
- function makeId() {
1172
- if (state.idFactory == null) {
1173
- throw new Error("Can't generate id. Id factory is missing.");
1174
- }
1175
- return state.idFactory();
1176
- }
1177
- function dispatch(op) {
1178
- state.flushData.storageOperations.push(op);
1179
- tryFlushing();
1180
- }
1181
- function createRecord$1(data) {
1182
- return createRecord(makeId(), data);
1183
- }
1184
- function createList$1() {
1185
- return createList(makeId());
1186
- }
1187
- function fetchStorage(initialStorageFactory) {
1188
- state.initialStorageFactory = initialStorageFactory;
1189
- state.storageState = LiveStorageState.Loading;
1190
- state.flushData.messages.push({ type: ClientMessageType.FetchStorage });
1191
- tryFlushing();
1192
- }
1193
- function updateRecord(record, overrides) {
1194
- updateDoc(state.doc.updateRecord(record.id, overrides));
1195
- }
1196
- function pushItem(list, item) {
1197
- updateDoc(state.doc.pushItem(list.id, item));
1198
- }
1199
- function deleteItem(list, index) {
1200
- updateDoc(state.doc.deleteItem(list.id, index));
1201
- }
1202
- function deleteItemById(list, itemId) {
1203
- updateDoc(state.doc.deleteItemById(list.id, itemId));
1204
- }
1205
- function moveItem(list, index, targetIndex) {
1206
- updateDoc(state.doc.moveItem(list.id, index, targetIndex));
1207
- }
1208
- return {
1209
- // Internal
1210
- onOpen,
1211
- onClose,
1212
- onMessage,
1213
- authenticationSuccess,
1214
- heartbeat,
1215
- onNavigatorOnline,
1216
- // onWakeUp,
1217
- onVisibilityChange,
1218
- // Core
1219
- connect,
1220
- disconnect,
1221
- subscribe,
1222
- unsubscribe,
1223
- // Presence
1224
- updatePresence,
1225
- broadcastEvent,
1226
- // Storage
1227
- fetchStorage,
1228
- createRecord: createRecord$1,
1229
- updateRecord,
1230
- createList: createList$1,
1231
- pushItem,
1232
- deleteItem,
1233
- deleteItemById,
1234
- moveItem,
1235
- selectors: {
1236
- // Core
1237
- getConnectionState,
1238
- getCurrentUser,
1239
- // Presence
1240
- getPresence,
1241
- getOthers,
1242
- // Storage
1243
- getStorage,
1244
- },
1245
- };
1246
- }
1247
- function defaultState(me) {
1248
- return {
1249
- connection: { state: "closed" },
1250
- socket: null,
1251
- listeners: {
1252
- storage: [],
1253
- event: [],
1254
- others: [],
1255
- "my-presence": [],
1256
- error: [],
1257
- connection: [],
1258
- },
1259
- numberOfRetry: 0,
1260
- lastFlushTime: 0,
1261
- timeoutHandles: {
1262
- flush: null,
1263
- reconnect: 0,
1264
- pongTimeout: 0,
1265
- },
1266
- flushData: {
1267
- presence: me == null ? {} : me,
1268
- messages: [],
1269
- storageOperations: [],
1270
- },
1271
- intervalHandles: {
1272
- heartbeat: 0,
1273
- },
1274
- me: me == null ? {} : me,
1275
- users: {},
1276
- others: makeOthers({}),
1277
- storageState: LiveStorageState.NotInitialized,
1278
- initialStorageFactory: null,
1279
- doc: null,
1280
- idFactory: null,
1281
- };
1282
- }
1283
- function createRoom(name, options) {
1284
- const throttleDelay = options.throttle || 100;
1285
- const liveblocksServer = options.liveblocksServer || "wss://liveblocks.net";
1286
- const authEndpoint = options.authEndpoint;
1287
- const state = defaultState(options.initialPresence);
1288
- const machine = makeStateMachine(state, {
1289
- throttleDelay,
1290
- liveblocksServer,
1291
- authEndpoint,
1292
- room: name,
1293
- });
1294
- const room = {
1295
- /////////////
1296
- // Core //
1297
- /////////////
1298
- getConnectionState: machine.selectors.getConnectionState,
1299
- getCurrentUser: machine.selectors.getCurrentUser,
1300
- subscribe: machine.subscribe,
1301
- unsubscribe: machine.unsubscribe,
1302
- /////////////
1303
- // Storage //
1304
- /////////////
1305
- getStorage: machine.selectors.getStorage,
1306
- fetchStorage: machine.fetchStorage,
1307
- createRecord: machine.createRecord,
1308
- createList: machine.createList,
1309
- updateRecord: machine.updateRecord,
1310
- pushItem: machine.pushItem,
1311
- deleteItem: machine.deleteItem,
1312
- deleteItemById: machine.deleteItemById,
1313
- moveItem: machine.moveItem,
1314
- //////////////
1315
- // Presence //
1316
- //////////////
1317
- getPresence: machine.selectors.getPresence,
1318
- updatePresence: machine.updatePresence,
1319
- getOthers: machine.selectors.getOthers,
1320
- broadcastEvent: machine.broadcastEvent,
1321
- };
1322
- return {
1323
- connect: machine.connect,
1324
- disconnect: machine.disconnect,
1325
- onNavigatorOnline: machine.onNavigatorOnline,
1326
- onVisibilityChange: machine.onVisibilityChange,
1327
- room,
1328
- };
1329
- }
1330
- class LiveblocksError extends Error {
1331
- constructor(message, code) {
1332
- super(message);
1333
- this.code = code;
1334
- }
1335
- }
1336
-
1337
- /**
1338
- * Create a client that will be responsible to communicate with liveblocks servers.
1339
- *
1340
- * ### Example
1341
- * ```
1342
- * const client = createClient({
1343
- * authEndpoint: "/api/auth"
1344
- * })
1345
- * ```
1346
- */
1347
- function createClient(options) {
1348
- if (typeof options.throttle === "number") {
1349
- if (options.throttle < 80 || options.throttle > 1000) {
1350
- throw new Error("Liveblocks client throttle should be between 80 and 1000 ms");
1351
- }
1352
- }
1353
- const rooms = new Map();
1354
- function getRoom(roomId) {
1355
- const internalRoom = rooms.get(roomId);
1356
- return internalRoom ? internalRoom.room : null;
1357
- }
1358
- function enter(roomId, initialPresence) {
1359
- let internalRoom = rooms.get(roomId);
1360
- if (internalRoom) {
1361
- return internalRoom.room;
1362
- }
1363
- internalRoom = createRoom(roomId, Object.assign(Object.assign({}, options), { initialPresence }));
1364
- rooms.set(roomId, internalRoom);
1365
- internalRoom.connect();
1366
- return internalRoom.room;
1367
- }
1368
- function leave(roomId) {
1369
- let room = rooms.get(roomId);
1370
- if (room) {
1371
- room.disconnect();
1372
- rooms.delete(roomId);
1373
- }
1374
- }
1375
- if (typeof window !== "undefined") {
1376
- // TODO: Expose a way to clear these
1377
- window.addEventListener("online", () => {
1378
- for (const [, room] of rooms) {
1379
- room.onNavigatorOnline();
1380
- }
1381
- });
1382
- }
1383
- if (typeof document !== "undefined") {
1384
- document.addEventListener("visibilitychange", () => {
1385
- for (const [, room] of rooms) {
1386
- room.onVisibilityChange(document.visibilityState);
1387
- }
1388
- });
1389
- }
1390
- return {
1391
- getRoom,
1392
- enter,
1393
- leave,
1394
- };
6
+ /*! *****************************************************************************
7
+ Copyright (c) Microsoft Corporation.
8
+
9
+ Permission to use, copy, modify, and/or distribute this software for any
10
+ purpose with or without fee is hereby granted.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
13
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
14
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
15
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
17
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18
+ PERFORMANCE OF THIS SOFTWARE.
19
+ ***************************************************************************** */
20
+
21
+ function __awaiter(thisArg, _arguments, P, generator) {
22
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23
+ return new (P || (P = Promise))(function (resolve, reject) {
24
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
28
+ });
29
+ }
30
+
31
+ function __generator(thisArg, body) {
32
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
33
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
34
+ function verb(n) { return function (v) { return step([n, v]); }; }
35
+ function step(op) {
36
+ if (f) throw new TypeError("Generator is already executing.");
37
+ while (_) try {
38
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
39
+ if (y = 0, t) op = [op[0] & 2, t.value];
40
+ switch (op[0]) {
41
+ case 0: case 1: t = op; break;
42
+ case 4: _.label++; return { value: op[1], done: false };
43
+ case 5: _.label++; y = op[1]; op = [0]; continue;
44
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
45
+ default:
46
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
47
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
48
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
49
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
50
+ if (t[2]) _.ops.pop();
51
+ _.trys.pop(); continue;
52
+ }
53
+ op = body.call(thisArg, _);
54
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
55
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
56
+ }
1395
57
  }
1396
58
 
1397
59
  var ClientContext = React.createContext(null);
@@ -1418,7 +80,7 @@ function useClient() {
1418
80
  * That means that you can't have 2 RoomProvider with the same room id in your react tree.
1419
81
  */
1420
82
  function RoomProvider(_a) {
1421
- var id = _a.id, children = _a.children, defaultPresence = _a.defaultPresence;
83
+ var id = _a.id, children = _a.children, defaultPresence = _a.defaultPresence, defaultStorageRoot = _a.defaultStorageRoot;
1422
84
  var client = useClient();
1423
85
  React.useEffect(function () {
1424
86
  return function () {
@@ -1426,7 +88,10 @@ function RoomProvider(_a) {
1426
88
  };
1427
89
  }, [client, id]);
1428
90
  var room = client.getRoom(id) ||
1429
- client.enter(id, defaultPresence ? defaultPresence() : undefined);
91
+ client.enter(id, {
92
+ defaultPresence: defaultPresence ? defaultPresence() : undefined,
93
+ defaultStorageRoot: defaultStorageRoot,
94
+ });
1430
95
  return React.createElement(RoomContext.Provider, { value: room }, children);
1431
96
  }
1432
97
  /**
@@ -1444,8 +109,7 @@ function useRoom() {
1444
109
  * It is different from the setState function returned by the useState hook from React.
1445
110
  * You don't need to pass the full presence object to update it.
1446
111
  *
1447
- * ### Example
1448
- * ``` typescript
112
+ * @example
1449
113
  * import { useMyPresence } from "@liveblocks/react";
1450
114
  *
1451
115
  * const [myPresence, updateMyPresence] = useMyPresence();
@@ -1453,7 +117,6 @@ function useRoom() {
1453
117
  * updateMyPresence({ y: 0 });
1454
118
  *
1455
119
  * // At the next render, "myPresence" will be equal to "{ x: 0, y: 0 }"
1456
- * ```
1457
120
  */
1458
121
  function useMyPresence() {
1459
122
  var room = useRoom();
@@ -1475,8 +138,7 @@ function useMyPresence() {
1475
138
  * useUpdateMyPresence is similar to useMyPresence but it only returns the function to update the current user presence.
1476
139
  * If you don't use the current user presence in your component, but you need to update it (e.g. live cursor), it's better to use useUpdateMyPresence to avoid unnecessary renders.
1477
140
  *
1478
- * ### Example
1479
- * ``` typescript
141
+ * @example
1480
142
  * import { useUpdateMyPresence } from "@liveblocks/react";
1481
143
  *
1482
144
  * const updateMyPresence = useUpdateMyPresence();
@@ -1484,7 +146,6 @@ function useMyPresence() {
1484
146
  * updateMyPresence({ y: 0 });
1485
147
  *
1486
148
  * // At the next render, the presence of the current user will be equal to "{ x: 0, y: 0 }"
1487
- * ```
1488
149
  */
1489
150
  function useUpdateMyPresence() {
1490
151
  var room = useRoom();
@@ -1495,8 +156,7 @@ function useUpdateMyPresence() {
1495
156
  /**
1496
157
  * Returns an object that lets you get information about all the the users currently connected in the room.
1497
158
  *
1498
- * ### Example
1499
- * ``` typescript
159
+ * @example
1500
160
  * import { useOthers } from "@liveblocks/react";
1501
161
  *
1502
162
  * const others = useOthers();
@@ -1510,7 +170,6 @@ function useUpdateMyPresence() {
1510
170
  * return <Cursor key={connectionId} cursor={presence.cursor} />
1511
171
  * })
1512
172
  * }
1513
- * ```
1514
173
  */
1515
174
  function useOthers() {
1516
175
  var room = useRoom();
@@ -1529,14 +188,12 @@ function useOthers() {
1529
188
  /**
1530
189
  * Returns a callback that lets you broadcast custom events to other users in the room
1531
190
  *
1532
- * ### Example
1533
- * ``` typescript
191
+ * @example
1534
192
  * import { useBroadcastEvent } from "@liveblocks/react";
1535
193
  *
1536
194
  * const broadcast = useBroadcastEvent();
1537
195
  *
1538
196
  * broadcast({ type: "CUSTOM_EVENT", data: { x: 0, y: 0 } });
1539
- * ```
1540
197
  */
1541
198
  function useBroadcastEvent() {
1542
199
  var room = useRoom();
@@ -1547,14 +204,12 @@ function useBroadcastEvent() {
1547
204
  /**
1548
205
  * useErrorListener is a react hook that lets you react to potential room connection errors.
1549
206
  *
1550
- * ### Example
1551
- * ``` typescript
207
+ * @example
1552
208
  * import { useErrorListener } from "@liveblocks/react";
1553
209
  *
1554
210
  * useErrorListener(er => {
1555
211
  * console.error(er);
1556
212
  * })
1557
- * ```
1558
213
  */
1559
214
  function useErrorListener(callback) {
1560
215
  var room = useRoom();
@@ -1573,8 +228,7 @@ function useErrorListener(callback) {
1573
228
  /**
1574
229
  * useEventListener is a react hook that lets you react to event broadcasted by other users in the room.
1575
230
  *
1576
- * ### Example
1577
- * ``` typescript
231
+ * @example
1578
232
  * import { useEventListener } from "@liveblocks/react";
1579
233
  *
1580
234
  * useEventListener(({ connectionId, event }) => {
@@ -1582,7 +236,6 @@ function useErrorListener(callback) {
1582
236
  * // Do something
1583
237
  * }
1584
238
  * });
1585
- * ```
1586
239
  */
1587
240
  function useEventListener(callback) {
1588
241
  var room = useRoom();
@@ -1601,16 +254,14 @@ function useEventListener(callback) {
1601
254
  }, [room]);
1602
255
  }
1603
256
  /**
1604
- * useCurrentUser is a react hook that lets you react to get the current user once it is connected to the room.
257
+ * Gets the current user once it is connected to the room.
1605
258
  *
1606
- * ### Example
1607
- * ``` typescript
1608
- * import { useCurrentUser } from "@liveblocks/react";
259
+ * @example
260
+ * import { useSelf } from "@liveblocks/react";
1609
261
  *
1610
- * const user = useCurrentUser();
1611
- * ```
262
+ * const user = useSelf();
1612
263
  */
1613
- function useCurrentUser() {
264
+ function useSelf() {
1614
265
  var room = useRoom();
1615
266
  var _a = React.useState(0), update = _a[1];
1616
267
  React.useEffect(function () {
@@ -1624,74 +275,115 @@ function useCurrentUser() {
1624
275
  room.unsubscribe("connection", onChange);
1625
276
  };
1626
277
  }, [room]);
1627
- return room.getCurrentUser();
278
+ return room.getSelf();
1628
279
  }
1629
- function useStorage(initialStorage) {
280
+ function useStorage() {
1630
281
  var room = useRoom();
1631
- var storage = room.getStorage();
1632
- var _a = React.useState(0), update = _a[1];
282
+ var _a = React.useState(null), root = _a[0], setState = _a[1];
1633
283
  React.useEffect(function () {
1634
- function onStorageChange() {
1635
- update(function (x) { return x + 1; });
284
+ function fetchStorage() {
285
+ return __awaiter(this, void 0, void 0, function () {
286
+ var storage;
287
+ return __generator(this, function (_a) {
288
+ switch (_a.label) {
289
+ case 0: return [4 /*yield*/, room.getStorage()];
290
+ case 1:
291
+ storage = _a.sent();
292
+ setState(storage.root);
293
+ return [2 /*return*/];
294
+ }
295
+ });
296
+ });
1636
297
  }
1637
- room.fetchStorage(initialStorage);
1638
- room.subscribe("storage", onStorageChange);
1639
- return function () {
1640
- room.unsubscribe("storage", onStorageChange);
1641
- };
298
+ fetchStorage();
299
+ return function () { };
1642
300
  }, [room]);
1643
- var root = storage.state === LiveStorageState.Loaded
1644
- ? storage.root
1645
- : null;
1646
- var actions = useStorageActions();
1647
- return [root, actions];
301
+ return [root];
1648
302
  }
1649
- function useStorageActions() {
1650
- var room = useRoom();
1651
- return React.useMemo(function () {
1652
- function createRecord(data) {
1653
- return room.createRecord(data);
303
+ function useMap(key) {
304
+ var _a;
305
+ var root = useStorage()[0];
306
+ var _b = React.useState(0), setCount = _b[1];
307
+ React.useEffect(function () {
308
+ if (root == null) {
309
+ return;
1654
310
  }
1655
- function updateRecord(record, overrides) {
1656
- return room.updateRecord(record, overrides);
311
+ var map = root.get(key);
312
+ if (map == null) {
313
+ map = new client.LiveMap();
314
+ root.set(key, map);
315
+ }
316
+ function onChange() {
317
+ setCount(function (x) { return x + 1; });
318
+ }
319
+ map.subscribe(onChange);
320
+ setCount(function (x) { return x + 1; });
321
+ return function () {
322
+ return map.unsubscribe(onChange);
323
+ };
324
+ }, [root]);
325
+ return (_a = root === null || root === void 0 ? void 0 : root.get(key)) !== null && _a !== void 0 ? _a : null;
326
+ }
327
+ function useList(key) {
328
+ var _a;
329
+ var root = useStorage()[0];
330
+ var _b = React.useState(0), setCount = _b[1];
331
+ React.useEffect(function () {
332
+ if (root == null) {
333
+ return;
1657
334
  }
1658
- function createList() {
1659
- return room.createList();
335
+ var list = root.get(key);
336
+ if (list == null) {
337
+ list = new client.LiveList();
338
+ root.set(key, list);
1660
339
  }
1661
- function moveItem(list, index, targetIndex) {
1662
- return room.moveItem(list, index, targetIndex);
340
+ function onChange() {
341
+ setCount(function (x) { return x + 1; });
1663
342
  }
1664
- function deleteItem(list, index) {
1665
- return room.deleteItem(list, index);
343
+ list.subscribe(onChange);
344
+ setCount(function (x) { return x + 1; });
345
+ return function () {
346
+ return list.unsubscribe(onChange);
347
+ };
348
+ }, [root]);
349
+ return (_a = root === null || root === void 0 ? void 0 : root.get(key)) !== null && _a !== void 0 ? _a : null;
350
+ }
351
+ function useObject(key) {
352
+ var _a;
353
+ var root = useStorage()[0];
354
+ var _b = React.useState(0), setCount = _b[1];
355
+ React.useEffect(function () {
356
+ if (root == null) {
357
+ return;
1666
358
  }
1667
- function deleteItemById(list, itemId) {
1668
- return room.deleteItemById(list, itemId);
359
+ var obj = root.get(key);
360
+ if (obj == null) {
361
+ obj = new client.LiveObject();
362
+ root.set(key, obj);
1669
363
  }
1670
- function pushItem(list, item) {
1671
- return room.pushItem(list, item);
364
+ function onChange() {
365
+ setCount(function (x) { return x + 1; });
1672
366
  }
1673
- return {
1674
- createRecord: createRecord,
1675
- updateRecord: updateRecord,
1676
- createList: createList,
1677
- moveItem: moveItem,
1678
- deleteItem: deleteItem,
1679
- deleteItemById: deleteItemById,
1680
- pushItem: pushItem,
367
+ obj.subscribe(onChange);
368
+ setCount(function (x) { return x + 1; });
369
+ return function () {
370
+ return obj.unsubscribe(onChange);
1681
371
  };
1682
- }, [room]);
372
+ }, [root]);
373
+ return (_a = root === null || root === void 0 ? void 0 : root.get(key)) !== null && _a !== void 0 ? _a : null;
1683
374
  }
1684
375
 
1685
376
  exports.LiveblocksProvider = LiveblocksProvider;
1686
377
  exports.RoomProvider = RoomProvider;
1687
- exports.createClient = createClient;
1688
378
  exports.useBroadcastEvent = useBroadcastEvent;
1689
- exports.useCurrentUser = useCurrentUser;
1690
379
  exports.useErrorListener = useErrorListener;
1691
380
  exports.useEventListener = useEventListener;
381
+ exports.useList = useList;
382
+ exports.useMap = useMap;
1692
383
  exports.useMyPresence = useMyPresence;
384
+ exports.useObject = useObject;
1693
385
  exports.useOthers = useOthers;
386
+ exports.useSelf = useSelf;
1694
387
  exports.useStorage = useStorage;
1695
- exports.useStorageActions = useStorageActions;
1696
388
  exports.useUpdateMyPresence = useUpdateMyPresence;
1697
389
  //# sourceMappingURL=index.js.map