mirror of
https://github.com/outline/outline.git
synced 2026-08-03 13:27:25 +03:00
chore: Upgrade to Typescript 6 (#12737)
* chore: Upgrade TypeScript to 6.0
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
UnsubscribeIcon,
|
||||
} from "outline-icons";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Collection from "~/models/Collection";
|
||||
import { CollectionEdit } from "~/components/Collection/CollectionEdit";
|
||||
import { CollectionNew } from "~/components/Collection/CollectionNew";
|
||||
@@ -173,7 +174,7 @@ export const importDocument = createAction({
|
||||
});
|
||||
history.push(document.path);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
toast.dismiss(toastId);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
OpenIcon,
|
||||
} from "outline-icons";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Icon from "@shared/components/Icon";
|
||||
import type { NavigationNode } from "@shared/types";
|
||||
import { ExportContentType, TeamPreference } from "@shared/types";
|
||||
@@ -1123,7 +1124,7 @@ export const importDocument = createAction({
|
||||
);
|
||||
history.push(document.url);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
toast.dismiss(toastId);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import copy from "copy-to-clipboard";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type Share from "~/models/Share";
|
||||
import { createAction, createInternalLinkAction } from "..";
|
||||
import { ArrowIcon, CopyIcon, TrashIcon } from "outline-icons";
|
||||
@@ -53,7 +54,7 @@ export const revokeShareFactory = ({
|
||||
await stores.shares.revoke(share);
|
||||
toast.message(t("Share link revoked"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { useCallback } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import type { FormData } from "./CollectionForm";
|
||||
import { CollectionForm } from "./CollectionForm";
|
||||
@@ -23,7 +24,7 @@ export const CollectionEdit = observer(function CollectionEdit_({
|
||||
await collection?.save(data);
|
||||
onSubmit?.();
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
}
|
||||
},
|
||||
[collection, onSubmit]
|
||||
|
||||
@@ -2,6 +2,7 @@ import { runInAction } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useCallback } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import history from "~/utils/history";
|
||||
import type { FormData } from "./CollectionForm";
|
||||
@@ -26,7 +27,7 @@ export const CollectionNew = observer(function CollectionNew_({
|
||||
onSubmit?.();
|
||||
history.push(collection.path);
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
}
|
||||
},
|
||||
[collections, onSubmit]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type Comment from "~/models/Comment";
|
||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||
import Text from "~/components/Text";
|
||||
@@ -21,7 +22,7 @@ function CommentDeleteDialog({ comment, onSubmit }: Props) {
|
||||
await comment.delete();
|
||||
onSubmit?.();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type { NavigationNode } from "@shared/types";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import type Collection from "~/models/Collection";
|
||||
@@ -50,7 +51,7 @@ function ConfirmMoveDialog({ collection, item, ...rest }: Props) {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
} finally {
|
||||
dialogs.closeAllModals();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import Text from "~/components/Text";
|
||||
@@ -44,7 +45,7 @@ const ConfirmationDialog: React.FC<Props> = ({
|
||||
}
|
||||
dialogs.closeAllModals();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { getLuminance } from "polished";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import Icon from "@shared/components/Icon";
|
||||
import { colorPalette } from "@shared/utils/collections";
|
||||
import type { Option } from "~/components/InputSelect";
|
||||
@@ -27,7 +28,7 @@ const DefaultCollectionInputSelect = observer(
|
||||
const { t } = useTranslation();
|
||||
const { collections, ui } = useStores();
|
||||
const [fetching, setFetching] = useState(false);
|
||||
const [fetchError, setFetchError] = useState();
|
||||
const [fetchError, setFetchError] = useState<Error>();
|
||||
|
||||
React.useEffect(() => {
|
||||
async function fetchData() {
|
||||
@@ -41,7 +42,7 @@ const DefaultCollectionInputSelect = observer(
|
||||
toast.error(
|
||||
t("Collections could not be loaded, please reload the app")
|
||||
);
|
||||
setFetchError(error);
|
||||
setFetchError(toError(error));
|
||||
} finally {
|
||||
setFetching(false);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { s, ellipsis } from "@shared/styles";
|
||||
import EventBoundary from "@shared/components/EventBoundary";
|
||||
|
||||
@@ -109,7 +110,7 @@ function EditableTitle(
|
||||
setValue(value);
|
||||
setIsEditing(true);
|
||||
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
throw error;
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
||||
import { useCallback } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { settingsPath } from "~/utils/routeHelpers";
|
||||
import type { FormData } from "./OAuthClientForm";
|
||||
@@ -24,7 +25,7 @@ export const OAuthClientNew = observer(function OAuthClientNew_({
|
||||
onSubmit?.();
|
||||
history.push(settingsPath("applications", oauthClient.id));
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
}
|
||||
},
|
||||
[oauthClients, history, onSubmit]
|
||||
|
||||
@@ -2,6 +2,7 @@ import { isEqual } from "es-toolkit/compat";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Waypoint } from "react-waypoint";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { Pagination } from "@shared/constants";
|
||||
import ArrowKeyNavigation from "~/components/ArrowKeyNavigation";
|
||||
import DelayedMount from "~/components/DelayedMount";
|
||||
@@ -176,7 +177,7 @@ const PaginatedList = <T extends PaginatedItem>({
|
||||
|
||||
setIsFetchingInitial(false);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
setError(toError(err));
|
||||
} finally {
|
||||
// only the most recent fetch should end the loading state
|
||||
if (counter >= fetchCounter) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Squircle from "@shared/components/Squircle";
|
||||
import { s } from "@shared/styles";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
@@ -186,7 +187,7 @@ export const AccessControlList = observer(
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -240,7 +241,7 @@ export const AccessControlList = observer(
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Squircle from "@shared/components/Squircle";
|
||||
import { UrlHelper } from "@shared/utils/UrlHelper";
|
||||
import type Collection from "~/models/Collection";
|
||||
@@ -71,7 +72,7 @@ function InnerPublicAccess(
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
@@ -100,7 +101,8 @@ function InnerPublicAccess(
|
||||
urlId: isEmpty(val) ? null : val,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message.includes("must be unique")) {
|
||||
const message = errToString(err);
|
||||
if (message.includes("must be unique")) {
|
||||
setValidationError(t("Sorry, this link has already been used"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Flex from "@shared/components/Flex";
|
||||
import Squircle from "@shared/components/Squircle";
|
||||
import { UrlHelper } from "@shared/utils/UrlHelper";
|
||||
@@ -81,7 +82,7 @@ function PublicAccess(
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
@@ -110,7 +111,8 @@ function PublicAccess(
|
||||
urlId: isEmpty(val) ? null : val,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message.includes("must be unique")) {
|
||||
const message = errToString(err);
|
||||
if (message.includes("must be unique")) {
|
||||
setValidationError(t("Sorry, this link has already been used"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { s } from "@shared/styles";
|
||||
import { HStack } from "~/components/primitives/HStack";
|
||||
import { AttachmentPreset } from "@shared/types";
|
||||
@@ -70,7 +71,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ title: val || null });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}, 500),
|
||||
[share]
|
||||
@@ -100,7 +101,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ iconUrl: attachment.url });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
if (fileInputRef.current) {
|
||||
@@ -116,7 +117,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ iconUrl: null });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}, [share]);
|
||||
|
||||
@@ -126,7 +127,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ allowIndexing: checked });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[share]
|
||||
@@ -138,7 +139,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ allowSubscriptions: checked });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[share]
|
||||
@@ -150,7 +151,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ showLastUpdated: checked });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[share]
|
||||
@@ -162,7 +163,7 @@ function ShareSettingsPopover({ share, children }: Props) {
|
||||
await share.save({ showTOC: checked });
|
||||
hasChangesRef.current = true;
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[share]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import Badge from "~/components/Badge";
|
||||
import { client } from "~/utils/ApiClient";
|
||||
import Logger from "~/utils/Logger";
|
||||
@@ -20,7 +21,7 @@ export default function Version() {
|
||||
setVersionsBehind(res.data.versionsBehind);
|
||||
}
|
||||
} catch (error) {
|
||||
Logger.error("Failed to load version info", error);
|
||||
Logger.error("Failed to load version info", toError(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useDrag, useDrop } from "react-dnd";
|
||||
import { getEmptyImage } from "react-dnd-html5-backend";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Icon from "@shared/components/Icon";
|
||||
import type { NavigationNode } from "@shared/types";
|
||||
import type Collection from "~/models/Collection";
|
||||
@@ -275,7 +276,7 @@ export function useDropToChangeCollection(
|
||||
)
|
||||
);
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,7 +371,7 @@ export function useDropToReparentDocument(
|
||||
)
|
||||
);
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -483,7 +484,7 @@ export function useDropToReorderDocument(
|
||||
})
|
||||
);
|
||||
} else {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,7 +621,7 @@ export function useDropToUnpublish() {
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
canDrop: (item) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { UserRole } from "@shared/types";
|
||||
import { UserValidation } from "@shared/validations";
|
||||
import type User from "~/models/User";
|
||||
@@ -157,7 +158,7 @@ export const UserChangeAvatarDialog = observer(function UserChangeAvatarDialog({
|
||||
await user.save({ avatarUrl });
|
||||
toast.success(t("Profile picture updated"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -205,7 +206,7 @@ export function UserChangeEmailDialog({ user, onSubmit }: Props) {
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
setError(errToString(err));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
FileOperationType,
|
||||
ImportState,
|
||||
} from "@shared/types";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import type RootStore from "~/stores/RootStore";
|
||||
import type Collection from "~/models/Collection";
|
||||
import type Comment from "~/models/Comment";
|
||||
@@ -340,7 +341,7 @@ function useDocumentHandlers() {
|
||||
force: event.userId === currentUserId,
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch document after add_user", err);
|
||||
Logger.error("Failed to fetch document after add_user", toError(err));
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -439,7 +440,10 @@ function useCollectionHandlers() {
|
||||
try {
|
||||
await collections.fetch(collectionId, { force: true });
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch collection after archive", err);
|
||||
Logger.error(
|
||||
"Failed to fetch collection after archive",
|
||||
toError(err)
|
||||
);
|
||||
}
|
||||
|
||||
documents.unarchivedInCollection(collectionId).forEach(
|
||||
@@ -475,7 +479,10 @@ function useCollectionHandlers() {
|
||||
try {
|
||||
await collections.fetch(collectionId, { force: true });
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch collection after restore", err);
|
||||
Logger.error(
|
||||
"Failed to fetch collection after restore",
|
||||
toError(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -487,7 +494,7 @@ function useCollectionHandlers() {
|
||||
force: event.userId === currentUserId,
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch collection after add_user", err);
|
||||
Logger.error("Failed to fetch collection after add_user", toError(err));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -505,7 +512,10 @@ function useCollectionHandlers() {
|
||||
try {
|
||||
await collections.fetch(event.collectionId!);
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch collection after add_group", err);
|
||||
Logger.error(
|
||||
"Failed to fetch collection after add_group",
|
||||
toError(err)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -637,7 +647,10 @@ function useUserHandlers() {
|
||||
try {
|
||||
await collections.fetchAll();
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch collections after demote", err);
|
||||
Logger.error(
|
||||
"Failed to fetch collections after demote",
|
||||
toError(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -68,7 +68,7 @@ function usePosition({
|
||||
fromPos = view.coordsAtPos(selection.from);
|
||||
toPos = view.coordsAtPos(selection.to, -1);
|
||||
} catch (err) {
|
||||
Logger.warn("Unable to calculate selection position", err);
|
||||
Logger.warn("Unable to calculate selection position", { err });
|
||||
return defaultPosition;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
const right = Math.max(fromPos.right, toPos.right);
|
||||
return new DOMRect(left, top, right - left, bottom - top);
|
||||
} catch (err) {
|
||||
Logger.warn("Unable to calculate caret position", err);
|
||||
Logger.warn("Unable to calculate caret position", { err });
|
||||
return new DOMRect();
|
||||
}
|
||||
}, [props.isActive, view]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { find } from "es-toolkit/compat";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import embeds from "@shared/editor/embeds";
|
||||
import { IntegrationType, TeamPreference } from "@shared/types";
|
||||
import type Integration from "~/models/Integration";
|
||||
@@ -24,7 +25,7 @@ export default function useEmbeds(loadIfMissing = false) {
|
||||
type: IntegrationType.Embed,
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch embed integrations", err);
|
||||
Logger.error("Failed to fetch embed integrations", toError(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useState, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { useSidebarContext } from "~/components/Sidebar/components/SidebarContext";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { documentPath } from "~/utils/routeHelpers";
|
||||
@@ -61,13 +62,13 @@ export default function useImportDocument(
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
toast.dismiss(toastId);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(`${t("Could not import file")}. ${err.message}`);
|
||||
toast.error(`${t("Could not import file")}. ${errToString(err)}`);
|
||||
} finally {
|
||||
setImporting(false);
|
||||
importingLock = false;
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function useRequest<T = unknown>(
|
||||
const [data, setData] = useState<T>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
const [error, setError] = useState();
|
||||
const [error, setError] = useState<unknown>();
|
||||
|
||||
const request = useCallback(async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -110,7 +110,9 @@ export function useUserMenuActions(targetUser: User | null) {
|
||||
toast.success(t(`Invite was resent to ${targetUser.name}`));
|
||||
} catch (err) {
|
||||
toast.error(
|
||||
err.message ?? t(`An error occurred while sending the invite`)
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: t(`An error occurred while sending the invite`)
|
||||
);
|
||||
}
|
||||
}, [users, targetUser, t]);
|
||||
|
||||
@@ -153,7 +153,7 @@ export default abstract class Model {
|
||||
// @ts-expect-error TODO
|
||||
this[key] = data[key];
|
||||
} catch (error) {
|
||||
Logger.warn(`Error setting ${key} on model`, error);
|
||||
Logger.warn(`Error setting ${key} on model`, { error });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import invariant from "invariant";
|
||||
import { singular } from "pluralize";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import type Model from "../base/Model";
|
||||
import Logger from "~/utils/Logger";
|
||||
|
||||
@@ -61,7 +62,7 @@ export const getInverseRelationsForModelClass = (targetClass: typeof Model) => {
|
||||
} catch (error) {
|
||||
Logger.error(
|
||||
`Error resolving relation ${modelName}.${propertyName} for target ${targetClass.modelName}:`,
|
||||
error
|
||||
toError(error)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { ApiKeyValidation } from "@shared/validations";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
@@ -85,7 +86,7 @@ function ApiKeyNew({ onSubmit }: Props) {
|
||||
);
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Redirect,
|
||||
} from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { s } from "@shared/styles";
|
||||
import { StatusFilter } from "@shared/types";
|
||||
import type Collection from "~/models/Collection";
|
||||
@@ -109,7 +110,7 @@ const CollectionScene = observer(function CollectionScene_() {
|
||||
setError(undefined);
|
||||
await collections.fetch(id);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
setError(toError(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import type { RouteComponentProps, StaticContext } from "react-router";
|
||||
import { Redirect, useLocation } from "react-router";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { TeamPreference } from "@shared/types";
|
||||
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
|
||||
import { RevisionHelper } from "@shared/utils/RevisionHelper";
|
||||
@@ -105,7 +106,7 @@ function DataLoader({ match, children }: Props) {
|
||||
force: missingPolicy,
|
||||
});
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
setError(toError(err));
|
||||
}
|
||||
}
|
||||
void fetchDocument();
|
||||
@@ -152,7 +153,7 @@ function DataLoader({ match, children }: Props) {
|
||||
documentId: document.id,
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.error("Failed to fetch views", err);
|
||||
Logger.error("Failed to fetch views", toError(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AllSelection, TextSelection } from "prosemirror-state";
|
||||
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
|
||||
import { TextHelper } from "@shared/utils/TextHelper";
|
||||
import type Document from "~/models/Document";
|
||||
@@ -182,7 +183,7 @@ export function useDocumentSave({
|
||||
ui.setActiveDocument(savedDocument);
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
setIsPublishing(false);
|
||||
@@ -341,7 +342,8 @@ export function useDocumentSave({
|
||||
() => () => {
|
||||
autosave.cancel();
|
||||
const currentDoc = editorRef.current?.view.state.doc;
|
||||
const isEditorEmpty = !currentDoc || ProsemirrorHelper.isEmpty(currentDoc);
|
||||
const isEditorEmpty =
|
||||
!currentDoc || ProsemirrorHelper.isEmpty(currentDoc);
|
||||
|
||||
if (
|
||||
shouldAutoDeleteDraftOnUnmount({
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type Document from "~/models/Document";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
@@ -64,7 +65,7 @@ function DocumentDelete({ document, onSubmit }: Props) {
|
||||
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setDeleting(false);
|
||||
}
|
||||
@@ -90,7 +91,7 @@ function DocumentDelete({ document, onSubmit }: Props) {
|
||||
await document.archive();
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setArchiving(false);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Empty from "~/components/Empty";
|
||||
import Heading from "~/components/Heading";
|
||||
@@ -57,7 +58,7 @@ const Error403 = ({ documentId }: Props) => {
|
||||
setRequested(true);
|
||||
toast.success(t("Access request sent"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setRequesting(false);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useTranslation, Trans } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { UserRole } from "@shared/types";
|
||||
import { parseEmail } from "@shared/utils/email";
|
||||
import { UserValidation } from "@shared/validations";
|
||||
@@ -66,7 +67,7 @@ function Invite({ onSubmit }: Props) {
|
||||
toast.message(t("Those email addresses are already invited"));
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { Client } from "@shared/types";
|
||||
import ButtonLarge from "~/components/ButtonLarge";
|
||||
import InputLarge from "~/components/InputLarge";
|
||||
@@ -122,7 +123,7 @@ function AuthenticationProvider(props: Props) {
|
||||
// Submit form natively to let browser handle redirect and cookies
|
||||
formRef.current?.submit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { OAuthClientValidation } from "@shared/validations";
|
||||
import type OAuthClient from "~/models/oauth/OAuthClient";
|
||||
import Breadcrumb from "~/components/Breadcrumb";
|
||||
@@ -103,7 +104,7 @@ const Application = observer(function Application({ oauthClient }: Props) {
|
||||
: t("Application updated")
|
||||
);
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
}
|
||||
},
|
||||
[oauthClient, t]
|
||||
@@ -115,7 +116,7 @@ const Application = observer(function Application({ oauthClient }: Props) {
|
||||
await oauthClient.rotateClientSecret();
|
||||
toast.success(t("Client secret rotated"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { EmailIcon, PadlockIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||
import Flex from "~/components/Flex";
|
||||
import Heading from "~/components/Heading";
|
||||
@@ -52,7 +53,7 @@ function Authentication() {
|
||||
await team.save({ guestSignin: checked });
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[team, t]
|
||||
@@ -64,7 +65,7 @@ function Authentication() {
|
||||
await provider.save({ isEnabled });
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[t]
|
||||
@@ -114,7 +115,7 @@ function Authentication() {
|
||||
});
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
@@ -143,7 +144,7 @@ function Authentication() {
|
||||
});
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[t]
|
||||
@@ -308,7 +309,7 @@ function Authentication() {
|
||||
await team.save({ passkeysEnabled: checked });
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -370,7 +371,7 @@ const DisableGroupSyncDialog = observer(function DisableGroupSyncDialog({
|
||||
toast.success(t("Settings saved"));
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { ThemeProvider, useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { buildDarkTheme, buildLightTheme } from "@shared/styles/theme";
|
||||
import type { CustomTheme } from "@shared/types";
|
||||
import { TOCPosition, TeamPreference } from "@shared/types";
|
||||
@@ -110,7 +111,7 @@ function Details() {
|
||||
});
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ProfileIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Heading from "~/components/Heading";
|
||||
import Input from "~/components/Input";
|
||||
@@ -30,7 +31,7 @@ const Profile = () => {
|
||||
await user.save({ name });
|
||||
toast.success(t("Profile saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useState } from "react";
|
||||
import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { TeamPreference, EmailDisplay } from "@shared/types";
|
||||
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||
import Heading from "~/components/Heading";
|
||||
@@ -86,7 +87,7 @@ function Security() {
|
||||
await team.save(newData);
|
||||
showSuccessMessage();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[team, showSuccessMessage]
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useEffect, useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
|
||||
import { Action } from "~/components/Actions";
|
||||
import Breadcrumb from "~/components/Breadcrumb";
|
||||
@@ -101,7 +102,7 @@ const TemplateSetting = observer(function Template_({ template }: Props) {
|
||||
await template.save();
|
||||
history.push(settingsPath("templates"));
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ShapesIcon } from "outline-icons";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
|
||||
import Template from "~/models/Template";
|
||||
import { Action } from "~/components/Actions";
|
||||
@@ -63,7 +64,7 @@ function TemplateNewScene() {
|
||||
await template.save();
|
||||
history.push(settingsPath("templates"));
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import Input from "~/components/Input";
|
||||
@@ -37,7 +38,7 @@ function DomainManagement({ onSuccess }: Props) {
|
||||
setExistingDomainsTouched(false);
|
||||
updateLastKnownDomainCount(allowedDomains.length);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}, [team, allowedDomains, onSuccess]);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import Dropzone from "react-dropzone";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { s } from "@shared/styles";
|
||||
import {
|
||||
AttachmentPreset,
|
||||
@@ -89,7 +90,7 @@ function DropToImport({ disabled, onSubmit, children, format }: Props) {
|
||||
),
|
||||
});
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setImporting(false);
|
||||
setUploadProgress(0);
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Spinner from "@shared/components/Spinner";
|
||||
import {
|
||||
FileOperationFormat,
|
||||
@@ -72,7 +73,7 @@ const FileOperationListItem = ({ fileOperation }: Props) => {
|
||||
toast.success(t("Export deleted"));
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
}, [fileOperation, fileOperations, t]);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Group from "~/models/Group";
|
||||
import type User from "~/models/User";
|
||||
import Invite from "~/scenes/Invite";
|
||||
@@ -64,7 +65,7 @@ export function CreateGroupDialog() {
|
||||
dialogs.closeAllModals();
|
||||
history.push(settingsPath("groups", group.id, "members"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -135,7 +136,7 @@ export function EditGroupDialog({ group, onSubmit }: Props) {
|
||||
});
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -434,7 +435,7 @@ const GroupMemberListItem = observer(function ({
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -6,6 +6,7 @@ import AvatarEditor from "react-avatar-editor";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { s } from "@shared/styles";
|
||||
import { AttachmentPreset } from "@shared/types";
|
||||
import { AttachmentValidation } from "@shared/validations";
|
||||
@@ -49,7 +50,7 @@ const ImageUpload: React.FC<Props> = ({
|
||||
});
|
||||
void onSuccess(attachment.url);
|
||||
} catch (err) {
|
||||
onError(err.message);
|
||||
onError(errToString(err));
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
setIsCropping(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useMemo, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useTheme } from "styled-components";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Spinner from "@shared/components/Spinner";
|
||||
import { ImportState } from "@shared/types";
|
||||
import type Import from "~/models/Import";
|
||||
@@ -61,7 +62,7 @@ export const ImportListItem = observer(({ importModel }: Props) => {
|
||||
await importModel.cancel();
|
||||
toast.success(t("Import canceled"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,7 +89,7 @@ export const ImportListItem = observer(({ importModel }: Props) => {
|
||||
await importModel.delete();
|
||||
toast.success(t("Import deleted"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as React from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import { OneTimePasswordInput } from "~/components/OneTimePasswordInput";
|
||||
@@ -38,7 +39,7 @@ function TeamDelete({ onSubmit }: Props) {
|
||||
await auth.requestDeleteTeam();
|
||||
setWaitingCode(true);
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
}
|
||||
},
|
||||
[auth]
|
||||
@@ -56,7 +57,7 @@ function TeamDelete({ onSubmit }: Props) {
|
||||
});
|
||||
onSubmit();
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
toast.error(errToString(error));
|
||||
}
|
||||
},
|
||||
[auth, onSubmit]
|
||||
|
||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type User from "~/models/User";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
@@ -31,7 +32,7 @@ function TeamNew({ user }: Props) {
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as React from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import { OneTimePasswordInput } from "~/components/OneTimePasswordInput";
|
||||
@@ -37,7 +38,7 @@ function UserDelete({ onSubmit }: Props) {
|
||||
await auth.requestDeleteUser();
|
||||
setWaitingCode(true);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[auth]
|
||||
@@ -55,7 +56,7 @@ function UserDelete({ onSubmit }: Props) {
|
||||
});
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[auth, onSubmit]
|
||||
|
||||
+15
-3
@@ -3,6 +3,7 @@ import invariant from "invariant";
|
||||
import { isNil } from "es-toolkit/compat";
|
||||
import { observable, action, computed, autorun, runInAction } from "mobx";
|
||||
import { getCookie, setCookie } from "tiny-cookie";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import type { CustomTheme } from "@shared/types";
|
||||
import Storage from "@shared/utils/Storage";
|
||||
import { getCookieDomain, parseDomain } from "@shared/utils/domains";
|
||||
@@ -255,9 +256,20 @@ export default class AuthStore extends Store<Team> {
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.error === "user_suspended") {
|
||||
if (
|
||||
err instanceof Error &&
|
||||
"error" in err &&
|
||||
err.error === "user_suspended"
|
||||
) {
|
||||
this.isSuspended = true;
|
||||
this.suspendedContactEmail = err.data.adminEmail;
|
||||
if (
|
||||
"data" in err &&
|
||||
err.data instanceof Object &&
|
||||
"adminEmail" in err.data &&
|
||||
typeof err.data.adminEmail === "string"
|
||||
) {
|
||||
this.suspendedContactEmail = err.data.adminEmail;
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
@@ -342,7 +354,7 @@ export default class AuthStore extends Store<Team> {
|
||||
// invalidate authentication token on server and unset auth cookie
|
||||
await client.post(`/auth.delete`);
|
||||
} catch (err) {
|
||||
Logger.error("Failed to delete authentication", err);
|
||||
Logger.error("Failed to delete authentication", toError(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { subMinutes } from "date-fns";
|
||||
import { action } from "mobx";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type { UnfurlResourceType } from "@shared/types";
|
||||
import Unfurl from "~/models/Unfurl";
|
||||
import { client } from "~/utils/ApiClient";
|
||||
@@ -89,7 +90,7 @@ class UnfurlsStore extends Store<Unfurl<any>> {
|
||||
} catch (err) {
|
||||
Logger.warn("Failed to unfurl url", {
|
||||
url,
|
||||
message: err.message,
|
||||
message: errToString(err),
|
||||
});
|
||||
return;
|
||||
} finally {
|
||||
|
||||
Vendored
+2
@@ -1,5 +1,7 @@
|
||||
declare module "~/utils/autotrack";
|
||||
|
||||
declare module "vite/modulepreload-polyfill";
|
||||
|
||||
declare module "emoji-mart";
|
||||
|
||||
declare module "string-replace-to-array";
|
||||
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
/// <reference types="google.analytics" />
|
||||
import type RootStore from "~/stores/RootStore";
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support
|
||||
// data can be a string, Blob, File, or dataURL
|
||||
export default function download(
|
||||
this: unknown,
|
||||
data: Blob | string | File,
|
||||
strFileName: string,
|
||||
strMimeType?: string
|
||||
|
||||
+1
-1
@@ -375,7 +375,7 @@
|
||||
"rimraf": "^6.1.3",
|
||||
"rollup-plugin-webpack-stats": "2.1.11",
|
||||
"terser": "^5.48.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript": "^6.0.3",
|
||||
"unplugin-swc": "^1.5.9",
|
||||
"vite-plugin-babel": "^1.7.3",
|
||||
"vite-tsconfig-paths": "^6.1.1",
|
||||
|
||||
@@ -4,6 +4,7 @@ import jwt from "jsonwebtoken";
|
||||
import type { Context } from "koa";
|
||||
import Router from "koa-router";
|
||||
import type { Profile } from "passport";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { slugifyDomain } from "@shared/utils/domains";
|
||||
import { parseEmail } from "@shared/utils/email";
|
||||
import accountProvisioner from "@server/commands/accountProvisioner";
|
||||
@@ -226,7 +227,7 @@ if (env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET) {
|
||||
|
||||
return done(null, result.user, { ...result, client });
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
return done(toError(err), null);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import type Integration from "~/models/Integration";
|
||||
import { IntegrationScene } from "~/scenes/Settings/components/IntegrationScene";
|
||||
@@ -67,7 +68,7 @@ function DiagramsNet() {
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { Context } from "koa";
|
||||
import Router from "koa-router";
|
||||
|
||||
import { Strategy } from "passport-oauth2";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { languages } from "@shared/i18n";
|
||||
import { slugifyDomain } from "@shared/utils/domains";
|
||||
import { parseEmail } from "@shared/utils/email";
|
||||
@@ -220,7 +221,7 @@ if (env.DISCORD_CLIENT_ID && env.DISCORD_CLIENT_SECRET) {
|
||||
});
|
||||
return done(null, result.user, { ...result, client });
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
return done(toError(err), null);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Router from "koa-router";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { Client, NotificationEventType } from "@shared/types";
|
||||
import { parseDomain } from "@shared/utils/domains";
|
||||
import InviteAcceptedEmail from "@server/emails/templates/InviteAcceptedEmail";
|
||||
@@ -161,8 +162,11 @@ const emailCallback = async (ctx: APIContext<T.EmailCallbackReq>) => {
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.debug("authentication", err);
|
||||
return ctx.redirect(`/?notice=auth-error&description=${err.message}`);
|
||||
const message = errToString(err);
|
||||
Logger.debug("authentication", message);
|
||||
return ctx.redirect(
|
||||
`/?notice=auth-error&description=${encodeURIComponent(message)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toError } from "@shared/utils/error";
|
||||
import auth from "@server/middlewares/authentication";
|
||||
import Router from "koa-router";
|
||||
import * as T from "./schema";
|
||||
@@ -102,7 +103,10 @@ router.get(
|
||||
|
||||
ctx.redirect(FigmaUtils.successUrl());
|
||||
} catch (err) {
|
||||
Logger.error("Encountered error during Figma OAuth callback", err);
|
||||
Logger.error(
|
||||
"Encountered error during Figma OAuth callback",
|
||||
toError(err)
|
||||
);
|
||||
ctx.redirect(FigmaUtils.errorUrl("unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import fetch from "@server/utils/fetch";
|
||||
import env from "./env";
|
||||
import { FigmaUtils } from "../shared/FigmaUtils";
|
||||
@@ -173,7 +174,7 @@ export class Figma {
|
||||
} catch (err) {
|
||||
Logger.error(
|
||||
`Error fetching Figma file metadata for integration ${integration.id}`,
|
||||
err
|
||||
toError(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Sequelize } from "sequelize";
|
||||
import type { Endpoints, OctokitResponse } from "@octokit/types";
|
||||
import { Octokit } from "octokit";
|
||||
import pluralize from "pluralize";
|
||||
import { toError, errToString } from "@shared/utils/error";
|
||||
import type { IntegrationType } from "@shared/types";
|
||||
import { IntegrationService, UnfurlResourceType } from "@shared/types";
|
||||
import Logger from "@server/logging/Logger";
|
||||
@@ -385,8 +386,10 @@ export class GitHub {
|
||||
|
||||
return GitHub.transformData(res.data, resource.type);
|
||||
} catch (err) {
|
||||
Logger.warn("Failed to fetch resource from GitHub", err);
|
||||
return { error: err.message || "Unknown error" };
|
||||
Logger.warn("Failed to fetch resource from GitHub", toError(err));
|
||||
return {
|
||||
error: errToString(err) || "Unknown error",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -398,7 +401,7 @@ export class GitHub {
|
||||
try {
|
||||
project = await client.requestProject(resource);
|
||||
} catch (err) {
|
||||
Logger.warn("Failed to fetch project from GitHub", err);
|
||||
Logger.warn("Failed to fetch project from GitHub", toError(err));
|
||||
return { error: "Resource not found" };
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { s } from "@shared/styles";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
@@ -39,7 +40,7 @@ function GitLabConnectDialog() {
|
||||
dialogs.closeAllModals();
|
||||
redirectTo(res.data.redirectUrl);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toError } from "@shared/utils/error";
|
||||
import type { IssueSource } from "@shared/schema";
|
||||
import { IntegrationService, type IntegrationType } from "@shared/types";
|
||||
import Logger from "@server/logging/Logger";
|
||||
@@ -69,7 +70,7 @@ export class GitLabIssueProvider extends BaseIssueProvider {
|
||||
}))
|
||||
);
|
||||
} catch (err) {
|
||||
Logger.warn("Failed to fetch projects from GitLab", err);
|
||||
Logger.warn("Failed to fetch projects from GitLab", toError(err));
|
||||
}
|
||||
|
||||
return sources;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Router from "koa-router";
|
||||
import { Op } from "sequelize";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { IntegrationService, IntegrationType } from "@shared/types";
|
||||
import { createContext } from "@server/context";
|
||||
import { ValidationError } from "@server/errors";
|
||||
@@ -284,7 +285,10 @@ router.get(
|
||||
|
||||
ctx.redirect(GitLabUtils.url);
|
||||
} catch (err) {
|
||||
Logger.error("Encountered error during Gitlab OAuth callback", err);
|
||||
Logger.error(
|
||||
"Encountered error during Gitlab OAuth callback",
|
||||
toError(err)
|
||||
);
|
||||
ctx.redirect(GitLabUtils.errorUrl("unauthenticated"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
IntegrationService,
|
||||
UnfurlResourceType,
|
||||
} from "@shared/types";
|
||||
import { toError, errToString } from "@shared/utils/error";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import type { User } from "@server/models";
|
||||
import { Integration, IntegrationAuthentication } from "@server/models";
|
||||
@@ -267,8 +268,10 @@ export class GitLab {
|
||||
|
||||
return { error: "Resource not found" };
|
||||
} catch (err) {
|
||||
Logger.warn("Failed to fetch resource from GitLab", err);
|
||||
return { error: err.message || "Unknown error" };
|
||||
Logger.warn("Failed to fetch resource from GitLab", toError(err));
|
||||
return {
|
||||
error: errToString(err) || "Unknown error",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import Router from "koa-router";
|
||||
import { capitalize } from "es-toolkit/compat";
|
||||
import type { Profile } from "passport";
|
||||
import { Strategy as GoogleStrategy } from "passport-google-oauth2";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { languages } from "@shared/i18n";
|
||||
import { slugifyDomain } from "@shared/utils/domains";
|
||||
import accountProvisioner from "@server/commands/accountProvisioner";
|
||||
@@ -159,7 +160,7 @@ if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
|
||||
|
||||
return done(null, result.user, { ...result, client });
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
return done(toError(err), null);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import type Integration from "~/models/Integration";
|
||||
import { IntegrationScene } from "~/scenes/Settings/components/IntegrationScene";
|
||||
@@ -63,7 +64,7 @@ function GoogleAnalytics() {
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toError, errToString } from "@shared/utils/error";
|
||||
import type { JSONObject } from "@shared/types";
|
||||
import { UnfurlResourceType } from "@shared/types";
|
||||
import Logger from "@server/logging/Logger";
|
||||
@@ -29,8 +30,13 @@ class Iframely {
|
||||
);
|
||||
return await res.json();
|
||||
} catch (err) {
|
||||
Logger.error(`Error fetching data from Iframely for url: ${url}`, err);
|
||||
return { error: err.message || "Unknown error" };
|
||||
Logger.error(
|
||||
`Error fetching data from Iframely for url: ${url}`,
|
||||
toError(err)
|
||||
);
|
||||
return {
|
||||
error: errToString(err) || "Unknown error",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Router from "koa-router";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { IntegrationService, IntegrationType } from "@shared/types";
|
||||
import { ValidationError } from "@server/errors";
|
||||
import Logger from "@server/logging/Logger";
|
||||
@@ -107,7 +108,10 @@ router.get(
|
||||
|
||||
ctx.redirect(LinearUtils.successUrl());
|
||||
} catch (err) {
|
||||
Logger.error("Encountered error during Linear OAuth callback", err);
|
||||
Logger.error(
|
||||
"Encountered error during Linear OAuth callback",
|
||||
toError(err)
|
||||
);
|
||||
ctx.redirect(LinearUtils.errorUrl("unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Issue, WorkflowState } from "@linear/sdk";
|
||||
import { LinearClient } from "@linear/sdk";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import fetch from "@server/utils/fetch";
|
||||
import { sortBy } from "es-toolkit/compat";
|
||||
import { z } from "zod";
|
||||
@@ -155,8 +156,9 @@ export class Linear {
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.warn("Failed to fetch resource from Linear", err);
|
||||
return { error: err.message || "Unknown error" };
|
||||
const error = toError(err);
|
||||
Logger.warn("Failed to fetch resource from Linear", error);
|
||||
return { error: error.message || "Unknown error" };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toError } from "@shared/utils/error";
|
||||
import type { IntegrationType } from "@shared/types";
|
||||
import { IntegrationService } from "@shared/types";
|
||||
import Logger from "@server/logging/Logger";
|
||||
@@ -21,6 +22,6 @@ export async function uninstall(
|
||||
await Linear.revokeAccess(authentication.token);
|
||||
} catch (err) {
|
||||
// suppress error since this is a best-effort operation.
|
||||
Logger.error("Failed to revoke Linear access token", err);
|
||||
Logger.error("Failed to revoke Linear access token", toError(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import type Integration from "~/models/Integration";
|
||||
import { IntegrationScene } from "~/scenes/Settings/components/IntegrationScene";
|
||||
@@ -71,7 +72,7 @@ function Matomo() {
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type { ImportInput } from "@shared/schema";
|
||||
import type { CollectionPermission } from "@shared/types";
|
||||
import { IntegrationService } from "@shared/types";
|
||||
@@ -50,7 +51,7 @@ export function ImportDialog({ integrationId, onSubmit }: Props) {
|
||||
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
resetSubmitting();
|
||||
}
|
||||
}, [
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Router from "koa-router";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import env from "../env";
|
||||
import { fetchOIDCConfiguration } from "../oidcDiscovery";
|
||||
@@ -63,7 +64,7 @@ if (hasManualConfig) {
|
||||
|
||||
return router;
|
||||
} catch (error) {
|
||||
Logger.fatal("Failed to discover OIDC configuration", error);
|
||||
Logger.fatal("Failed to discover OIDC configuration", toError(error));
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -3,6 +3,7 @@ import JWT from "jsonwebtoken";
|
||||
import type { Context } from "koa";
|
||||
import type Router from "koa-router";
|
||||
import { get } from "es-toolkit/compat";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { slugifyDomain } from "@shared/utils/domains";
|
||||
import { parseEmail } from "@shared/utils/email";
|
||||
import { isBase64Url } from "@shared/utils/urls";
|
||||
@@ -110,7 +111,7 @@ export function createOIDCRouter(
|
||||
sub?: string;
|
||||
};
|
||||
} catch (err) {
|
||||
Logger.error("id_token decode threw error: ", err);
|
||||
Logger.error("id_token decode threw error: ", toError(err));
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
@@ -233,7 +234,7 @@ export function createOIDCRouter(
|
||||
});
|
||||
return done(null, result.user, { ...result, client });
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
return done(toError(err), null);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { InternalError } from "@server/errors";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import fetch from "@server/utils/fetch";
|
||||
@@ -85,7 +86,7 @@ export async function fetchOIDCConfiguration(
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
Logger.error("Failed to fetch OIDC configuration", error);
|
||||
Logger.error("Failed to fetch OIDC configuration", toError(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { startRegistration } from "@simplewebauthn/browser";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type { JSONObject } from "@shared/types";
|
||||
import { observer } from "mobx-react";
|
||||
import { KeyIcon, PlusIcon } from "outline-icons";
|
||||
@@ -78,7 +79,7 @@ function PasskeysSettings() {
|
||||
await loadPasskeys();
|
||||
} catch (err) {
|
||||
toast.error(
|
||||
err.message || t("Failed to register passkey. Please try again.")
|
||||
errToString(err) || t("Failed to register passkey. Please try again.")
|
||||
);
|
||||
} finally {
|
||||
setIsRegistering(false);
|
||||
@@ -113,7 +114,8 @@ function PasskeysSettings() {
|
||||
await loadPasskeys();
|
||||
} catch (err) {
|
||||
toast.error(
|
||||
err.message || t("Failed to delete passkey. Please try again.")
|
||||
errToString(err) ||
|
||||
t("Failed to delete passkey. Please try again.")
|
||||
);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import Button from "~/components/Button";
|
||||
import Flex from "~/components/Flex";
|
||||
import Input from "~/components/Input";
|
||||
@@ -44,7 +45,7 @@ function RenamePasskeyDialog({
|
||||
onSuccess();
|
||||
} catch (err) {
|
||||
toast.error(
|
||||
err.message || t("Failed to update passkey. Please try again.")
|
||||
errToString(err) || t("Failed to update passkey. Please try again.")
|
||||
);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isoBase64URL } from "@simplewebauthn/server/helpers";
|
||||
import type { AuthenticatorTransportFuture } from "@simplewebauthn/server";
|
||||
import Router from "koa-router";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { User, UserPasskey, Team } from "@server/models";
|
||||
import auth from "@server/middlewares/authentication";
|
||||
import validate from "@server/middlewares/validate";
|
||||
@@ -156,7 +157,7 @@ router.post(
|
||||
expectedRPID: getRpID(ctx),
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
const err = toError(error);
|
||||
Logger.error("passkeys: Registration verification failed", err);
|
||||
throw ValidationError(err.message);
|
||||
}
|
||||
@@ -306,7 +307,10 @@ router.post(
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.error("passkeys: Authentication verification failed", err);
|
||||
Logger.error(
|
||||
"passkeys: Authentication verification failed",
|
||||
toError(err)
|
||||
);
|
||||
throw ValidationError("Passkey authentication failed. Please try again.");
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +264,10 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
|
||||
count,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message.includes("syntax error in tsquery")) {
|
||||
if (
|
||||
err instanceof Error &&
|
||||
err.message.includes("syntax error in tsquery")
|
||||
) {
|
||||
throw ValidationError("Invalid search query");
|
||||
}
|
||||
throw err;
|
||||
@@ -456,7 +459,10 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
|
||||
count,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message.includes("syntax error in tsquery")) {
|
||||
if (
|
||||
err instanceof Error &&
|
||||
err.message.includes("syntax error in tsquery")
|
||||
) {
|
||||
throw ValidationError("Invalid search query");
|
||||
}
|
||||
throw err;
|
||||
|
||||
@@ -3,6 +3,7 @@ import Router from "koa-router";
|
||||
import { escapeRegExp } from "es-toolkit/compat";
|
||||
import queryString from "query-string";
|
||||
import { z } from "zod";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { IntegrationService, IntegrationType } from "@shared/types";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
import {
|
||||
@@ -155,7 +156,7 @@ router.post(
|
||||
callback_id = parsed.callback_id;
|
||||
token = parsed.token;
|
||||
} catch (err) {
|
||||
Logger.error("Failed to parse Slack interactive payload", err, {
|
||||
Logger.error("Failed to parse Slack interactive payload", toError(err), {
|
||||
payload,
|
||||
});
|
||||
throw ValidationError("Invalid payload");
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Context } from "koa";
|
||||
import Router from "koa-router";
|
||||
import type { Profile } from "passport";
|
||||
import { Strategy as SlackStrategy } from "passport-slack-oauth2";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { IntegrationService, IntegrationType } from "@shared/types";
|
||||
import accountProvisioner from "@server/commands/accountProvisioner";
|
||||
import { ValidationError } from "@server/errors";
|
||||
@@ -128,7 +129,7 @@ if (env.SLACK_CLIENT_ID && env.SLACK_CLIENT_SECRET) {
|
||||
});
|
||||
return done(null, result.user, { ...result, client });
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
return done(toError(err), null);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import querystring, { type ParsedUrlQueryInput } from "node:querystring";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { InvalidRequestError } from "@server/errors";
|
||||
import fetch from "@server/utils/fetch";
|
||||
import { SlackUtils } from "../shared/SlackUtils";
|
||||
@@ -31,7 +32,7 @@ export async function post(
|
||||
});
|
||||
data = await response.json();
|
||||
} catch (err) {
|
||||
throw InvalidRequestError(err.message);
|
||||
throw InvalidRequestError(errToString(err));
|
||||
}
|
||||
|
||||
if (!data.ok) {
|
||||
@@ -76,7 +77,7 @@ export async function request(
|
||||
});
|
||||
data = await response.json();
|
||||
} catch (err) {
|
||||
throw InvalidRequestError(err.message);
|
||||
throw InvalidRequestError(errToString(err));
|
||||
}
|
||||
|
||||
if (!data.ok) {
|
||||
|
||||
@@ -70,7 +70,7 @@ router.post(
|
||||
try {
|
||||
await attachment.writeFile(file);
|
||||
} catch (err) {
|
||||
if (err.message.includes("permission denied")) {
|
||||
if (err instanceof Error && err.message.includes("permission denied")) {
|
||||
throw Error(
|
||||
`Permission denied writing to "${key}". Check the host machine file system permissions.`
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { existsSync, mkdirSync } from "node:fs";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import env from "@server/env";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import {
|
||||
@@ -18,7 +19,7 @@ if (env.FILE_STORAGE === "local") {
|
||||
} catch (err) {
|
||||
Logger.fatal(
|
||||
`Failed to create directory for local file storage at ${env.FILE_STORAGE_LOCAL_ROOT_DIR}`,
|
||||
err
|
||||
toError(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { IntegrationType, IntegrationService } from "@shared/types";
|
||||
import type Integration from "~/models/Integration";
|
||||
import { IntegrationScene } from "~/scenes/Settings/components/IntegrationScene";
|
||||
@@ -75,7 +76,7 @@ function Umami() {
|
||||
|
||||
toast.success(t("Settings saved"));
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[integrations, integration, t]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import type WebhookSubscription from "~/models/WebhookSubscription";
|
||||
import WebhookSubscriptionForm from "./WebhookSubscriptionForm";
|
||||
|
||||
@@ -33,7 +34,7 @@ function WebhookSubscriptionEdit({ onSubmit, webhookSubscription }: Props) {
|
||||
toast.success(t("Webhook updated"));
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[t, onSubmit, webhookSubscription]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import WebhookSubscriptionForm from "./WebhookSubscriptionForm";
|
||||
|
||||
@@ -32,7 +33,7 @@ function WebhookSubscriptionNew({ onSubmit }: Props) {
|
||||
toast.success(t("Webhook created"));
|
||||
onSubmit();
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
toast.error(errToString(err));
|
||||
}
|
||||
},
|
||||
[t, onSubmit, webhookSubscriptions]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FetchError } from "node-fetch";
|
||||
import { Op } from "sequelize";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import { colorPalette } from "@shared/utils/collections";
|
||||
import WebhookDisabledEmail from "@server/emails/templates/WebhookDisabledEmail";
|
||||
import env from "@server/env";
|
||||
@@ -815,13 +816,14 @@ export default class DeliverWebhookTask extends BaseTask<Props> {
|
||||
});
|
||||
status = response.ok ? "success" : "failed";
|
||||
} catch (err) {
|
||||
const error = toError(err);
|
||||
if (isExpectedNetworkError(err) && env.isCloudHosted) {
|
||||
Logger.warn(`Failed to send webhook: ${err.message}`, {
|
||||
Logger.warn(`Failed to send webhook: ${error.message}`, {
|
||||
event,
|
||||
deliveryId: delivery.id,
|
||||
});
|
||||
} else {
|
||||
Logger.error("Failed to send webhook", err, {
|
||||
Logger.error("Failed to send webhook", error, {
|
||||
event,
|
||||
deliveryId: delivery.id,
|
||||
});
|
||||
@@ -857,10 +859,14 @@ export default class DeliverWebhookTask extends BaseTask<Props> {
|
||||
try {
|
||||
await this.checkAndDisableSubscription(subscription);
|
||||
} catch (err) {
|
||||
Logger.error("Failed to check and disable recent deliveries", err, {
|
||||
event,
|
||||
deliveryId: delivery.id,
|
||||
});
|
||||
Logger.error(
|
||||
"Failed to check and disable recent deliveries",
|
||||
toError(err),
|
||||
{
|
||||
event,
|
||||
deliveryId: delivery.id,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export default class Queue {
|
||||
handler?: (job: unknown, done: () => void) => void;
|
||||
|
||||
done() {
|
||||
//
|
||||
}
|
||||
@@ -15,7 +17,7 @@ export default class Queue {
|
||||
return 0;
|
||||
}
|
||||
|
||||
add = function (data: unknown) {
|
||||
add(data: unknown) {
|
||||
const job = this.createJob(data);
|
||||
|
||||
if (!this.handler) {
|
||||
@@ -23,19 +25,19 @@ export default class Queue {
|
||||
}
|
||||
|
||||
this.handler(job, this.done);
|
||||
};
|
||||
}
|
||||
|
||||
process = function (handler: (job: unknown, done: () => void) => void) {
|
||||
process(handler: (job: unknown, done: () => void) => void) {
|
||||
if (this.handler) {
|
||||
throw Error("Cannot define a handler more than once per Queue instance");
|
||||
}
|
||||
|
||||
this.handler = handler;
|
||||
};
|
||||
}
|
||||
|
||||
createJob = function (data: unknown) {
|
||||
createJob(data: unknown) {
|
||||
return {
|
||||
data,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
Extension,
|
||||
} from "@hocuspocus/server";
|
||||
import * as Y from "yjs";
|
||||
import { toError } from "@shared/utils/error";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import { trace } from "@server/logging/tracing";
|
||||
import Document from "@server/models/Document";
|
||||
@@ -135,7 +136,7 @@ export default class PersistenceExtension implements Extension {
|
||||
clientVersion,
|
||||
});
|
||||
} catch (err) {
|
||||
Logger.error("Unable to persist document", err, {
|
||||
Logger.error("Unable to persist document", toError(err), {
|
||||
documentId,
|
||||
userId: context.user?.id,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { TeamDomain } from "@server/models";
|
||||
import Collection from "@server/models/Collection";
|
||||
import UserAuthentication from "@server/models/UserAuthentication";
|
||||
@@ -184,7 +185,9 @@ describe("accountProvisioner", () => {
|
||||
}
|
||||
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.id).toEqual("invalid_authentication");
|
||||
expect(
|
||||
error instanceof Error && "id" in error ? error.id : undefined
|
||||
).toEqual("invalid_authentication");
|
||||
});
|
||||
|
||||
it("should throw an error when authentication provider is disabled", async () => {
|
||||
@@ -533,7 +536,7 @@ describe("accountProvisioner", () => {
|
||||
error = err;
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("Invalid authentication");
|
||||
expect(errToString(error)).toEqual("Invalid authentication");
|
||||
});
|
||||
|
||||
it("should always use existing team if self-hosted", async () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from "node:path";
|
||||
import { readFile } from "fs-extra";
|
||||
import invariant from "invariant";
|
||||
import { toError, errToString } from "@shared/utils/error";
|
||||
import { CollectionPermission, UserRole } from "@shared/types";
|
||||
import env from "@server/env";
|
||||
import {
|
||||
@@ -136,7 +137,11 @@ async function accountProvisioner(
|
||||
} catch (err) {
|
||||
// The account could not be provisioned for the provided teamId
|
||||
// check to see if we can try authentication using email matching only
|
||||
if (err.id === "invalid_authentication") {
|
||||
if (
|
||||
err instanceof Error &&
|
||||
"id" in err &&
|
||||
err.id === "invalid_authentication"
|
||||
) {
|
||||
const authProvider = await AuthenticationProvider.findOne({
|
||||
where: {
|
||||
name: authenticationProviderParams.name,
|
||||
@@ -163,10 +168,10 @@ async function accountProvisioner(
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (err.id) {
|
||||
if (err instanceof Error && "id" in err && err.id) {
|
||||
throw err;
|
||||
} else {
|
||||
throw InvalidAuthenticationError(err.message);
|
||||
throw InvalidAuthenticationError(errToString(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +265,7 @@ async function accountProvisioner(
|
||||
});
|
||||
} catch (err) {
|
||||
// Group sync failure should never block login
|
||||
Logger.error("Group sync failed during login", err, {
|
||||
Logger.error("Group sync failed during login", toError(err), {
|
||||
userId: user.id,
|
||||
provider: authenticationProviderParams.name,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { createContext } from "@server/context";
|
||||
import Attachment from "@server/models/Attachment";
|
||||
import { sequelize } from "@server/storage/database";
|
||||
@@ -103,7 +104,7 @@ describe("documentImporter", () => {
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
error = errToString(err);
|
||||
}
|
||||
|
||||
expect(error).toEqual("File type application/octet-stream not supported");
|
||||
@@ -278,7 +279,7 @@ describe("documentImporter", () => {
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
error = errToString(err);
|
||||
}
|
||||
|
||||
expect(error).toEqual("File type executable/zip not supported");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { subDays } from "date-fns";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import { Attachment, Document } from "@server/models";
|
||||
import { buildAttachment, buildDocument } from "@server/test/factories";
|
||||
import { mockTaskSchedule } from "@server/test/support";
|
||||
@@ -33,7 +34,7 @@ describe("documentPermanentDeleter", () => {
|
||||
try {
|
||||
await documentPermanentDeleter([document]);
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
error = errToString(err);
|
||||
}
|
||||
|
||||
expect(error).toEqual(
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
buildDocument,
|
||||
buildImport,
|
||||
} from "@server/test/factories";
|
||||
import { errToString } from "@shared/utils/error";
|
||||
import teamPermanentDeleter from "./teamPermanentDeleter";
|
||||
|
||||
describe("teamPermanentDeleter", () => {
|
||||
@@ -207,7 +208,7 @@ describe("teamPermanentDeleter", () => {
|
||||
try {
|
||||
await teamPermanentDeleter(team);
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
error = errToString(err);
|
||||
}
|
||||
|
||||
expect(error).toEqual(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user