@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.
@@ -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
  */
@@ -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
- ops.push(...newValue._serialize(this._id, key, this._doc));
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
  }
@@ -65,7 +65,7 @@ export class LiveRegister extends AbstractCrdt {
65
65
  data: this.data,
66
66
  };
67
67
  }
68
- _attachChild(id, key, crdt, isLocal) {
68
+ _attachChild(id, key, crdt, opId, isLocal) {
69
69
  throw new Error("Method not implemented.");
70
70
  }
71
71
  _detachChild(crdt) {
package/lib/esm/client.js CHANGED
@@ -26,11 +26,7 @@ import { createRoom } from "./room";
26
26
  */
27
27
  export function createClient(options) {
28
28
  const clientOptions = options;
29
- if (typeof clientOptions.throttle === "number") {
30
- if (clientOptions.throttle < 80 || clientOptions.throttle > 1000) {
31
- throw new Error("Liveblocks client throttle should be between 80 and 1000 ms");
32
- }
33
- }
29
+ const throttleDelay = getThrottleDelayFromOptions(options);
34
30
  const rooms = new Map();
35
31
  function getRoom(roomId) {
36
32
  const internalRoom = rooms.get(roomId);
@@ -41,9 +37,21 @@ export function createClient(options) {
41
37
  if (internalRoom) {
42
38
  return internalRoom.room;
43
39
  }
44
- internalRoom = createRoom(roomId, Object.assign(Object.assign({}, clientOptions), options));
40
+ internalRoom = createRoom({
41
+ defaultPresence: options.defaultPresence,
42
+ defaultStorageRoot: options.defaultStorageRoot,
43
+ }, {
44
+ room: roomId,
45
+ throttleDelay,
46
+ WebSocketPolyfill: clientOptions.WebSocketPolyfill,
47
+ fetchPolyfill: clientOptions.fetchPolyfill,
48
+ liveblocksServer: clientOptions.liveblocksServer || "wss://liveblocks.net/v5",
49
+ authentication: prepareAuthentication(clientOptions),
50
+ });
45
51
  rooms.set(roomId, internalRoom);
46
- internalRoom.connect();
52
+ if (!options.DO_NOT_USE_withoutConnecting) {
53
+ internalRoom.connect();
54
+ }
47
55
  return internalRoom.room;
48
56
  }
49
57
  function leave(roomId) {
@@ -74,3 +82,38 @@ export function createClient(options) {
74
82
  leave,
75
83
  };
76
84
  }
85
+ function getThrottleDelayFromOptions(options) {
86
+ if (options.throttle === undefined) {
87
+ return 100;
88
+ }
89
+ if (typeof options.throttle !== "number" ||
90
+ options.throttle < 80 ||
91
+ options.throttle > 1000) {
92
+ throw new Error("throttle should be a number between 80 and 1000.");
93
+ }
94
+ return options.throttle;
95
+ }
96
+ function prepareAuthentication(clientOptions) {
97
+ // TODO: throw descriptive errors for invalid options
98
+ if (typeof clientOptions.publicApiKey === "string") {
99
+ return {
100
+ type: "public",
101
+ publicApiKey: clientOptions.publicApiKey,
102
+ url: clientOptions.publicAuthorizeEndpoint ||
103
+ "https://liveblocks.io/api/public/authorize",
104
+ };
105
+ }
106
+ else if (typeof clientOptions.authEndpoint === "string") {
107
+ return {
108
+ type: "private",
109
+ url: clientOptions.authEndpoint,
110
+ };
111
+ }
112
+ else if (typeof clientOptions.authEndpoint === "function") {
113
+ return {
114
+ type: "custom",
115
+ callback: clientOptions.authEndpoint,
116
+ };
117
+ }
118
+ throw new Error("Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient");
119
+ }
package/lib/esm/room.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Others, Presence, ClientOptions, Room, MyPresenceCallback, OthersEventCallback, AuthEndpoint, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions } from "./types";
1
+ import { Others, Presence, Room, MyPresenceCallback, OthersEventCallback, EventCallback, User, Connection, ErrorCallback, AuthenticationToken, ConnectionCallback, StorageCallback, StorageUpdate, BroadcastOptions, AuthorizeResponse, Authentication } from "./types";
2
2
  import { ClientMessage, Op } from "./live";
3
3
  import { LiveMap } from "./LiveMap";
4
4
  import { LiveObject } from "./LiveObject";
@@ -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
- publicApiKey?: string;
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(name: string, options: ClientOptions & {
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.authEndpoint, context.room, context.publicApiKey);
58
+ const { token } = yield auth(context.room);
60
59
  const parsedToken = parseToken(token);
61
- const socket = new WebSocket(`${context.liveblocksServer}/?token=${token}`);
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 || getItem(op.id) != 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 || getItem(op.id) != 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 || getItem(op.id) != 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 || getItem(op.id) != 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 === WebSocket.OPEN) {
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 !== WebSocket.OPEN) {
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(name, options) {
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
+ }
@@ -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.0",
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
- "typescript": "^4.4.0"
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,3 +0,0 @@
1
- import { AuthEndpoint, AuthenticationToken } from "./types";
2
- export default function auth(endpoint: AuthEndpoint, room: string, publicApiKey?: string): Promise<string>;
3
- export declare function parseToken(token: string): AuthenticationToken;
@@ -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,3 +0,0 @@
1
- import { AuthEndpoint, AuthenticationToken } from "./types";
2
- export default function auth(endpoint: AuthEndpoint, room: string, publicApiKey?: string): Promise<string>;
3
- export declare function parseToken(token: string): AuthenticationToken;
@@ -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
- }