@liveblocks/client 0.14.0 → 0.14.3
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/cjs/AbstractCrdt.d.ts +2 -1
- package/lib/cjs/LiveList.d.ts +1 -1
- package/lib/cjs/LiveList.js +4 -1
- package/lib/cjs/LiveMap.d.ts +1 -1
- package/lib/cjs/LiveMap.js +4 -1
- package/lib/cjs/LiveObject.d.ts +1 -1
- package/lib/cjs/LiveObject.js +35 -3
- package/lib/cjs/LiveRegister.d.ts +1 -1
- package/lib/cjs/LiveRegister.js +1 -1
- package/lib/cjs/client.js +50 -7
- package/lib/cjs/room.d.ts +8 -8
- package/lib/cjs/room.js +102 -62
- package/lib/cjs/types.d.ts +13 -0
- package/lib/esm/AbstractCrdt.d.ts +2 -1
- package/lib/esm/LiveList.d.ts +1 -1
- package/lib/esm/LiveList.js +4 -1
- package/lib/esm/LiveMap.d.ts +1 -1
- package/lib/esm/LiveMap.js +4 -1
- package/lib/esm/LiveObject.d.ts +1 -1
- package/lib/esm/LiveObject.js +35 -3
- package/lib/esm/LiveRegister.d.ts +1 -1
- package/lib/esm/LiveRegister.js +1 -1
- package/lib/esm/client.js +50 -7
- package/lib/esm/room.d.ts +8 -8
- package/lib/esm/room.js +101 -42
- package/lib/esm/types.d.ts +13 -0
- package/package.json +6 -2
- package/lib/cjs/authentication.d.ts +0 -3
- package/lib/cjs/authentication.js +0 -71
- package/lib/esm/authentication.d.ts +0 -3
- package/lib/esm/authentication.js +0 -66
|
@@ -8,6 +8,7 @@ export declare type ApplyResult = {
|
|
|
8
8
|
export interface Doc {
|
|
9
9
|
generateId: () => string;
|
|
10
10
|
generateOpId: () => string;
|
|
11
|
+
getItem: (id: string) => AbstractCrdt | undefined;
|
|
11
12
|
addItem: (id: string, item: AbstractCrdt) => void;
|
|
12
13
|
deleteItem: (id: string) => void;
|
|
13
14
|
dispatch: (ops: Op[], reverseOps: Op[], modified: AbstractCrdt[]) => void;
|
|
@@ -45,7 +46,7 @@ export declare abstract class AbstractCrdt {
|
|
|
45
46
|
/**
|
|
46
47
|
* INTERNAL
|
|
47
48
|
*/
|
|
48
|
-
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
49
|
+
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
49
50
|
/**
|
|
50
51
|
* INTERNAL
|
|
51
52
|
*/
|
package/lib/cjs/LiveList.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare class LiveList<T> extends AbstractCrdt {
|
|
|
25
25
|
/**
|
|
26
26
|
* INTERNAL
|
|
27
27
|
*/
|
|
28
|
-
_attachChild(id: string, key: string, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
28
|
+
_attachChild(id: string, key: string, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
29
29
|
/**
|
|
30
30
|
* INTERNAL
|
|
31
31
|
*/
|
package/lib/cjs/LiveList.js
CHANGED
|
@@ -96,11 +96,14 @@ class LiveList extends AbstractCrdt_1.AbstractCrdt {
|
|
|
96
96
|
/**
|
|
97
97
|
* INTERNAL
|
|
98
98
|
*/
|
|
99
|
-
_attachChild(id, key, child, isLocal) {
|
|
99
|
+
_attachChild(id, key, child, opId, isLocal) {
|
|
100
100
|
var _a;
|
|
101
101
|
if (this._doc == null) {
|
|
102
102
|
throw new Error("Can't attach child if doc is not present");
|
|
103
103
|
}
|
|
104
|
+
if (this._doc.getItem(id) !== undefined) {
|
|
105
|
+
return { modified: false };
|
|
106
|
+
}
|
|
104
107
|
child._attach(id, this._doc);
|
|
105
108
|
child._setParentLink(this, key);
|
|
106
109
|
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key);
|
package/lib/cjs/LiveMap.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
|
|
|
23
23
|
/**
|
|
24
24
|
* INTERNAL
|
|
25
25
|
*/
|
|
26
|
-
_attachChild(id: string, key: TKey, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
26
|
+
_attachChild(id: string, key: TKey, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
27
27
|
/**
|
|
28
28
|
* INTERNAL
|
|
29
29
|
*/
|
package/lib/cjs/LiveMap.js
CHANGED
|
@@ -100,10 +100,13 @@ class LiveMap extends AbstractCrdt_1.AbstractCrdt {
|
|
|
100
100
|
/**
|
|
101
101
|
* INTERNAL
|
|
102
102
|
*/
|
|
103
|
-
_attachChild(id, key, child, isLocal) {
|
|
103
|
+
_attachChild(id, key, child, opId, isLocal) {
|
|
104
104
|
if (this._doc == null) {
|
|
105
105
|
throw new Error("Can't attach child if doc is not present");
|
|
106
106
|
}
|
|
107
|
+
if (this._doc.getItem(id) !== undefined) {
|
|
108
|
+
return { modified: false };
|
|
109
|
+
}
|
|
107
110
|
const previousValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key);
|
|
108
111
|
let reverse;
|
|
109
112
|
if (previousValue) {
|
package/lib/cjs/LiveObject.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare class LiveObject<T extends Record<string, any> = Record<string, a
|
|
|
27
27
|
/**
|
|
28
28
|
* INTERNAL
|
|
29
29
|
*/
|
|
30
|
-
_attachChild(id: string, key: keyof T, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
30
|
+
_attachChild(id: string, key: keyof T, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
31
31
|
/**
|
|
32
32
|
* INTERNAL
|
|
33
33
|
*/
|
package/lib/cjs/LiveObject.js
CHANGED
|
@@ -106,10 +106,32 @@ class LiveObject extends AbstractCrdt_1.AbstractCrdt {
|
|
|
106
106
|
/**
|
|
107
107
|
* INTERNAL
|
|
108
108
|
*/
|
|
109
|
-
_attachChild(id, key, child, isLocal) {
|
|
109
|
+
_attachChild(id, key, child, opId, isLocal) {
|
|
110
110
|
if (this._doc == null) {
|
|
111
111
|
throw new Error("Can't attach child if doc is not present");
|
|
112
112
|
}
|
|
113
|
+
if (this._doc.getItem(id) !== undefined) {
|
|
114
|
+
if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === opId) {
|
|
115
|
+
// Acknowlegment from local operation
|
|
116
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key);
|
|
117
|
+
}
|
|
118
|
+
return { modified: false };
|
|
119
|
+
}
|
|
120
|
+
if (isLocal) {
|
|
121
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
|
|
122
|
+
}
|
|
123
|
+
else if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === undefined) {
|
|
124
|
+
// Remote operation with no local change => apply operation
|
|
125
|
+
}
|
|
126
|
+
else if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === opId) {
|
|
127
|
+
// Acknowlegment from local operation
|
|
128
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key);
|
|
129
|
+
return { modified: false };
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Conflict, ignore remote operation
|
|
133
|
+
return { modified: false };
|
|
134
|
+
}
|
|
113
135
|
const previousValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
|
|
114
136
|
let reverse;
|
|
115
137
|
if ((0, utils_1.isCrdt)(previousValue)) {
|
|
@@ -284,7 +306,6 @@ class LiveObject extends AbstractCrdt_1.AbstractCrdt {
|
|
|
284
306
|
data: {},
|
|
285
307
|
};
|
|
286
308
|
for (const key in overrides) {
|
|
287
|
-
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
|
|
288
309
|
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
|
|
289
310
|
if (oldValue instanceof AbstractCrdt_1.AbstractCrdt) {
|
|
290
311
|
reverseOps.push(...oldValue._serialize(this._id, key));
|
|
@@ -300,10 +321,16 @@ class LiveObject extends AbstractCrdt_1.AbstractCrdt {
|
|
|
300
321
|
if (newValue instanceof AbstractCrdt_1.AbstractCrdt) {
|
|
301
322
|
newValue._setParentLink(this, key);
|
|
302
323
|
newValue._attach(this._doc.generateId(), this._doc);
|
|
303
|
-
|
|
324
|
+
const newAttachChildOps = newValue._serialize(this._id, key, this._doc);
|
|
325
|
+
const createCrdtOp = newAttachChildOps.find((op) => op.parentId === this._id);
|
|
326
|
+
if (createCrdtOp) {
|
|
327
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, createCrdtOp.opId);
|
|
328
|
+
}
|
|
329
|
+
ops.push(...newAttachChildOps);
|
|
304
330
|
}
|
|
305
331
|
else {
|
|
306
332
|
updatedProps[key] = newValue;
|
|
333
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
|
|
307
334
|
}
|
|
308
335
|
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue);
|
|
309
336
|
}
|
|
@@ -378,6 +405,11 @@ _LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _
|
|
|
378
405
|
if (__classPrivateFieldGet(this, _LiveObject_map, "f").has(key) === false) {
|
|
379
406
|
return { modified: false };
|
|
380
407
|
}
|
|
408
|
+
// If a local operation exists on the same key
|
|
409
|
+
// prevent flickering by not applying delete op.
|
|
410
|
+
if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) !== undefined) {
|
|
411
|
+
return { modified: false };
|
|
412
|
+
}
|
|
381
413
|
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
|
|
382
414
|
let reverse = [];
|
|
383
415
|
if ((0, utils_1.isCrdt)(oldValue)) {
|
|
@@ -19,7 +19,7 @@ export declare class LiveRegister<TValue = any> extends AbstractCrdt {
|
|
|
19
19
|
* INTERNAL
|
|
20
20
|
*/
|
|
21
21
|
_toSerializedCrdt(): SerializedCrdt;
|
|
22
|
-
_attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
22
|
+
_attachChild(id: string, key: string, crdt: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
23
23
|
_detachChild(crdt: AbstractCrdt): void;
|
|
24
24
|
_apply(op: Op, isLocal: boolean): ApplyResult;
|
|
25
25
|
}
|
package/lib/cjs/LiveRegister.js
CHANGED
package/lib/cjs/client.js
CHANGED
|
@@ -29,11 +29,7 @@ const room_1 = require("./room");
|
|
|
29
29
|
*/
|
|
30
30
|
function createClient(options) {
|
|
31
31
|
const clientOptions = options;
|
|
32
|
-
|
|
33
|
-
if (clientOptions.throttle < 80 || clientOptions.throttle > 1000) {
|
|
34
|
-
throw new Error("Liveblocks client throttle should be between 80 and 1000 ms");
|
|
35
|
-
}
|
|
36
|
-
}
|
|
32
|
+
const throttleDelay = getThrottleDelayFromOptions(options);
|
|
37
33
|
const rooms = new Map();
|
|
38
34
|
function getRoom(roomId) {
|
|
39
35
|
const internalRoom = rooms.get(roomId);
|
|
@@ -44,9 +40,21 @@ function createClient(options) {
|
|
|
44
40
|
if (internalRoom) {
|
|
45
41
|
return internalRoom.room;
|
|
46
42
|
}
|
|
47
|
-
internalRoom = (0, room_1.createRoom)(
|
|
43
|
+
internalRoom = (0, room_1.createRoom)({
|
|
44
|
+
defaultPresence: options.defaultPresence,
|
|
45
|
+
defaultStorageRoot: options.defaultStorageRoot,
|
|
46
|
+
}, {
|
|
47
|
+
room: roomId,
|
|
48
|
+
throttleDelay,
|
|
49
|
+
WebSocketPolyfill: clientOptions.WebSocketPolyfill,
|
|
50
|
+
fetchPolyfill: clientOptions.fetchPolyfill,
|
|
51
|
+
liveblocksServer: clientOptions.liveblocksServer || "wss://liveblocks.net/v5",
|
|
52
|
+
authentication: prepareAuthentication(clientOptions),
|
|
53
|
+
});
|
|
48
54
|
rooms.set(roomId, internalRoom);
|
|
49
|
-
|
|
55
|
+
if (!options.DO_NOT_USE_withoutConnecting) {
|
|
56
|
+
internalRoom.connect();
|
|
57
|
+
}
|
|
50
58
|
return internalRoom.room;
|
|
51
59
|
}
|
|
52
60
|
function leave(roomId) {
|
|
@@ -78,3 +86,38 @@ function createClient(options) {
|
|
|
78
86
|
};
|
|
79
87
|
}
|
|
80
88
|
exports.createClient = createClient;
|
|
89
|
+
function getThrottleDelayFromOptions(options) {
|
|
90
|
+
if (options.throttle === undefined) {
|
|
91
|
+
return 100;
|
|
92
|
+
}
|
|
93
|
+
if (typeof options.throttle !== "number" ||
|
|
94
|
+
options.throttle < 80 ||
|
|
95
|
+
options.throttle > 1000) {
|
|
96
|
+
throw new Error("throttle should be a number between 80 and 1000.");
|
|
97
|
+
}
|
|
98
|
+
return options.throttle;
|
|
99
|
+
}
|
|
100
|
+
function prepareAuthentication(clientOptions) {
|
|
101
|
+
// TODO: throw descriptive errors for invalid options
|
|
102
|
+
if (typeof clientOptions.publicApiKey === "string") {
|
|
103
|
+
return {
|
|
104
|
+
type: "public",
|
|
105
|
+
publicApiKey: clientOptions.publicApiKey,
|
|
106
|
+
url: clientOptions.publicAuthorizeEndpoint ||
|
|
107
|
+
"https://liveblocks.io/api/public/authorize",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
else if (typeof clientOptions.authEndpoint === "string") {
|
|
111
|
+
return {
|
|
112
|
+
type: "private",
|
|
113
|
+
url: clientOptions.authEndpoint,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
else if (typeof clientOptions.authEndpoint === "function") {
|
|
117
|
+
return {
|
|
118
|
+
type: "custom",
|
|
119
|
+
callback: clientOptions.authEndpoint,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
throw new Error("Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient");
|
|
123
|
+
}
|
package/lib/cjs/room.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Others, Presence,
|
|
1
|
+
import { Others, Presence, Room, MyPresenceCallback, OthersEventCallback, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions, AuthorizeResponse, Authentication } from "./types";
|
|
2
2
|
import { ClientMessage, Op } from "./live";
|
|
3
3
|
import { LiveMap } from "./LiveMap";
|
|
4
4
|
import { LiveObject } from "./LiveObject";
|
|
@@ -66,7 +66,7 @@ export declare type State = {
|
|
|
66
66
|
offlineOperations: Map<string, Op>;
|
|
67
67
|
};
|
|
68
68
|
export declare type Effects = {
|
|
69
|
-
authenticate(): void;
|
|
69
|
+
authenticate(auth: (room: string) => Promise<AuthorizeResponse>, createWebSocket: (token: string) => WebSocket): void;
|
|
70
70
|
send(messages: ClientMessage[]): void;
|
|
71
71
|
delayFlush(delay: number): number;
|
|
72
72
|
startHeartbeatInterval(): number;
|
|
@@ -75,13 +75,13 @@ export declare type Effects = {
|
|
|
75
75
|
};
|
|
76
76
|
declare type Context = {
|
|
77
77
|
room: string;
|
|
78
|
-
authEndpoint: AuthEndpoint;
|
|
79
|
-
liveblocksServer: string;
|
|
80
78
|
throttleDelay: number;
|
|
81
|
-
|
|
79
|
+
fetchPolyfill?: typeof fetch;
|
|
80
|
+
WebSocketPolyfill?: typeof WebSocket;
|
|
81
|
+
authentication: Authentication;
|
|
82
|
+
liveblocksServer: string;
|
|
82
83
|
};
|
|
83
84
|
export declare function makeStateMachine(state: State, context: Context, mockedEffects?: Effects): {
|
|
84
|
-
onOpen: () => void;
|
|
85
85
|
onClose: (event: {
|
|
86
86
|
code: number;
|
|
87
87
|
wasClean: boolean;
|
|
@@ -152,8 +152,8 @@ export declare type InternalRoom = {
|
|
|
152
152
|
onNavigatorOnline: () => void;
|
|
153
153
|
onVisibilityChange: (visibilityState: VisibilityState) => void;
|
|
154
154
|
};
|
|
155
|
-
export declare function createRoom(
|
|
155
|
+
export declare function createRoom(options: {
|
|
156
156
|
defaultPresence?: Presence;
|
|
157
157
|
defaultStorageRoot?: Record<string, any>;
|
|
158
|
-
}): InternalRoom;
|
|
158
|
+
}, context: Context): InternalRoom;
|
|
159
159
|
export {};
|
package/lib/cjs/room.js
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
22
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -30,7 +11,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
30
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
12
|
exports.createRoom = exports.defaultState = exports.makeStateMachine = void 0;
|
|
32
13
|
const utils_1 = require("./utils");
|
|
33
|
-
const authentication_1 = __importStar(require("./authentication"));
|
|
34
14
|
const live_1 = require("./live");
|
|
35
15
|
const LiveMap_1 = require("./LiveMap");
|
|
36
16
|
const LiveObject_1 = require("./LiveObject");
|
|
@@ -75,16 +55,12 @@ function log(...params) {
|
|
|
75
55
|
}
|
|
76
56
|
function makeStateMachine(state, context, mockedEffects) {
|
|
77
57
|
const effects = mockedEffects || {
|
|
78
|
-
authenticate() {
|
|
58
|
+
authenticate(auth, createWebSocket) {
|
|
79
59
|
return __awaiter(this, void 0, void 0, function* () {
|
|
80
60
|
try {
|
|
81
|
-
const token = yield (
|
|
82
|
-
const parsedToken =
|
|
83
|
-
const socket =
|
|
84
|
-
socket.addEventListener("message", onMessage);
|
|
85
|
-
socket.addEventListener("open", onOpen);
|
|
86
|
-
socket.addEventListener("close", onClose);
|
|
87
|
-
socket.addEventListener("error", onError);
|
|
61
|
+
const { token } = yield auth(context.room);
|
|
62
|
+
const parsedToken = parseToken(token);
|
|
63
|
+
const socket = createWebSocket(token);
|
|
88
64
|
authenticationSuccess(parsedToken, socket);
|
|
89
65
|
}
|
|
90
66
|
catch (er) {
|
|
@@ -187,6 +163,7 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
187
163
|
function load(items) {
|
|
188
164
|
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
189
165
|
return LiveObject_1.LiveObject._deserialize(root, parentToChildren, {
|
|
166
|
+
getItem,
|
|
190
167
|
addItem,
|
|
191
168
|
deleteItem,
|
|
192
169
|
generateId,
|
|
@@ -362,31 +339,31 @@ function makeStateMachine(state, context, mockedEffects) {
|
|
|
362
339
|
}
|
|
363
340
|
case live_1.OpType.CreateObject: {
|
|
364
341
|
const parent = state.items.get(op.parentId);
|
|
365
|
-
if (parent == null
|
|
342
|
+
if (parent == null) {
|
|
366
343
|
return { modified: false };
|
|
367
344
|
}
|
|
368
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), isLocal);
|
|
345
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject_1.LiveObject(op.data), op.opId, isLocal);
|
|
369
346
|
}
|
|
370
347
|
case live_1.OpType.CreateList: {
|
|
371
348
|
const parent = state.items.get(op.parentId);
|
|
372
|
-
if (parent == null
|
|
349
|
+
if (parent == null) {
|
|
373
350
|
return { modified: false };
|
|
374
351
|
}
|
|
375
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), isLocal);
|
|
352
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList_1.LiveList(), op.opId, isLocal);
|
|
376
353
|
}
|
|
377
354
|
case live_1.OpType.CreateRegister: {
|
|
378
355
|
const parent = state.items.get(op.parentId);
|
|
379
|
-
if (parent == null
|
|
356
|
+
if (parent == null) {
|
|
380
357
|
return { modified: false };
|
|
381
358
|
}
|
|
382
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), isLocal);
|
|
359
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister_1.LiveRegister(op.data), op.opId, isLocal);
|
|
383
360
|
}
|
|
384
361
|
case live_1.OpType.CreateMap: {
|
|
385
362
|
const parent = state.items.get(op.parentId);
|
|
386
|
-
if (parent == null
|
|
363
|
+
if (parent == null) {
|
|
387
364
|
return { modified: false };
|
|
388
365
|
}
|
|
389
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), isLocal);
|
|
366
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap_1.LiveMap(), op.opId, isLocal);
|
|
390
367
|
}
|
|
391
368
|
}
|
|
392
369
|
return { modified: false };
|
|
@@ -433,15 +410,14 @@ See v0.13 release notes for more information.
|
|
|
433
410
|
: null;
|
|
434
411
|
}
|
|
435
412
|
function connect() {
|
|
436
|
-
if (typeof window === "undefined") {
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
413
|
if (state.connection.state !== "closed" &&
|
|
440
414
|
state.connection.state !== "unavailable") {
|
|
441
415
|
return null;
|
|
442
416
|
}
|
|
417
|
+
const auth = prepareAuthEndpoint(context.authentication, context.fetchPolyfill);
|
|
418
|
+
const createWebSocket = prepareCreateWebSocket(context.liveblocksServer, context.WebSocketPolyfill);
|
|
443
419
|
updateConnection({ state: "authenticating" });
|
|
444
|
-
effects.authenticate();
|
|
420
|
+
effects.authenticate(auth, createWebSocket);
|
|
445
421
|
}
|
|
446
422
|
function updatePresence(overrides, options) {
|
|
447
423
|
const oldValues = {};
|
|
@@ -468,6 +444,10 @@ See v0.13 release notes for more information.
|
|
|
468
444
|
}
|
|
469
445
|
}
|
|
470
446
|
function authenticationSuccess(token, socket) {
|
|
447
|
+
socket.addEventListener("message", onMessage);
|
|
448
|
+
socket.addEventListener("open", onOpen);
|
|
449
|
+
socket.addEventListener("close", onClose);
|
|
450
|
+
socket.addEventListener("error", onError);
|
|
471
451
|
updateConnection({
|
|
472
452
|
state: "connecting",
|
|
473
453
|
id: token.actor,
|
|
@@ -684,6 +664,11 @@ See v0.13 release notes for more information.
|
|
|
684
664
|
if (state.connection.state === "connecting") {
|
|
685
665
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
686
666
|
state.numberOfRetry = 0;
|
|
667
|
+
// Re-broadcast the user presence during a reconnect.
|
|
668
|
+
if (state.lastConnectionId !== undefined) {
|
|
669
|
+
state.buffer.presence = state.me;
|
|
670
|
+
tryFlushing();
|
|
671
|
+
}
|
|
687
672
|
state.lastConnectionId = state.connection.id;
|
|
688
673
|
if (state.root) {
|
|
689
674
|
state.buffer.messages.push({ type: live_1.ClientMessageType.FetchStorage });
|
|
@@ -701,7 +686,7 @@ See v0.13 release notes for more information.
|
|
|
701
686
|
}
|
|
702
687
|
clearTimeout(state.timeoutHandles.pongTimeout);
|
|
703
688
|
state.timeoutHandles.pongTimeout = effects.schedulePongTimeout();
|
|
704
|
-
if (state.socket.readyState ===
|
|
689
|
+
if (state.socket.readyState === state.socket.OPEN) {
|
|
705
690
|
state.socket.send("ping");
|
|
706
691
|
}
|
|
707
692
|
}
|
|
@@ -748,7 +733,7 @@ See v0.13 release notes for more information.
|
|
|
748
733
|
state.offlineOperations.set(op.opId, op);
|
|
749
734
|
});
|
|
750
735
|
}
|
|
751
|
-
if (state.socket == null || state.socket.readyState !==
|
|
736
|
+
if (state.socket == null || state.socket.readyState !== state.socket.OPEN) {
|
|
752
737
|
state.buffer.storageOperations = [];
|
|
753
738
|
return;
|
|
754
739
|
}
|
|
@@ -952,7 +937,6 @@ See v0.13 release notes for more information.
|
|
|
952
937
|
}
|
|
953
938
|
return {
|
|
954
939
|
// Internal
|
|
955
|
-
onOpen,
|
|
956
940
|
onClose,
|
|
957
941
|
onMessage,
|
|
958
942
|
authenticationSuccess,
|
|
@@ -1042,26 +1026,9 @@ function defaultState(me, defaultStorageRoot) {
|
|
|
1042
1026
|
};
|
|
1043
1027
|
}
|
|
1044
1028
|
exports.defaultState = defaultState;
|
|
1045
|
-
function createRoom(
|
|
1046
|
-
const throttleDelay = options.throttle || 100;
|
|
1047
|
-
const liveblocksServer = options.liveblocksServer || "wss://liveblocks.net/v5";
|
|
1048
|
-
let authEndpoint;
|
|
1049
|
-
if (options.authEndpoint) {
|
|
1050
|
-
authEndpoint = options.authEndpoint;
|
|
1051
|
-
}
|
|
1052
|
-
else {
|
|
1053
|
-
const publicAuthorizeEndpoint = options.publicAuthorizeEndpoint ||
|
|
1054
|
-
"https://liveblocks.io/api/public/authorize";
|
|
1055
|
-
authEndpoint = publicAuthorizeEndpoint;
|
|
1056
|
-
}
|
|
1029
|
+
function createRoom(options, context) {
|
|
1057
1030
|
const state = defaultState(options.defaultPresence, options.defaultStorageRoot);
|
|
1058
|
-
const machine = makeStateMachine(state,
|
|
1059
|
-
throttleDelay,
|
|
1060
|
-
liveblocksServer,
|
|
1061
|
-
authEndpoint,
|
|
1062
|
-
room: name,
|
|
1063
|
-
publicApiKey: options.publicApiKey,
|
|
1064
|
-
});
|
|
1031
|
+
const machine = makeStateMachine(state, context);
|
|
1065
1032
|
const room = {
|
|
1066
1033
|
/////////////
|
|
1067
1034
|
// Core //
|
|
@@ -1106,3 +1073,76 @@ class LiveblocksError extends Error {
|
|
|
1106
1073
|
this.code = code;
|
|
1107
1074
|
}
|
|
1108
1075
|
}
|
|
1076
|
+
function parseToken(token) {
|
|
1077
|
+
const tokenParts = token.split(".");
|
|
1078
|
+
if (tokenParts.length !== 3) {
|
|
1079
|
+
throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
1080
|
+
}
|
|
1081
|
+
const data = JSON.parse(atob(tokenParts[1]));
|
|
1082
|
+
if (typeof data.actor !== "number") {
|
|
1083
|
+
throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
1084
|
+
}
|
|
1085
|
+
return data;
|
|
1086
|
+
}
|
|
1087
|
+
function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
|
|
1088
|
+
if (typeof window === "undefined" && WebSocketPolyfill == null) {
|
|
1089
|
+
throw new Error("To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill.");
|
|
1090
|
+
}
|
|
1091
|
+
const ws = WebSocketPolyfill || WebSocket;
|
|
1092
|
+
return (token) => {
|
|
1093
|
+
return new ws(`${liveblocksServer}/?token=${token}`);
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function prepareAuthEndpoint(authentication, fetchPolyfill) {
|
|
1097
|
+
if (authentication.type === "public") {
|
|
1098
|
+
if (typeof window === "undefined" && fetchPolyfill == null) {
|
|
1099
|
+
throw new Error("To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill.");
|
|
1100
|
+
}
|
|
1101
|
+
return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
1102
|
+
room,
|
|
1103
|
+
publicApiKey: authentication.publicApiKey,
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
if (authentication.type === "private") {
|
|
1107
|
+
if (typeof window === "undefined" && fetchPolyfill == null) {
|
|
1108
|
+
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.");
|
|
1109
|
+
}
|
|
1110
|
+
return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
1111
|
+
room,
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
if (authentication.type === "custom") {
|
|
1115
|
+
return authentication.callback;
|
|
1116
|
+
}
|
|
1117
|
+
throw new Error("Internal error. Unexpected authentication type");
|
|
1118
|
+
}
|
|
1119
|
+
function fetchAuthEndpoint(fetch, endpoint, body) {
|
|
1120
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1121
|
+
const res = yield fetch(endpoint, {
|
|
1122
|
+
method: "POST",
|
|
1123
|
+
headers: {
|
|
1124
|
+
"Content-Type": "application/json",
|
|
1125
|
+
},
|
|
1126
|
+
body: JSON.stringify(body),
|
|
1127
|
+
});
|
|
1128
|
+
if (!res.ok) {
|
|
1129
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1130
|
+
}
|
|
1131
|
+
let authResponse = null;
|
|
1132
|
+
try {
|
|
1133
|
+
authResponse = yield res.json();
|
|
1134
|
+
}
|
|
1135
|
+
catch (er) {
|
|
1136
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1137
|
+
}
|
|
1138
|
+
if (typeof authResponse.token !== "string") {
|
|
1139
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1140
|
+
}
|
|
1141
|
+
return authResponse;
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
class AuthenticationError extends Error {
|
|
1145
|
+
constructor(message) {
|
|
1146
|
+
super(message);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -121,6 +121,8 @@ export declare type AuthEndpoint = string | AuthEndpointCallback;
|
|
|
121
121
|
*/
|
|
122
122
|
export declare type ClientOptions = {
|
|
123
123
|
throttle?: number;
|
|
124
|
+
fetchPolyfill?: any;
|
|
125
|
+
WebSocketPolyfill?: any;
|
|
124
126
|
} & ({
|
|
125
127
|
publicApiKey: string;
|
|
126
128
|
authEndpoint?: never;
|
|
@@ -131,6 +133,17 @@ export declare type ClientOptions = {
|
|
|
131
133
|
export declare type AuthorizeResponse = {
|
|
132
134
|
token: string;
|
|
133
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
|
+
};
|
|
134
147
|
declare type ConnectionState = "closed" | "authenticating" | "unavailable" | "failed" | "open" | "connecting";
|
|
135
148
|
export declare type Connection = {
|
|
136
149
|
state: "closed" | "authenticating" | "unavailable" | "failed";
|
|
@@ -8,6 +8,7 @@ export declare type ApplyResult = {
|
|
|
8
8
|
export interface Doc {
|
|
9
9
|
generateId: () => string;
|
|
10
10
|
generateOpId: () => string;
|
|
11
|
+
getItem: (id: string) => AbstractCrdt | undefined;
|
|
11
12
|
addItem: (id: string, item: AbstractCrdt) => void;
|
|
12
13
|
deleteItem: (id: string) => void;
|
|
13
14
|
dispatch: (ops: Op[], reverseOps: Op[], modified: AbstractCrdt[]) => void;
|
|
@@ -45,7 +46,7 @@ export declare abstract class AbstractCrdt {
|
|
|
45
46
|
/**
|
|
46
47
|
* INTERNAL
|
|
47
48
|
*/
|
|
48
|
-
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
49
|
+
abstract _attachChild(id: string, key: string, crdt: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
49
50
|
/**
|
|
50
51
|
* INTERNAL
|
|
51
52
|
*/
|
package/lib/esm/LiveList.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare class LiveList<T> extends AbstractCrdt {
|
|
|
25
25
|
/**
|
|
26
26
|
* INTERNAL
|
|
27
27
|
*/
|
|
28
|
-
_attachChild(id: string, key: string, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
28
|
+
_attachChild(id: string, key: string, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
29
29
|
/**
|
|
30
30
|
* INTERNAL
|
|
31
31
|
*/
|
package/lib/esm/LiveList.js
CHANGED
|
@@ -93,11 +93,14 @@ export class LiveList extends AbstractCrdt {
|
|
|
93
93
|
/**
|
|
94
94
|
* INTERNAL
|
|
95
95
|
*/
|
|
96
|
-
_attachChild(id, key, child, isLocal) {
|
|
96
|
+
_attachChild(id, key, child, opId, isLocal) {
|
|
97
97
|
var _a;
|
|
98
98
|
if (this._doc == null) {
|
|
99
99
|
throw new Error("Can't attach child if doc is not present");
|
|
100
100
|
}
|
|
101
|
+
if (this._doc.getItem(id) !== undefined) {
|
|
102
|
+
return { modified: false };
|
|
103
|
+
}
|
|
101
104
|
child._attach(id, this._doc);
|
|
102
105
|
child._setParentLink(this, key);
|
|
103
106
|
const index = __classPrivateFieldGet(this, _LiveList_items, "f").findIndex((entry) => entry[1] === key);
|
package/lib/esm/LiveMap.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare class LiveMap<TKey extends string, TValue> extends AbstractCrdt {
|
|
|
23
23
|
/**
|
|
24
24
|
* INTERNAL
|
|
25
25
|
*/
|
|
26
|
-
_attachChild(id: string, key: TKey, child: AbstractCrdt, isLocal: boolean): ApplyResult;
|
|
26
|
+
_attachChild(id: string, key: TKey, child: AbstractCrdt, opId: string, isLocal: boolean): ApplyResult;
|
|
27
27
|
/**
|
|
28
28
|
* INTERNAL
|
|
29
29
|
*/
|
package/lib/esm/LiveMap.js
CHANGED
|
@@ -97,10 +97,13 @@ export class LiveMap extends AbstractCrdt {
|
|
|
97
97
|
/**
|
|
98
98
|
* INTERNAL
|
|
99
99
|
*/
|
|
100
|
-
_attachChild(id, key, child, isLocal) {
|
|
100
|
+
_attachChild(id, key, child, opId, isLocal) {
|
|
101
101
|
if (this._doc == null) {
|
|
102
102
|
throw new Error("Can't attach child if doc is not present");
|
|
103
103
|
}
|
|
104
|
+
if (this._doc.getItem(id) !== undefined) {
|
|
105
|
+
return { modified: false };
|
|
106
|
+
}
|
|
104
107
|
const previousValue = __classPrivateFieldGet(this, _LiveMap_map, "f").get(key);
|
|
105
108
|
let reverse;
|
|
106
109
|
if (previousValue) {
|