Compare commits
18 Commits
master
...
arnost/col
Author | SHA1 | Date | |
---|---|---|---|
|
bef0f3e51d | ||
|
28b0095c8a | ||
|
c4ff0594e3 | ||
|
4608e809b1 | ||
|
db5149ab5d | ||
|
2bdf09153c | ||
|
62df03d78d | ||
|
a1d3350131 | ||
|
a2d371bf1d | ||
|
e340103250 | ||
|
0567af1bcb | ||
|
2ffeff442a | ||
|
ef190ebf30 | ||
|
e1ff9791f2 | ||
|
aa91af8f7d | ||
|
52254bca7c | ||
|
addf9d71fa | ||
|
1badf14a93 |
@ -54,7 +54,7 @@
|
|||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"roughjs": "4.5.2",
|
"roughjs": "4.5.2",
|
||||||
"sass": "1.51.0",
|
"sass": "1.51.0",
|
||||||
"socket.io-client": "2.3.1",
|
"socket.io-client": "4.6.1",
|
||||||
"tunnel-rat": "0.1.2",
|
"tunnel-rat": "0.1.2",
|
||||||
"workbox-background-sync": "^6.5.4",
|
"workbox-background-sync": "^6.5.4",
|
||||||
"workbox-broadcast-update": "^6.5.4",
|
"workbox-broadcast-update": "^6.5.4",
|
||||||
|
@ -2377,6 +2377,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
toast: {
|
toast: {
|
||||||
message: string;
|
message: string;
|
||||||
closable?: boolean;
|
closable?: boolean;
|
||||||
|
spinner?: boolean;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
} | null,
|
} | null,
|
||||||
) => {
|
) => {
|
||||||
|
@ -25,6 +25,17 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Toast__message--spinner {
|
||||||
|
padding: 0 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toast__spinner {
|
||||||
|
position: absolute;
|
||||||
|
left: 1.5rem;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
import { useCallback, useEffect, useRef } from "react";
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
import { CloseIcon } from "./icons";
|
import { CloseIcon } from "./icons";
|
||||||
|
import Spinner from "./Spinner";
|
||||||
import "./Toast.scss";
|
import "./Toast.scss";
|
||||||
import { ToolButton } from "./ToolButton";
|
import { ToolButton } from "./ToolButton";
|
||||||
|
|
||||||
@ -9,12 +11,14 @@ export const Toast = ({
|
|||||||
message,
|
message,
|
||||||
onClose,
|
onClose,
|
||||||
closable = false,
|
closable = false,
|
||||||
|
spinner = true,
|
||||||
// To prevent autoclose, pass duration as Infinity
|
// To prevent autoclose, pass duration as Infinity
|
||||||
duration = DEFAULT_TOAST_TIMEOUT,
|
duration = DEFAULT_TOAST_TIMEOUT,
|
||||||
}: {
|
}: {
|
||||||
message: string;
|
message: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
closable?: boolean;
|
closable?: boolean;
|
||||||
|
spinner?: boolean;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
}) => {
|
}) => {
|
||||||
const timerRef = useRef<number>(0);
|
const timerRef = useRef<number>(0);
|
||||||
@ -44,7 +48,18 @@ export const Toast = ({
|
|||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
>
|
>
|
||||||
<p className="Toast__message">{message}</p>
|
{spinner && (
|
||||||
|
<div className="Toast__spinner">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p
|
||||||
|
className={clsx("Toast__message", {
|
||||||
|
"Toast__message--spinner": spinner,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
{closable && (
|
{closable && (
|
||||||
<ToolButton
|
<ToolButton
|
||||||
icon={CloseIcon}
|
icon={CloseIcon}
|
||||||
|
@ -7,6 +7,8 @@ export const SYNC_FULL_SCENE_INTERVAL_MS = 20000;
|
|||||||
export const SYNC_BROWSER_TABS_TIMEOUT = 50;
|
export const SYNC_BROWSER_TABS_TIMEOUT = 50;
|
||||||
export const CURSOR_SYNC_TIMEOUT = 33; // ~30fps
|
export const CURSOR_SYNC_TIMEOUT = 33; // ~30fps
|
||||||
export const DELETED_ELEMENT_TIMEOUT = 24 * 60 * 60 * 1000; // 1 day
|
export const DELETED_ELEMENT_TIMEOUT = 24 * 60 * 60 * 1000; // 1 day
|
||||||
|
export const PAUSE_COLLABORATION_TIMEOUT = 30000;
|
||||||
|
export const RESUME_FALLBACK_TIMEOUT = 5000;
|
||||||
|
|
||||||
export const FILE_UPLOAD_MAX_BYTES = 3 * 1024 * 1024; // 3 MiB
|
export const FILE_UPLOAD_MAX_BYTES = 3 * 1024 * 1024; // 3 MiB
|
||||||
// 1 year (https://stackoverflow.com/a/25201898/927631)
|
// 1 year (https://stackoverflow.com/a/25201898/927631)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import throttle from "lodash.throttle";
|
import throttle from "lodash.throttle";
|
||||||
import { PureComponent } from "react";
|
import { PureComponent } from "react";
|
||||||
import { ExcalidrawImperativeAPI } from "../../types";
|
import { ExcalidrawImperativeAPI, PauseCollaborationState } from "../../types";
|
||||||
import { ErrorDialog } from "../../components/ErrorDialog";
|
import { ErrorDialog } from "../../components/ErrorDialog";
|
||||||
import { APP_NAME, ENV, EVENT } from "../../constants";
|
import { APP_NAME, ENV, EVENT } from "../../constants";
|
||||||
import { ImportedDataState } from "../../data/types";
|
import { ImportedDataState } from "../../data/types";
|
||||||
@ -16,6 +16,7 @@ import { Collaborator, Gesture } from "../../types";
|
|||||||
import {
|
import {
|
||||||
preventUnload,
|
preventUnload,
|
||||||
resolvablePromise,
|
resolvablePromise,
|
||||||
|
upsertMap,
|
||||||
withBatchedUpdates,
|
withBatchedUpdates,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import {
|
import {
|
||||||
@ -24,12 +25,15 @@ import {
|
|||||||
FIREBASE_STORAGE_PREFIXES,
|
FIREBASE_STORAGE_PREFIXES,
|
||||||
INITIAL_SCENE_UPDATE_TIMEOUT,
|
INITIAL_SCENE_UPDATE_TIMEOUT,
|
||||||
LOAD_IMAGES_TIMEOUT,
|
LOAD_IMAGES_TIMEOUT,
|
||||||
|
PAUSE_COLLABORATION_TIMEOUT,
|
||||||
WS_SCENE_EVENT_TYPES,
|
WS_SCENE_EVENT_TYPES,
|
||||||
SYNC_FULL_SCENE_INTERVAL_MS,
|
SYNC_FULL_SCENE_INTERVAL_MS,
|
||||||
|
RESUME_FALLBACK_TIMEOUT,
|
||||||
} from "../app_constants";
|
} from "../app_constants";
|
||||||
import {
|
import {
|
||||||
generateCollaborationLinkData,
|
generateCollaborationLinkData,
|
||||||
getCollaborationLink,
|
getCollaborationLink,
|
||||||
|
getCollaborationLinkData,
|
||||||
getCollabServer,
|
getCollabServer,
|
||||||
getSyncableElements,
|
getSyncableElements,
|
||||||
SocketUpdateDataSource,
|
SocketUpdateDataSource,
|
||||||
@ -43,8 +47,8 @@ import {
|
|||||||
saveToFirebase,
|
saveToFirebase,
|
||||||
} from "../data/firebase";
|
} from "../data/firebase";
|
||||||
import {
|
import {
|
||||||
importUsernameFromLocalStorage,
|
importUsernameAndIdFromLocalStorage,
|
||||||
saveUsernameToLocalStorage,
|
saveUsernameAndIdToLocalStorage,
|
||||||
} from "../data/localStorage";
|
} from "../data/localStorage";
|
||||||
import Portal from "./Portal";
|
import Portal from "./Portal";
|
||||||
import RoomDialog from "./RoomDialog";
|
import RoomDialog from "./RoomDialog";
|
||||||
@ -71,16 +75,19 @@ import { resetBrowserStateVersions } from "../data/tabSync";
|
|||||||
import { LocalData } from "../data/LocalData";
|
import { LocalData } from "../data/LocalData";
|
||||||
import { atom, useAtom } from "jotai";
|
import { atom, useAtom } from "jotai";
|
||||||
import { appJotaiStore } from "../app-jotai";
|
import { appJotaiStore } from "../app-jotai";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
|
||||||
export const collabAPIAtom = atom<CollabAPI | null>(null);
|
export const collabAPIAtom = atom<CollabAPI | null>(null);
|
||||||
export const collabDialogShownAtom = atom(false);
|
export const collabDialogShownAtom = atom(false);
|
||||||
export const isCollaboratingAtom = atom(false);
|
export const isCollaboratingAtom = atom(false);
|
||||||
export const isOfflineAtom = atom(false);
|
export const isOfflineAtom = atom(false);
|
||||||
|
export const isCollaborationPausedAtom = atom(false);
|
||||||
|
|
||||||
interface CollabState {
|
interface CollabState {
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
username: string;
|
username: string;
|
||||||
activeRoomLink: string;
|
activeRoomLink: string;
|
||||||
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CollabInstance = InstanceType<typeof Collab>;
|
type CollabInstance = InstanceType<typeof Collab>;
|
||||||
@ -94,6 +101,7 @@ export interface CollabAPI {
|
|||||||
syncElements: CollabInstance["syncElements"];
|
syncElements: CollabInstance["syncElements"];
|
||||||
fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
|
fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
|
||||||
setUsername: (username: string) => void;
|
setUsername: (username: string) => void;
|
||||||
|
isPaused: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PublicProps {
|
interface PublicProps {
|
||||||
@ -108,6 +116,7 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
excalidrawAPI: Props["excalidrawAPI"];
|
excalidrawAPI: Props["excalidrawAPI"];
|
||||||
activeIntervalId: number | null;
|
activeIntervalId: number | null;
|
||||||
idleTimeoutId: number | null;
|
idleTimeoutId: number | null;
|
||||||
|
pauseTimeoutId: number | null;
|
||||||
|
|
||||||
private socketInitializationTimer?: number;
|
private socketInitializationTimer?: number;
|
||||||
private lastBroadcastedOrReceivedSceneVersion: number = -1;
|
private lastBroadcastedOrReceivedSceneVersion: number = -1;
|
||||||
@ -115,9 +124,13 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
const { username, userId } = importUsernameAndIdFromLocalStorage() || {};
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
username: importUsernameFromLocalStorage() || "",
|
username: username || "",
|
||||||
|
userId: userId || "",
|
||||||
activeRoomLink: "",
|
activeRoomLink: "",
|
||||||
};
|
};
|
||||||
this.portal = new Portal(this);
|
this.portal = new Portal(this);
|
||||||
@ -149,6 +162,7 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
this.excalidrawAPI = props.excalidrawAPI;
|
this.excalidrawAPI = props.excalidrawAPI;
|
||||||
this.activeIntervalId = null;
|
this.activeIntervalId = null;
|
||||||
this.idleTimeoutId = null;
|
this.idleTimeoutId = null;
|
||||||
|
this.pauseTimeoutId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -167,6 +181,7 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
fetchImageFilesFromFirebase: this.fetchImageFilesFromFirebase,
|
fetchImageFilesFromFirebase: this.fetchImageFilesFromFirebase,
|
||||||
stopCollaboration: this.stopCollaboration,
|
stopCollaboration: this.stopCollaboration,
|
||||||
setUsername: this.setUsername,
|
setUsername: this.setUsername,
|
||||||
|
isPaused: this.isPaused,
|
||||||
};
|
};
|
||||||
|
|
||||||
appJotaiStore.set(collabAPIAtom, collabAPI);
|
appJotaiStore.set(collabAPIAtom, collabAPI);
|
||||||
@ -207,6 +222,10 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
window.clearTimeout(this.idleTimeoutId);
|
window.clearTimeout(this.idleTimeoutId);
|
||||||
this.idleTimeoutId = null;
|
this.idleTimeoutId = null;
|
||||||
}
|
}
|
||||||
|
if (this.pauseTimeoutId) {
|
||||||
|
window.clearTimeout(this.pauseTimeoutId);
|
||||||
|
this.pauseTimeoutId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isCollaborating = () => appJotaiStore.get(isCollaboratingAtom)!;
|
isCollaborating = () => appJotaiStore.get(isCollaboratingAtom)!;
|
||||||
@ -310,6 +329,126 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fallbackResumeTimeout: null | ReturnType<typeof setTimeout> = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the pause and resume states of a collaboration session.
|
||||||
|
* This function gets triggered when a change in the collaboration pause state is detected.
|
||||||
|
* Based on the state, the function carries out the following actions:
|
||||||
|
* 1. `PAUSED`: Saves the current scene to Firebase, disconnects the socket, and updates the scene to view mode.
|
||||||
|
* 2. `RESUMED`: Connects the socket, shows a toast message, sets a fallback to fetch data from Firebase, and resets the pause timeout if any.
|
||||||
|
* 3. `SYNCED`: Clears the fallback timeout if any, updates the collaboration pause state, and updates the scene to editing mode.
|
||||||
|
*
|
||||||
|
* @param state - The new state of the collaboration session. It is one of the values of `PauseCollaborationState` enum, which includes `PAUSED`, `RESUMED`, and `SYNCED`.
|
||||||
|
*/
|
||||||
|
onPauseCollaborationChange = (state: PauseCollaborationState) => {
|
||||||
|
switch (state) {
|
||||||
|
case PauseCollaborationState.PAUSED: {
|
||||||
|
if (this.portal.socket) {
|
||||||
|
// Save current scene to firebase
|
||||||
|
this.saveCollabRoomToFirebase(
|
||||||
|
getSyncableElements(
|
||||||
|
this.excalidrawAPI.getSceneElementsIncludingDeleted(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.portal.socket.disconnect();
|
||||||
|
this.portal.socketInitialized = false;
|
||||||
|
this.setIsCollaborationPaused(true);
|
||||||
|
|
||||||
|
this.excalidrawAPI.updateScene({
|
||||||
|
appState: { viewModeEnabled: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PauseCollaborationState.RESUMED: {
|
||||||
|
if (this.portal.socket && this.isPaused()) {
|
||||||
|
this.portal.socket.connect();
|
||||||
|
this.portal.socket.emit(WS_SCENE_EVENT_TYPES.INIT);
|
||||||
|
|
||||||
|
this.excalidrawAPI.setToast({
|
||||||
|
message: t("toast.reconnectRoomServer"),
|
||||||
|
duration: Infinity,
|
||||||
|
spinner: true,
|
||||||
|
closable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fallback to fetch data from firebase when reconnecting to scene without collaborators
|
||||||
|
const fallbackResumeHandler = async () => {
|
||||||
|
const roomLinkData = getCollaborationLinkData(
|
||||||
|
this.state.activeRoomLink,
|
||||||
|
);
|
||||||
|
if (!roomLinkData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const elements = await loadFromFirebase(
|
||||||
|
roomLinkData.roomId,
|
||||||
|
roomLinkData.roomKey,
|
||||||
|
this.portal.socket,
|
||||||
|
);
|
||||||
|
if (elements) {
|
||||||
|
this.setLastBroadcastedOrReceivedSceneVersion(
|
||||||
|
getSceneVersion(elements),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.excalidrawAPI.updateScene({
|
||||||
|
elements,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.onPauseCollaborationChange(PauseCollaborationState.SYNCED);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set timeout to fallback to fetch data from firebase
|
||||||
|
this.fallbackResumeTimeout = setTimeout(
|
||||||
|
fallbackResumeHandler,
|
||||||
|
RESUME_FALLBACK_TIMEOUT,
|
||||||
|
);
|
||||||
|
|
||||||
|
// When no users are in the room, we fallback to fetch data from firebase immediately and clear fallback timeout
|
||||||
|
this.portal.socket.on("first-in-room", () => {
|
||||||
|
if (this.portal.socket) {
|
||||||
|
this.portal.socket.off("first-in-room");
|
||||||
|
// Recall init event to initialize collab with other users (fixes https://github.com/excalidraw/excalidraw/pull/6638#issuecomment-1600799080)
|
||||||
|
this.portal.socket.emit(WS_SCENE_EVENT_TYPES.INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackResumeHandler();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear pause timeout if exists
|
||||||
|
if (this.pauseTimeoutId) {
|
||||||
|
clearTimeout(this.pauseTimeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PauseCollaborationState.SYNCED: {
|
||||||
|
if (this.fallbackResumeTimeout) {
|
||||||
|
clearTimeout(this.fallbackResumeTimeout);
|
||||||
|
this.fallbackResumeTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isPaused()) {
|
||||||
|
this.setIsCollaborationPaused(false);
|
||||||
|
|
||||||
|
this.excalidrawAPI.updateScene({
|
||||||
|
appState: { viewModeEnabled: false },
|
||||||
|
});
|
||||||
|
this.excalidrawAPI.setToast(null);
|
||||||
|
this.excalidrawAPI.scrollToContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
isPaused = () => appJotaiStore.get(isCollaborationPausedAtom)!;
|
||||||
|
|
||||||
|
setIsCollaborationPaused = (isPaused: boolean) => {
|
||||||
|
appJotaiStore.set(isCollaborationPausedAtom, isPaused);
|
||||||
|
};
|
||||||
|
|
||||||
private destroySocketClient = (opts?: { isUnload: boolean }) => {
|
private destroySocketClient = (opts?: { isUnload: boolean }) => {
|
||||||
this.lastBroadcastedOrReceivedSceneVersion = -1;
|
this.lastBroadcastedOrReceivedSceneVersion = -1;
|
||||||
this.portal.close();
|
this.portal.close();
|
||||||
@ -388,6 +527,11 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.state.userId) {
|
||||||
|
const userId = nanoid();
|
||||||
|
this.onUserIdChange(userId);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.portal.socket) {
|
if (this.portal.socket) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -502,6 +646,7 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
elements: reconciledElements,
|
elements: reconciledElements,
|
||||||
scrollToContent: true,
|
scrollToContent: true,
|
||||||
});
|
});
|
||||||
|
this.onPauseCollaborationChange(PauseCollaborationState.SYNCED);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -511,36 +656,63 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "MOUSE_LOCATION": {
|
case "MOUSE_LOCATION": {
|
||||||
const { pointer, button, username, selectedElementIds } =
|
const {
|
||||||
decryptedData.payload;
|
pointer,
|
||||||
const socketId: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["socketId"] =
|
button,
|
||||||
decryptedData.payload.socketId ||
|
username,
|
||||||
// @ts-ignore legacy, see #2094 (#2097)
|
selectedElementIds,
|
||||||
decryptedData.payload.socketID;
|
userId,
|
||||||
|
socketId,
|
||||||
const collaborators = new Map(this.collaborators);
|
} = decryptedData.payload;
|
||||||
const user = collaborators.get(socketId) || {}!;
|
const collaborators = upsertMap(
|
||||||
user.pointer = pointer;
|
userId,
|
||||||
user.button = button;
|
{
|
||||||
user.selectedElementIds = selectedElementIds;
|
username,
|
||||||
user.username = username;
|
pointer,
|
||||||
collaborators.set(socketId, user);
|
button,
|
||||||
|
selectedElementIds,
|
||||||
|
socketId,
|
||||||
|
},
|
||||||
|
this.collaborators,
|
||||||
|
);
|
||||||
this.excalidrawAPI.updateScene({
|
this.excalidrawAPI.updateScene({
|
||||||
collaborators,
|
collaborators: new Map(collaborators),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "IDLE_STATUS": {
|
case "IDLE_STATUS": {
|
||||||
const { userState, socketId, username } = decryptedData.payload;
|
const { userState, username, userId, socketId } =
|
||||||
const collaborators = new Map(this.collaborators);
|
decryptedData.payload;
|
||||||
const user = collaborators.get(socketId) || {}!;
|
const collaborators = upsertMap(
|
||||||
user.userState = userState;
|
userId,
|
||||||
user.username = username;
|
{
|
||||||
|
username,
|
||||||
|
userState,
|
||||||
|
userId,
|
||||||
|
socketId,
|
||||||
|
},
|
||||||
|
this.collaborators,
|
||||||
|
);
|
||||||
this.excalidrawAPI.updateScene({
|
this.excalidrawAPI.updateScene({
|
||||||
collaborators,
|
collaborators: new Map(collaborators),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "USER_JOINED": {
|
||||||
|
const { username, userId, socketId } = decryptedData.payload;
|
||||||
|
const collaborators = upsertMap(
|
||||||
|
userId,
|
||||||
|
{
|
||||||
|
username,
|
||||||
|
userId,
|
||||||
|
socketId,
|
||||||
|
},
|
||||||
|
this.collaborators,
|
||||||
|
);
|
||||||
|
this.excalidrawAPI.updateScene({
|
||||||
|
collaborators: new Map(collaborators),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -609,6 +781,15 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
} else {
|
} else {
|
||||||
this.portal.socketInitialized = true;
|
this.portal.socketInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.portal.socket) {
|
||||||
|
this.portal.brodcastUserJoinedRoom({
|
||||||
|
username: this.state.username,
|
||||||
|
userId: this.state.userId,
|
||||||
|
socketId: this.portal.socket.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -696,6 +877,10 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
window.clearInterval(this.activeIntervalId);
|
window.clearInterval(this.activeIntervalId);
|
||||||
this.activeIntervalId = null;
|
this.activeIntervalId = null;
|
||||||
}
|
}
|
||||||
|
this.pauseTimeoutId = window.setTimeout(
|
||||||
|
() => this.onPauseCollaborationChange(PauseCollaborationState.PAUSED),
|
||||||
|
PAUSE_COLLABORATION_TIMEOUT,
|
||||||
|
);
|
||||||
this.onIdleStateChange(UserIdleState.AWAY);
|
this.onIdleStateChange(UserIdleState.AWAY);
|
||||||
} else {
|
} else {
|
||||||
this.idleTimeoutId = window.setTimeout(this.reportIdle, IDLE_THRESHOLD);
|
this.idleTimeoutId = window.setTimeout(this.reportIdle, IDLE_THRESHOLD);
|
||||||
@ -704,6 +889,11 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
ACTIVE_THRESHOLD,
|
ACTIVE_THRESHOLD,
|
||||||
);
|
);
|
||||||
this.onIdleStateChange(UserIdleState.ACTIVE);
|
this.onIdleStateChange(UserIdleState.ACTIVE);
|
||||||
|
if (this.pauseTimeoutId) {
|
||||||
|
window.clearTimeout(this.pauseTimeoutId);
|
||||||
|
this.onPauseCollaborationChange(PauseCollaborationState.RESUMED);
|
||||||
|
this.pauseTimeoutId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -725,17 +915,12 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setCollaborators(sockets: string[]) {
|
setCollaborators(sockets: string[]) {
|
||||||
const collaborators: InstanceType<typeof Collab>["collaborators"] =
|
this.collaborators.forEach((value, key) => {
|
||||||
new Map();
|
if (value.socketId && !sockets.includes(value.socketId)) {
|
||||||
for (const socketId of sockets) {
|
this.collaborators.delete(key);
|
||||||
if (this.collaborators.has(socketId)) {
|
|
||||||
collaborators.set(socketId, this.collaborators.get(socketId)!);
|
|
||||||
} else {
|
|
||||||
collaborators.set(socketId, {});
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
this.collaborators = collaborators;
|
this.excalidrawAPI.updateScene({ collaborators: this.collaborators });
|
||||||
this.excalidrawAPI.updateScene({ collaborators });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLastBroadcastedOrReceivedSceneVersion = (version: number) => {
|
public setLastBroadcastedOrReceivedSceneVersion = (version: number) => {
|
||||||
@ -821,7 +1006,12 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
|
|
||||||
onUsernameChange = (username: string) => {
|
onUsernameChange = (username: string) => {
|
||||||
this.setUsername(username);
|
this.setUsername(username);
|
||||||
saveUsernameToLocalStorage(username);
|
saveUsernameAndIdToLocalStorage(username, this.state.userId);
|
||||||
|
};
|
||||||
|
|
||||||
|
onUserIdChange = (userId: string) => {
|
||||||
|
this.setState({ userId });
|
||||||
|
saveUsernameAndIdToLocalStorage(this.state.username, userId);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -37,6 +37,29 @@ class Portal {
|
|||||||
this.roomId = id;
|
this.roomId = id;
|
||||||
this.roomKey = key;
|
this.roomKey = key;
|
||||||
|
|
||||||
|
this.initializeSocketListeners();
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
if (!this.socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.queueFileUpload.flush();
|
||||||
|
this.socket.close();
|
||||||
|
this.socket = null;
|
||||||
|
this.roomId = null;
|
||||||
|
this.roomKey = null;
|
||||||
|
this.socketInitialized = false;
|
||||||
|
this.broadcastedElementVersions = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeSocketListeners() {
|
||||||
|
if (!this.socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize socket listeners
|
// Initialize socket listeners
|
||||||
this.socket.on("init-room", () => {
|
this.socket.on("init-room", () => {
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
@ -54,21 +77,6 @@ class Portal {
|
|||||||
this.socket.on("room-user-change", (clients: string[]) => {
|
this.socket.on("room-user-change", (clients: string[]) => {
|
||||||
this.collab.setCollaborators(clients);
|
this.collab.setCollaborators(clients);
|
||||||
});
|
});
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
if (!this.socket) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.queueFileUpload.flush();
|
|
||||||
this.socket.close();
|
|
||||||
this.socket = null;
|
|
||||||
this.roomId = null;
|
|
||||||
this.roomKey = null;
|
|
||||||
this.socketInitialized = false;
|
|
||||||
this.broadcastedElementVersions = new Map();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isOpen() {
|
isOpen() {
|
||||||
@ -181,13 +189,14 @@ class Portal {
|
|||||||
};
|
};
|
||||||
|
|
||||||
broadcastIdleChange = (userState: UserIdleState) => {
|
broadcastIdleChange = (userState: UserIdleState) => {
|
||||||
if (this.socket?.id) {
|
if (this.socket) {
|
||||||
const data: SocketUpdateDataSource["IDLE_STATUS"] = {
|
const data: SocketUpdateDataSource["IDLE_STATUS"] = {
|
||||||
type: "IDLE_STATUS",
|
type: "IDLE_STATUS",
|
||||||
payload: {
|
payload: {
|
||||||
socketId: this.socket.id,
|
|
||||||
userState,
|
userState,
|
||||||
username: this.collab.state.username,
|
username: this.collab.state.username,
|
||||||
|
userId: this.collab.state.userId,
|
||||||
|
socketId: this.socket.id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return this._broadcastSocketData(
|
return this._broadcastSocketData(
|
||||||
@ -201,16 +210,17 @@ class Portal {
|
|||||||
pointer: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointer"];
|
pointer: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["pointer"];
|
||||||
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
button: SocketUpdateDataSource["MOUSE_LOCATION"]["payload"]["button"];
|
||||||
}) => {
|
}) => {
|
||||||
if (this.socket?.id) {
|
if (this.socket) {
|
||||||
const data: SocketUpdateDataSource["MOUSE_LOCATION"] = {
|
const data: SocketUpdateDataSource["MOUSE_LOCATION"] = {
|
||||||
type: "MOUSE_LOCATION",
|
type: "MOUSE_LOCATION",
|
||||||
payload: {
|
payload: {
|
||||||
socketId: this.socket.id,
|
|
||||||
pointer: payload.pointer,
|
pointer: payload.pointer,
|
||||||
button: payload.button || "up",
|
button: payload.button || "up",
|
||||||
selectedElementIds:
|
selectedElementIds:
|
||||||
this.collab.excalidrawAPI.getAppState().selectedElementIds,
|
this.collab.excalidrawAPI.getAppState().selectedElementIds,
|
||||||
username: this.collab.state.username,
|
username: this.collab.state.username,
|
||||||
|
userId: this.collab.state.userId,
|
||||||
|
socketId: this.socket.id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return this._broadcastSocketData(
|
return this._broadcastSocketData(
|
||||||
@ -219,6 +229,23 @@ class Portal {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
brodcastUserJoinedRoom = (payload: {
|
||||||
|
username: string;
|
||||||
|
userId: string;
|
||||||
|
socketId: string;
|
||||||
|
}) => {
|
||||||
|
if (this.socket) {
|
||||||
|
const data: SocketUpdateDataSource["USER_JOINED"] = {
|
||||||
|
type: "USER_JOINED",
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
return this._broadcastSocketData(
|
||||||
|
data as SocketUpdateData,
|
||||||
|
false, // volatile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Portal;
|
export default Portal;
|
||||||
|
@ -106,19 +106,29 @@ export type SocketUpdateDataSource = {
|
|||||||
MOUSE_LOCATION: {
|
MOUSE_LOCATION: {
|
||||||
type: "MOUSE_LOCATION";
|
type: "MOUSE_LOCATION";
|
||||||
payload: {
|
payload: {
|
||||||
socketId: string;
|
|
||||||
pointer: { x: number; y: number };
|
pointer: { x: number; y: number };
|
||||||
button: "down" | "up";
|
button: "down" | "up";
|
||||||
selectedElementIds: AppState["selectedElementIds"];
|
selectedElementIds: AppState["selectedElementIds"];
|
||||||
username: string;
|
username: string;
|
||||||
|
userId: string;
|
||||||
|
socketId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
IDLE_STATUS: {
|
IDLE_STATUS: {
|
||||||
type: "IDLE_STATUS";
|
type: "IDLE_STATUS";
|
||||||
payload: {
|
payload: {
|
||||||
socketId: string;
|
|
||||||
userState: UserIdleState;
|
userState: UserIdleState;
|
||||||
username: string;
|
username: string;
|
||||||
|
userId: string;
|
||||||
|
socketId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
USER_JOINED: {
|
||||||
|
type: "USER_JOINED";
|
||||||
|
payload: {
|
||||||
|
username: string;
|
||||||
|
userId: string;
|
||||||
|
socketId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -8,11 +8,14 @@ import { clearElementsForLocalStorage } from "../../element";
|
|||||||
import { STORAGE_KEYS } from "../app_constants";
|
import { STORAGE_KEYS } from "../app_constants";
|
||||||
import { ImportedDataState } from "../../data/types";
|
import { ImportedDataState } from "../../data/types";
|
||||||
|
|
||||||
export const saveUsernameToLocalStorage = (username: string) => {
|
export const saveUsernameAndIdToLocalStorage = (
|
||||||
|
username: string,
|
||||||
|
userId: string,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
STORAGE_KEYS.LOCAL_STORAGE_COLLAB,
|
STORAGE_KEYS.LOCAL_STORAGE_COLLAB,
|
||||||
JSON.stringify({ username }),
|
JSON.stringify({ username, userId }),
|
||||||
);
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Unable to access window.localStorage
|
// Unable to access window.localStorage
|
||||||
@ -20,11 +23,14 @@ export const saveUsernameToLocalStorage = (username: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const importUsernameFromLocalStorage = (): string | null => {
|
export const importUsernameAndIdFromLocalStorage = (): {
|
||||||
|
username: string;
|
||||||
|
userId: string;
|
||||||
|
} | null => {
|
||||||
try {
|
try {
|
||||||
const data = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_COLLAB);
|
const data = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_COLLAB);
|
||||||
if (data) {
|
if (data) {
|
||||||
return JSON.parse(data).username;
|
return JSON.parse(data);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Unable to access localStorage
|
// Unable to access localStorage
|
||||||
|
@ -65,7 +65,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
getLibraryItemsFromStorage,
|
getLibraryItemsFromStorage,
|
||||||
importFromLocalStorage,
|
importFromLocalStorage,
|
||||||
importUsernameFromLocalStorage,
|
importUsernameAndIdFromLocalStorage,
|
||||||
} from "./data/localStorage";
|
} from "./data/localStorage";
|
||||||
import CustomStats from "./CustomStats";
|
import CustomStats from "./CustomStats";
|
||||||
import { restore, restoreAppState, RestoredDataState } from "../data/restore";
|
import { restore, restoreAppState, RestoredDataState } from "../data/restore";
|
||||||
@ -411,7 +411,8 @@ const ExcalidrawWrapper = () => {
|
|||||||
// don't sync if local state is newer or identical to browser state
|
// don't sync if local state is newer or identical to browser state
|
||||||
if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) {
|
if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) {
|
||||||
const localDataState = importFromLocalStorage();
|
const localDataState = importFromLocalStorage();
|
||||||
const username = importUsernameFromLocalStorage();
|
const username =
|
||||||
|
importUsernameAndIdFromLocalStorage()?.username ?? "";
|
||||||
let langCode = languageDetector.detect() || defaultLang.code;
|
let langCode = languageDetector.detect() || defaultLang.code;
|
||||||
if (Array.isArray(langCode)) {
|
if (Array.isArray(langCode)) {
|
||||||
langCode = langCode[0];
|
langCode = langCode[0];
|
||||||
|
@ -411,7 +411,8 @@
|
|||||||
"fileSavedToFilename": "Saved to {filename}",
|
"fileSavedToFilename": "Saved to {filename}",
|
||||||
"canvas": "canvas",
|
"canvas": "canvas",
|
||||||
"selection": "selection",
|
"selection": "selection",
|
||||||
"pasteAsSingleElement": "Use {{shortcut}} to paste as a single element,\nor paste into an existing text editor"
|
"pasteAsSingleElement": "Use {{shortcut}} to paste as a single element,\nor paste into an existing text editor",
|
||||||
|
"reconnectRoomServer": "Reconnecting to server"
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
"transparent": "Transparent",
|
"transparent": "Transparent",
|
||||||
|
@ -619,8 +619,8 @@ export const _renderScene = ({
|
|||||||
if (renderConfig.remoteSelectedElementIds[element.id]) {
|
if (renderConfig.remoteSelectedElementIds[element.id]) {
|
||||||
selectionColors.push(
|
selectionColors.push(
|
||||||
...renderConfig.remoteSelectedElementIds[element.id].map(
|
...renderConfig.remoteSelectedElementIds[element.id].map(
|
||||||
(socketId) => {
|
(userId) => {
|
||||||
const background = getClientColor(socketId);
|
const background = getClientColor(userId);
|
||||||
return background;
|
return background;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -55,6 +55,7 @@ export type Collaborator = {
|
|||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
// user id. If supplied, we'll filter out duplicates when rendering user avatars.
|
// user id. If supplied, we'll filter out duplicates when rendering user avatars.
|
||||||
id?: string;
|
id?: string;
|
||||||
|
socketId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DataURL = string & { _brand: "DataURL" };
|
export type DataURL = string & { _brand: "DataURL" };
|
||||||
@ -376,6 +377,12 @@ export enum UserIdleState {
|
|||||||
IDLE = "idle",
|
IDLE = "idle",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PauseCollaborationState {
|
||||||
|
PAUSED = "paused",
|
||||||
|
RESUMED = "resumed",
|
||||||
|
SYNCED = "synced",
|
||||||
|
}
|
||||||
|
|
||||||
export type ExportOpts = {
|
export type ExportOpts = {
|
||||||
saveFileToDisk?: boolean;
|
saveFileToDisk?: boolean;
|
||||||
onExportToBackend?: (
|
onExportToBackend?: (
|
||||||
|
11
src/utils.ts
11
src/utils.ts
@ -907,3 +907,14 @@ export const isOnlyExportingSingleFrame = (
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const upsertMap = <T>(key: T, value: object, map: Map<T, object>) => {
|
||||||
|
if (!map.has(key)) {
|
||||||
|
map.set(key, value);
|
||||||
|
} else {
|
||||||
|
const old = map.get(key);
|
||||||
|
map.set(key, { ...old, ...value });
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
};
|
||||||
|
193
yarn.lock
193
yarn.lock
@ -2329,6 +2329,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@sinonjs/commons" "^1.7.0"
|
"@sinonjs/commons" "^1.7.0"
|
||||||
|
|
||||||
|
"@socket.io/component-emitter@~3.1.0":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||||
|
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||||
|
|
||||||
"@surma/rollup-plugin-off-main-thread@^2.2.3":
|
"@surma/rollup-plugin-off-main-thread@^2.2.3":
|
||||||
version "2.2.3"
|
version "2.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
|
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
|
||||||
@ -3168,11 +3173,6 @@ adjust-sourcemap-loader@^4.0.0:
|
|||||||
loader-utils "^2.0.0"
|
loader-utils "^2.0.0"
|
||||||
regex-parser "^2.2.11"
|
regex-parser "^2.2.11"
|
||||||
|
|
||||||
after@0.8.2:
|
|
||||||
version "0.8.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
|
|
||||||
integrity sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==
|
|
||||||
|
|
||||||
agent-base@6:
|
agent-base@6:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||||
@ -3398,11 +3398,6 @@ array.prototype.tosorted@^1.1.1:
|
|||||||
es-shim-unscopables "^1.0.0"
|
es-shim-unscopables "^1.0.0"
|
||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.1.3"
|
||||||
|
|
||||||
arraybuffer.slice@~0.0.7:
|
|
||||||
version "0.0.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
|
|
||||||
integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==
|
|
||||||
|
|
||||||
asap@~2.0.6:
|
asap@~2.0.6:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||||
@ -3423,11 +3418,6 @@ astral-regex@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
|
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
|
||||||
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
|
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
|
||||||
|
|
||||||
async-limiter@~1.0.0:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
|
||||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
|
||||||
|
|
||||||
async@^2.6.4:
|
async@^2.6.4:
|
||||||
version "2.6.4"
|
version "2.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
|
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
|
||||||
@ -3620,11 +3610,6 @@ babel-preset-react-app@^10.0.1:
|
|||||||
babel-plugin-macros "^3.1.0"
|
babel-plugin-macros "^3.1.0"
|
||||||
babel-plugin-transform-react-remove-prop-types "^0.4.24"
|
babel-plugin-transform-react-remove-prop-types "^0.4.24"
|
||||||
|
|
||||||
backo2@1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
|
||||||
integrity sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==
|
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
@ -3635,11 +3620,6 @@ base64-arraybuffer-es6@^0.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86"
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86"
|
||||||
integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==
|
integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==
|
||||||
|
|
||||||
base64-arraybuffer@0.1.4:
|
|
||||||
version "0.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
|
|
||||||
integrity sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==
|
|
||||||
|
|
||||||
basic-auth@^2.0.1:
|
basic-auth@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
|
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
|
||||||
@ -3672,11 +3652,6 @@ binary-extensions@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||||
|
|
||||||
blob@0.0.5:
|
|
||||||
version "0.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
|
|
||||||
integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==
|
|
||||||
|
|
||||||
bluebird@^3.5.5:
|
bluebird@^3.5.5:
|
||||||
version "3.7.2"
|
version "3.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||||
@ -4077,21 +4052,6 @@ commondir@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||||
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
|
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
|
||||||
|
|
||||||
component-bind@1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
|
|
||||||
integrity sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==
|
|
||||||
|
|
||||||
component-emitter@~1.3.0:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
|
||||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
|
||||||
|
|
||||||
component-inherit@0.0.3:
|
|
||||||
version "0.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
|
|
||||||
integrity sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==
|
|
||||||
|
|
||||||
compressible@~2.0.16:
|
compressible@~2.0.16:
|
||||||
version "2.0.18"
|
version "2.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
||||||
@ -4464,7 +4424,7 @@ debug@2.6.9, debug@^2.6.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
@ -4478,13 +4438,6 @@ debug@^3.2.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@~3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
|
||||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
|
||||||
dependencies:
|
|
||||||
ms "2.0.0"
|
|
||||||
|
|
||||||
decimal.js@^10.2.1:
|
decimal.js@^10.2.1:
|
||||||
version "10.4.3"
|
version "10.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
|
||||||
@ -4818,33 +4771,21 @@ encodeurl@~1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||||
|
|
||||||
engine.io-client@~3.4.0:
|
engine.io-client@~6.4.0:
|
||||||
version "3.4.4"
|
version "6.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.4.tgz#77d8003f502b0782dd792b073a4d2cf7ca5ab967"
|
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.4.0.tgz#88cd3082609ca86d7d3c12f0e746d12db4f47c91"
|
||||||
integrity sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==
|
integrity sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==
|
||||||
dependencies:
|
dependencies:
|
||||||
component-emitter "~1.3.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
component-inherit "0.0.3"
|
debug "~4.3.1"
|
||||||
debug "~3.1.0"
|
engine.io-parser "~5.0.3"
|
||||||
engine.io-parser "~2.2.0"
|
ws "~8.11.0"
|
||||||
has-cors "1.1.0"
|
xmlhttprequest-ssl "~2.0.0"
|
||||||
indexof "0.0.1"
|
|
||||||
parseqs "0.0.6"
|
|
||||||
parseuri "0.0.6"
|
|
||||||
ws "~6.1.0"
|
|
||||||
xmlhttprequest-ssl "~1.5.4"
|
|
||||||
yeast "0.1.2"
|
|
||||||
|
|
||||||
engine.io-parser@~2.2.0:
|
engine.io-parser@~5.0.3:
|
||||||
version "2.2.1"
|
version "5.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7"
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.7.tgz#ed5eae76c71f398284c578ab6deafd3ba7e4e4f6"
|
||||||
integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==
|
integrity sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==
|
||||||
dependencies:
|
|
||||||
after "0.8.2"
|
|
||||||
arraybuffer.slice "~0.0.7"
|
|
||||||
base64-arraybuffer "0.1.4"
|
|
||||||
blob "0.0.5"
|
|
||||||
has-binary2 "~1.0.2"
|
|
||||||
|
|
||||||
enhanced-resolve@^5.10.0:
|
enhanced-resolve@^5.10.0:
|
||||||
version "5.12.0"
|
version "5.12.0"
|
||||||
@ -5915,18 +5856,6 @@ has-bigints@^1.0.1, has-bigints@^1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||||
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
|
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
|
||||||
|
|
||||||
has-binary2@~1.0.2:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
|
|
||||||
integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==
|
|
||||||
dependencies:
|
|
||||||
isarray "2.0.1"
|
|
||||||
|
|
||||||
has-cors@1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
|
|
||||||
integrity sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==
|
|
||||||
|
|
||||||
has-flag@^3.0.0:
|
has-flag@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||||
@ -6251,11 +6180,6 @@ indent-string@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
||||||
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
||||||
|
|
||||||
indexof@0.0.1:
|
|
||||||
version "0.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
|
|
||||||
integrity sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==
|
|
||||||
|
|
||||||
inflight@^1.0.4:
|
inflight@^1.0.4:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||||
@ -6539,11 +6463,6 @@ is-wsl@^2.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-docker "^2.0.0"
|
is-docker "^2.0.0"
|
||||||
|
|
||||||
isarray@2.0.1:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
|
|
||||||
integrity sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==
|
|
||||||
|
|
||||||
isarray@^2.0.5:
|
isarray@^2.0.5:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||||
@ -8084,16 +8003,6 @@ parse5@6.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||||
|
|
||||||
parseqs@0.0.6:
|
|
||||||
version "0.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5"
|
|
||||||
integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==
|
|
||||||
|
|
||||||
parseuri@0.0.6:
|
|
||||||
version "0.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a"
|
|
||||||
integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==
|
|
||||||
|
|
||||||
parseurl@~1.3.2, parseurl@~1.3.3:
|
parseurl@~1.3.2, parseurl@~1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
@ -9767,31 +9676,23 @@ sliced@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
|
resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
|
||||||
integrity sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==
|
integrity sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==
|
||||||
|
|
||||||
socket.io-client@2.3.1:
|
socket.io-client@4.6.1:
|
||||||
version "2.3.1"
|
version "4.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.1.tgz#91a4038ef4d03c19967bb3c646fec6e0eaa78cff"
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.6.1.tgz#80d97d5eb0feca448a0fb6d69a7b222d3d547eab"
|
||||||
integrity sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ==
|
integrity sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
backo2 "1.0.2"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
component-bind "1.0.0"
|
debug "~4.3.2"
|
||||||
component-emitter "~1.3.0"
|
engine.io-client "~6.4.0"
|
||||||
debug "~3.1.0"
|
socket.io-parser "~4.2.1"
|
||||||
engine.io-client "~3.4.0"
|
|
||||||
has-binary2 "~1.0.2"
|
|
||||||
indexof "0.0.1"
|
|
||||||
parseqs "0.0.6"
|
|
||||||
parseuri "0.0.6"
|
|
||||||
socket.io-parser "~3.3.0"
|
|
||||||
to-array "0.1.4"
|
|
||||||
|
|
||||||
socket.io-parser@~3.3.0:
|
socket.io-parser@~4.2.1:
|
||||||
version "3.3.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.3.tgz#3a8b84823eba87f3f7624e64a8aaab6d6318a72f"
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.3.tgz#926bcc6658e2ae0883dc9dee69acbdc76e4e3667"
|
||||||
integrity sha512-qOg87q1PMWWTeO01768Yh9ogn7chB9zkKtQnya41Y355S0UmpXgpcrFwAgjYJxu9BdKug5r5e9YtVSeWhKBUZg==
|
integrity sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
component-emitter "~1.3.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~3.1.0"
|
debug "~4.3.1"
|
||||||
isarray "2.0.1"
|
|
||||||
|
|
||||||
sockjs@^0.3.24:
|
sockjs@^0.3.24:
|
||||||
version "0.3.24"
|
version "0.3.24"
|
||||||
@ -10325,11 +10226,6 @@ tmpl@1.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
|
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
|
||||||
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
|
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
|
||||||
|
|
||||||
to-array@0.1.4:
|
|
||||||
version "0.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
|
|
||||||
integrity sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==
|
|
||||||
|
|
||||||
to-fast-properties@^2.0.0:
|
to-fast-properties@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||||
@ -11153,12 +11049,10 @@ ws@^8.13.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
|
||||||
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
|
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
|
||||||
|
|
||||||
ws@~6.1.0:
|
ws@~8.11.0:
|
||||||
version "6.1.4"
|
version "8.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||||
integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==
|
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||||
dependencies:
|
|
||||||
async-limiter "~1.0.0"
|
|
||||||
|
|
||||||
xml-name-validator@^3.0.0:
|
xml-name-validator@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
@ -11170,10 +11064,10 @@ xmlchars@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||||
|
|
||||||
xmlhttprequest-ssl@~1.5.4:
|
xmlhttprequest-ssl@~2.0.0:
|
||||||
version "1.5.5"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||||
integrity sha512-/bFPLUgJrfGUL10AIv4Y7/CUt6so9CLtB/oFxQSHseSDNNCdC6vwwKEqwLN6wNPBg9YWXAiMu8jkf6RPRS/75Q==
|
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
||||||
|
|
||||||
xmlhttprequest@1.8.0:
|
xmlhttprequest@1.8.0:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
@ -11218,11 +11112,6 @@ yargs@^16.2.0:
|
|||||||
y18n "^5.0.5"
|
y18n "^5.0.5"
|
||||||
yargs-parser "^20.2.2"
|
yargs-parser "^20.2.2"
|
||||||
|
|
||||||
yeast@0.1.2:
|
|
||||||
version "0.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
|
|
||||||
integrity sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==
|
|
||||||
|
|
||||||
yocto-queue@^0.1.0:
|
yocto-queue@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user