@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
package/lib/esm/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/esm/LiveObject.js
CHANGED
|
@@ -103,10 +103,32 @@ export class LiveObject extends AbstractCrdt {
|
|
|
103
103
|
/**
|
|
104
104
|
* INTERNAL
|
|
105
105
|
*/
|
|
106
|
-
_attachChild(id, key, child, isLocal) {
|
|
106
|
+
_attachChild(id, key, child, opId, isLocal) {
|
|
107
107
|
if (this._doc == null) {
|
|
108
108
|
throw new Error("Can't attach child if doc is not present");
|
|
109
109
|
}
|
|
110
|
+
if (this._doc.getItem(id) !== undefined) {
|
|
111
|
+
if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === opId) {
|
|
112
|
+
// Acknowlegment from local operation
|
|
113
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key);
|
|
114
|
+
}
|
|
115
|
+
return { modified: false };
|
|
116
|
+
}
|
|
117
|
+
if (isLocal) {
|
|
118
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
|
|
119
|
+
}
|
|
120
|
+
else if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === undefined) {
|
|
121
|
+
// Remote operation with no local change => apply operation
|
|
122
|
+
}
|
|
123
|
+
else if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) === opId) {
|
|
124
|
+
// Acknowlegment from local operation
|
|
125
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").delete(key);
|
|
126
|
+
return { modified: false };
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Conflict, ignore remote operation
|
|
130
|
+
return { modified: false };
|
|
131
|
+
}
|
|
110
132
|
const previousValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
|
|
111
133
|
let reverse;
|
|
112
134
|
if (isCrdt(previousValue)) {
|
|
@@ -281,7 +303,6 @@ export class LiveObject extends AbstractCrdt {
|
|
|
281
303
|
data: {},
|
|
282
304
|
};
|
|
283
305
|
for (const key in overrides) {
|
|
284
|
-
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
|
|
285
306
|
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
|
|
286
307
|
if (oldValue instanceof AbstractCrdt) {
|
|
287
308
|
reverseOps.push(...oldValue._serialize(this._id, key));
|
|
@@ -297,10 +318,16 @@ export class LiveObject extends AbstractCrdt {
|
|
|
297
318
|
if (newValue instanceof AbstractCrdt) {
|
|
298
319
|
newValue._setParentLink(this, key);
|
|
299
320
|
newValue._attach(this._doc.generateId(), this._doc);
|
|
300
|
-
|
|
321
|
+
const newAttachChildOps = newValue._serialize(this._id, key, this._doc);
|
|
322
|
+
const createCrdtOp = newAttachChildOps.find((op) => op.parentId === this._id);
|
|
323
|
+
if (createCrdtOp) {
|
|
324
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, createCrdtOp.opId);
|
|
325
|
+
}
|
|
326
|
+
ops.push(...newAttachChildOps);
|
|
301
327
|
}
|
|
302
328
|
else {
|
|
303
329
|
updatedProps[key] = newValue;
|
|
330
|
+
__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").set(key, opId);
|
|
304
331
|
}
|
|
305
332
|
__classPrivateFieldGet(this, _LiveObject_map, "f").set(key, newValue);
|
|
306
333
|
}
|
|
@@ -374,6 +401,11 @@ _LiveObject_map = new WeakMap(), _LiveObject_propToLastUpdate = new WeakMap(), _
|
|
|
374
401
|
if (__classPrivateFieldGet(this, _LiveObject_map, "f").has(key) === false) {
|
|
375
402
|
return { modified: false };
|
|
376
403
|
}
|
|
404
|
+
// If a local operation exists on the same key
|
|
405
|
+
// prevent flickering by not applying delete op.
|
|
406
|
+
if (__classPrivateFieldGet(this, _LiveObject_propToLastUpdate, "f").get(key) !== undefined) {
|
|
407
|
+
return { modified: false };
|
|
408
|
+
}
|
|
377
409
|
const oldValue = __classPrivateFieldGet(this, _LiveObject_map, "f").get(key);
|
|
378
410
|
let reverse = [];
|
|
379
411
|
if (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/esm/LiveRegister.js
CHANGED
package/lib/esm/client.js
CHANGED
|
@@ -26,11 +26,7 @@ import { createRoom } from "./room";
|
|
|
26
26
|
*/
|
|
27
27
|
export function createClient(options) {
|
|
28
28
|
const clientOptions = options;
|
|
29
|
-
|
|
30
|
-
if (clientOptions.throttle < 80 || clientOptions.throttle > 1000) {
|
|
31
|
-
throw new Error("Liveblocks client throttle should be between 80 and 1000 ms");
|
|
32
|
-
}
|
|
33
|
-
}
|
|
29
|
+
const throttleDelay = getThrottleDelayFromOptions(options);
|
|
34
30
|
const rooms = new Map();
|
|
35
31
|
function getRoom(roomId) {
|
|
36
32
|
const internalRoom = rooms.get(roomId);
|
|
@@ -41,9 +37,21 @@ export function createClient(options) {
|
|
|
41
37
|
if (internalRoom) {
|
|
42
38
|
return internalRoom.room;
|
|
43
39
|
}
|
|
44
|
-
internalRoom = createRoom(
|
|
40
|
+
internalRoom = createRoom({
|
|
41
|
+
defaultPresence: options.defaultPresence,
|
|
42
|
+
defaultStorageRoot: options.defaultStorageRoot,
|
|
43
|
+
}, {
|
|
44
|
+
room: roomId,
|
|
45
|
+
throttleDelay,
|
|
46
|
+
WebSocketPolyfill: clientOptions.WebSocketPolyfill,
|
|
47
|
+
fetchPolyfill: clientOptions.fetchPolyfill,
|
|
48
|
+
liveblocksServer: clientOptions.liveblocksServer || "wss://liveblocks.net/v5",
|
|
49
|
+
authentication: prepareAuthentication(clientOptions),
|
|
50
|
+
});
|
|
45
51
|
rooms.set(roomId, internalRoom);
|
|
46
|
-
|
|
52
|
+
if (!options.DO_NOT_USE_withoutConnecting) {
|
|
53
|
+
internalRoom.connect();
|
|
54
|
+
}
|
|
47
55
|
return internalRoom.room;
|
|
48
56
|
}
|
|
49
57
|
function leave(roomId) {
|
|
@@ -74,3 +82,38 @@ export function createClient(options) {
|
|
|
74
82
|
leave,
|
|
75
83
|
};
|
|
76
84
|
}
|
|
85
|
+
function getThrottleDelayFromOptions(options) {
|
|
86
|
+
if (options.throttle === undefined) {
|
|
87
|
+
return 100;
|
|
88
|
+
}
|
|
89
|
+
if (typeof options.throttle !== "number" ||
|
|
90
|
+
options.throttle < 80 ||
|
|
91
|
+
options.throttle > 1000) {
|
|
92
|
+
throw new Error("throttle should be a number between 80 and 1000.");
|
|
93
|
+
}
|
|
94
|
+
return options.throttle;
|
|
95
|
+
}
|
|
96
|
+
function prepareAuthentication(clientOptions) {
|
|
97
|
+
// TODO: throw descriptive errors for invalid options
|
|
98
|
+
if (typeof clientOptions.publicApiKey === "string") {
|
|
99
|
+
return {
|
|
100
|
+
type: "public",
|
|
101
|
+
publicApiKey: clientOptions.publicApiKey,
|
|
102
|
+
url: clientOptions.publicAuthorizeEndpoint ||
|
|
103
|
+
"https://liveblocks.io/api/public/authorize",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else if (typeof clientOptions.authEndpoint === "string") {
|
|
107
|
+
return {
|
|
108
|
+
type: "private",
|
|
109
|
+
url: clientOptions.authEndpoint,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
else if (typeof clientOptions.authEndpoint === "function") {
|
|
113
|
+
return {
|
|
114
|
+
type: "custom",
|
|
115
|
+
callback: clientOptions.authEndpoint,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
throw new Error("Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient");
|
|
119
|
+
}
|
package/lib/esm/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/esm/room.js
CHANGED
|
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { getTreesDiffOperations, isSameNodeOrChildOf, remove } from "./utils";
|
|
11
|
-
import auth, { parseToken } from "./authentication";
|
|
12
11
|
import { ClientMessageType, ServerMessageType, OpType, } from "./live";
|
|
13
12
|
import { LiveMap } from "./LiveMap";
|
|
14
13
|
import { LiveObject } from "./LiveObject";
|
|
@@ -53,16 +52,12 @@ function log(...params) {
|
|
|
53
52
|
}
|
|
54
53
|
export function makeStateMachine(state, context, mockedEffects) {
|
|
55
54
|
const effects = mockedEffects || {
|
|
56
|
-
authenticate() {
|
|
55
|
+
authenticate(auth, createWebSocket) {
|
|
57
56
|
return __awaiter(this, void 0, void 0, function* () {
|
|
58
57
|
try {
|
|
59
|
-
const token = yield auth(context.
|
|
58
|
+
const { token } = yield auth(context.room);
|
|
60
59
|
const parsedToken = parseToken(token);
|
|
61
|
-
const socket =
|
|
62
|
-
socket.addEventListener("message", onMessage);
|
|
63
|
-
socket.addEventListener("open", onOpen);
|
|
64
|
-
socket.addEventListener("close", onClose);
|
|
65
|
-
socket.addEventListener("error", onError);
|
|
60
|
+
const socket = createWebSocket(token);
|
|
66
61
|
authenticationSuccess(parsedToken, socket);
|
|
67
62
|
}
|
|
68
63
|
catch (er) {
|
|
@@ -165,6 +160,7 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
165
160
|
function load(items) {
|
|
166
161
|
const [root, parentToChildren] = buildRootAndParentToChildren(items);
|
|
167
162
|
return LiveObject._deserialize(root, parentToChildren, {
|
|
163
|
+
getItem,
|
|
168
164
|
addItem,
|
|
169
165
|
deleteItem,
|
|
170
166
|
generateId,
|
|
@@ -340,31 +336,31 @@ export function makeStateMachine(state, context, mockedEffects) {
|
|
|
340
336
|
}
|
|
341
337
|
case OpType.CreateObject: {
|
|
342
338
|
const parent = state.items.get(op.parentId);
|
|
343
|
-
if (parent == null
|
|
339
|
+
if (parent == null) {
|
|
344
340
|
return { modified: false };
|
|
345
341
|
}
|
|
346
|
-
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data), isLocal);
|
|
342
|
+
return parent._attachChild(op.id, op.parentKey, new LiveObject(op.data), op.opId, isLocal);
|
|
347
343
|
}
|
|
348
344
|
case OpType.CreateList: {
|
|
349
345
|
const parent = state.items.get(op.parentId);
|
|
350
|
-
if (parent == null
|
|
346
|
+
if (parent == null) {
|
|
351
347
|
return { modified: false };
|
|
352
348
|
}
|
|
353
|
-
return parent._attachChild(op.id, op.parentKey, new LiveList(), isLocal);
|
|
349
|
+
return parent._attachChild(op.id, op.parentKey, new LiveList(), op.opId, isLocal);
|
|
354
350
|
}
|
|
355
351
|
case OpType.CreateRegister: {
|
|
356
352
|
const parent = state.items.get(op.parentId);
|
|
357
|
-
if (parent == null
|
|
353
|
+
if (parent == null) {
|
|
358
354
|
return { modified: false };
|
|
359
355
|
}
|
|
360
|
-
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data), isLocal);
|
|
356
|
+
return parent._attachChild(op.id, op.parentKey, new LiveRegister(op.data), op.opId, isLocal);
|
|
361
357
|
}
|
|
362
358
|
case OpType.CreateMap: {
|
|
363
359
|
const parent = state.items.get(op.parentId);
|
|
364
|
-
if (parent == null
|
|
360
|
+
if (parent == null) {
|
|
365
361
|
return { modified: false };
|
|
366
362
|
}
|
|
367
|
-
return parent._attachChild(op.id, op.parentKey, new LiveMap(), isLocal);
|
|
363
|
+
return parent._attachChild(op.id, op.parentKey, new LiveMap(), op.opId, isLocal);
|
|
368
364
|
}
|
|
369
365
|
}
|
|
370
366
|
return { modified: false };
|
|
@@ -411,15 +407,14 @@ See v0.13 release notes for more information.
|
|
|
411
407
|
: null;
|
|
412
408
|
}
|
|
413
409
|
function connect() {
|
|
414
|
-
if (typeof window === "undefined") {
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
410
|
if (state.connection.state !== "closed" &&
|
|
418
411
|
state.connection.state !== "unavailable") {
|
|
419
412
|
return null;
|
|
420
413
|
}
|
|
414
|
+
const auth = prepareAuthEndpoint(context.authentication, context.fetchPolyfill);
|
|
415
|
+
const createWebSocket = prepareCreateWebSocket(context.liveblocksServer, context.WebSocketPolyfill);
|
|
421
416
|
updateConnection({ state: "authenticating" });
|
|
422
|
-
effects.authenticate();
|
|
417
|
+
effects.authenticate(auth, createWebSocket);
|
|
423
418
|
}
|
|
424
419
|
function updatePresence(overrides, options) {
|
|
425
420
|
const oldValues = {};
|
|
@@ -446,6 +441,10 @@ See v0.13 release notes for more information.
|
|
|
446
441
|
}
|
|
447
442
|
}
|
|
448
443
|
function authenticationSuccess(token, socket) {
|
|
444
|
+
socket.addEventListener("message", onMessage);
|
|
445
|
+
socket.addEventListener("open", onOpen);
|
|
446
|
+
socket.addEventListener("close", onClose);
|
|
447
|
+
socket.addEventListener("error", onError);
|
|
449
448
|
updateConnection({
|
|
450
449
|
state: "connecting",
|
|
451
450
|
id: token.actor,
|
|
@@ -662,6 +661,11 @@ See v0.13 release notes for more information.
|
|
|
662
661
|
if (state.connection.state === "connecting") {
|
|
663
662
|
updateConnection(Object.assign(Object.assign({}, state.connection), { state: "open" }));
|
|
664
663
|
state.numberOfRetry = 0;
|
|
664
|
+
// Re-broadcast the user presence during a reconnect.
|
|
665
|
+
if (state.lastConnectionId !== undefined) {
|
|
666
|
+
state.buffer.presence = state.me;
|
|
667
|
+
tryFlushing();
|
|
668
|
+
}
|
|
665
669
|
state.lastConnectionId = state.connection.id;
|
|
666
670
|
if (state.root) {
|
|
667
671
|
state.buffer.messages.push({ type: ClientMessageType.FetchStorage });
|
|
@@ -679,7 +683,7 @@ See v0.13 release notes for more information.
|
|
|
679
683
|
}
|
|
680
684
|
clearTimeout(state.timeoutHandles.pongTimeout);
|
|
681
685
|
state.timeoutHandles.pongTimeout = effects.schedulePongTimeout();
|
|
682
|
-
if (state.socket.readyState ===
|
|
686
|
+
if (state.socket.readyState === state.socket.OPEN) {
|
|
683
687
|
state.socket.send("ping");
|
|
684
688
|
}
|
|
685
689
|
}
|
|
@@ -726,7 +730,7 @@ See v0.13 release notes for more information.
|
|
|
726
730
|
state.offlineOperations.set(op.opId, op);
|
|
727
731
|
});
|
|
728
732
|
}
|
|
729
|
-
if (state.socket == null || state.socket.readyState !==
|
|
733
|
+
if (state.socket == null || state.socket.readyState !== state.socket.OPEN) {
|
|
730
734
|
state.buffer.storageOperations = [];
|
|
731
735
|
return;
|
|
732
736
|
}
|
|
@@ -930,7 +934,6 @@ See v0.13 release notes for more information.
|
|
|
930
934
|
}
|
|
931
935
|
return {
|
|
932
936
|
// Internal
|
|
933
|
-
onOpen,
|
|
934
937
|
onClose,
|
|
935
938
|
onMessage,
|
|
936
939
|
authenticationSuccess,
|
|
@@ -1018,26 +1021,9 @@ export function defaultState(me, defaultStorageRoot) {
|
|
|
1018
1021
|
offlineOperations: new Map(),
|
|
1019
1022
|
};
|
|
1020
1023
|
}
|
|
1021
|
-
export function createRoom(
|
|
1022
|
-
const throttleDelay = options.throttle || 100;
|
|
1023
|
-
const liveblocksServer = options.liveblocksServer || "wss://liveblocks.net/v5";
|
|
1024
|
-
let authEndpoint;
|
|
1025
|
-
if (options.authEndpoint) {
|
|
1026
|
-
authEndpoint = options.authEndpoint;
|
|
1027
|
-
}
|
|
1028
|
-
else {
|
|
1029
|
-
const publicAuthorizeEndpoint = options.publicAuthorizeEndpoint ||
|
|
1030
|
-
"https://liveblocks.io/api/public/authorize";
|
|
1031
|
-
authEndpoint = publicAuthorizeEndpoint;
|
|
1032
|
-
}
|
|
1024
|
+
export function createRoom(options, context) {
|
|
1033
1025
|
const state = defaultState(options.defaultPresence, options.defaultStorageRoot);
|
|
1034
|
-
const machine = makeStateMachine(state,
|
|
1035
|
-
throttleDelay,
|
|
1036
|
-
liveblocksServer,
|
|
1037
|
-
authEndpoint,
|
|
1038
|
-
room: name,
|
|
1039
|
-
publicApiKey: options.publicApiKey,
|
|
1040
|
-
});
|
|
1026
|
+
const machine = makeStateMachine(state, context);
|
|
1041
1027
|
const room = {
|
|
1042
1028
|
/////////////
|
|
1043
1029
|
// Core //
|
|
@@ -1081,3 +1067,76 @@ class LiveblocksError extends Error {
|
|
|
1081
1067
|
this.code = code;
|
|
1082
1068
|
}
|
|
1083
1069
|
}
|
|
1070
|
+
function parseToken(token) {
|
|
1071
|
+
const tokenParts = token.split(".");
|
|
1072
|
+
if (tokenParts.length !== 3) {
|
|
1073
|
+
throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
1074
|
+
}
|
|
1075
|
+
const data = JSON.parse(atob(tokenParts[1]));
|
|
1076
|
+
if (typeof data.actor !== "number") {
|
|
1077
|
+
throw new Error(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
1078
|
+
}
|
|
1079
|
+
return data;
|
|
1080
|
+
}
|
|
1081
|
+
function prepareCreateWebSocket(liveblocksServer, WebSocketPolyfill) {
|
|
1082
|
+
if (typeof window === "undefined" && WebSocketPolyfill == null) {
|
|
1083
|
+
throw new Error("To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill.");
|
|
1084
|
+
}
|
|
1085
|
+
const ws = WebSocketPolyfill || WebSocket;
|
|
1086
|
+
return (token) => {
|
|
1087
|
+
return new ws(`${liveblocksServer}/?token=${token}`);
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function prepareAuthEndpoint(authentication, fetchPolyfill) {
|
|
1091
|
+
if (authentication.type === "public") {
|
|
1092
|
+
if (typeof window === "undefined" && fetchPolyfill == null) {
|
|
1093
|
+
throw new Error("To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill.");
|
|
1094
|
+
}
|
|
1095
|
+
return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
1096
|
+
room,
|
|
1097
|
+
publicApiKey: authentication.publicApiKey,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
if (authentication.type === "private") {
|
|
1101
|
+
if (typeof window === "undefined" && fetchPolyfill == null) {
|
|
1102
|
+
throw new Error("To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill.");
|
|
1103
|
+
}
|
|
1104
|
+
return (room) => fetchAuthEndpoint(fetchPolyfill || fetch, authentication.url, {
|
|
1105
|
+
room,
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
if (authentication.type === "custom") {
|
|
1109
|
+
return authentication.callback;
|
|
1110
|
+
}
|
|
1111
|
+
throw new Error("Internal error. Unexpected authentication type");
|
|
1112
|
+
}
|
|
1113
|
+
function fetchAuthEndpoint(fetch, endpoint, body) {
|
|
1114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1115
|
+
const res = yield fetch(endpoint, {
|
|
1116
|
+
method: "POST",
|
|
1117
|
+
headers: {
|
|
1118
|
+
"Content-Type": "application/json",
|
|
1119
|
+
},
|
|
1120
|
+
body: JSON.stringify(body),
|
|
1121
|
+
});
|
|
1122
|
+
if (!res.ok) {
|
|
1123
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1124
|
+
}
|
|
1125
|
+
let authResponse = null;
|
|
1126
|
+
try {
|
|
1127
|
+
authResponse = yield res.json();
|
|
1128
|
+
}
|
|
1129
|
+
catch (er) {
|
|
1130
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1131
|
+
}
|
|
1132
|
+
if (typeof authResponse.token !== "string") {
|
|
1133
|
+
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
1134
|
+
}
|
|
1135
|
+
return authResponse;
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
class AuthenticationError extends Error {
|
|
1139
|
+
constructor(message) {
|
|
1140
|
+
super(message);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
package/lib/esm/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";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liveblocks/client",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/cjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -29,9 +29,13 @@
|
|
|
29
29
|
"@babel/preset-env": "^7.12.16",
|
|
30
30
|
"@babel/preset-typescript": "^7.12.16",
|
|
31
31
|
"@types/jest": "^26.0.21",
|
|
32
|
+
"@types/node-fetch": "^2.6.1",
|
|
33
|
+
"@types/ws": "^8.2.2",
|
|
32
34
|
"babel-jest": "^26.6.3",
|
|
33
35
|
"jest": "^26.6.3",
|
|
34
|
-
"
|
|
36
|
+
"node-fetch": "2.6.7",
|
|
37
|
+
"typescript": "^4.4.0",
|
|
38
|
+
"ws": "^8.5.0"
|
|
35
39
|
},
|
|
36
40
|
"repository": {
|
|
37
41
|
"type": "git",
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.parseToken = void 0;
|
|
13
|
-
function fetchAuthorize(endpoint, room, publicApiKey) {
|
|
14
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
-
const res = yield fetch(endpoint, {
|
|
16
|
-
method: "POST",
|
|
17
|
-
headers: {
|
|
18
|
-
"Content-Type": "application/json",
|
|
19
|
-
},
|
|
20
|
-
body: JSON.stringify({
|
|
21
|
-
room,
|
|
22
|
-
publicApiKey,
|
|
23
|
-
}),
|
|
24
|
-
});
|
|
25
|
-
if (!res.ok) {
|
|
26
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
27
|
-
}
|
|
28
|
-
let authResponse = null;
|
|
29
|
-
try {
|
|
30
|
-
authResponse = yield res.json();
|
|
31
|
-
}
|
|
32
|
-
catch (er) {
|
|
33
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
34
|
-
}
|
|
35
|
-
if (typeof authResponse.token !== "string") {
|
|
36
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
37
|
-
}
|
|
38
|
-
return authResponse.token;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
function auth(endpoint, room, publicApiKey) {
|
|
42
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
if (typeof endpoint === "string") {
|
|
44
|
-
return fetchAuthorize(endpoint, room, publicApiKey);
|
|
45
|
-
}
|
|
46
|
-
if (typeof endpoint === "function") {
|
|
47
|
-
const { token } = yield endpoint(room);
|
|
48
|
-
// TODO: Validation
|
|
49
|
-
return token;
|
|
50
|
-
}
|
|
51
|
-
throw new Error("Authentication error. Liveblocks could not parse the response of your authentication endpoint");
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
exports.default = auth;
|
|
55
|
-
class AuthenticationError extends Error {
|
|
56
|
-
constructor(message) {
|
|
57
|
-
super(message);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function parseToken(token) {
|
|
61
|
-
const tokenParts = token.split(".");
|
|
62
|
-
if (tokenParts.length !== 3) {
|
|
63
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
64
|
-
}
|
|
65
|
-
const data = JSON.parse(atob(tokenParts[1]));
|
|
66
|
-
if (typeof data.actor !== "number") {
|
|
67
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
68
|
-
}
|
|
69
|
-
return data;
|
|
70
|
-
}
|
|
71
|
-
exports.parseToken = parseToken;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
function fetchAuthorize(endpoint, room, publicApiKey) {
|
|
11
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
-
const res = yield fetch(endpoint, {
|
|
13
|
-
method: "POST",
|
|
14
|
-
headers: {
|
|
15
|
-
"Content-Type": "application/json",
|
|
16
|
-
},
|
|
17
|
-
body: JSON.stringify({
|
|
18
|
-
room,
|
|
19
|
-
publicApiKey,
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
if (!res.ok) {
|
|
23
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
24
|
-
}
|
|
25
|
-
let authResponse = null;
|
|
26
|
-
try {
|
|
27
|
-
authResponse = yield res.json();
|
|
28
|
-
}
|
|
29
|
-
catch (er) {
|
|
30
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
31
|
-
}
|
|
32
|
-
if (typeof authResponse.token !== "string") {
|
|
33
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication "${endpoint}"`);
|
|
34
|
-
}
|
|
35
|
-
return authResponse.token;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
export default function auth(endpoint, room, publicApiKey) {
|
|
39
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
-
if (typeof endpoint === "string") {
|
|
41
|
-
return fetchAuthorize(endpoint, room, publicApiKey);
|
|
42
|
-
}
|
|
43
|
-
if (typeof endpoint === "function") {
|
|
44
|
-
const { token } = yield endpoint(room);
|
|
45
|
-
// TODO: Validation
|
|
46
|
-
return token;
|
|
47
|
-
}
|
|
48
|
-
throw new Error("Authentication error. Liveblocks could not parse the response of your authentication endpoint");
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
class AuthenticationError extends Error {
|
|
52
|
-
constructor(message) {
|
|
53
|
-
super(message);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export function parseToken(token) {
|
|
57
|
-
const tokenParts = token.split(".");
|
|
58
|
-
if (tokenParts.length !== 3) {
|
|
59
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
60
|
-
}
|
|
61
|
-
const data = JSON.parse(atob(tokenParts[1]));
|
|
62
|
-
if (typeof data.actor !== "number") {
|
|
63
|
-
throw new AuthenticationError(`Authentication error. Liveblocks could not parse the response of your authentication endpoint`);
|
|
64
|
-
}
|
|
65
|
-
return data;
|
|
66
|
-
}
|