@liveblocks/core 1.1.0-test1 → 1.1.0
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/dist/index.d.ts +93 -33
- package/dist/index.js +299 -191
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -636,14 +636,34 @@ declare enum WebsocketCloseCodes {
|
|
|
636
636
|
CLOSE_WITHOUT_RETRY = 4999
|
|
637
637
|
}
|
|
638
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Old connection statuses, here for backward-compatibility reasons only.
|
|
641
|
+
*/
|
|
642
|
+
declare type LegacyConnectionStatus = "closed" | "authenticating" | "connecting" | "open" | "unavailable" | "failed";
|
|
643
|
+
/**
|
|
644
|
+
* Returns a human-readable status indicating the current connection status of
|
|
645
|
+
* a Room, as returned by `room.getStatus()`. Can be used to implement
|
|
646
|
+
* a connection status badge.
|
|
647
|
+
*/
|
|
648
|
+
declare type Status = "initial" | "connecting" | "connected" | "reconnecting" | "disconnected";
|
|
649
|
+
/**
|
|
650
|
+
* Used to report about app-level reconnection issues.
|
|
651
|
+
*
|
|
652
|
+
* Normal (quick) reconnects won't be reported as a "lost connection". Instead,
|
|
653
|
+
* the application will only get an event if the reconnection attempts by the
|
|
654
|
+
* client are taking (much) longer than usual. Definitely a situation you want
|
|
655
|
+
* to inform your users about, for example, by throwing a toast message on
|
|
656
|
+
* screen, or show a "trying to reconnect" banner.
|
|
657
|
+
*/
|
|
658
|
+
declare type LostConnectionEvent = "lost" | "restored" | "failed";
|
|
639
659
|
/**
|
|
640
660
|
* Arbitrary record that will be used as the authentication "token". It's the
|
|
641
661
|
* value that is returned by calling the authentication delegate, and will get
|
|
642
662
|
* passed to the connection factory delegate. This value will be remembered by
|
|
643
663
|
* the connection manager, but its value will not be interpreted, so it can be
|
|
644
|
-
* any
|
|
664
|
+
* any value (except null).
|
|
645
665
|
*/
|
|
646
|
-
declare type BaseAuthResult =
|
|
666
|
+
declare type BaseAuthResult = NonNullable<Json>;
|
|
647
667
|
declare type Delegates<T extends BaseAuthResult> = {
|
|
648
668
|
authenticate: () => Promise<T>;
|
|
649
669
|
createSocket: (token: T) => IWebSocketInstance;
|
|
@@ -735,28 +755,6 @@ declare type CustomEvent<TRoomEvent extends Json> = {
|
|
|
735
755
|
connectionId: number;
|
|
736
756
|
event: TRoomEvent;
|
|
737
757
|
};
|
|
738
|
-
declare type Connection = {
|
|
739
|
-
status: "closed";
|
|
740
|
-
} | {
|
|
741
|
-
status: "authenticating";
|
|
742
|
-
} | {
|
|
743
|
-
status: "connecting";
|
|
744
|
-
id: number;
|
|
745
|
-
userId?: string;
|
|
746
|
-
userInfo?: Json;
|
|
747
|
-
isReadOnly: boolean;
|
|
748
|
-
} | {
|
|
749
|
-
status: "open";
|
|
750
|
-
id: number;
|
|
751
|
-
userId?: string;
|
|
752
|
-
userInfo?: Json;
|
|
753
|
-
isReadOnly: boolean;
|
|
754
|
-
} | {
|
|
755
|
-
status: "unavailable";
|
|
756
|
-
} | {
|
|
757
|
-
status: "failed";
|
|
758
|
-
};
|
|
759
|
-
declare type ConnectionStatus = Connection["status"];
|
|
760
758
|
declare type StorageStatus = "not-loaded" | "loading" | "synchronizing" | "synchronized";
|
|
761
759
|
interface History {
|
|
762
760
|
/**
|
|
@@ -893,12 +891,47 @@ declare type SubscribeFn<TPresence extends JsonObject, _TStorage extends LsonObj
|
|
|
893
891
|
*/
|
|
894
892
|
(type: "error", listener: ErrorCallback): () => void;
|
|
895
893
|
/**
|
|
896
|
-
*
|
|
894
|
+
* @deprecated This API will be removed in a future version of Liveblocks.
|
|
895
|
+
* Prefer using the newer `.subscribe('status')` API.
|
|
896
|
+
*
|
|
897
|
+
* We recommend making the following changes if you use these APIs:
|
|
898
|
+
*
|
|
899
|
+
* OLD APIs NEW APIs
|
|
900
|
+
* .getConnectionState() --> .getStatus()
|
|
901
|
+
* .subscribe('connection') --> .subscribe('status')
|
|
902
|
+
*
|
|
903
|
+
* OLD STATUSES NEW STATUSES
|
|
904
|
+
* closed --> initial
|
|
905
|
+
* authenticating --> connecting
|
|
906
|
+
* connecting --> connecting
|
|
907
|
+
* open --> connected
|
|
908
|
+
* unavailable --> reconnecting
|
|
909
|
+
* failed --> disconnected
|
|
910
|
+
*
|
|
911
|
+
* Subscribe to legacy connection status updates.
|
|
912
|
+
*
|
|
913
|
+
* @returns Unsubscribe function.
|
|
914
|
+
*
|
|
915
|
+
*/
|
|
916
|
+
(type: "connection", listener: Callback<LegacyConnectionStatus>): () => void;
|
|
917
|
+
/**
|
|
918
|
+
* Subscribe to connection status updates. The callback will be called any
|
|
919
|
+
* time the status changes.
|
|
897
920
|
*
|
|
898
921
|
* @returns Unsubscribe function.
|
|
899
922
|
*
|
|
900
923
|
*/
|
|
901
|
-
(type: "
|
|
924
|
+
(type: "status", listener: Callback<Status>): () => void;
|
|
925
|
+
/**
|
|
926
|
+
* Subscribe to the exceptional event where reconnecting to the Liveblocks
|
|
927
|
+
* servers is taking longer than usual. This typically is a sign of a client
|
|
928
|
+
* that has lost internet connectivity.
|
|
929
|
+
*
|
|
930
|
+
* This isn't problematic (because the Liveblocks client is still trying to
|
|
931
|
+
* reconnect), but it's typically a good idea to inform users about it if
|
|
932
|
+
* the connection takes too long to recover.
|
|
933
|
+
*/
|
|
934
|
+
(type: "lost-connection", listener: Callback<LostConnectionEvent>): () => void;
|
|
902
935
|
/**
|
|
903
936
|
* Subscribes to changes made on a Live structure. Returns an unsubscribe function.
|
|
904
937
|
* In a future version, we will also expose what exactly changed in the Live structure.
|
|
@@ -975,7 +1008,30 @@ declare type Room<TPresence extends JsonObject, TStorage extends LsonObject, TUs
|
|
|
975
1008
|
* metadata and connection ID (from the auth server).
|
|
976
1009
|
*/
|
|
977
1010
|
isSelfAware(): boolean;
|
|
978
|
-
|
|
1011
|
+
/**
|
|
1012
|
+
* @deprecated This API will be removed in a future version of Liveblocks.
|
|
1013
|
+
* Prefer using `.getStatus()` instead.
|
|
1014
|
+
*
|
|
1015
|
+
* We recommend making the following changes if you use these APIs:
|
|
1016
|
+
*
|
|
1017
|
+
* OLD APIs NEW APIs
|
|
1018
|
+
* .getConnectionState() --> .getStatus()
|
|
1019
|
+
* .subscribe('connection') --> .subscribe('status')
|
|
1020
|
+
*
|
|
1021
|
+
* OLD STATUSES NEW STATUSES
|
|
1022
|
+
* closed --> initial
|
|
1023
|
+
* authenticating --> connecting
|
|
1024
|
+
* connecting --> connecting
|
|
1025
|
+
* open --> connected
|
|
1026
|
+
* unavailable --> reconnecting
|
|
1027
|
+
* failed --> disconnected
|
|
1028
|
+
*/
|
|
1029
|
+
getConnectionState(): LegacyConnectionStatus;
|
|
1030
|
+
/**
|
|
1031
|
+
* Return the current connection status for this room. Can be used to display
|
|
1032
|
+
* a status badge for your Liveblocks connection.
|
|
1033
|
+
*/
|
|
1034
|
+
getStatus(): Status;
|
|
979
1035
|
readonly subscribe: SubscribeFn<TPresence, TStorage, TUserMeta, TRoomEvent>;
|
|
980
1036
|
/**
|
|
981
1037
|
* Room's history contains functions that let you undo and redo operation made on by the current client on the presence and storage.
|
|
@@ -1056,6 +1112,9 @@ declare type Room<TPresence extends JsonObject, TStorage extends LsonObject, TUs
|
|
|
1056
1112
|
*/
|
|
1057
1113
|
getStorageSnapshot(): LiveObject<TStorage> | null;
|
|
1058
1114
|
readonly events: {
|
|
1115
|
+
readonly connection: Observable<LegacyConnectionStatus>;
|
|
1116
|
+
readonly status: Observable<Status>;
|
|
1117
|
+
readonly lostConnection: Observable<LostConnectionEvent>;
|
|
1059
1118
|
readonly customEvent: Observable<{
|
|
1060
1119
|
connectionId: number;
|
|
1061
1120
|
event: TRoomEvent;
|
|
@@ -1066,12 +1125,12 @@ declare type Room<TPresence extends JsonObject, TStorage extends LsonObject, TUs
|
|
|
1066
1125
|
event: OthersEvent<TPresence, TUserMeta>;
|
|
1067
1126
|
}>;
|
|
1068
1127
|
readonly error: Observable<Error>;
|
|
1069
|
-
readonly connection: Observable<ConnectionStatus>;
|
|
1070
1128
|
readonly storage: Observable<StorageUpdate[]>;
|
|
1071
1129
|
readonly history: Observable<HistoryEvent>;
|
|
1072
1130
|
/**
|
|
1073
|
-
* Subscribe to the storage loaded event. Will fire
|
|
1074
|
-
*
|
|
1131
|
+
* Subscribe to the storage loaded event. Will fire any time a full Storage
|
|
1132
|
+
* copy is downloaded. (This happens after the initial connect, and on
|
|
1133
|
+
* every reconnect.)
|
|
1075
1134
|
*/
|
|
1076
1135
|
readonly storageDidLoad: Observable<void>;
|
|
1077
1136
|
readonly storageStatus: Observable<StorageStatus>;
|
|
@@ -1168,6 +1227,7 @@ declare type AuthEndpoint = string | ((room: string) => Promise<{
|
|
|
1168
1227
|
*/
|
|
1169
1228
|
declare type ClientOptions = {
|
|
1170
1229
|
throttle?: number;
|
|
1230
|
+
lostConnectionTimeout?: number;
|
|
1171
1231
|
polyfills?: Polyfills;
|
|
1172
1232
|
unstable_fallbackToHTTP?: boolean;
|
|
1173
1233
|
/**
|
|
@@ -1762,7 +1822,7 @@ declare type ClientToPanelMessage =
|
|
|
1762
1822
|
| {
|
|
1763
1823
|
msg: "room::sync::full";
|
|
1764
1824
|
roomId: string;
|
|
1765
|
-
status:
|
|
1825
|
+
status: Status;
|
|
1766
1826
|
storage: readonly LsonTreeNode[] | null;
|
|
1767
1827
|
me: UserTreeNode | null;
|
|
1768
1828
|
others: readonly UserTreeNode[];
|
|
@@ -1773,7 +1833,7 @@ declare type ClientToPanelMessage =
|
|
|
1773
1833
|
| {
|
|
1774
1834
|
msg: "room::sync::partial";
|
|
1775
1835
|
roomId: string;
|
|
1776
|
-
status?:
|
|
1836
|
+
status?: Status;
|
|
1777
1837
|
storage?: readonly LsonTreeNode[];
|
|
1778
1838
|
me?: UserTreeNode;
|
|
1779
1839
|
others?: readonly UserTreeNode[];
|
|
@@ -1824,4 +1884,4 @@ declare type EnsureJson<T> = [
|
|
|
1824
1884
|
[K in keyof T]: EnsureJson<T[K]>;
|
|
1825
1885
|
};
|
|
1826
1886
|
|
|
1827
|
-
export { AckOp, AppOnlyAuthToken, AuthToken, BaseAuthResult, BaseUserMeta, BroadcastEventClientMsg, BroadcastOptions, BroadcastedEventServerMsg, Client, ClientMsg, ClientMsgCode,
|
|
1887
|
+
export { AckOp, AppOnlyAuthToken, AuthToken, BaseAuthResult, BaseUserMeta, BroadcastEventClientMsg, BroadcastOptions, BroadcastedEventServerMsg, Client, ClientMsg, ClientMsgCode, CrdtType, CreateChildOp, CreateListOp, CreateMapOp, CreateObjectOp, CreateOp, CreateRegisterOp, CreateRootObjectOp, Delegates, DeleteCrdtOp, DeleteObjectKeyOp, DevToolsTreeNode as DevTools, protocol as DevToolsMsg, EnsureJson, FetchStorageClientMsg, History, IWebSocket, IWebSocketCloseEvent, IWebSocketEvent, IWebSocketInstance, IWebSocketMessageEvent, IdTuple, Immutable, InitialDocumentStateServerMsg, Json, JsonArray, JsonObject, JsonScalar, LegacyConnectionStatus, LiveList, LiveListUpdate, LiveMap, LiveMapUpdate, LiveNode, LiveObject, LiveObjectUpdate, LiveStructure, LostConnectionEvent, Lson, LsonObject, NodeMap, Op, OpCode, Others, ParentToChildNodeMap, PlainLson, PlainLsonFields, PlainLsonList, PlainLsonMap, PlainLsonObject, RejectedStorageOpServerMsg, Resolve, Room, RoomAuthToken, RoomInitializers, RoomStateServerMsg, SerializedChild, SerializedCrdt, SerializedList, SerializedMap, SerializedObject, SerializedRegister, SerializedRootObject, ServerMsg, ServerMsgCode, SetParentKeyOp, Status, StorageStatus, StorageUpdate, ToImmutable, ToJson, UpdateObjectOp, UpdatePresenceClientMsg, UpdatePresenceServerMsg, UpdateStorageClientMsg, UpdateStorageServerMsg, User, UserJoinServerMsg, UserLeftServerMsg, WebsocketCloseCodes, asArrayWithLegacyMethods, asPos, assert, assertNever, b64decode, createClient, deprecate, deprecateIf, errorIf, freeze, isAppOnlyAuthToken, isAuthToken, isChildCrdt, isJsonArray, isJsonObject, isJsonScalar, isPlainObject, isRoomAuthToken, isRootCrdt, legacy_patchImmutableObject, lsonToJson, makePosition, nn, patchLiveObjectKey, shallow, throwUsageError, toPlainLson, tryParseJson, withTimeout };
|
package/dist/index.js
CHANGED
|
@@ -157,7 +157,7 @@ var onMessageFromPanel = eventSource.observable;
|
|
|
157
157
|
// src/devtools/index.ts
|
|
158
158
|
var VERSION = true ? (
|
|
159
159
|
/* istanbul ignore next */
|
|
160
|
-
"1.1.0
|
|
160
|
+
"1.1.0"
|
|
161
161
|
) : "dev";
|
|
162
162
|
var _devtoolsSetupHasRun = false;
|
|
163
163
|
function setupDevTools(getAllRooms) {
|
|
@@ -199,7 +199,7 @@ function startSyncStream(room) {
|
|
|
199
199
|
fullSync(room);
|
|
200
200
|
unsubsByRoomId.set(room.id, [
|
|
201
201
|
// When the connection status changes
|
|
202
|
-
room.events.
|
|
202
|
+
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
203
203
|
// When storage initializes, send the update
|
|
204
204
|
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
205
205
|
// Any time storage updates, send the new storage root
|
|
@@ -213,7 +213,7 @@ function partialSyncConnection(room) {
|
|
|
213
213
|
sendToPanel({
|
|
214
214
|
msg: "room::sync::partial",
|
|
215
215
|
roomId: room.id,
|
|
216
|
-
status: room.
|
|
216
|
+
status: room.getStatus()
|
|
217
217
|
});
|
|
218
218
|
}
|
|
219
219
|
function partialSyncStorage(room) {
|
|
@@ -254,7 +254,7 @@ function fullSync(room) {
|
|
|
254
254
|
sendToPanel({
|
|
255
255
|
msg: "room::sync::full",
|
|
256
256
|
roomId: room.id,
|
|
257
|
-
status: room.
|
|
257
|
+
status: room.getStatus(),
|
|
258
258
|
storage: (_a = root == null ? void 0 : root.toTreeNode("root").payload) != null ? _a : null,
|
|
259
259
|
me,
|
|
260
260
|
others
|
|
@@ -823,22 +823,37 @@ function withTimeout(promise, millis, errmsg = "Timed out") {
|
|
|
823
823
|
}
|
|
824
824
|
|
|
825
825
|
// src/connection.ts
|
|
826
|
-
function
|
|
826
|
+
function newToLegacyStatus(status) {
|
|
827
|
+
switch (status) {
|
|
828
|
+
case "connecting":
|
|
829
|
+
return "connecting";
|
|
830
|
+
case "connected":
|
|
831
|
+
return "open";
|
|
832
|
+
case "reconnecting":
|
|
833
|
+
return "unavailable";
|
|
834
|
+
case "disconnected":
|
|
835
|
+
return "failed";
|
|
836
|
+
case "initial":
|
|
837
|
+
return "closed";
|
|
838
|
+
default:
|
|
839
|
+
return "closed";
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function toNewConnectionStatus(machine) {
|
|
843
|
+
const state = machine.currentState;
|
|
827
844
|
switch (state) {
|
|
828
845
|
case "@ok.connected":
|
|
829
846
|
case "@ok.awaiting-pong":
|
|
830
|
-
return "
|
|
847
|
+
return "connected";
|
|
831
848
|
case "@idle.initial":
|
|
832
|
-
return "
|
|
849
|
+
return "initial";
|
|
833
850
|
case "@auth.busy":
|
|
834
851
|
case "@auth.backoff":
|
|
835
|
-
return "authenticating";
|
|
836
852
|
case "@connecting.busy":
|
|
837
|
-
return "connecting";
|
|
838
853
|
case "@connecting.backoff":
|
|
839
|
-
return "
|
|
854
|
+
return machine.context.successCount > 0 ? "reconnecting" : "connecting";
|
|
840
855
|
case "@idle.failed":
|
|
841
|
-
return "
|
|
856
|
+
return "disconnected";
|
|
842
857
|
default:
|
|
843
858
|
return assertNever(state, "Unknown state");
|
|
844
859
|
}
|
|
@@ -873,6 +888,9 @@ function increaseBackoffDelayAggressively(context) {
|
|
|
873
888
|
backoffDelay: nextBackoffDelay(context.backoffDelay, BACKOFF_DELAYS_SLOW)
|
|
874
889
|
});
|
|
875
890
|
}
|
|
891
|
+
function resetSuccessCount(context) {
|
|
892
|
+
context.patch({ successCount: 0 });
|
|
893
|
+
}
|
|
876
894
|
function log(level, message) {
|
|
877
895
|
const logger = level === 2 /* ERROR */ ? error : level === 1 /* WARN */ ? warn : (
|
|
878
896
|
/* black hole */
|
|
@@ -883,9 +901,34 @@ function log(level, message) {
|
|
|
883
901
|
logger(message);
|
|
884
902
|
};
|
|
885
903
|
}
|
|
886
|
-
function
|
|
887
|
-
|
|
888
|
-
(
|
|
904
|
+
function logPrematureErrorOrCloseEvent(e) {
|
|
905
|
+
const conn = "Connection to Liveblocks websocket server";
|
|
906
|
+
return (ctx) => {
|
|
907
|
+
if (e instanceof Error) {
|
|
908
|
+
warn(`${conn} could not be established. ${String(e)}`);
|
|
909
|
+
} else {
|
|
910
|
+
warn(
|
|
911
|
+
isCloseEvent(e) ? `${conn} closed prematurely (code: ${e.code}). Retrying in ${ctx.backoffDelay}ms.` : `${conn} could not be established.`
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function logCloseEvent(event) {
|
|
917
|
+
return (ctx) => {
|
|
918
|
+
warn(
|
|
919
|
+
`Connection to Liveblocks websocket server closed (code: ${event.code}). Retrying in ${ctx.backoffDelay}ms.`
|
|
920
|
+
);
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
var logPermanentClose = log(
|
|
924
|
+
1 /* WARN */,
|
|
925
|
+
"Connection to WebSocket closed permanently. Won't retry."
|
|
926
|
+
);
|
|
927
|
+
function isCloseEvent(error2) {
|
|
928
|
+
return !(error2 instanceof Error) && error2.type === "close";
|
|
929
|
+
}
|
|
930
|
+
function isCustomCloseEvent(error2) {
|
|
931
|
+
return isCloseEvent(error2) && error2.code >= 4e3 && error2.code < 4100;
|
|
889
932
|
}
|
|
890
933
|
function enableTracing(machine) {
|
|
891
934
|
const start = (/* @__PURE__ */ new Date()).getTime();
|
|
@@ -916,16 +959,18 @@ function defineConnectivityEvents(machine) {
|
|
|
916
959
|
const statusDidChange = makeEventSource();
|
|
917
960
|
const didConnect = makeEventSource();
|
|
918
961
|
const didDisconnect = makeEventSource();
|
|
919
|
-
let
|
|
920
|
-
const unsubscribe = machine.events.didEnterState.subscribe((
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
|
|
962
|
+
let lastStatus = null;
|
|
963
|
+
const unsubscribe = machine.events.didEnterState.subscribe(() => {
|
|
964
|
+
const currStatus = toNewConnectionStatus(machine);
|
|
965
|
+
if (currStatus !== lastStatus) {
|
|
966
|
+
statusDidChange.notify(currStatus);
|
|
967
|
+
}
|
|
968
|
+
if (lastStatus === "connected" && currStatus !== "connected") {
|
|
924
969
|
didDisconnect.notify();
|
|
925
|
-
} else if (
|
|
970
|
+
} else if (lastStatus !== "connected" && currStatus === "connected") {
|
|
926
971
|
didConnect.notify();
|
|
927
972
|
}
|
|
928
|
-
|
|
973
|
+
lastStatus = currStatus;
|
|
929
974
|
});
|
|
930
975
|
return {
|
|
931
976
|
statusDidChange: statusDidChange.observable,
|
|
@@ -940,6 +985,7 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
940
985
|
onMessage.pause();
|
|
941
986
|
const onLiveblocksError = makeEventSource();
|
|
942
987
|
const initialContext = {
|
|
988
|
+
successCount: 0,
|
|
943
989
|
token: null,
|
|
944
990
|
socket: null,
|
|
945
991
|
backoffDelay: RESET_DELAY
|
|
@@ -948,11 +994,11 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
948
994
|
machine.addTransitions("*", {
|
|
949
995
|
RECONNECT: {
|
|
950
996
|
target: "@auth.backoff",
|
|
951
|
-
effect: increaseBackoffDelay
|
|
997
|
+
effect: [increaseBackoffDelay, resetSuccessCount]
|
|
952
998
|
},
|
|
953
999
|
DISCONNECT: "@idle.initial"
|
|
954
1000
|
});
|
|
955
|
-
machine.addTransitions("@idle.*", {
|
|
1001
|
+
machine.onEnter("@idle.*", resetSuccessCount).addTransitions("@idle.*", {
|
|
956
1002
|
CONNECT: (_, ctx) => (
|
|
957
1003
|
// If we still have a known token, try to reconnect to the socket directly,
|
|
958
1004
|
// otherwise, try to obtain a new token
|
|
@@ -1097,41 +1143,41 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1097
1143
|
effect: log(2 /* ERROR */, err.message)
|
|
1098
1144
|
};
|
|
1099
1145
|
}
|
|
1146
|
+
if (isCloseEvent(err) && err.code === 4999) {
|
|
1147
|
+
return {
|
|
1148
|
+
target: "@idle.failed",
|
|
1149
|
+
effect: log(2 /* ERROR */, err.reason)
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
if (isCustomCloseEvent(err) && err.code !== 4001) {
|
|
1153
|
+
return {
|
|
1154
|
+
target: "@connecting.backoff",
|
|
1155
|
+
effect: [
|
|
1156
|
+
increaseBackoffDelayAggressively,
|
|
1157
|
+
logPrematureErrorOrCloseEvent(err)
|
|
1158
|
+
]
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1100
1161
|
return {
|
|
1101
1162
|
target: "@auth.backoff",
|
|
1102
|
-
effect: [
|
|
1103
|
-
// Increase the backoff delay conditionally
|
|
1104
|
-
// TODO: This is ugly. DRY this up with the other code 40xx checks elsewhere.
|
|
1105
|
-
!(err instanceof Error) && err.type === "close" && err.code >= 4e3 && err.code <= 4100 ? increaseBackoffDelayAggressively : increaseBackoffDelay,
|
|
1106
|
-
// Produce a useful log message
|
|
1107
|
-
(ctx) => {
|
|
1108
|
-
if (err instanceof Error) {
|
|
1109
|
-
warn(String(err));
|
|
1110
|
-
} else {
|
|
1111
|
-
warn(
|
|
1112
|
-
err.type === "close" ? `Connection to Liveblocks websocket server closed prematurely (code: ${err.code}). Retrying in ${ctx.backoffDelay}ms.` : "Connection to Liveblocks websocket server could not be established."
|
|
1113
|
-
);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
]
|
|
1163
|
+
effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
|
|
1117
1164
|
};
|
|
1118
1165
|
}
|
|
1119
1166
|
);
|
|
1120
|
-
|
|
1167
|
+
const sendHeartbeat = {
|
|
1121
1168
|
target: "@ok.awaiting-pong",
|
|
1122
|
-
effect:
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const noPongAction = {
|
|
1127
|
-
target: "@connecting.busy",
|
|
1128
|
-
// Log implicit connection loss and drop the current open socket
|
|
1129
|
-
effect: log(
|
|
1130
|
-
1 /* WARN */,
|
|
1131
|
-
"Received no pong from server, assume implicit connection loss."
|
|
1132
|
-
)
|
|
1169
|
+
effect: (ctx) => {
|
|
1170
|
+
var _a;
|
|
1171
|
+
(_a = ctx.socket) == null ? void 0 : _a.send("ping");
|
|
1172
|
+
}
|
|
1133
1173
|
};
|
|
1134
|
-
machine.
|
|
1174
|
+
machine.addTimedTransition("@ok.connected", HEARTBEAT_INTERVAL, sendHeartbeat).addTransitions("@ok.connected", {
|
|
1175
|
+
NAVIGATOR_OFFLINE: sendHeartbeat,
|
|
1176
|
+
// Don't take the browser's word for it when it says it's offline. Do a ping/pong to make sure.
|
|
1177
|
+
WINDOW_GOT_FOCUS: sendHeartbeat
|
|
1178
|
+
});
|
|
1179
|
+
machine.onEnter("@ok.*", (ctx) => {
|
|
1180
|
+
ctx.patch({ successCount: ctx.successCount + 1 });
|
|
1135
1181
|
const timerID = setTimeout(
|
|
1136
1182
|
// On the next tick, start delivering all messages that have already
|
|
1137
1183
|
// been received, and continue synchronous delivery of all future
|
|
@@ -1139,13 +1185,20 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1139
1185
|
onMessage.unpause,
|
|
1140
1186
|
0
|
|
1141
1187
|
);
|
|
1142
|
-
return (
|
|
1143
|
-
teardownSocket(
|
|
1144
|
-
|
|
1188
|
+
return (ctx2) => {
|
|
1189
|
+
teardownSocket(ctx2.socket);
|
|
1190
|
+
ctx2.patch({ socket: null });
|
|
1145
1191
|
clearTimeout(timerID);
|
|
1146
1192
|
onMessage.pause();
|
|
1147
1193
|
};
|
|
1148
|
-
}).
|
|
1194
|
+
}).addTransitions("@ok.awaiting-pong", { PONG: "@ok.connected" }).addTimedTransition("@ok.awaiting-pong", PONG_TIMEOUT, {
|
|
1195
|
+
target: "@connecting.busy",
|
|
1196
|
+
// Log implicit connection loss and drop the current open socket
|
|
1197
|
+
effect: log(
|
|
1198
|
+
1 /* WARN */,
|
|
1199
|
+
"Received no pong from server, assume implicit connection loss."
|
|
1200
|
+
)
|
|
1201
|
+
}).addTransitions("@ok.*", {
|
|
1149
1202
|
// When a socket receives an error, this can cause the closing of the
|
|
1150
1203
|
// socket, or not. So always check to see if the socket is still OPEN or
|
|
1151
1204
|
// not. When still OPEN, don't transition.
|
|
@@ -1163,37 +1216,31 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1163
1216
|
if (e.event.code === 4999) {
|
|
1164
1217
|
return {
|
|
1165
1218
|
target: "@idle.failed",
|
|
1166
|
-
effect:
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1219
|
+
effect: logPermanentClose
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
if (e.event.code === 4001) {
|
|
1223
|
+
return {
|
|
1224
|
+
target: "@auth.backoff",
|
|
1225
|
+
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
|
|
1170
1226
|
};
|
|
1171
1227
|
}
|
|
1172
|
-
if (e.event
|
|
1228
|
+
if (isCustomCloseEvent(e.event)) {
|
|
1173
1229
|
return {
|
|
1174
1230
|
target: "@connecting.backoff",
|
|
1175
1231
|
effect: [
|
|
1176
1232
|
increaseBackoffDelayAggressively,
|
|
1177
|
-
(
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
if (event.code >= 4e3 && event.code <= 4100) {
|
|
1182
|
-
const err = new LiveblocksError(event.reason, event.code);
|
|
1183
|
-
onLiveblocksError.notify(err);
|
|
1184
|
-
}
|
|
1233
|
+
logCloseEvent(e.event),
|
|
1234
|
+
() => {
|
|
1235
|
+
const err = new LiveblocksError(e.event.reason, e.event.code);
|
|
1236
|
+
onLiveblocksError.notify(err);
|
|
1185
1237
|
}
|
|
1186
1238
|
]
|
|
1187
1239
|
};
|
|
1188
1240
|
}
|
|
1189
1241
|
return {
|
|
1190
1242
|
target: "@connecting.backoff",
|
|
1191
|
-
effect: [
|
|
1192
|
-
increaseBackoffDelay,
|
|
1193
|
-
(ctx) => warn(
|
|
1194
|
-
`Connection to Liveblocks websocket server closed (code: ${e.event.code}). Retrying in ${ctx.backoffDelay}ms.`
|
|
1195
|
-
)
|
|
1196
|
-
]
|
|
1243
|
+
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
|
|
1197
1244
|
};
|
|
1198
1245
|
}
|
|
1199
1246
|
});
|
|
@@ -1202,7 +1249,10 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1202
1249
|
const win = typeof window !== "undefined" ? window : void 0;
|
|
1203
1250
|
const root = win != null ? win : doc;
|
|
1204
1251
|
machine.onEnter("*", (ctx) => {
|
|
1205
|
-
function
|
|
1252
|
+
function onNetworkOffline() {
|
|
1253
|
+
machine.send({ type: "NAVIGATOR_OFFLINE" });
|
|
1254
|
+
}
|
|
1255
|
+
function onNetworkBackOnline() {
|
|
1206
1256
|
machine.send({ type: "NAVIGATOR_ONLINE" });
|
|
1207
1257
|
}
|
|
1208
1258
|
function onVisibilityChange() {
|
|
@@ -1210,11 +1260,13 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1210
1260
|
machine.send({ type: "WINDOW_GOT_FOCUS" });
|
|
1211
1261
|
}
|
|
1212
1262
|
}
|
|
1213
|
-
win == null ? void 0 : win.addEventListener("online",
|
|
1263
|
+
win == null ? void 0 : win.addEventListener("online", onNetworkBackOnline);
|
|
1264
|
+
win == null ? void 0 : win.addEventListener("offline", onNetworkOffline);
|
|
1214
1265
|
root == null ? void 0 : root.addEventListener("visibilitychange", onVisibilityChange);
|
|
1215
1266
|
return () => {
|
|
1216
1267
|
root == null ? void 0 : root.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1217
|
-
win == null ? void 0 : win.removeEventListener("online",
|
|
1268
|
+
win == null ? void 0 : win.removeEventListener("online", onNetworkBackOnline);
|
|
1269
|
+
win == null ? void 0 : win.removeEventListener("offline", onNetworkOffline);
|
|
1218
1270
|
teardownSocket(ctx.socket);
|
|
1219
1271
|
};
|
|
1220
1272
|
});
|
|
@@ -1249,22 +1301,21 @@ var ManagedSocket = class {
|
|
|
1249
1301
|
this.events = events;
|
|
1250
1302
|
this.cleanups = cleanups;
|
|
1251
1303
|
}
|
|
1252
|
-
|
|
1304
|
+
getLegacyStatus() {
|
|
1305
|
+
return newToLegacyStatus(this.getStatus());
|
|
1306
|
+
}
|
|
1307
|
+
getStatus() {
|
|
1253
1308
|
try {
|
|
1254
|
-
return
|
|
1309
|
+
return toNewConnectionStatus(this.machine);
|
|
1255
1310
|
} catch (e) {
|
|
1256
|
-
return "
|
|
1311
|
+
return "initial";
|
|
1257
1312
|
}
|
|
1258
1313
|
}
|
|
1259
1314
|
/**
|
|
1260
1315
|
* Returns the current auth token.
|
|
1261
1316
|
*/
|
|
1262
1317
|
get token() {
|
|
1263
|
-
|
|
1264
|
-
if (tok === null) {
|
|
1265
|
-
throw new Error("Unexpected null token here");
|
|
1266
|
-
}
|
|
1267
|
-
return tok;
|
|
1318
|
+
return this.machine.context.token;
|
|
1268
1319
|
}
|
|
1269
1320
|
/**
|
|
1270
1321
|
* Call this method to try to connect to a WebSocket. This only has an effect
|
|
@@ -4173,9 +4224,6 @@ function makeIdFactory(connectionId) {
|
|
|
4173
4224
|
let count = 0;
|
|
4174
4225
|
return () => `${connectionId}:${count++}`;
|
|
4175
4226
|
}
|
|
4176
|
-
function isConnectionSelfAware(connection) {
|
|
4177
|
-
return connection.status === "open" || connection.status === "connecting";
|
|
4178
|
-
}
|
|
4179
4227
|
function userToTreeNode(key, user) {
|
|
4180
4228
|
return {
|
|
4181
4229
|
type: "User",
|
|
@@ -4204,7 +4252,6 @@ function createRoom(options, config) {
|
|
|
4204
4252
|
config.enableDebugLogging
|
|
4205
4253
|
);
|
|
4206
4254
|
const context = {
|
|
4207
|
-
lastConnectionId: null,
|
|
4208
4255
|
buffer: {
|
|
4209
4256
|
flushTimerID: void 0,
|
|
4210
4257
|
lastFlushedAt: 0,
|
|
@@ -4218,7 +4265,7 @@ function createRoom(options, config) {
|
|
|
4218
4265
|
messages: [],
|
|
4219
4266
|
storageOperations: []
|
|
4220
4267
|
},
|
|
4221
|
-
|
|
4268
|
+
sessionInfo: new ValueRef(null),
|
|
4222
4269
|
me: new MeRef(initialPresence),
|
|
4223
4270
|
others: new OthersRef(),
|
|
4224
4271
|
initialStorage,
|
|
@@ -4238,55 +4285,78 @@ function createRoom(options, config) {
|
|
|
4238
4285
|
};
|
|
4239
4286
|
const doNotBatchUpdates = (cb) => cb();
|
|
4240
4287
|
const batchUpdates = (_d = config.unstable_batchedUpdates) != null ? _d : doNotBatchUpdates;
|
|
4288
|
+
let lastToken;
|
|
4241
4289
|
function onStatusDidChange(newStatus) {
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4290
|
+
var _a2;
|
|
4291
|
+
const token = (_a2 = managedSocket.token) == null ? void 0 : _a2.parsed;
|
|
4292
|
+
if (token !== void 0 && token !== lastToken) {
|
|
4293
|
+
context.sessionInfo.set({
|
|
4294
|
+
id: token.actor,
|
|
4295
|
+
userInfo: token.info,
|
|
4296
|
+
userId: token.id,
|
|
4297
|
+
isReadOnly: isStorageReadOnly(token.scopes)
|
|
4249
4298
|
});
|
|
4250
|
-
|
|
4251
|
-
context.connection.set({ status: newStatus });
|
|
4299
|
+
lastToken = token;
|
|
4252
4300
|
}
|
|
4253
4301
|
batchUpdates(() => {
|
|
4254
|
-
eventHub.
|
|
4302
|
+
eventHub.status.notify(newStatus);
|
|
4303
|
+
eventHub.connection.notify(newToLegacyStatus(newStatus));
|
|
4255
4304
|
});
|
|
4256
4305
|
}
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4306
|
+
let _connectionLossTimerId;
|
|
4307
|
+
let _hasLostConnection = false;
|
|
4308
|
+
function handleConnectionLossEvent(newStatus) {
|
|
4309
|
+
if (newStatus === "reconnecting") {
|
|
4310
|
+
_connectionLossTimerId = setTimeout(() => {
|
|
4311
|
+
batchUpdates(() => {
|
|
4312
|
+
eventHub.lostConnection.notify("lost");
|
|
4313
|
+
_hasLostConnection = true;
|
|
4314
|
+
context.others.clearOthers();
|
|
4315
|
+
notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
|
|
4316
|
+
});
|
|
4317
|
+
}, config.lostConnectionTimeout);
|
|
4318
|
+
} else {
|
|
4319
|
+
clearTimeout(_connectionLossTimerId);
|
|
4320
|
+
if (_hasLostConnection) {
|
|
4321
|
+
if (newStatus === "disconnected") {
|
|
4322
|
+
batchUpdates(() => {
|
|
4323
|
+
eventHub.lostConnection.notify("failed");
|
|
4324
|
+
});
|
|
4325
|
+
} else {
|
|
4326
|
+
batchUpdates(() => {
|
|
4327
|
+
eventHub.lostConnection.notify("restored");
|
|
4328
|
+
});
|
|
4329
|
+
}
|
|
4330
|
+
_hasLostConnection = false;
|
|
4331
|
+
}
|
|
4273
4332
|
}
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4333
|
+
}
|
|
4334
|
+
function onDidConnect() {
|
|
4335
|
+
const sessionInfo = context.sessionInfo.current;
|
|
4336
|
+
if (sessionInfo === null) {
|
|
4337
|
+
throw new Error("Unexpected missing session info");
|
|
4338
|
+
}
|
|
4339
|
+
context.buffer.me = {
|
|
4340
|
+
type: "full",
|
|
4341
|
+
data: (
|
|
4342
|
+
// Because context.me.current is a readonly object, we'll have to
|
|
4343
|
+
// make a copy here. Otherwise, type errors happen later when
|
|
4344
|
+
// "patching" my presence.
|
|
4345
|
+
__spreadValues({}, context.me.current)
|
|
4346
|
+
)
|
|
4347
|
+
};
|
|
4348
|
+
context.idFactory = makeIdFactory(sessionInfo.id);
|
|
4349
|
+
if (_getStorage$ !== null) {
|
|
4350
|
+
refreshStorage({ flush: false });
|
|
4278
4351
|
}
|
|
4279
|
-
|
|
4352
|
+
flushNowOrSoon();
|
|
4280
4353
|
}
|
|
4281
4354
|
function onDidDisconnect() {
|
|
4282
4355
|
clearTimeout(context.buffer.flushTimerID);
|
|
4283
|
-
batchUpdates(() => {
|
|
4284
|
-
context.others.clearOthers();
|
|
4285
|
-
notify({ others: [{ type: "reset" }] }, doNotBatchUpdates);
|
|
4286
|
-
});
|
|
4287
4356
|
}
|
|
4288
4357
|
managedSocket.events.onMessage.subscribe(handleServerMessage);
|
|
4289
4358
|
managedSocket.events.statusDidChange.subscribe(onStatusDidChange);
|
|
4359
|
+
managedSocket.events.statusDidChange.subscribe(handleConnectionLossEvent);
|
|
4290
4360
|
managedSocket.events.didConnect.subscribe(onDidConnect);
|
|
4291
4361
|
managedSocket.events.didDisconnect.subscribe(onDidDisconnect);
|
|
4292
4362
|
managedSocket.events.onLiveblocksError.subscribe((err) => {
|
|
@@ -4340,7 +4410,8 @@ function createRoom(options, config) {
|
|
|
4340
4410
|
}
|
|
4341
4411
|
},
|
|
4342
4412
|
assertStorageIsWritable: () => {
|
|
4343
|
-
|
|
4413
|
+
var _a2;
|
|
4414
|
+
if ((_a2 = context.sessionInfo.current) == null ? void 0 : _a2.isReadOnly) {
|
|
4344
4415
|
throw new Error(
|
|
4345
4416
|
"Cannot write to storage with a read only user, please ensure the user has write permissions"
|
|
4346
4417
|
);
|
|
@@ -4348,22 +4419,26 @@ function createRoom(options, config) {
|
|
|
4348
4419
|
}
|
|
4349
4420
|
};
|
|
4350
4421
|
const eventHub = {
|
|
4422
|
+
connection: makeEventSource(),
|
|
4423
|
+
// Old/deprecated API
|
|
4424
|
+
status: makeEventSource(),
|
|
4425
|
+
// New/recommended API
|
|
4426
|
+
lostConnection: makeEventSource(),
|
|
4351
4427
|
customEvent: makeEventSource(),
|
|
4352
4428
|
me: makeEventSource(),
|
|
4353
4429
|
others: makeEventSource(),
|
|
4354
4430
|
error: makeEventSource(),
|
|
4355
|
-
connection: makeEventSource(),
|
|
4356
4431
|
storage: makeEventSource(),
|
|
4357
4432
|
history: makeEventSource(),
|
|
4358
4433
|
storageDidLoad: makeEventSource(),
|
|
4359
4434
|
storageStatus: makeEventSource()
|
|
4360
4435
|
};
|
|
4361
4436
|
function sendMessages(messageOrMessages) {
|
|
4362
|
-
var _a2;
|
|
4437
|
+
var _a2, _b2;
|
|
4363
4438
|
const message = JSON.stringify(messageOrMessages);
|
|
4364
4439
|
if (config.unstable_fallbackToHTTP) {
|
|
4365
4440
|
const size = new TextEncoder().encode(message).length;
|
|
4366
|
-
if (size > MAX_MESSAGE_SIZE && managedSocket.token.raw && config.httpSendEndpoint) {
|
|
4441
|
+
if (size > MAX_MESSAGE_SIZE && ((_a2 = managedSocket.token) == null ? void 0 : _a2.raw) && config.httpSendEndpoint) {
|
|
4367
4442
|
if (isTokenExpired(managedSocket.token.parsed)) {
|
|
4368
4443
|
return managedSocket.reconnect();
|
|
4369
4444
|
}
|
|
@@ -4371,7 +4446,7 @@ function createRoom(options, config) {
|
|
|
4371
4446
|
message,
|
|
4372
4447
|
managedSocket.token.raw,
|
|
4373
4448
|
config.httpSendEndpoint,
|
|
4374
|
-
(
|
|
4449
|
+
(_b2 = config.polyfills) == null ? void 0 : _b2.fetch
|
|
4375
4450
|
);
|
|
4376
4451
|
warn(
|
|
4377
4452
|
"Message was too large for websockets and sent over HTTP instead"
|
|
@@ -4382,15 +4457,17 @@ function createRoom(options, config) {
|
|
|
4382
4457
|
managedSocket.send(message);
|
|
4383
4458
|
}
|
|
4384
4459
|
const self = new DerivedRef(
|
|
4385
|
-
context.
|
|
4460
|
+
context.sessionInfo,
|
|
4386
4461
|
context.me,
|
|
4387
|
-
(
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4462
|
+
(info, me) => {
|
|
4463
|
+
return info !== null ? {
|
|
4464
|
+
connectionId: info.id,
|
|
4465
|
+
id: info.userId,
|
|
4466
|
+
info: info.userInfo,
|
|
4467
|
+
presence: me,
|
|
4468
|
+
isReadOnly: info.isReadOnly
|
|
4469
|
+
} : null;
|
|
4470
|
+
}
|
|
4394
4471
|
);
|
|
4395
4472
|
const selfAsTreeNode = new DerivedRef(
|
|
4396
4473
|
self,
|
|
@@ -4400,7 +4477,7 @@ function createRoom(options, config) {
|
|
|
4400
4477
|
if (message.items.length === 0) {
|
|
4401
4478
|
throw new Error("Internal error: cannot load storage without items");
|
|
4402
4479
|
}
|
|
4403
|
-
if (context.root) {
|
|
4480
|
+
if (context.root !== void 0) {
|
|
4404
4481
|
updateRoot(message.items, batchedUpdatesWrapper);
|
|
4405
4482
|
} else {
|
|
4406
4483
|
context.root = LiveObject._fromItems(message.items, pool);
|
|
@@ -4412,7 +4489,7 @@ function createRoom(options, config) {
|
|
|
4412
4489
|
}
|
|
4413
4490
|
}
|
|
4414
4491
|
function updateRoot(items, batchedUpdatesWrapper) {
|
|
4415
|
-
if (
|
|
4492
|
+
if (context.root === void 0) {
|
|
4416
4493
|
return;
|
|
4417
4494
|
}
|
|
4418
4495
|
const currentItems = /* @__PURE__ */ new Map();
|
|
@@ -4459,11 +4536,9 @@ function createRoom(options, config) {
|
|
|
4459
4536
|
});
|
|
4460
4537
|
}
|
|
4461
4538
|
function getConnectionId() {
|
|
4462
|
-
const
|
|
4463
|
-
if (
|
|
4464
|
-
return
|
|
4465
|
-
} else if (context.lastConnectionId !== null) {
|
|
4466
|
-
return context.lastConnectionId;
|
|
4539
|
+
const info = context.sessionInfo.current;
|
|
4540
|
+
if (info) {
|
|
4541
|
+
return info.id;
|
|
4467
4542
|
}
|
|
4468
4543
|
throw new Error(
|
|
4469
4544
|
"Internal. Tried to get connection id but connection was never open"
|
|
@@ -4612,7 +4687,7 @@ function createRoom(options, config) {
|
|
|
4612
4687
|
}
|
|
4613
4688
|
context.activeBatch.updates.presence = true;
|
|
4614
4689
|
} else {
|
|
4615
|
-
|
|
4690
|
+
flushNowOrSoon();
|
|
4616
4691
|
batchUpdates(() => {
|
|
4617
4692
|
if (options2 == null ? void 0 : options2.addToHistory) {
|
|
4618
4693
|
addToUndoStack(
|
|
@@ -4699,7 +4774,7 @@ function createRoom(options, config) {
|
|
|
4699
4774
|
data: context.me.current,
|
|
4700
4775
|
targetActor: message.actor
|
|
4701
4776
|
});
|
|
4702
|
-
|
|
4777
|
+
flushNowOrSoon();
|
|
4703
4778
|
const user = context.others.getUser(message.actor);
|
|
4704
4779
|
return user ? { type: "enter", user } : void 0;
|
|
4705
4780
|
}
|
|
@@ -4785,9 +4860,7 @@ function createRoom(options, config) {
|
|
|
4785
4860
|
const unacknowledgedOps = new Map(context.unacknowledgedOps);
|
|
4786
4861
|
createOrUpdateRootFromMessage(message, doNotBatchUpdates);
|
|
4787
4862
|
applyAndSendOps(unacknowledgedOps, doNotBatchUpdates);
|
|
4788
|
-
|
|
4789
|
-
_getInitialStateResolver();
|
|
4790
|
-
}
|
|
4863
|
+
_resolveStoragePromise == null ? void 0 : _resolveStoragePromise();
|
|
4791
4864
|
notifyStorageStatus();
|
|
4792
4865
|
eventHub.storageDidLoad.notify();
|
|
4793
4866
|
break;
|
|
@@ -4834,7 +4907,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4834
4907
|
notify(updates, doNotBatchUpdates);
|
|
4835
4908
|
});
|
|
4836
4909
|
}
|
|
4837
|
-
function
|
|
4910
|
+
function flushNowOrSoon() {
|
|
4838
4911
|
const storageOps = context.buffer.storageOperations;
|
|
4839
4912
|
if (storageOps.length > 0) {
|
|
4840
4913
|
for (const op of storageOps) {
|
|
@@ -4842,7 +4915,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4842
4915
|
}
|
|
4843
4916
|
notifyStorageStatus();
|
|
4844
4917
|
}
|
|
4845
|
-
if (managedSocket.
|
|
4918
|
+
if (managedSocket.getStatus() !== "connected") {
|
|
4846
4919
|
context.buffer.storageOperations = [];
|
|
4847
4920
|
return;
|
|
4848
4921
|
}
|
|
@@ -4864,7 +4937,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4864
4937
|
} else {
|
|
4865
4938
|
clearTimeout(context.buffer.flushTimerID);
|
|
4866
4939
|
context.buffer.flushTimerID = setTimeout(
|
|
4867
|
-
|
|
4940
|
+
flushNowOrSoon,
|
|
4868
4941
|
config.throttleDelay - elapsedMillis
|
|
4869
4942
|
);
|
|
4870
4943
|
}
|
|
@@ -4900,31 +4973,39 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4900
4973
|
function broadcastEvent(event, options2 = {
|
|
4901
4974
|
shouldQueueEventIfNotReady: false
|
|
4902
4975
|
}) {
|
|
4903
|
-
if (managedSocket.
|
|
4976
|
+
if (managedSocket.getStatus() !== "connected" && !options2.shouldQueueEventIfNotReady) {
|
|
4904
4977
|
return;
|
|
4905
4978
|
}
|
|
4906
4979
|
context.buffer.messages.push({
|
|
4907
4980
|
type: 103 /* BROADCAST_EVENT */,
|
|
4908
4981
|
event
|
|
4909
4982
|
});
|
|
4910
|
-
|
|
4983
|
+
flushNowOrSoon();
|
|
4911
4984
|
}
|
|
4912
4985
|
function dispatchOps(ops) {
|
|
4913
4986
|
context.buffer.storageOperations.push(...ops);
|
|
4914
|
-
|
|
4987
|
+
flushNowOrSoon();
|
|
4988
|
+
}
|
|
4989
|
+
let _getStorage$ = null;
|
|
4990
|
+
let _resolveStoragePromise = null;
|
|
4991
|
+
function refreshStorage(options2) {
|
|
4992
|
+
const messages = context.buffer.messages;
|
|
4993
|
+
if (!messages.some((msg) => msg.type === 200 /* FETCH_STORAGE */)) {
|
|
4994
|
+
messages.push({ type: 200 /* FETCH_STORAGE */ });
|
|
4995
|
+
}
|
|
4996
|
+
if (options2.flush) {
|
|
4997
|
+
flushNowOrSoon();
|
|
4998
|
+
}
|
|
4915
4999
|
}
|
|
4916
|
-
let _getInitialStatePromise = null;
|
|
4917
|
-
let _getInitialStateResolver = null;
|
|
4918
5000
|
function startLoadingStorage() {
|
|
4919
|
-
if (
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
);
|
|
5001
|
+
if (_getStorage$ === null) {
|
|
5002
|
+
refreshStorage({ flush: true });
|
|
5003
|
+
_getStorage$ = new Promise((resolve) => {
|
|
5004
|
+
_resolveStoragePromise = resolve;
|
|
5005
|
+
});
|
|
4925
5006
|
notifyStorageStatus();
|
|
4926
5007
|
}
|
|
4927
|
-
return
|
|
5008
|
+
return _getStorage$;
|
|
4928
5009
|
}
|
|
4929
5010
|
function getStorageSnapshot() {
|
|
4930
5011
|
const root = context.root;
|
|
@@ -4937,7 +5018,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4937
5018
|
}
|
|
4938
5019
|
function getStorage() {
|
|
4939
5020
|
return __async(this, null, function* () {
|
|
4940
|
-
if (context.root) {
|
|
5021
|
+
if (context.root !== void 0) {
|
|
4941
5022
|
return Promise.resolve({
|
|
4942
5023
|
root: context.root
|
|
4943
5024
|
});
|
|
@@ -4968,7 +5049,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4968
5049
|
context.buffer.storageOperations.push(op);
|
|
4969
5050
|
}
|
|
4970
5051
|
}
|
|
4971
|
-
|
|
5052
|
+
flushNowOrSoon();
|
|
4972
5053
|
}
|
|
4973
5054
|
function redo() {
|
|
4974
5055
|
if (context.activeBatch) {
|
|
@@ -4990,7 +5071,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4990
5071
|
context.buffer.storageOperations.push(op);
|
|
4991
5072
|
}
|
|
4992
5073
|
}
|
|
4993
|
-
|
|
5074
|
+
flushNowOrSoon();
|
|
4994
5075
|
}
|
|
4995
5076
|
function batch(callback) {
|
|
4996
5077
|
if (context.activeBatch) {
|
|
@@ -5022,7 +5103,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5022
5103
|
dispatchOps(currentBatch.ops);
|
|
5023
5104
|
}
|
|
5024
5105
|
notify(currentBatch.updates, doNotBatchUpdates);
|
|
5025
|
-
|
|
5106
|
+
flushNowOrSoon();
|
|
5026
5107
|
}
|
|
5027
5108
|
});
|
|
5028
5109
|
return returnValue;
|
|
@@ -5038,13 +5119,11 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5038
5119
|
}
|
|
5039
5120
|
}
|
|
5040
5121
|
function getStorageStatus() {
|
|
5041
|
-
if (_getInitialStatePromise === null) {
|
|
5042
|
-
return "not-loaded";
|
|
5043
|
-
}
|
|
5044
5122
|
if (context.root === void 0) {
|
|
5045
|
-
return "loading";
|
|
5123
|
+
return _getStorage$ === null ? "not-loaded" : "loading";
|
|
5124
|
+
} else {
|
|
5125
|
+
return context.unacknowledgedOps.size === 0 ? "synchronized" : "synchronizing";
|
|
5046
5126
|
}
|
|
5047
|
-
return context.unacknowledgedOps.size === 0 ? "synchronized" : "synchronizing";
|
|
5048
5127
|
}
|
|
5049
5128
|
let _lastStorageStatus = getStorageStatus();
|
|
5050
5129
|
function notifyStorageStatus() {
|
|
@@ -5059,11 +5138,15 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5059
5138
|
(others) => others.map((other, index) => userToTreeNode(`Other ${index}`, other))
|
|
5060
5139
|
);
|
|
5061
5140
|
const events = {
|
|
5141
|
+
connection: eventHub.connection.observable,
|
|
5142
|
+
// Old/deprecated API
|
|
5143
|
+
status: eventHub.status.observable,
|
|
5144
|
+
// New/recommended API
|
|
5145
|
+
lostConnection: eventHub.lostConnection.observable,
|
|
5062
5146
|
customEvent: eventHub.customEvent.observable,
|
|
5063
5147
|
others: eventHub.others.observable,
|
|
5064
5148
|
me: eventHub.me.observable,
|
|
5065
5149
|
error: eventHub.error.observable,
|
|
5066
|
-
connection: eventHub.connection.observable,
|
|
5067
5150
|
storage: eventHub.storage.observable,
|
|
5068
5151
|
history: eventHub.history.observable,
|
|
5069
5152
|
storageDidLoad: eventHub.storageDidLoad.observable,
|
|
@@ -5091,7 +5174,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5091
5174
|
send: {
|
|
5092
5175
|
// These exist only for our E2E testing app
|
|
5093
5176
|
explicitClose: (event) => managedSocket._privateSendMachineEvent({ type: "EXPLICIT_SOCKET_CLOSE", event }),
|
|
5094
|
-
implicitClose: () => managedSocket._privateSendMachineEvent({ type: "
|
|
5177
|
+
implicitClose: () => managedSocket._privateSendMachineEvent({ type: "NAVIGATOR_OFFLINE" })
|
|
5095
5178
|
}
|
|
5096
5179
|
},
|
|
5097
5180
|
id: config.roomId,
|
|
@@ -5118,8 +5201,9 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5118
5201
|
getStorageStatus,
|
|
5119
5202
|
events,
|
|
5120
5203
|
// Core
|
|
5121
|
-
|
|
5122
|
-
|
|
5204
|
+
getStatus: () => managedSocket.getStatus(),
|
|
5205
|
+
getConnectionState: () => managedSocket.getLegacyStatus(),
|
|
5206
|
+
isSelfAware: () => context.sessionInfo.current !== null,
|
|
5123
5207
|
getSelf: () => self.current,
|
|
5124
5208
|
// Presence
|
|
5125
5209
|
getPresence: () => context.me.current,
|
|
@@ -5171,6 +5255,12 @@ function makeClassicSubscribeFn(events) {
|
|
|
5171
5255
|
return events.connection.subscribe(
|
|
5172
5256
|
callback
|
|
5173
5257
|
);
|
|
5258
|
+
case "status":
|
|
5259
|
+
return events.status.subscribe(callback);
|
|
5260
|
+
case "lost-connection":
|
|
5261
|
+
return events.lostConnection.subscribe(
|
|
5262
|
+
callback
|
|
5263
|
+
);
|
|
5174
5264
|
case "history":
|
|
5175
5265
|
return events.history.subscribe(callback);
|
|
5176
5266
|
case "storage-status":
|
|
@@ -5204,7 +5294,7 @@ function makeClassicSubscribeFn(events) {
|
|
|
5204
5294
|
return subscribe;
|
|
5205
5295
|
}
|
|
5206
5296
|
function isRoomEventName(value) {
|
|
5207
|
-
return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "
|
|
5297
|
+
return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "history" || value === "status" || value === "storage-status" || value === "lost-connection" || value === "connection";
|
|
5208
5298
|
}
|
|
5209
5299
|
function makeCreateSocketDelegateForRoom(liveblocksServer, WebSocketPolyfill) {
|
|
5210
5300
|
return (richToken) => {
|
|
@@ -5221,7 +5311,7 @@ function makeCreateSocketDelegateForRoom(liveblocksServer, WebSocketPolyfill) {
|
|
|
5221
5311
|
// @ts-ignore (__PACKAGE_VERSION__ will be injected by the build script)
|
|
5222
5312
|
true ? (
|
|
5223
5313
|
/* istanbul ignore next */
|
|
5224
|
-
"1.1.0
|
|
5314
|
+
"1.1.0"
|
|
5225
5315
|
) : "dev"}`
|
|
5226
5316
|
);
|
|
5227
5317
|
};
|
|
@@ -5324,20 +5414,28 @@ function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
|
5324
5414
|
var MIN_THROTTLE = 16;
|
|
5325
5415
|
var MAX_THROTTLE = 1e3;
|
|
5326
5416
|
var DEFAULT_THROTTLE = 100;
|
|
5417
|
+
var MIN_LOST_CONNECTION_TIMEOUT = 200;
|
|
5418
|
+
var RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT = 1e3;
|
|
5419
|
+
var MAX_LOST_CONNECTION_TIMEOUT = 3e4;
|
|
5420
|
+
var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3;
|
|
5327
5421
|
function getServerFromClientOptions(clientOptions) {
|
|
5328
5422
|
const rawOptions = clientOptions;
|
|
5329
5423
|
return typeof rawOptions.liveblocksServer === "string" ? rawOptions.liveblocksServer : "wss://api.liveblocks.io/v6";
|
|
5330
5424
|
}
|
|
5331
5425
|
function createClient(options) {
|
|
5426
|
+
var _a, _b;
|
|
5332
5427
|
const clientOptions = options;
|
|
5333
|
-
const throttleDelay =
|
|
5428
|
+
const throttleDelay = getThrottle((_a = clientOptions.throttle) != null ? _a : DEFAULT_THROTTLE);
|
|
5429
|
+
const lostConnectionTimeout = getLostConnectionTimeout(
|
|
5430
|
+
(_b = clientOptions.lostConnectionTimeout) != null ? _b : DEFAULT_LOST_CONNECTION_TIMEOUT
|
|
5431
|
+
);
|
|
5334
5432
|
const rooms = /* @__PURE__ */ new Map();
|
|
5335
5433
|
function getRoom(roomId) {
|
|
5336
5434
|
const room = rooms.get(roomId);
|
|
5337
5435
|
return room ? room : null;
|
|
5338
5436
|
}
|
|
5339
5437
|
function enter(roomId, options2) {
|
|
5340
|
-
var
|
|
5438
|
+
var _a2, _b2, _c;
|
|
5341
5439
|
const existingRoom = rooms.get(roomId);
|
|
5342
5440
|
if (existingRoom !== void 0) {
|
|
5343
5441
|
return existingRoom;
|
|
@@ -5348,12 +5446,13 @@ function createClient(options) {
|
|
|
5348
5446
|
);
|
|
5349
5447
|
const newRoom = createRoom(
|
|
5350
5448
|
{
|
|
5351
|
-
initialPresence: (
|
|
5449
|
+
initialPresence: (_a2 = options2.initialPresence) != null ? _a2 : {},
|
|
5352
5450
|
initialStorage: options2.initialStorage
|
|
5353
5451
|
},
|
|
5354
5452
|
{
|
|
5355
5453
|
roomId,
|
|
5356
5454
|
throttleDelay,
|
|
5455
|
+
lostConnectionTimeout,
|
|
5357
5456
|
polyfills: clientOptions.polyfills,
|
|
5358
5457
|
delegates: clientOptions.mockedDelegates,
|
|
5359
5458
|
enableDebugLogging: clientOptions.enableDebugLogging,
|
|
@@ -5370,7 +5469,7 @@ function createClient(options) {
|
|
|
5370
5469
|
rooms.set(roomId, newRoom);
|
|
5371
5470
|
setupDevTools(() => Array.from(rooms.keys()));
|
|
5372
5471
|
linkDevTools(roomId, newRoom);
|
|
5373
|
-
const shouldConnect = (
|
|
5472
|
+
const shouldConnect = (_b2 = options2.shouldInitiallyConnect) != null ? _b2 : true;
|
|
5374
5473
|
if (shouldConnect) {
|
|
5375
5474
|
if (typeof atob === "undefined") {
|
|
5376
5475
|
if (((_c = clientOptions.polyfills) == null ? void 0 : _c.atob) === void 0) {
|
|
@@ -5398,16 +5497,25 @@ function createClient(options) {
|
|
|
5398
5497
|
leave
|
|
5399
5498
|
};
|
|
5400
5499
|
}
|
|
5401
|
-
function
|
|
5402
|
-
if (
|
|
5403
|
-
return DEFAULT_THROTTLE;
|
|
5404
|
-
}
|
|
5405
|
-
if (typeof options.throttle !== "number" || options.throttle < MIN_THROTTLE || options.throttle > MAX_THROTTLE) {
|
|
5500
|
+
function checkBounds(option, value, min, max, recommendedMin) {
|
|
5501
|
+
if (typeof value !== "number" || value < min || value > max) {
|
|
5406
5502
|
throw new Error(
|
|
5407
|
-
|
|
5503
|
+
`${option} should be a number between ${recommendedMin != null ? recommendedMin : min} and ${max}.`
|
|
5408
5504
|
);
|
|
5409
5505
|
}
|
|
5410
|
-
return
|
|
5506
|
+
return value;
|
|
5507
|
+
}
|
|
5508
|
+
function getThrottle(value) {
|
|
5509
|
+
return checkBounds("throttle", value, MIN_THROTTLE, MAX_THROTTLE);
|
|
5510
|
+
}
|
|
5511
|
+
function getLostConnectionTimeout(value) {
|
|
5512
|
+
return checkBounds(
|
|
5513
|
+
"lostConnectionTimeout",
|
|
5514
|
+
value,
|
|
5515
|
+
MIN_LOST_CONNECTION_TIMEOUT,
|
|
5516
|
+
MAX_LOST_CONNECTION_TIMEOUT,
|
|
5517
|
+
RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT
|
|
5518
|
+
);
|
|
5411
5519
|
}
|
|
5412
5520
|
function prepareAuthentication(clientOptions, roomId) {
|
|
5413
5521
|
const { publicApiKey, authEndpoint } = clientOptions;
|