feat: add github oauth

This commit is contained in:
2024-11-17 17:54:41 +07:00
parent c8e61ed4aa
commit a6cc450ea7
22 changed files with 419 additions and 74 deletions
-42
View File
@@ -1,42 +0,0 @@
import { ExpoConfig, ConfigContext } from "expo/config";
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: "Vaulterm",
slug: "vaulterm",
version: "1.0.0",
orientation: "portrait",
icon: "./assets/images/icon.png",
scheme: "vaulterm",
userInterfaceStyle: "automatic",
newArchEnabled: true,
splash: {
image: "./assets/images/splash.png",
resizeMode: "contain",
backgroundColor: "#ffffff",
},
ios: {
supportsTablet: true,
},
android: {
package: "sh.rul.vaulterm",
adaptiveIcon: {
foregroundImage: "./assets/images/adaptive-icon.png",
backgroundColor: "#ffffff",
},
},
web: {
bundler: "metro",
output: "single",
favicon: "./assets/images/favicon.png",
},
plugins: ["expo-router"],
experiments: {
typedRoutes: true,
},
extra: {
eas: {
projectId: "3e0112c1-f0ed-423c-b5cf-95633f23f6dc",
},
},
});
+41
View File
@@ -0,0 +1,41 @@
{
"name": "Vaulterm",
"slug": "vaulterm",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "vaulterm",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"package": "sh.rul.vaulterm",
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"bundler": "metro",
"output": "single",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router"
],
"experiments": {
"typedRoutes": true
},
"extra": {
"eas": {
"projectId": "3e0112c1-f0ed-423c-b5cf-95633f23f6dc"
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ const Drawer = (props: DrawerContentComponentProps) => {
return (
<View pt={insets.top} flex={1}>
<View py="$4" px="$2">
<View py="$4" px="$3">
<UserMenuButton />
</View>
@@ -1,18 +1,11 @@
import React from "react";
import {
Avatar,
Button,
ListItem,
Separator,
Text,
useMedia,
View,
} from "tamagui";
import { Button, ListItem, Separator, Text, useMedia, View } from "tamagui";
import MenuButton from "../ui/menu-button";
import Icons from "../ui/icons";
import { logout, setTeam, useTeamId } from "@/stores/auth";
import { useUser } from "@/hooks/useUser";
import TeamForm, { teamFormModal } from "@/pages/team/components/team-form";
import Avatar from "../ui/avatar";
const UserMenuButton = () => {
const user = useUser();
@@ -31,16 +24,21 @@ const UserMenuButton = () => {
borderWidth={0}
justifyContent="flex-start"
borderRadius="$10"
py={0}
px="$2"
p={0}
gap="$1"
>
<Avatar circular size="$3">
<Avatar.Fallback bg="$blue4" />
</Avatar>
<Avatar size="$4" src={user?.image} />
<View flex={1}>
<Text textAlign='left' numberOfLines={1}>{user?.name}</Text>
<Text textAlign='left' numberOfLines={1} fontWeight="600" mt="$1.5">
<Text textAlign="left" numberOfLines={1}>
{user?.name}
</Text>
<Text
textAlign="left"
numberOfLines={1}
fontWeight="600"
mt="$1.5"
>
{team ? `${team.icon} ${team.name}` : "Personal"}
</Text>
</View>
+25
View File
@@ -0,0 +1,25 @@
import React from "react";
import { GetProps, Avatar as BaseAvatar } from "tamagui";
import Icons from "./icons";
type AvatarProps = GetProps<typeof BaseAvatar> & {
src?: string;
$image?: GetProps<typeof BaseAvatar.Image>;
};
const Avatar = ({ src, $image, ...props }: AvatarProps) => {
return (
<BaseAvatar circular {...props}>
{src ? <BaseAvatar.Image src={src} {...$image} /> : null}
<BaseAvatar.Fallback
bg="$blue4"
alignItems="center"
justifyContent="center"
>
<Icons name="account" size={16} />
</BaseAvatar.Fallback>
</BaseAvatar>
);
};
export default Avatar;
+19
View File
@@ -0,0 +1,19 @@
import api from "@/lib/api";
import { useQuery } from "@tanstack/react-query";
export const useServerConfig = (configName?: string | string[]) => {
return useQuery({
queryKey: ["server/config", configName],
queryFn: () => {
const keys = Array.isArray(configName)
? configName.join(",")
: configName;
return api("/server/config", { params: { keys } });
},
select: (data) => {
return typeof configName === "string"
? (data[configName] as string)
: data;
},
});
};
+2
View File
@@ -31,8 +31,10 @@
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"expo": "~52.0.6",
"expo-auth-session": "~6.0.0",
"expo-blur": "~14.0.1",
"expo-constants": "~17.0.3",
"expo-crypto": "~14.0.1",
"expo-font": "~13.0.0",
"expo-haptics": "~14.0.0",
"expo-linking": "~7.0.2",
@@ -0,0 +1,42 @@
import React, { useEffect, useMemo } from "react";
import { makeRedirectUri, useAuthRequest } from "expo-auth-session";
import appConfig from "@/app.json";
import { Button } from "tamagui";
import { useOAuthCallback } from "../hooks";
import { useServerConfig } from "@/hooks/useServerConfig";
const LoginGithubButton = () => {
const { data: clientId } = useServerConfig("github_client_id");
const discovery = useMemo(() => {
return {
authorizationEndpoint: "https://github.com/login/oauth/authorize",
tokenEndpoint: "https://github.com/login/oauth/access_token",
revocationEndpoint: `https://github.com/settings/connections/applications/${clientId}`,
};
}, [clientId]);
const oauth = useOAuthCallback("github");
const [request, response, promptAsync] = useAuthRequest(
{
clientId: clientId,
scopes: ["identity"],
redirectUri: makeRedirectUri({ scheme: appConfig.scheme }),
},
discovery
);
useEffect(() => {
if (response?.type === "success") {
const { code } = response.params;
oauth.mutate(code);
}
}, [response]);
return (
<Button disabled={!request} onPress={() => promptAsync()}>
Login with Github
</Button>
);
};
export default LoginGithubButton;
+17
View File
@@ -42,3 +42,20 @@ export const useRegisterMutation = () => {
},
});
};
export const useOAuthCallback = (type: string) => {
return useMutation({
mutationFn: async (code: string) => {
const res = await api(`/auth/oauth/${type}/callback?code=${code}`);
const { data } = loginResultSchema.safeParse(res);
if (!data) {
throw new Error("Invalid response!");
}
return data;
},
onSuccess(data) {
authStore.setState({ token: data.sessionId });
router.replace("/");
},
});
};
+16 -2
View File
@@ -6,17 +6,21 @@ import { useZForm } from "@/hooks/useZForm";
import { Link, router, Stack } from "expo-router";
import Button from "@/components/ui/button";
import ThemeSwitcher from "@/components/containers/theme-switcher";
import { useMutation } from "@tanstack/react-query";
import { z } from "zod";
import * as WebBrowser from "expo-web-browser";
import { ErrorAlert } from "@/components/ui/alert";
import { loginSchema } from "./schema";
import Icons from "@/components/ui/icons";
import tamaguiConfig from "@/tamagui.config";
import { useLoginMutation } from "./hooks";
import LoginGithubButton from "./components/login-github";
import { useServerConfig } from "@/hooks/useServerConfig";
WebBrowser.maybeCompleteAuthSession();
export default function LoginPage() {
const form = useZForm(loginSchema);
const login = useLoginMutation();
const { data: oauthList } = useServerConfig("oauth");
const onSubmit = form.handleSubmit((values) => {
login.mutate(values);
@@ -98,6 +102,16 @@ export default function LoginPage() {
</Link>
</XStack>
{oauthList?.length > 0 && (
<>
<Text textAlign="center" fontSize="$3">
or
</Text>
{oauthList.includes("github") && <LoginGithubButton />}
</>
)}
<Separator w="100%" />
<Button
@@ -1,5 +1,5 @@
import React, { useMemo, useState } from "react";
import { Avatar, Button, ListItem, View, YGroup } from "tamagui";
import { Button, ListItem, View, YGroup } from "tamagui";
import MenuButton from "@/components/ui/menu-button";
import Icons from "@/components/ui/icons";
import SearchInput from "@/components/ui/search-input";
@@ -7,6 +7,7 @@ import { useTeamId } from "@/stores/auth";
import { changeRoleModal } from "./change-role-form";
import { useRemoveMemberMutation } from "../hooks/query";
import { showDialog } from "@/hooks/useDialog";
import Avatar from "@/components/ui/avatar";
type Props = {
members?: any[];
@@ -59,11 +60,7 @@ const MemberList = ({ members, allowWrite }: Props) => {
title={member.user?.name}
subTitle={member.role}
pr="$2"
icon={
<Avatar size="$3" circular>
<Avatar.Fallback bg="$blue5" />
</Avatar>
}
icon={<Avatar src={member.user?.image} size="$4" />}
iconAfter={
allowWrite ? (
<MemberActionButton
+45
View File
@@ -49,12 +49,18 @@ importers:
expo:
specifier: ~52.0.6
version: 52.0.6(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
expo-auth-session:
specifier: ~6.0.0
version: 6.0.0([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
expo-blur:
specifier: ~14.0.1
version: 14.0.1([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
expo-constants:
specifier: ~17.0.3
version: 17.0.3([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))
expo-crypto:
specifier: ~14.0.1
version: 14.0.1([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))
expo-font:
specifier: ~13.0.0
version: 13.0.1([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected])
@@ -3159,6 +3165,11 @@ packages:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
[email protected]:
resolution: {integrity: sha512-w+1quSmKp8SYKT+GAFHSN5c6u+PqoVRIfpsLyRQrQdOnBA9dA8Hw6JT9sHNFmA30A2v1b/sdYZE3qKuRJFNSWQ==}
peerDependencies:
expo: '*'
[email protected]:
resolution: {integrity: sha512-WatvD7JVC89EsllXFYcS/rji3ajVzE2B/USo0TqedsETixwyVCQfrrvCdCPQyuKghrxVNEj8bQ/Qbea/RZLYjg==}
peerDependencies:
@@ -3166,6 +3177,12 @@ packages:
react: '*'
react-native: '*'
[email protected]:
resolution: {integrity: sha512-t40IvmUnWPdSFTr/d3FxDo3qbHdt6hPoRApZ9KH8/UoTjkdoSKnxi6W0/svpISDPMi25gB0lNYwy72YUisl1Yw==}
peerDependencies:
react: '*'
react-native: '*'
[email protected]:
resolution: {integrity: sha512-3Q6jFBLbY8n2vwk28ycUC+eIlVhnlqwkXUKk/Lfaj+SGV3AZMQyrixe7OYwJdUfwqETBrnYYMB6uNrJzOSbG+g==}
peerDependencies:
@@ -3179,6 +3196,11 @@ packages:
expo: '*'
react-native: '*'
[email protected]:
resolution: {integrity: sha512-/gGpD9UAz8fgZtU08cwwqeQElkFmMy2Hc8lLa9laSjD3YN0XM07zDJyJ+CC1VhQ63G8WpUnq1IHSmaPbbLp+oQ==}
peerDependencies:
expo: '*'
[email protected]:
resolution: {integrity: sha512-HKe0dGW3FWYFi1F3THVnTRueTG7j0onmEpUJKRB4UbjeHD2723cn/EutcG216wvrJeebe8w3+00F8Z4xk+9Jrw==}
peerDependencies:
@@ -10010,6 +10032,10 @@ snapshots:
jest-message-util: 29.7.0
jest-util: 29.7.0
[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])):
dependencies:
expo: 52.0.6(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]):
dependencies:
'@expo/image-utils': 0.6.3
@@ -10022,6 +10048,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]):
dependencies:
expo-application: 6.0.1([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))
expo-constants: 17.0.3([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))
expo-crypto: 14.0.1([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))
expo-linking: 7.0.2([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
expo-web-browser: 14.0.1([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))
invariant: 2.2.4
react: 18.3.1
react-native: 0.76.1(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])
transitivePeerDependencies:
- expo
- supports-color
[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]):
dependencies:
expo: 52.0.6(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
@@ -10037,6 +10077,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])):
dependencies:
base64-js: 1.5.1
expo: 52.0.6(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])
[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])):
dependencies:
expo: 52.0.6(@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@expo/[email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])))([email protected]([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected]))([email protected])