diff --git a/api/list/domain/[query].ts b/api/list/domain/[query].ts new file mode 100644 index 0000000..5216490 --- /dev/null +++ b/api/list/domain/[query].ts @@ -0,0 +1,30 @@ +import { NowRequest, NowResponse } from '@vercel/node'; +import fetch from 'cross-fetch'; +import { send, sendError } from '../../../util/http'; + +export default async function handler( + req: NowRequest, + res: NowResponse +): Promise { + const { query } = req.query; + + if (!query || typeof query !== 'string') { + return sendError(res, new Error('No query given')); + } + + try { + const response = await fetch( + `https://domainr.p.rapidapi.com/v2/search?query=${encodeURIComponent( + query + )}`, + { + headers: { + 'X-RapidAPI-Key': process.env.DOMAINR_API_KEY, + }, + } + ).then((res) => res.json()); + send(res, response); + } catch (err) { + sendError(res, err); + } +} diff --git a/api/services/domain/[query].ts b/api/services/domain/[query].ts index 9a18101..019aee3 100644 --- a/api/services/domain/[query].ts +++ b/api/services/domain/[query].ts @@ -1,21 +1,46 @@ -import whois from 'whois-json'; -import { send, sendError } from '../../../util/http'; import { NowRequest, NowResponse } from '@vercel/node'; +import fetch from 'cross-fetch'; +import { send, sendError } from '../../../util/http'; export default async function handler( req: NowRequest, res: NowResponse ): Promise { const { query } = req.query; + console.log(query); if (!query || typeof query !== 'string') { return sendError(res, new Error('No query given')); } try { - const response = await whois(query, { follow: 3, verbose: true }); - const availability = response[0].data.domainName ? false : true; - send(res, { availability }); + const response = await fetch( + `https://domainr.p.rapidapi.com/v2/status?domain=${encodeURIComponent( + query + )}`, + { + headers: { + 'X-RapidAPI-Key': process.env.DOMAINR_API_KEY, + }, + } + ).then((res) => res.json()); + if (!response.status) { + throw new Error('Internal Server Error'); + } + const availability = response.status.map((stat) => ({ + query: stat.domain, + availability: stat.summary === 'inactive', + })); + if (availability.length === 0) { + return send(res, { availability: [] }); + } + // NOTE: for backward compatibility + send(res, { + availability: + response.status.length > 1 + ? availability + : availability[0].availability, + }); } catch (err) { sendError(res, err); } diff --git a/api/services/gitlab/[query].ts b/api/services/gitlab/[query].ts index 81e758f..518b813 100644 --- a/api/services/gitlab/[query].ts +++ b/api/services/gitlab/[query].ts @@ -1,6 +1,6 @@ -import { send, sendError } from '../../../util/http'; import { NowRequest, NowResponse } from '@vercel/node'; -import nodeFetch from 'isomorphic-unfetch'; +import nodeFetch from 'cross-fetch'; +import { send, sendError } from '../../../util/http'; export default async function handler( req: NowRequest, diff --git a/package.json b/package.json index 7a96d8e..fc1826d 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@sentry/browser": "^5.21.1", + "cross-fetch": "^3.0.6", "easy-peasy": "^3.3.1", "fetch-suspense": "^1.2.2", "framer-motion": "^2.5.1", @@ -18,9 +19,7 @@ "i18next-chained-backend": "^2.0.1", "i18next-localstorage-backend": "^3.1.1", "i18next-xhr-backend": "^3.2.2", - "isomorphic-unfetch": "^3.0.0", "npm-name": "^6.0.1", - "prop-types": "^15.7.2", "rc-tooltip": "^5.0.0", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -34,9 +33,8 @@ "react-spinners": "^0.9.0", "react-toastify": "^6.0.8", "styled-components": "^5.1.1", - "swr": "^0.3.0", - "validator": "^13.1.0", - "whois-json": "^2.0.4" + "swr": "^0.3.2", + "validator": "^13.1.0" }, "devDependencies": { "@sentry/cli": "^1.55.1", diff --git a/src/components/Suggestion.tsx b/src/components/Suggestion.tsx index 15cbb2a..fee8826 100644 --- a/src/components/Suggestion.tsx +++ b/src/components/Suggestion.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState, useRef } from 'react'; import styled from 'styled-components'; import { useTranslation } from 'react-i18next'; -import fetch from 'isomorphic-unfetch'; +import fetch from 'cross-fetch'; import { TiArrowSync } from 'react-icons/ti'; import { motion } from 'framer-motion'; diff --git a/src/components/cards/core.tsx b/src/components/cards/core.tsx index 6504997..11adee4 100644 --- a/src/components/cards/core.tsx +++ b/src/components/cards/core.tsx @@ -28,11 +28,24 @@ export const Card: React.FC<{ title: string }> = ({ title, children }) => { ); }; -export const Repeater: React.FC<{ +export interface CommonRepeaterProps { items: string[]; moreItems?: string[]; +} + +export interface MultiShotRepeaterProps extends CommonRepeaterProps { + singleShot?: false; children: (name: string) => React.ReactNode; -}> = ({ items = [], moreItems = [], children }) => { +} + +export interface SingleShotRepeaterProps extends CommonRepeaterProps { + singleShot: true; + children: (name: string[]) => React.ReactNode; +} + +export type RepeaterProps = MultiShotRepeaterProps | SingleShotRepeaterProps; + +export const Repeater: React.FC = (props) => { const [revealAlternatives, setRevealAlternatives] = useState(false); const { t } = useTranslation(); @@ -43,19 +56,29 @@ export const Repeater: React.FC<{ useEffect(() => { setRevealAlternatives(false); - }, [items, moreItems]); + }, [props.items, props.moreItems]); + + const { items, moreItems = [] } = props; return ( <> - {items.map((name) => ( - {children(name)} - ))} + {props.singleShot === true ? ( + {props.children(items)} + ) : ( + items.map((name) => ( + {props.children(name)} + )) + )} - {revealAlternatives - ? moreItems.map((name) => ( - {children(name)} + {revealAlternatives ? ( + props.singleShot ? ( + {props.children(moreItems)} + ) : ( + moreItems.map((name) => ( + {props.children(name)} )) - : null} + ) + ) : null} {moreItems.length > 0 && !revealAlternatives ? ( ) : null} diff --git a/src/components/cards/providers/AppStore.tsx b/src/components/cards/providers/AppStore.tsx index 3da1ab6..fe6a1dc 100644 --- a/src/components/cards/providers/AppStore.tsx +++ b/src/components/cards/providers/AppStore.tsx @@ -7,9 +7,10 @@ import { Card, Result } from '../core'; const Search: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const term = encodeURIComponent(query); const response = useFetch( - `/api/services/appstore/${term}?country=${t('countryCode')}` + `/api/services/appstore/${encodeURIComponent(query)}?country=${t( + 'countryCode' + )}` ) as { result: Array<{ name: string; viewURL: string; price: number; id: string }>; }; diff --git a/src/components/cards/providers/Cratesio.tsx b/src/components/cards/providers/Cratesio.tsx index e8ecc79..e4ee4d2 100644 --- a/src/components/cards/providers/Cratesio.tsx +++ b/src/components/cards/providers/Cratesio.tsx @@ -1,11 +1,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { SiRust } from 'react-icons/si'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const CratesioCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Domains.tsx b/src/components/cards/providers/Domains.tsx index 7a02089..c8531fe 100644 --- a/src/components/cards/providers/Domains.tsx +++ b/src/components/cards/providers/Domains.tsx @@ -1,17 +1,35 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { MdDomain } from 'react-icons/md'; - -import { Card, Repeater, DedicatedAvailability } from '../core'; import { zones } from '../../../util/zones'; +import { Card, DedicatedAvailability, Repeater } from '../core'; +import useSWR from 'swr'; +import fetch from 'cross-fetch'; +import { normalize } from '../../../util/text'; + +export interface DomainrResponse { + results: { + domain: string; + host: string; + subdomain: string; + zone: string; + path: string; + registerURL: string; + }[]; +} + +function fetcher(url: string) { + return fetch(url, {}).then((res) => res.json()); +} const DomainCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { + alphanumeric: false, + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const domainHackSuggestions = zones .map((zone) => new RegExp(`${zone}$`).exec(lowerCase.slice(1))) @@ -23,40 +41,63 @@ const DomainCard: React.FC<{ query: string }> = ({ query }) => { lowerCase.substring(m.index + 1) ); - const names = [ - `${lowerCase}.com`, - `${lowerCase}.org`, - `${lowerCase}.app`, - `${lowerCase}.dev`, - `${lowerCase}.io`, - `${lowerCase}.sh`, - ...domainHackSuggestions, - ]; - const moreNames = [ + const { data } = useSWR( + `/api/list/domain/${encodeURIComponent(query)}`, + fetcher + ); + + const domainrSuggestions = + data?.results + ?.filter((res) => res.subdomain !== '' && res.path === '') + ?.map((res) => res.domain) ?? []; + + const names = + // use Set() to eliminate dupes + new Set([ + ...['com', 'org', 'app', 'io'].map((tld) => lowerCase + '.' + tld), + ...domainHackSuggestions, + ...domainrSuggestions, + ]); + + const moreNames = new Set([ `${lowerCase}app.com`, `get${lowerCase}.com`, - `${lowerCase}.co`, - `${lowerCase}.tools`, - `${lowerCase}.build`, - `${lowerCase}.run`, - `${lowerCase}.ai`, - `${lowerCase}.design`, - `${lowerCase}.directory`, - `${lowerCase}.guru`, - `${lowerCase}.ninja`, - `${lowerCase}.net`, - `${lowerCase}.info`, - `${lowerCase}.biz`, - `${lowerCase}.website`, - `${lowerCase}.eu`, - ]; + ...[ + 'co', + 'dev', + 'sh', + 'tools', + 'build', + 'run', + 'ai', + 'design', + 'directory', + 'guru', + 'ninja', + 'net', + 'info', + 'biz', + 'website', + 'eu', + ].map((tld) => lowerCase + '.' + tld), + ]); + + for (const name of moreNames) { + if (names.has(name)) { + moreNames.delete(name); + } + } return ( - - {(name) => ( + + {(names: string[]) => ( = ({ query }) => { const { t } = useTranslation(); - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/GitHubRepository.tsx b/src/components/cards/providers/GitHubRepository.tsx index 7c6be82..c12a984 100644 --- a/src/components/cards/providers/GitHubRepository.tsx +++ b/src/components/cards/providers/GitHubRepository.tsx @@ -1,13 +1,15 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaGithub } from 'react-icons/fa'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const GithubCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query, {}); + const lowerCase = normalizedQuery.toLowerCase(); - const names = [query, `${lowerCase}-dev`, `${lowerCase}-org`]; + const names = [normalizedQuery, `${lowerCase}-dev`, `${lowerCase}-org`]; const moreNames = [ `${lowerCase}hq`, `${lowerCase}-team`, diff --git a/src/components/cards/providers/GitHubSearch.tsx b/src/components/cards/providers/GitHubSearch.tsx index 8897b9d..26b6c3e 100644 --- a/src/components/cards/providers/GitHubSearch.tsx +++ b/src/components/cards/providers/GitHubSearch.tsx @@ -7,10 +7,12 @@ import { Card, Result } from '../core'; const Search: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const searchQuery = encodeURIComponent(`${query} in:name`); + const searchQuery = `${query} in:name`; const limit = 10; const response = useFetch( - `https://api.github.com/search/repositories?q=${searchQuery}&per_page=${limit}` + `https://api.github.com/search/repositories?q=${encodeURIComponent( + searchQuery + )}&per_page=${limit}` ) as { items: Array<{ full_name: string; diff --git a/src/components/cards/providers/GitLab.tsx b/src/components/cards/providers/GitLab.tsx index 26b180d..84f6a16 100644 --- a/src/components/cards/providers/GitLab.tsx +++ b/src/components/cards/providers/GitLab.tsx @@ -1,12 +1,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaGitlab } from 'react-icons/fa'; +import { normalize } from '../../../util/text'; import { Card, Repeater, DedicatedAvailability } from '../core'; const GitLabCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Heroku.tsx b/src/components/cards/providers/Heroku.tsx index b025e84..3432634 100644 --- a/src/components/cards/providers/Heroku.tsx +++ b/src/components/cards/providers/Heroku.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { DiHeroku } from 'react-icons/di'; +import { normalize } from '../../../util/text'; import { Card, Repeater, DedicatedAvailability } from '../core'; const HerokuCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Homebrew.tsx b/src/components/cards/providers/Homebrew.tsx index 4ff6269..28e7157 100644 --- a/src/components/cards/providers/Homebrew.tsx +++ b/src/components/cards/providers/Homebrew.tsx @@ -1,12 +1,14 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { IoIosBeer } from 'react-icons/io'; +import { normalize } from '../../../util/text'; import { Card, Repeater, ExistentialAvailability } from '../core'; const HomebrewCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Instagram.tsx b/src/components/cards/providers/Instagram.tsx index e44c652..0451d7b 100644 --- a/src/components/cards/providers/Instagram.tsx +++ b/src/components/cards/providers/Instagram.tsx @@ -1,14 +1,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaInstagram } from 'react-icons/fa'; +import { normalize } from '../../../util/text'; import { Card, Repeater, ExistentialAvailability } from '../core'; const InstagramCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query, { allowHyphens: false }); + const lowerCase = normalizedQuery.toLowerCase(); - const names = [query]; + const names = [normalizedQuery]; const moreNames = [`${lowerCase}app`, `${lowerCase}_hq`, `get.${lowerCase}`]; return ( diff --git a/src/components/cards/providers/JsOrg.tsx b/src/components/cards/providers/JsOrg.tsx index f798be0..6ee2986 100644 --- a/src/components/cards/providers/JsOrg.tsx +++ b/src/components/cards/providers/JsOrg.tsx @@ -1,17 +1,17 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaJsSquare } from 'react-icons/fa'; +import { normalize } from '../../../util/text'; import { Card, Repeater, DedicatedAvailability } from '../core'; const JsOrgCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); - + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; return ( diff --git a/src/components/cards/providers/Linux.tsx b/src/components/cards/providers/Linux.tsx index b46feba..7879464 100644 --- a/src/components/cards/providers/Linux.tsx +++ b/src/components/cards/providers/Linux.tsx @@ -1,11 +1,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { SiDebian, SiUbuntu } from 'react-icons/si'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const LinuxCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Netlify.tsx b/src/components/cards/providers/Netlify.tsx index 99b84d2..73aa584 100644 --- a/src/components/cards/providers/Netlify.tsx +++ b/src/components/cards/providers/Netlify.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { normalize } from '../../../util/text'; import { NetlifyIcon } from '../../Icons'; import { Card, Repeater, DedicatedAvailability } from '../core'; const NetlifyCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Npm.tsx b/src/components/cards/providers/Npm.tsx index 264ebb7..bb060a9 100644 --- a/src/components/cards/providers/Npm.tsx +++ b/src/components/cards/providers/Npm.tsx @@ -1,11 +1,15 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { RiNpmjsFill, RiNpmjsLine } from 'react-icons/ri'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const NpmCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase, `${lowerCase}-js`]; const moreNames = [`${lowerCase}js`]; diff --git a/src/components/cards/providers/Ocaml.tsx b/src/components/cards/providers/Ocaml.tsx index cfee834..f06b2be 100644 --- a/src/components/cards/providers/Ocaml.tsx +++ b/src/components/cards/providers/Ocaml.tsx @@ -1,12 +1,14 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { normalize } from '../../../util/text'; import { OcamlIcon } from '../../Icons'; import { Card, Repeater, DedicatedAvailability } from '../core'; const OcamlCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const lowerCase = query.toLowerCase(); + const normalizedQuery = normalize(query, { allowUnderscore: false }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/PyPI.tsx b/src/components/cards/providers/PyPI.tsx index 477fe86..cfd2b12 100644 --- a/src/components/cards/providers/PyPI.tsx +++ b/src/components/cards/providers/PyPI.tsx @@ -2,14 +2,15 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaPython } from 'react-icons/fa'; -import { capitalize } from '../../../util/text'; +import { capitalize, normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const PypiCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - - const names = [query]; - const moreNames = [`Py${capitalize(query)}`]; + const normalizedQuery = normalize(query); + const capitalCase = capitalize(normalizedQuery); + const names = [normalizedQuery]; + const moreNames = [`Py${capitalCase}`]; return ( diff --git a/src/components/cards/providers/RubyGems.tsx b/src/components/cards/providers/RubyGems.tsx index e470b06..292c3dd 100644 --- a/src/components/cards/providers/RubyGems.tsx +++ b/src/components/cards/providers/RubyGems.tsx @@ -1,13 +1,15 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { SiRubygems } from 'react-icons/si'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const RubyGemsCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - - const names = [query]; - const moreNames = [`${query.toLowerCase()}-rb`]; + const normalizedQuery = normalize(query); + const lowerCase = normalizedQuery.toLowerCase(); + const names = [normalizedQuery]; + const moreNames = [`${lowerCase}-rb`]; return ( diff --git a/src/components/cards/providers/S3.tsx b/src/components/cards/providers/S3.tsx index a09d696..eb7d375 100644 --- a/src/components/cards/providers/S3.tsx +++ b/src/components/cards/providers/S3.tsx @@ -1,16 +1,17 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaAws } from 'react-icons/fa'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const S3Card: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Slack.tsx b/src/components/cards/providers/Slack.tsx index d1cba18..67ab6dd 100644 --- a/src/components/cards/providers/Slack.tsx +++ b/src/components/cards/providers/Slack.tsx @@ -1,16 +1,15 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaSlack } from 'react-icons/fa'; +import { normalize } from '../../../util/text'; import { Card, DedicatedAvailability, Repeater } from '../core'; const SlackCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { allowUnderscore: false }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/components/cards/providers/Spectrum.tsx b/src/components/cards/providers/Spectrum.tsx index 2b1d0d7..c89aef2 100644 --- a/src/components/cards/providers/Spectrum.tsx +++ b/src/components/cards/providers/Spectrum.tsx @@ -2,10 +2,12 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Card, Repeater, DedicatedAvailability } from '../core'; import { SpectrumIcon } from '../../Icons'; +import { normalize } from '../../../util/text'; const SpectrumCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const names = [query]; + const normalizedQuery = normalize(query); + const names = [normalizedQuery]; return ( diff --git a/src/components/cards/providers/Twitter.tsx b/src/components/cards/providers/Twitter.tsx index 7d86437..d624a84 100644 --- a/src/components/cards/providers/Twitter.tsx +++ b/src/components/cards/providers/Twitter.tsx @@ -2,19 +2,17 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { FaTwitter } from 'react-icons/fa'; -import { capitalize } from '../../../util/text'; +import { capitalize, normalize } from '../../../util/text'; import { Card, Repeater, DedicatedAvailability } from '../core'; const TwitterCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/-/g, '_'); - const lowerCase = sanitizedQuery.toLowerCase(); - const capitalCase = capitalize(sanitizedQuery); + const normalizedQuery = normalize(query, { allowHyphens: false }); + const lowerCase = normalizedQuery.toLowerCase(); + const capitalCase = capitalize(normalizedQuery); - const names = [sanitizedQuery, `${capitalCase}App`, `${lowerCase}hq`]; + const names = [normalizedQuery, `${capitalCase}App`, `${lowerCase}hq`]; const moreNames = [ `hey${lowerCase}`, `${capitalCase}Team`, diff --git a/src/components/cards/providers/Vercel.tsx b/src/components/cards/providers/Vercel.tsx index 1117728..5f737b2 100644 --- a/src/components/cards/providers/Vercel.tsx +++ b/src/components/cards/providers/Vercel.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { normalize } from '../../../util/text'; import { NowIcon } from '../../Icons'; import { Card, Repeater, DedicatedAvailability } from '../core'; const VercelCard: React.FC<{ query: string }> = ({ query }) => { const { t } = useTranslation(); - - const sanitizedQuery = query - .replace(/[^0-9a-zA-Z_-]/g, '') - .replace(/_/g, '-'); - const lowerCase = sanitizedQuery.toLowerCase(); + const normalizedQuery = normalize(query, { + allowUnderscore: false, + }); + const lowerCase = normalizedQuery.toLowerCase(); const names = [lowerCase]; diff --git a/src/util/text.ts b/src/util/text.ts index d019ccd..86d39d4 100644 --- a/src/util/text.ts +++ b/src/util/text.ts @@ -5,11 +5,43 @@ export function capitalize(text: string): string { export function sanitize(text: string): string { return text - .replace(/[\s@+!#$%^&*()[\]./<>{}]/g, '') + .replace(/[@+!#$%^&*()[\]./<>{}]/g, '') .normalize('NFD') .replace(/[\u0300-\u036f]/g, ''); } +export interface NormalizeOptions { + alphanumeric?: boolean; + allowSpaces?: boolean; + allowUnderscore?: boolean; + allowHyphens?: boolean; +} + +export function normalize( + text: string, + { + alphanumeric = true, + allowSpaces = false, + allowUnderscore = true, + allowHyphens = true, + }: NormalizeOptions = {} +): string { + if (alphanumeric) { + text = text.replace(/[^0-9a-zA-Z-_\s]/g, ''); + } + if (!allowUnderscore) { + text = text.replace(/_/g, '-'); + } + if (!allowHyphens) { + text = text.replace(/-/g, allowUnderscore ? '_' : ' '); + } + if (!allowSpaces) { + text = text.replace(/\s/g, ''); + } + text = text.replace(/[-_\s]+$/, ''); + return text; +} + export function upper(word: string): string { return word.toUpperCase(); } diff --git a/tsconfig.json b/tsconfig.json index 11a3e7d..628564b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "isolatedModules": true, "noEmit": true, "jsx": "react", - "allowJs": true + "allowJs": true, + "downlevelIteration": true }, "include": ["src", "types"] } diff --git a/util/http.ts b/util/http.ts index 50e031c..90aac2c 100644 --- a/util/http.ts +++ b/util/http.ts @@ -1,5 +1,5 @@ import { NowResponse } from '@vercel/node'; -import nodeFetch from 'isomorphic-unfetch'; +import nodeFetch from 'cross-fetch'; export type HttpMethod = | 'GET' diff --git a/yarn.lock b/yarn.lock index 64555a3..7c08618 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3154,14 +3154,6 @@ callsites@^3.0.0: resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - camel-case@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" @@ -3253,30 +3245,6 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -change-case@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" - integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw== - dependencies: - camel-case "^3.0.0" - constant-case "^2.0.0" - dot-case "^2.1.0" - header-case "^1.0.0" - is-lower-case "^1.1.0" - is-upper-case "^1.1.0" - lower-case "^1.1.1" - lower-case-first "^1.0.0" - no-case "^2.3.2" - param-case "^2.1.0" - pascal-case "^2.0.0" - path-case "^2.1.0" - sentence-case "^2.1.0" - snake-case "^2.1.0" - swap-case "^1.1.0" - title-case "^2.1.0" - upper-case "^1.1.1" - upper-case-first "^1.1.0" - chardet@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3394,15 +3362,6 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - clone-deep@^0.2.4: version "0.2.4" resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" @@ -3611,14 +3570,6 @@ console-browserify@^1.1.0: resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -constant-case@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" - integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= - dependencies: - snake-case "^2.1.0" - upper-case "^1.1.1" - constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -3771,6 +3722,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-fetch@^3.0.6: + version "3.0.6" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== + dependencies: + node-fetch "2.6.1" + cross-spawn@7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" @@ -4155,11 +4113,6 @@ decompress-response@^5.0.0: dependencies: mimic-response "^2.0.0" -dedent-js@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz#bee5fb7c9e727d85dffa24590d10ec1ab1255305" - integrity sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU= - deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -4435,13 +4388,6 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -dot-case@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" - integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4= - dependencies: - no-case "^2.2.0" - dot-case@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" @@ -5793,14 +5739,6 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -header-case@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" - integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.3" - hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" @@ -5886,7 +5824,7 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" -html-entities@^1.2.1, html-entities@^1.3.1: +html-entities@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== @@ -6486,13 +6424,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-lower-case@^1.1.0: - version "1.1.3" - resolved "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" - integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= - dependencies: - lower-case "^1.1.0" - is-negative-zero@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" @@ -6619,13 +6550,6 @@ is-typedarray@~1.0.0: resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-upper-case@^1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" - integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= - dependencies: - upper-case "^1.1.0" - is-url-superb@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" @@ -6682,14 +6606,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isomorphic-unfetch@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.0.0.tgz#de6d80abde487b17de2c400a7ef9e5ecc2efb362" - integrity sha512-V0tmJSYfkKokZ5mgl0cmfQMTb7MLHsBMngTkbLY0eXvKqiVRRoZP04Ly+KhKrJfKtzC9E6Pp15Jo+bwh7Vi2XQ== - dependencies: - node-fetch "^2.2.0" - unfetch "^4.0.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -7616,18 +7532,6 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0 || ^4.0.0" -lower-case-first@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" - integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= - dependencies: - lower-case "^1.1.2" - -lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: - version "1.1.4" - resolved "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= - lower-case@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" @@ -8108,13 +8012,6 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -no-case@^2.2.0, no-case@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - no-case@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" @@ -8133,7 +8030,7 @@ nock@^13.0.4: lodash.set "^4.3.2" propagate "^2.0.0" -node-fetch@^2.2.0, node-fetch@^2.6.0: +node-fetch@2.6.1, node-fetch@^2.6.0: version "2.6.1" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -8606,13 +8503,6 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" -param-case@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= - dependencies: - no-case "^2.2.0" - param-case@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" @@ -8679,14 +8569,6 @@ parseurl@~1.3.2, parseurl@~1.3.3: resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascal-case@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" - integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4= - dependencies: - camel-case "^3.0.0" - upper-case-first "^1.1.0" - pascal-case@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" @@ -8705,13 +8587,6 @@ path-browserify@0.0.1: resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== -path-case@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" - integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU= - dependencies: - no-case "^2.2.0" - path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -10756,14 +10631,6 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -sentence-case@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" - integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= - dependencies: - no-case "^2.2.0" - upper-case-first "^1.1.2" - serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -10937,18 +10804,6 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -smart-buffer@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" - integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== - -snake-case@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" - integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= - dependencies: - no-case "^2.2.0" - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -11000,14 +10855,6 @@ sockjs@0.3.20: uuid "^3.4.0" websocket-driver "0.6.5" -socks@^2.2.2: - version "2.4.4" - resolved "https://registry.npmjs.org/socks/-/socks-2.4.4.tgz#f1a3382e7814ae28c97bb82a38bc1ac24b21cca2" - integrity sha512-7LmHN4IHj1Vpd/k8D872VGCHJ6yIVyeFkfIBExRmGPYQ/kdUkpdg9eKh9oOzYYYKQhuxavayJHTnmBG+EzluUA== - dependencies: - ip "^1.1.5" - smart-buffer "^4.1.0" - sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -11243,7 +11090,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.1.0: version "4.2.0" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== @@ -11469,15 +11316,7 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -swap-case@^1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" - integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= - dependencies: - lower-case "^1.1.1" - upper-case "^1.1.1" - -swr@^0.3.0: +swr@^0.3.2: version "0.3.2" resolved "https://registry.npmjs.org/swr/-/swr-0.3.2.tgz#1197e06553f71afbc42dba758459f12daea96805" integrity sha512-Bs5Bihq1hQ66O5bdKaL47iZ2nlAaBsd8tTLRLkw9stZeuBEfH7zSuQI95S2TpchL0ybsMq3isWwuso2uPvCfHA== @@ -11608,14 +11447,6 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3: resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== -title-case@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" - integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o= - dependencies: - no-case "^2.2.0" - upper-case "^1.0.3" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -11829,16 +11660,6 @@ typescript@^4.0.2: resolved "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== -underscore@^1.9.1: - version "1.11.0" - resolved "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz#dd7c23a195db34267186044649870ff1bab5929e" - integrity sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw== - -unfetch@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" - integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg== - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -11924,18 +11745,6 @@ upath@^1.1.1: resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -upper-case-first@^1.1.0, upper-case-first@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" - integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= - dependencies: - upper-case "^1.1.1" - -upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= - uri-js@^4.2.2: version "4.4.0" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" @@ -12327,25 +12136,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -whois-json@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/whois-json/-/whois-json-2.0.4.tgz#9480d2dc6c9b3702be0055eec844aedaac1fbb58" - integrity sha512-ui9Qe0qS4yWtFAPw0q+QPuuH+m4vDDtoO/YtqmF00YoPMMNplhya7SqTVAxThWIVXhWHPEKajFEulegmysdlwQ== - dependencies: - change-case "^3.0.2" - dedent-js "^1.0.1" - html-entities "^1.2.1" - whois "^2.6.0" - -whois@^2.6.0: - version "2.13.0" - resolved "https://registry.npmjs.org/whois/-/whois-2.13.0.tgz#b26c929a27c53f528fc2856531ae02fcdc8a586d" - integrity sha512-u2dtzTUUrpsUMa3d/CZNjcs4u/2o6MZUa7Kyp/HUze4wA8IETyZwkP6+SvUVdqxkqwp8cCCKZg0B1LyQ9vaPvQ== - dependencies: - socks "^2.2.2" - underscore "^1.9.1" - yargs "^15.4.1" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -12509,15 +12299,6 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -12608,7 +12389,7 @@ yaml@^1.10.0, yaml@^1.7.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== -yargs-parser@18.x, yargs-parser@^18.1.2: +yargs-parser@18.x: version "18.1.3" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -12640,23 +12421,6 @@ yargs@^13.3.0, yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"