Merge branch 'master' into use-accounts-image

This commit is contained in:
Karol Wypchło 2022-03-30 17:41:38 +02:00 committed by GitHub
commit 713579d04c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 2380 additions and 525 deletions

View File

@ -45,7 +45,5 @@ jobs:
uses: skynetlabs/deploy-to-skynet-action@v2
with:
upload-dir: packages/website/public
portal-url: https://skynetpro.net
skynet-jwt: ${{ secrets.SKYNET_JWT }}
github-token: ${{ secrets.GITHUB_TOKEN }}
registry-seed: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && secrets.WEBSITE_REGISTRY_SEED || '' }}

View File

@ -4,8 +4,15 @@
name: Nginx Lua Unit Tests
on:
push:
branches:
- "master"
paths:
- ".github/workflows/nginx-lua-unit-tests.yml"
- "docker/nginx/libs/**.lua"
pull_request:
paths:
- ".github/workflows/nginx-lua-unit-tests.yml"
- "docker/nginx/libs/**.lua"
jobs:
@ -25,9 +32,22 @@ jobs:
hererocks env --lua=5.1 -rlatest
source env/bin/activate
luarocks install busted
luarocks install luacov
luarocks install hasher
luarocks install luacheck
- name: Lint code
run: |
source env/bin/activate
luacheck docker/nginx/libs --std ngx_lua+busted
- name: Unit Tests
run: |
source env/bin/activate
busted --verbose --pattern=spec --directory=docker/nginx/libs .
busted --verbose --coverage --pattern=spec --directory=docker/nginx/libs .
cd docker/nginx/libs && luacov
- uses: codecov/codecov-action@v2
with:
directory: docker/nginx/libs
flags: nginx-lua

View File

@ -16,6 +16,7 @@ COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
CMD [ "bash", "-c", \
"./mo < /etc/nginx/conf.d.templates/server.account.conf > /etc/nginx/conf.d/server.account.conf ; \
./mo < /etc/nginx/conf.d.templates/server.api.conf > /etc/nginx/conf.d/server.api.conf; \
./mo < /etc/nginx/conf.d.templates/server.dnslink.conf > /etc/nginx/conf.d/server.dnslink.conf; \
./mo < /etc/nginx/conf.d.templates/server.hns.conf > /etc/nginx/conf.d/server.hns.conf; \
./mo < /etc/nginx/conf.d.templates/server.skylink.conf > /etc/nginx/conf.d/server.skylink.conf ; \
while :; do sleep 6h & wait ${!}; /usr/local/openresty/bin/openresty -s reload; done & \

View File

@ -12,5 +12,14 @@ server {
ssl_certificate /etc/ssl/local-certificate.crt;
ssl_certificate_key /etc/ssl/local-certificate.key;
set_by_lua_block $skynet_portal_domain { return "{{PORTAL_DOMAIN}}" }
set_by_lua_block $skynet_server_domain {
-- fall back to portal domain if server domain is not defined
if "{{SERVER_DOMAIN}}" == "" then
return "{{PORTAL_DOMAIN}}"
end
return "{{SERVER_DOMAIN}}"
}
include /etc/nginx/conf.d/server/server.dnslink;
}

View File

@ -16,7 +16,8 @@ log_by_lua_block {
})
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
ngx.log(ngx.ERR, "Failed accounts service request /track/download/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
ngx.log(ngx.ERR, "Failed accounts service request /track/download/" .. skylink .. ": ", error_response)
end
end
@ -40,7 +41,8 @@ log_by_lua_block {
})
if err or (res and res.status ~= ngx.HTTP_OK) then
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response)
end
end

View File

@ -19,7 +19,8 @@ log_by_lua_block {
})
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
ngx.log(ngx.ERR, "Failed accounts service request /track/registry/" .. registry_action .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
ngx.log(ngx.ERR, "Failed accounts service request /track/registry/" .. registry_action .. ": ", error_response)
end
end

View File

@ -15,7 +15,8 @@ log_by_lua_block {
})
if err or (res and res.status ~= ngx.HTTP_NO_CONTENT) then
ngx.log(ngx.ERR, "Failed accounts service request /track/upload/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
ngx.log(ngx.ERR, "Failed accounts service request /track/upload/" .. skylink .. ": ", error_response)
end
end
@ -40,7 +41,8 @@ log_by_lua_block {
})
if err or (res and res.status ~= ngx.HTTP_OK) then
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", err or ("[HTTP " .. res.status .. "] " .. res.body))
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
ngx.log(ngx.ERR, "Failed malware-scanner request /scan/" .. skylink .. ": ", error_response)
end
end

View File

@ -21,7 +21,7 @@ local function divide_string( str, max )
return result
end
local function number_to_bit( num, length )
local bits = {}
@ -144,7 +144,7 @@ function basexx.to_basexx( str, alphabet, bits, pad )
end
table.insert( result, pad )
return table.concat( result )
return table.concat( result )
end
--------------------------------------------------------------------------------
@ -225,16 +225,16 @@ local function length_error( len, d )
end
local z85Decoder = { 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00 }
function basexx.from_z85( str, ignore )

View File

@ -82,7 +82,8 @@ function _M.get_account_limits()
-- fail gracefully in case /user/limits failed
if err or (res and res.status ~= ngx.HTTP_OK) then
ngx.log(ngx.ERR, "Failed accounts service request /user/limits?unit=byte: ", err or ("[HTTP " .. res.status .. "] " .. res.body))
local error_response = err or ("[HTTP " .. res.status .. "] " .. res.body)
ngx.log(ngx.ERR, "Failed accounts service request /user/limits?unit=byte: ", error_response)
ngx.var.account_limits = cjson.encode(anon_limits)
elseif res and res.status == ngx.HTTP_OK then
ngx.var.account_limits = res.body
@ -109,7 +110,7 @@ function _M.has_subscription()
end
function _M.is_auth_required()
-- authentication is required if mode is set to "authenticated"
-- authentication is required if mode is set to "authenticated"
-- or "subscription" (require active subscription to a premium plan)
return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "authenticated" or _M.is_subscription_required()
end
@ -118,7 +119,7 @@ function _M.is_subscription_required()
return os.getenv("ACCOUNTS_LIMIT_ACCESS") == "subscription"
end
function is_access_always_allowed()
local is_access_always_allowed = function ()
-- options requests do not attach cookies - should always be available
-- requests should not be limited based on accounts if accounts are not enabled
return ngx.req.get_method() == "OPTIONS" or not _M.accounts_enabled()

View File

@ -27,7 +27,7 @@ function _M.hash(skylink)
-- parse with blake2b with key length of 32
local blake2bHashed = hasher.blake2b(rawMerkleRoot, 32)
-- hex encode the blake hash
local hexHashed = basexx.to_hex(blake2bHashed)

View File

@ -7,7 +7,7 @@ describe("parse", function()
it("should return unchanged base64 skylink", function()
assert.is.same(skynet_skylink.parse(base64), base64)
end)
it("should transform base32 skylink into base64", function()
assert.is.same(skynet_skylink.parse(base32), base64)
end)

View File

@ -15,31 +15,31 @@ describe("extract_cookie", function()
it("should return nil if cookie string is nil", function()
local cookie = utils.extract_cookie_value(nil, "aaa")
assert.is_nil(cookie)
end)
it("should return nil if cookie name is not found", function()
local cookie = utils.extract_cookie(cookie_string, "foo")
assert.is_nil(cookie)
end)
it("should return cookie if cookie_string starts with that cookie name", function()
local cookie = utils.extract_cookie(cookie_string, "aaa")
assert.are.equals(cookie, "aaa=bbb")
end)
it("should return cookie if cookie_string ends with that cookie name", function()
local cookie = utils.extract_cookie(cookie_string, "xxx")
assert.are.equals(cookie, "xxx=yyy")
end)
it("should return cookie with custom matcher", function()
local cookie = utils.extract_cookie(cookie_string, "skynet[-]jwt")
assert.are.equals(cookie, "skynet-jwt=MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==")
end)
end)
@ -49,31 +49,31 @@ describe("extract_cookie_value", function()
it("should return nil if cookie string is nil", function()
local value = utils.extract_cookie_value(nil, "aaa")
assert.is_nil(value)
end)
it("should return nil if cookie name is not found", function()
local value = utils.extract_cookie_value(cookie_string, "foo")
assert.is_nil(value)
end)
it("should return value if cookie_string starts with that cookie name", function()
local value = utils.extract_cookie_value(cookie_string, "aaa")
assert.are.equals(value, "bbb")
end)
it("should return cookie if cookie_string ends with that cookie name", function()
local value = utils.extract_cookie_value(cookie_string, "xxx")
assert.are.equals(value, "yyy")
end)
it("should return cookie with custom matcher", function()
local value = utils.extract_cookie_value(cookie_string, "skynet[-]jwt")
assert.are.equals(value, "MTY0NzUyr8jD-ytiWtspm0tGabKfooxeIDuWcXhJ3lnY0eEw==")
end)
end)

View File

@ -8,3 +8,18 @@ This is a Gatsby application. To run it locally, all you need is:
- `yarn install`
- `yarn start`
## Accessing remote APIs
To be able to log in on a local environment with your production credentials, you'll need to make the browser believe you're actually on the same domain, otherwise the browser will block the session cookie.
To do the trick, edit your `/etc/hosts` file and add a record like this:
```
127.0.0.1 local.skynetpro.net
```
then run `yarn develop:secure` -- it will run `gatsby develop` with `--https --host=local.skynetpro.net -p=443` options.
If you're on macOS, you may need to `sudo` the command to successfully bind to port `443`.
> **NOTE:** This should become easier once we have a docker image for the new dashboard.

View File

@ -6,8 +6,14 @@ import "@fontsource/sora/600.css"; // semibold
import "@fontsource/source-sans-pro/400.css"; // normal
import "@fontsource/source-sans-pro/600.css"; // semibold
import "./src/styles/global.css";
import { MODAL_ROOT_ID } from "./src/components/Modal";
export function wrapPageElement({ element, props }) {
const Layout = element.type.Layout ?? React.Fragment;
return <Layout {...props}>{element}</Layout>;
return (
<Layout {...props}>
{element}
<div id={MODAL_ROOT_ID} />
</Layout>
);
}

View File

@ -5,6 +5,7 @@ module.exports = {
title: `Accounts Dashboard`,
siteUrl: `https://www.yourdomain.tld`,
},
trailingSlash: "never",
plugins: [
"gatsby-plugin-image",
"gatsby-plugin-provide-react",
@ -26,7 +27,7 @@ module.exports = {
app.use(
"/api/",
createProxyMiddleware({
target: "https://account.siasky.net",
target: "https://account.skynetpro.net",
secure: false, // Do not reject self-signed certificates.
changeOrigin: true,
})

View File

@ -6,8 +6,14 @@ import "@fontsource/sora/600.css"; // semibold
import "@fontsource/source-sans-pro/400.css"; // normal
import "@fontsource/source-sans-pro/600.css"; // semibold
import "./src/styles/global.css";
import { MODAL_ROOT_ID } from "./src/components/Modal";
export function wrapPageElement({ element, props }) {
const Layout = element.type.Layout ?? React.Fragment;
return <Layout {...props}>{element}</Layout>;
return (
<Layout {...props}>
{element}
<div id={MODAL_ROOT_ID} />
</Layout>
);
}

View File

@ -9,6 +9,7 @@
],
"scripts": {
"develop": "gatsby develop",
"develop:secure": "gatsby develop --https --host=local.skynetpro.net -p=443",
"start": "gatsby develop",
"build": "gatsby build",
"serve": "gatsby serve",
@ -24,9 +25,11 @@
"classnames": "^2.3.1",
"copy-text-to-clipboard": "^3.0.1",
"dayjs": "^1.10.8",
"formik": "^2.2.9",
"gatsby": "^4.6.2",
"gatsby-plugin-postcss": "^5.7.0",
"http-status-codes": "^2.2.0",
"ky": "^0.30.0",
"nanoid": "^3.3.1",
"path-browserify": "^1.0.1",
"postcss": "^8.4.6",
@ -38,7 +41,8 @@
"react-use": "^17.3.2",
"skynet-js": "^3.0.2",
"swr": "^1.2.2",
"tailwindcss": "^3.0.23"
"tailwindcss": "^3.0.23",
"yup": "^0.32.11"
},
"devDependencies": {
"@babel/core": "^7.17.4",

View File

@ -0,0 +1,145 @@
import dayjs from "dayjs";
import cn from "classnames";
import { useCallback, useState } from "react";
import { Alert } from "../Alert";
import { Button } from "../Button";
import { AddSkylinkToAPIKeyForm } from "../forms/AddSkylinkToAPIKeyForm";
import { CogIcon, TrashIcon } from "../Icons";
import { Modal } from "../Modal";
import { useAPIKeyEdit } from "./useAPIKeyEdit";
import { useAPIKeyRemoval } from "./useAPIKeyRemoval";
export const APIKey = ({ apiKey, onRemoved, onEdited, onRemovalError }) => {
const { id, name, createdAt, skylinks } = apiKey;
const isPublic = apiKey.public === "true";
const [error, setError] = useState(null);
const onSkylinkListEdited = useCallback(() => {
setError(null);
onEdited();
}, [onEdited]);
const onSkylinkListEditFailure = (errorMessage) => setError(errorMessage);
const {
removalError,
removalInitiated,
prompt: promptRemoval,
abort: abortRemoval,
confirm: confirmRemoval,
} = useAPIKeyRemoval({
key: apiKey,
onSuccess: onRemoved,
onFailure: onRemovalError,
});
const {
editInitiated,
prompt: promptEdit,
abort: abortEdit,
addSkylink,
removeSkylink,
} = useAPIKeyEdit({
key: apiKey,
onSkylinkListUpdate: onSkylinkListEdited,
onSkylinkListUpdateFailure: onSkylinkListEditFailure,
});
const closeEditModal = useCallback(() => {
setError(null);
abortEdit();
}, [abortEdit]);
const skylinksNumber = skylinks?.length ?? 0;
const isNotConfigured = isPublic && skylinksNumber === 0;
const skylinksPhrasePrefix = skylinksNumber === 0 ? "No" : skylinksNumber;
const skylinksPhrase = `${skylinksPhrasePrefix} ${skylinksNumber === 1 ? "skylink" : "skylinks"} configured`;
return (
<li
className={cn(
"grid grid-cols-2 sm:grid-cols-[1fr_repeat(2,_max-content)] py-3 px-4 gap-x-8 items-center bg-white odd:bg-palette-100/50"
)}
>
<span className="col-span-2 sm:col-span-1 flex items-center">
<span className="flex flex-col">
<span className={cn("truncate", { "text-palette-300": !name })}>{name || "unnamed key"}</span>
<button
onClick={promptEdit}
className={cn("text-xs hover:underline decoration-dotted", {
"text-error": isNotConfigured,
"text-palette-400": !isNotConfigured,
})}
>
{skylinksPhrase}
</button>
</span>
</span>
<span className="col-span-2 my-4 border-t border-t-palette-200/50 sm:hidden" />
<span className="text-palette-400">{dayjs(createdAt).format("MMM DD, YYYY")}</span>
<span className="flex items-center justify-end">
{isPublic && (
<button
title="Add or remove skylinks"
className="p-1 transition-colors hover:text-primary"
onClick={promptEdit}
>
<CogIcon size={22} />
</button>
)}
<button title="Delete this API key" className="p-1 transition-colors hover:text-error" onClick={promptRemoval}>
<TrashIcon size={16} />
</button>
</span>
{removalInitiated && (
<Modal onClose={abortRemoval} className="flex flex-col gap-4 text-center">
<h4>Delete API key</h4>
<div>
<p>Are you sure you want to delete the following API key?</p>
<p className="font-semibold">{name || id}</p>
</div>
{removalError && <Alert $variant="error">{removalError}</Alert>}
<div className="flex gap-4 justify-center mt-4">
<Button $primary onClick={abortRemoval}>
Cancel
</Button>
<Button onClick={confirmRemoval}>Delete</Button>
</div>
</Modal>
)}
{editInitiated && (
<Modal onClose={closeEditModal} className="flex flex-col gap-4 text-center sm:px-8 sm:py-6">
<h4>Covered skylinks</h4>
{skylinks?.length > 0 ? (
<ul className="text-xs flex flex-col gap-2">
{skylinks.map((skylink) => (
<li key={skylink} className="grid grid-cols-[1fr_min-content] w-full gap-4 items-center">
<code className="whitespace-nowrap select-all truncate bg-palette-100 odd:bg-white p-1">
{skylink}
</code>
<button className="p-1 transition-colors hover:text-error" onClick={() => removeSkylink(skylink)}>
<TrashIcon size={16} />
</button>
</li>
))}
</ul>
) : (
<Alert $variant="info">No skylinks here yet. You can add the first one below 🙃</Alert>
)}
<div className="flex flex-col gap-4">
{error && <Alert $variant="error">{error}</Alert>}
<AddSkylinkToAPIKeyForm addSkylink={addSkylink} />
</div>
<div className="flex gap-4 justify-center mt-4">
<Button onClick={closeEditModal}>Close</Button>
</div>
</Modal>
)}
</li>
);
};

View File

@ -0,0 +1,14 @@
import { APIKey } from "./APIKey";
export const APIKeyList = ({ keys, reloadKeys, title }) => {
return (
<>
<h6 className="text-palette-300 mb-4">{title}</h6>
<ul className="mt-4">
{keys.map((key) => (
<APIKey key={key.id} apiKey={key} onEdited={reloadKeys} onRemoved={reloadKeys} />
))}
</ul>
</>
);
};

View File

@ -0,0 +1 @@
export * from "./APIKeyList";

View File

@ -0,0 +1,43 @@
import { useCallback, useState } from "react";
import accountsService from "../../services/accountsService";
export const useAPIKeyEdit = ({ key, onSkylinkListUpdate, onSkylinkListUpdateFailure }) => {
const [editInitiated, setEditInitiated] = useState(false);
const prompt = () => setEditInitiated(true);
const abort = () => setEditInitiated(false);
const updateSkylinkList = useCallback(
async (action, skylink) => {
try {
await accountsService.patch(`user/apikeys/${key.id}`, {
json: {
[action]: [skylink],
},
});
onSkylinkListUpdate();
return true;
} catch (err) {
if (err.response) {
const { message } = await err.response.json();
onSkylinkListUpdateFailure(message);
} else {
onSkylinkListUpdateFailure("Unknown error occured, please try again.");
}
return false;
}
},
[onSkylinkListUpdate, onSkylinkListUpdateFailure, key]
);
const addSkylink = (skylink) => updateSkylinkList("add", skylink);
const removeSkylink = (skylink) => updateSkylinkList("remove", skylink);
return {
editInitiated,
prompt,
abort,
addSkylink,
removeSkylink,
};
};

View File

@ -0,0 +1,41 @@
import { useCallback, useState } from "react";
import accountsService from "../../services/accountsService";
export const useAPIKeyRemoval = ({ key, onSuccess }) => {
const [removalInitiated, setRemovalInitiated] = useState(false);
const [removalError, setRemovalError] = useState(null);
const prompt = () => {
setRemovalError(null);
setRemovalInitiated(true);
};
const abort = () => setRemovalInitiated(false);
const confirm = useCallback(async () => {
setRemovalError(null);
try {
await accountsService.delete(`user/apikeys/${key.id}`);
setRemovalInitiated(false);
onSuccess();
} catch (err) {
let message = "There was an error processing your request. Please try again later.";
if (err.response) {
const response = await err.response.json();
if (response.message) {
message = response.message;
}
}
setRemovalError(message);
}
}, [onSuccess, key]);
return {
removalInitiated,
removalError,
prompt,
abort,
confirm,
};
};

View File

@ -0,0 +1,10 @@
import styled from "styled-components";
import cn from "classnames";
export const Alert = styled.div.attrs(({ $variant }) => ({
className: cn("px-3 py-2 sm:px-6 sm:py-4 rounded border", {
"bg-blue-100 border-blue-200 text-palette-400": $variant === "info",
"bg-red-100 border-red-200 text-error": $variant === "error",
"bg-green-100 border-green-200 text-palette-400": $variant === "success",
}),
}))``;

View File

@ -0,0 +1 @@
export * from "./Alert";

View File

@ -5,8 +5,8 @@ import styled from "styled-components";
/**
* Primary UI component for user interaction
*/
export const Button = styled.button.attrs(({ disabled, $primary }) => ({
type: "button",
export const Button = styled.button.attrs(({ disabled, $primary, type }) => ({
type,
className: cn("px-6 py-2.5 rounded-full font-sans uppercase text-xs tracking-wide transition-[opacity_filter]", {
"bg-primary text-palette-600": $primary,
"bg-white border-2 border-black text-palette-600": !$primary,
@ -14,6 +14,7 @@ export const Button = styled.button.attrs(({ disabled, $primary }) => ({
"hover:brightness-90": !disabled,
}),
}))``;
Button.propTypes = {
/**
* Is this the principal call to action on the page?
@ -23,9 +24,14 @@ Button.propTypes = {
* Prevent interaction on the button
*/
disabled: PropTypes.bool,
/**
* Type of button (button / submit)
*/
type: PropTypes.oneOf(["button", "submit"]),
};
Button.defaultProps = {
$primary: false,
disabled: false,
type: "button",
};

View File

@ -0,0 +1,56 @@
import PropTypes from "prop-types";
import cn from "classnames";
import { Field } from "formik";
export const TextField = ({ id, label, name, error, touched, className, ...props }) => {
return (
<div className="flex flex-col w-full gap-1">
{label && (
<label className="font-sans uppercase text-palette-300 text-xs" htmlFor={id}>
{label}
</label>
)}
<Field
id={id}
name={name}
className={cn("w-full py-2 px-4 bg-palette-100 rounded-sm placeholder:text-palette-200 outline-1", className, {
"focus:outline outline-palette-200": !error || !touched,
"outline outline-error": touched && error,
})}
{...props}
/>
{touched && error && (
<div className="text-error">
<small>{error}</small>
</div>
)}
</div>
);
};
/** Besides noted properties, it accepts all props accepted by:
* - a regular <input> element
* - Formik's <Field> component
*/
TextField.propTypes = {
/**
* ID for the field. Used to couple <label> and <input> elements
*/
id: PropTypes.string,
/**
* Label for the field
*/
label: PropTypes.string,
/**
* Name of the field
*/
name: PropTypes.string.isRequired,
/**
* Validation error message
*/
error: PropTypes.string,
/**
* Indicates wether or not the user touched the field already.
*/
touched: PropTypes.bool,
};

View File

@ -0,0 +1 @@
export * from "./TextField";

View File

@ -0,0 +1,6 @@
import { Link } from "gatsby";
import styled from "styled-components";
export default styled(Link).attrs({
className: "text-primary underline-offset-3 decoration-dotted hover:text-primary-light hover:underline",
})``;

View File

@ -0,0 +1,11 @@
import { withIconProps } from "../withIconProps";
export const ImportantNoteIcon = withIconProps(({ size, ...props }) => (
<svg width={size} height={size} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" {...props}>
<g fill="none" fillRule="evenodd">
<g fill="currentColor" fillRule="nonzero">
<path d="M16.028 6c5.523 0 10 4.477 10 10s-4.477 10-10 10h-9c-.405.017-.78-.212-.95-.58-.156-.372-.074-.802.21-1.09l2-2C6.82 20.549 6.02 18.31 6.028 16c0-5.523 4.477-10 10-10zm3.05 2.607c-3.526-1.458-7.592-.222-9.71 2.953-2.119 3.174-1.7 7.403 1 10.1.189.185.296.436.3.7 0 .267-.109.523-.3.71l-.93.93h6.59c3.817-.003 7.1-2.701 7.841-6.445.742-3.744-1.264-7.49-4.79-8.948zM16.028 18c.552 0 1 .448 1 1s-.448 1-1 1-1-.448-1-1 .448-1 1-1zM17 12v5h-2v-5h2z" />
</g>
</g>
</svg>
));

View File

@ -14,3 +14,4 @@ export * from "./icons/CopyIcon";
export * from "./icons/ShareIcon";
export * from "./icons/SimpleUploadIcon";
export * from "./icons/TrashIcon";
export * from "./icons/ImportantNoteIcon";

View File

@ -0,0 +1,37 @@
import cn from "classnames";
import PropTypes from "prop-types";
import { PlusIcon } from "../Icons";
import { Panel } from "../Panel";
import { ModalPortal } from "./ModalPortal";
import { Overlay } from "./Overlay";
export const Modal = ({ children, className, onClose }) => (
<ModalPortal>
<Overlay onClick={onClose}>
<div className={cn("relative w-modal max-w-modal shadow-sm rounded")}>
<button onClick={onClose} className="absolute top-[20px] right-[20px]">
<PlusIcon size={14} className="rotate-45" />
</button>
<Panel className={cn("px-8 py-6 sm:px-12 sm:py-10", className)}>{children}</Panel>
</div>
</Overlay>
</ModalPortal>
);
Modal.propTypes = {
/**
* Modal's body.
*/
children: PropTypes.node.isRequired,
/**
* Handler function to be called when user clicks on the "X" icon,
* or outside of the modal.
*/
onClose: PropTypes.func.isRequired,
/**
* Additional CSS classes to be applied to modal's body.
*/
className: PropTypes.string,
};

View File

@ -0,0 +1,16 @@
import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
export const MODAL_ROOT_ID = "__modal-root";
export const ModalPortal = ({ children }) => {
const ref = useRef();
const [isClientSide, setIsClientSide] = useState(false);
useEffect(() => {
ref.current = document.querySelector(MODAL_ROOT_ID) || document.body;
setIsClientSide(true);
}, []);
return isClientSide ? createPortal(children, ref.current) : null;
};

View File

@ -0,0 +1,42 @@
import { useRef } from "react";
import { useLockBodyScroll } from "react-use";
import PropTypes from "prop-types";
export const Overlay = ({ children, onClick }) => {
const overlayRef = useRef(null);
useLockBodyScroll(true);
const handleClick = (event) => {
if (event.target !== overlayRef.current) {
return;
}
event.nativeEvent.stopImmediatePropagation();
onClick?.(event);
};
return (
<div
ref={overlayRef}
role="presentation"
onClick={handleClick}
className="fixed inset-0 z-50 bg-palette-100/80 flex items-center justify-center"
>
{children}
</div>
);
};
Overlay.propTypes = {
/**
* Overlay's body.
*/
children: PropTypes.node.isRequired,
/**
* Handler function to be called when user clicks on the overlay
* (but not the overlay's content).
*/
onClick: PropTypes.func,
};

View File

@ -0,0 +1,2 @@
export * from "./ModalPortal";
export * from "./Modal";

View File

@ -1,4 +1,4 @@
import { Link } from "gatsby";
import { Link, navigate } from "gatsby";
import styled from "styled-components";
import { screen } from "../../lib/cssHelpers";
@ -7,6 +7,7 @@ import { CogIcon, LockClosedIcon, SkynetLogoIcon } from "../Icons";
import { PageContainer } from "../PageContainer";
import { NavBarLink, NavBarSection } from ".";
import accountsService from "../../services/accountsService";
const NavBarContainer = styled.div.attrs({
className: `grid sticky top-0 bg-white z-10 shadow-sm`,
@ -77,9 +78,13 @@ export const NavBar = () => (
partiallyActive
/>
<DropdownMenuLink
to="/logout"
as={Link}
onClick={async () => {
await accountsService.post("logout");
navigate("/auth/login");
// TODO: handle errors
}}
activeClassName="text-primary"
className="cursor-pointer"
icon={LockClosedIcon}
label="Log out"
/>

View File

@ -0,0 +1,29 @@
import React, { useState } from "react";
import styled, { keyframes } from "styled-components";
const fadeIn = keyframes`
0% { opacity: 0; }
100% { opacity: 1; }
`;
const Popper = styled.div.attrs({
className: `absolute left-full top-1/2 z-10 px-2 py-1 text-xs
bg-black/90 text-white rounded`,
})`
transform: translateY(-50%);
animation: ${fadeIn} 0.2s ease-in-out;
`;
export const Tooltip = ({ message, children, className }) => {
const [visible, setVisible] = useState(false);
const show = () => setVisible(true);
const hide = () => setVisible(false);
return (
<span className="relative" onMouseEnter={show} onMouseLeave={hide}>
{children}
{visible && <Popper className={className}>{message}</Popper>}
</span>
);
};

View File

@ -0,0 +1,78 @@
import * as Yup from "yup";
import { useState } from "react";
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import { Button } from "../Button";
import { TextField } from "../Form/TextField";
import accountsService from "../../services/accountsService";
const accountRemovalSchema = Yup.object().shape({
confirm: Yup.string().oneOf(["delete"], `Type "delete" to confirm`),
});
export const AccountRemovalForm = ({ abort, onSuccess }) => {
const [error, setError] = useState(false);
return (
<Formik
initialValues={{
confirm: "",
}}
validationSchema={accountRemovalSchema}
onSubmit={async () => {
try {
setError(false);
await accountsService.delete("user");
onSuccess();
} catch {
setError(true);
}
}}
>
{({ errors, touched, isValid, dirty }) => (
<Form className="flex flex-col gap-4">
<div>
<h4>Delete account</h4>
<p>
This will completely delete your account. <strong>This process can't be undone.</strong>
</p>
</div>
<hr className="border-palette-200/50" />
<p>Type "delete" in the field below to remove your account.</p>
<TextField
type="text"
name="confirm"
placeholder="delete"
error={errors.confirm}
touched={touched.confirm}
className="text-center"
autoFocus
/>
<div className="flex gap-4 justify-center mt-4">
<Button $primary onClick={abort}>
Cancel
</Button>
<Button type="submit" disabled={!isValid || !dirty}>
Delete
</Button>
</div>
{error && (
<div className="px-3 py-2 sm:px-6 sm:py-4 rounded border bg-red-100 border-red-200 text-error">
There was an error processing your request. Please try again later.
</div>
)}
</Form>
)}
</Formik>
);
};
AccountRemovalForm.propTypes = {
abort: PropTypes.func.isRequired,
onSuccess: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,111 @@
import * as Yup from "yup";
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import { Button } from "../Button";
import { TextField } from "../Form/TextField";
import accountsService from "../../services/accountsService";
const isPopulated = (value) => value?.length > 0;
const emailUpdateSchema = Yup.object().shape({
email: Yup.string().email("Please provide a valid email address"),
confirmEmail: Yup.string()
.oneOf([Yup.ref("email"), null], "Emails must match")
.when("email", {
is: isPopulated,
then: (schema) => schema.required("Please confirm new email address"),
}),
password: Yup.string().min(1, "Password can't be blank"),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password"), null], "Passwords must match")
.when("password", {
is: isPopulated,
then: (schema) => schema.required("Please confirm new password"),
}),
});
export const AccountSettingsForm = ({ user, onSuccess, onFailure }) => {
return (
<Formik
initialValues={{
email: "",
confirmEmail: "",
password: "",
confirmPassword: "",
}}
validationSchema={emailUpdateSchema}
onSubmit={async ({ email, password }, { resetForm }) => {
try {
const user = await accountsService
.put("user", {
json: {
email: email || undefined,
password: password || undefined,
},
})
.json();
resetForm();
await onSuccess(user);
} catch {
onFailure();
}
}}
>
{({ errors, touched, isValid, dirty }) => (
<Form>
<div className="flex flex-col w-full gap-8">
<div className="flex flex-col sm:flex-row gap-2">
<TextField
type="text"
id="email"
name="email"
label="Email address"
placeholder={user?.email}
error={errors.email}
touched={touched.email}
/>
<TextField
type="text"
id="confirmEmail"
name="confirmEmail"
label="Confirm new email address"
error={errors.confirmEmail}
touched={touched.confirmEmail}
/>
</div>
<div className="flex flex-col sm:flex-row gap-2">
<TextField
type="password"
id="password"
name="password"
label="New password"
error={errors.password}
touched={touched.password}
/>
<TextField
type="password"
id="confirmPassword"
name="confirmPassword"
label="Confirm new password"
error={errors.confirmPassword}
touched={touched.confirmPassword}
/>
</div>
</div>
<div className="flex mt-2 sm:mt-0 pt-5 justify-center">
<Button type="submit" className="px-24" disabled={!isValid || !dirty}>
Update
</Button>
</div>
</Form>
)}
</Formik>
);
};
AccountSettingsForm.propTypes = {
onSuccess: PropTypes.func.isRequired,
onFailure: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,114 @@
import * as Yup from "yup";
import { forwardRef, useImperativeHandle, useState } from "react";
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import accountsService from "../../services/accountsService";
import { Alert } from "../Alert";
import { Button } from "../Button";
import { CopyButton } from "../CopyButton";
import { TextField } from "../Form/TextField";
import { CircledProgressIcon, PlusIcon } from "../Icons";
const newAPIKeySchema = Yup.object().shape({
name: Yup.string(),
});
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
export const APIKeyType = {
Public: "public",
General: "general",
};
export const AddAPIKeyForm = forwardRef(({ onSuccess, type }, ref) => {
const [state, setState] = useState(State.Pure);
const [generatedKey, setGeneratedKey] = useState(null);
useImperativeHandle(ref, () => ({
reset: () => setState(State.Pure),
}));
return (
<div ref={ref} className="flex flex-col gap-4">
{state === State.Success && (
<Alert $variant="success" className="text-center">
<strong>Success!</strong>
<p>Please copy your new API key below. We'll never show it again!</p>
<div className="flex items-center gap-2 mt-4 justify-center">
<code className="p-2 rounded border border-palette-200 text-xs selection:bg-primary/30 truncate">
{generatedKey}
</code>
<CopyButton value={generatedKey} className="whitespace-nowrap" />
</div>
</Alert>
)}
{state === State.Failure && (
<Alert $variant="error">We were not able to generate a new key. Please try again later.</Alert>
)}
<Formik
initialValues={{
name: "",
}}
validationSchema={newAPIKeySchema}
onSubmit={async ({ name }, { resetForm }) => {
try {
const { key } = await accountsService
.post("user/apikeys", {
json: {
name,
public: type === APIKeyType.Public ? "true" : "false",
skylinks: type === APIKeyType.Public ? [] : null,
},
})
.json();
resetForm();
setGeneratedKey(key);
setState(State.Success);
onSuccess();
} catch {
setState(State.Failure);
}
}}
>
{({ errors, touched, isSubmitting }) => (
<Form className="grid grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-start">
<div className="flex items-center">
<TextField
type="text"
id="name"
name="name"
label="New API Key Name"
placeholder="my_applications_statistics"
error={errors.name}
touched={touched.name}
/>
</div>
<div className="flex mt-5 justify-center">
{isSubmitting ? (
<CircledProgressIcon size={38} className="text-palette-300 animate-[spin_3s_linear_infinite]" />
) : (
<Button type="submit" className="px-2.5">
<PlusIcon size={14} />
</Button>
)}
</div>
</Form>
)}
</Formik>
</div>
);
});
AddAPIKeyForm.displayName = "AddAPIKeyForm";
AddAPIKeyForm.propTypes = {
onSuccess: PropTypes.func.isRequired,
type: PropTypes.oneOf([APIKeyType.Public, APIKeyType.General]).isRequired,
};

View File

@ -0,0 +1,198 @@
import * as Yup from "yup";
import { forwardRef, useImperativeHandle, useState } from "react";
import PropTypes from "prop-types";
import { Formik, Form, FieldArray } from "formik";
import { parseSkylink } from "skynet-js";
import cn from "classnames";
import accountsService from "../../services/accountsService";
import { Alert } from "../Alert";
import { Button } from "../Button";
import { CopyButton } from "../CopyButton";
import { TextField } from "../Form/TextField";
import { PlusIcon, TrashIcon } from "../Icons";
const skylinkValidator = (optional) => (value) => {
if (!value) {
return optional;
}
try {
return parseSkylink(value) !== null;
} catch {
return false;
}
};
const newPublicAPIKeySchema = Yup.object().shape({
name: Yup.string(),
skylinks: Yup.array().of(Yup.string().test("skylink", "Provide a valid Skylink", skylinkValidator(false))),
nextSkylink: Yup.string().when("skylinks", {
is: (skylinks) => skylinks.length === 0,
then: (schema) => schema.test("skylink", "Provide a valid Skylink", skylinkValidator(true)),
otherwise: (schema) => schema.test("skylink", "Provide a valid Skylink", skylinkValidator(true)),
}),
});
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
export const AddPublicAPIKeyForm = forwardRef(({ onSuccess }, ref) => {
const [state, setState] = useState(State.Pure);
const [generatedKey, setGeneratedKey] = useState(null);
useImperativeHandle(ref, () => ({
reset: () => setState(State.Pure),
}));
return (
<div ref={ref} className="flex flex-col gap-4">
{state === State.Success && (
<Alert $variant="success" className="text-center">
<strong>Success!</strong>
<p>Please copy your new API key below. We'll never show it again!</p>
<div className="flex items-center gap-2 mt-4 justify-center">
<code className="p-2 rounded border border-palette-200 text-xs selection:bg-primary/30 truncate">
{generatedKey}
</code>
<CopyButton value={generatedKey} className="whitespace-nowrap" />
</div>
</Alert>
)}
{state === State.Failure && (
<Alert $variant="error">We were not able to generate a new key. Please try again later.</Alert>
)}
<Formik
initialValues={{
name: "",
skylinks: [],
nextSkylink: "",
}}
validationSchema={newPublicAPIKeySchema}
onSubmit={async ({ name, skylinks, nextSkylink }, { resetForm }) => {
try {
const { key } = await accountsService
.post("user/apikeys", {
json: {
name,
public: "true",
skylinks: [...skylinks, nextSkylink].filter(Boolean).map(parseSkylink),
},
})
.json();
resetForm();
setGeneratedKey(key);
setState(State.Success);
onSuccess();
} catch {
setState(State.Failure);
}
}}
>
{({ errors, touched, isSubmitting, values, isValid, setFieldValue, setFieldTouched }) => (
<Form className="flex flex-col gap-4">
<div className="flex items-center">
<TextField
type="text"
id="name"
name="name"
label="Public API Key Name"
placeholder="my_applications_statistics"
error={errors.name}
touched={touched.name}
/>
</div>
<div>
<h6 className="text-palette-300 mb-2">Skylinks accessible with the new key</h6>
<FieldArray
name="skylinks"
render={({ push, remove }) => {
const { skylinks = [] } = values;
const { skylinks: skylinksErrors = [] } = errors;
const { skylinks: skylinksTouched = [] } = touched;
const appendSkylink = (skylink) => {
push(skylink);
setFieldValue("nextSkylink", "", false);
setFieldTouched("nextSkylink", false);
};
const isNextSkylinkInvalid = Boolean(
errors.nextSkylink || !touched.nextSkylink || !values.nextSkylink
);
return (
<div className="flex flex-col gap-2">
{skylinks.map((_, index) => (
<div key={index} className="flex gap-4 items-start">
<TextField
type="text"
name={`skylinks.${index}`}
placeholder={`${index + 1}. skylink`}
error={skylinksErrors[index]}
touched={skylinksTouched[index]}
/>
<span className="w-[24px] shrink-0 mt-3">
<button type="button" onClick={() => remove(index)}>
<TrashIcon size={16} />
</button>
</span>
</div>
))}
<div className="flex gap-4 items-start">
<TextField
type="text"
name="nextSkylink"
placeholder={`Paste next skylink here`}
error={errors.nextSkylink}
touched={touched.nextSkylink}
onKeyPress={(event) => {
if (event.key === "Enter" && isValid) {
event.preventDefault();
appendSkylink(values.nextSkylink);
}
}}
/>
<button
type="button"
onClick={() => appendSkylink(values.nextSkylink)}
className={cn("shrink-0 mt-1.5 w-[24px] h-[24px]", {
"text-palette-300 cursor-not-allowed": isNextSkylinkInvalid,
})}
disabled={isNextSkylinkInvalid}
>
<PlusIcon size={15} />
</button>
</div>
</div>
);
}}
/>
</div>
<div className="flex mt-5 justify-center">
<Button
type="submit"
className={cn("px-2.5", { "cursor-wait": isSubmitting })}
disabled={!isValid || isSubmitting}
>
{isSubmitting ? "Generating" : "Generate"} your public key
</Button>
</div>
</Form>
)}
</Formik>
</div>
);
});
AddPublicAPIKeyForm.displayName = "AddAPIKeyForm";
AddPublicAPIKeyForm.propTypes = {
onSuccess: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,63 @@
import * as Yup from "yup";
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import { parseSkylink } from "skynet-js";
import { Button } from "../Button";
import { TextField } from "../Form/TextField";
import { CircledProgressIcon, PlusIcon } from "../Icons";
const newSkylinkSchema = Yup.object().shape({
skylink: Yup.string()
.required("Skylink is required")
.test("skylink", "Provide a valid Skylink", (value) => {
try {
return parseSkylink(value) !== null;
} catch {
return false;
}
}),
});
export const AddSkylinkToAPIKeyForm = ({ addSkylink }) => (
<Formik
initialValues={{
skylink: "",
}}
validationSchema={newSkylinkSchema}
onSubmit={async ({ skylink }, { resetForm }) => {
if (await addSkylink(parseSkylink(skylink))) {
resetForm();
}
}}
>
{({ errors, touched, isSubmitting }) => (
<Form className="grid grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-start">
<div className="flex items-center text-left">
<TextField
type="text"
id="skylink"
name="skylink"
label="New Skylink"
placeholder="Paste a new Skylink here"
error={errors.skylink}
touched={touched.skylink}
/>
</div>
<div className="flex mt-5 justify-center">
{isSubmitting ? (
<CircledProgressIcon size={38} className="text-palette-300 animate-[spin_3s_linear_infinite]" />
) : (
<Button type="submit" className="px-2.5">
<PlusIcon size={14} />
</Button>
)}
</div>
</Form>
)}
</Formik>
);
AddSkylinkToAPIKeyForm.propTypes = {
addSkylink: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,95 @@
import { useState } from "react";
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import { Link } from "gatsby";
import * as Yup from "yup";
import HighlightedLink from "../HighlightedLink";
import { TextField } from "../Form/TextField";
import { Button } from "../Button";
import accountsService from "../../services/accountsService";
const loginSchema = Yup.object().shape({
email: Yup.string().required("Email is required").email("Please provide a valid email address"),
password: Yup.string().required("Password is required"),
});
const INVALID_CREDENTIALS_ERRORS = ["password mismatch", "user not found"];
export const LoginForm = ({ onSuccess }) => {
const [error, setError] = useState(null);
return (
<Formik
initialValues={{
email: "",
password: "",
}}
validationSchema={loginSchema}
onSubmit={async (values) => {
try {
await accountsService.post("login", {
json: values,
});
onSuccess();
} catch (err) {
if (err.response) {
const data = await err.response.json();
if (INVALID_CREDENTIALS_ERRORS.includes(data.message)) {
setError("Invalid email address or password.");
} else {
setError(data.message);
}
} else {
setError("An error occured when logging you in. Please try again.");
}
}
}}
>
{({ errors, touched }) => (
<Form className="flex flex-col gap-4">
<h3 className="mt-4 mb-4">Log in to your account</h3>
{error && <p className="px-4 py-3 rounded border border-error bg-red-50 text-error mb-4">{error}</p>}
<TextField
type="text"
id="email"
name="email"
label="Email address"
error={errors.email}
touched={touched.email}
/>
<TextField
type="password"
id="password"
name="password"
label="Password"
error={errors.password}
touched={touched.password}
/>
<div>
<Link to="/auth/reset-password" className="text-sm inline transition-colors hover:text-primary">
Forgot your password?
</Link>
</div>
<div className="flex w-full justify-center mt-4">
<Button type="submit" className="px-12" $primary>
Log in
</Button>
</div>
<p className="text-sm text-center mt-8">
Don't have an account? <HighlightedLink to="/auth/signup">Sign up</HighlightedLink>
</p>
</Form>
)}
</Formik>
);
};
LoginForm.propTypes = {
onSuccess: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,57 @@
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import * as Yup from "yup";
import { TextField } from "../Form/TextField";
import { Button } from "../Button";
import accountsService from "../../services/accountsService";
const recoverySchema = Yup.object().shape({
email: Yup.string().required("Email is required").email("Please provide a valid email address"),
});
export const RecoveryForm = ({ onSuccess, onFailure }) => (
<Formik
initialValues={{
email: "",
}}
validationSchema={recoverySchema}
onSubmit={async (values) => {
try {
await accountsService.post("user/recover/request", {
json: values,
});
onSuccess();
} catch {
onFailure();
}
}}
>
{({ errors, touched }) => (
<Form className="flex flex-col gap-4">
<h3 className="mt-4 mb-8">Request account recovery</h3>
<TextField
type="text"
id="email"
name="email"
label="Email address"
error={errors.email}
touched={touched.email}
/>
<div className="flex w-full justify-center mt-4">
<Button type="submit" className="px-12" $primary>
Send recovery link
</Button>
</div>
</Form>
)}
</Formik>
);
RecoveryForm.propTypes = {
onFailure: PropTypes.func.isRequired,
onSuccess: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,67 @@
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import { TextField } from "../Form/TextField";
import { Button } from "../Button";
import { passwordSchema } from "./SignUpForm";
import accountsService from "../../services/accountsService";
export const ResetPasswordForm = ({ token, onSuccess, onFailure }) => (
<Formik
initialValues={{
password: "",
confirmPassword: "",
}}
validationSchema={passwordSchema}
onSubmit={async ({ password, confirmPassword }) => {
try {
await accountsService.post("user/recover", {
json: {
token,
password,
confirmPassword,
},
});
onSuccess();
} catch {
onFailure();
}
}}
>
{({ errors, touched }) => (
<Form className="flex flex-col gap-4">
<h3 className="mt-4 mb-8">Set your new password</h3>
<TextField
type="password"
id="password"
name="password"
label="New password"
error={errors.password}
touched={touched.password}
/>
<TextField
type="password"
id="confirmPassword"
name="confirmPassword"
label="Confirm new password"
error={errors.confirmPassword}
touched={touched.confirmPassword}
/>
<div className="flex w-full justify-center mt-4">
<Button type="submit" className="px-12" $primary>
Confirm
</Button>
</div>
</Form>
)}
</Formik>
);
ResetPasswordForm.propTypes = {
token: PropTypes.string.isRequired,
onFailure: PropTypes.func.isRequired,
onSuccess: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,109 @@
import PropTypes from "prop-types";
import { Formik, Form } from "formik";
import * as Yup from "yup";
import { TextField } from "../Form/TextField";
import { Button } from "../Button";
import accountsService from "../../services/accountsService";
export const passwordSchema = Yup.object().shape({
password: Yup.string().required("Password is required"),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password"), null], "Passwords must match")
.required("Please re-type your password"),
});
const registrationSchema = Yup.object()
.shape({
email: Yup.string().required("Email is required").email("Please provide a valid email address"),
})
.concat(passwordSchema);
const USER_EXISTS_ERROR = "identity already belongs to an existing user";
export const SignUpForm = ({ onSuccess, onFailure }) => (
<Formik
initialValues={{
email: "",
password: "",
confirmPassword: "",
}}
validationSchema={registrationSchema}
onSubmit={async ({ email, password }, { setErrors }) => {
try {
await accountsService.post("user", {
json: {
email,
password,
},
});
onSuccess();
} catch (err) {
let isFormErrorSet = false;
if (err.response) {
const data = await err.response.json();
// If it's a user error, let's capture it and handle within the form.
if (USER_EXISTS_ERROR === data.message) {
setErrors({ email: "This email address already in use." });
isFormErrorSet = true;
}
}
if (!isFormErrorSet) {
onFailure();
}
}
}}
>
{({ errors, touched }) => (
<Form className="flex flex-col gap-4">
<div className="mt-4 mb-8">
<h3>Create your free account</h3>
<p className="font-light font-sans text-lg">Includes 100 GB storage at basic speed</p>
</div>
<TextField
type="text"
id="email"
name="email"
label="Email address"
error={errors.email}
touched={touched.email}
/>
<TextField
type="password"
id="password"
name="password"
label="Password"
error={errors.password}
touched={touched.password}
/>
<TextField
type="password"
id="confirmPassword"
name="confirmPassword"
label="Confirm password"
error={errors.confirmPassword}
touched={touched.confirmPassword}
/>
<div className="flex w-full justify-center mt-4">
<Button type="submit" className="px-12" $primary>
Sign up!
</Button>
</div>
</Form>
)}
</Formik>
);
SignUpForm.propTypes = {
onFailure: PropTypes.func.isRequired,
onSuccess: PropTypes.func.isRequired,
};

View File

@ -0,0 +1 @@
export * from "./LoginForm";

View File

@ -0,0 +1,50 @@
import * as React from "react";
import styled from "styled-components";
import { SWRConfig } from "swr";
import { UserProvider } from "../contexts/user";
import { guestsOnly, allUsers } from "../lib/swrConfig";
const Layout = styled.div.attrs({
className: "min-h-screen w-screen bg-black flex",
})`
background-image: url(/images/auth-bg.svg);
background-repeat: no-repeat;
background-position: center center;
`;
const SloganContainer = styled.div.attrs({
className: "hidden md:flex lg:w-7/12 grow justify-center items-center relative overflow-hidden",
})``;
const Content = styled.div.attrs({
className: "w-full md:w-5/12 md:max-w-[680px] shrink-0",
})``;
const AuthLayout =
(swrConfig) =>
({ children }) => {
return (
<>
<SWRConfig value={swrConfig}>
<UserProvider>
<Layout>
<SloganContainer className="pl-20 pr-20 lg:pr-30 xl:pr-40">
<div className="">
<h1 className="text-4xl lg:text-5xl xl:text-6xl text-white">
The decentralized <span className="text-primary">revolution</span> starts with decentralized storage
</h1>
</div>
</SloganContainer>
<Content>{children}</Content>
</Layout>
</UserProvider>
</SWRConfig>
</>
);
};
// Some pages (e.g. email confirmation) need to be accessible to both logged-in and guest users.
export const AllUsersAuthLayout = AuthLayout(allUsers);
export default AuthLayout(guestsOnly);

View File

@ -23,6 +23,10 @@ const redirectAuthenticated = (key) =>
return response.json();
});
export const allUsers = {
fetcher: (key) => fetch(`${baseUrl}/${key}`).then((response) => response.json()),
};
export const authenticatedOnly = {
fetcher: redirectUnauthenticated,
};

View File

@ -0,0 +1,23 @@
import { navigate } from "gatsby";
import AuthLayout from "../../layouts/AuthLayout";
import { LoginForm } from "../../components/forms";
const LoginPage = ({ location }) => {
const query = new URLSearchParams(location.search);
const redirectTo = query.get("return_to");
return (
<div className="bg-white px-8 py-10 md:py-32 lg:px-16 xl:px-28 min-h-screen">
<div className="mb-4 md:mb-16">
<img src="/images/logo-black-text.svg" alt="Skynet" />
</div>
<LoginForm onSuccess={() => navigate(redirectTo || "/")} />
</div>
);
};
LoginPage.Layout = AuthLayout;
export default LoginPage;

View File

@ -0,0 +1,48 @@
import { useState } from "react";
import AuthLayout from "../../layouts/AuthLayout";
import { RecoveryForm } from "../../components/forms/RecoveryForm";
import HighlightedLink from "../../components/HighlightedLink";
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
const ResetPasswordPage = () => {
const [state, setState] = useState(State.Pure);
return (
<div className="bg-white px-8 py-10 md:py-32 lg:px-16 xl:px-28 min-h-screen">
<div className="mb-4 md:mb-16">
<img src="/images/logo-black-text.svg" alt="Skynet" />
</div>
{state !== State.Success && (
<RecoveryForm onSuccess={() => setState(State.Success)} onFailure={() => setState(State.Failure)} />
)}
{state === State.Success && (
<p className="text-primary text-center font-semibold">Please check your inbox for further instructions.</p>
)}
{state === State.Failure && (
<p className="text-error text-center">Something went wrong, please try again later.</p>
)}
<div className="text-sm text-center mt-8">
<p>
Suddenly remembered your password? <HighlightedLink to="/auth/login">Sign in</HighlightedLink>
</p>
<p>
Don't actually have an account? <HighlightedLink to="/auth/signup">Create one!</HighlightedLink>
</p>
</div>
</div>
);
};
ResetPasswordPage.Layout = AuthLayout;
export default ResetPasswordPage;

View File

@ -0,0 +1,56 @@
import { useEffect, useState } from "react";
import AuthLayout from "../../layouts/AuthLayout";
import HighlightedLink from "../../components/HighlightedLink";
import { SignUpForm } from "../../components/forms/SignUpForm";
import { navigate } from "gatsby";
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
const SignUpPage = () => {
const [state, setState] = useState(State.Pure);
useEffect(() => {
if (state === State.Success) {
const timer = setTimeout(() => navigate("/"), 3000);
return () => clearTimeout(timer);
}
}, [state]);
return (
<div className="bg-white px-8 py-10 md:py-32 lg:px-16 xl:px-28 min-h-screen">
<div className="mb-4 md:mb-16">
<img src="/images/logo-black-text.svg" alt="Skynet" />
</div>
{state !== State.Success && (
<SignUpForm onSuccess={() => setState(State.Success)} onFailure={() => setState(State.Failure)} />
)}
{state === State.Success && (
<div className="text-center">
<p className="text-primary font-semibold">Please check your inbox and confirm your email address.</p>
<p>You will be redirected to your dashboard shortly.</p>
<HighlightedLink to="/">Click here to go there now.</HighlightedLink>
</div>
)}
{state === State.Failure && (
<p className="text-error text-center">Something went wrong, please try again later.</p>
)}
<p className="text-sm text-center mt-8">
Already have an account? <HighlightedLink to="/auth/login">Sign in</HighlightedLink>
</p>
</div>
);
};
SignUpPage.Layout = AuthLayout;
export default SignUpPage;

View File

@ -1,63 +1,100 @@
import useSWR from "swr";
import dayjs from "dayjs";
import { useCallback, useRef } from "react";
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
import { TextInputBasic } from "../../components/TextInputBasic";
import { Button } from "../../components/Button";
import { TrashIcon } from "../../components/Icons";
import { AddAPIKeyForm, APIKeyType } from "../../components/forms/AddAPIKeyForm";
import { APIKeyList } from "../../components/APIKeyList/APIKeyList";
import { Alert } from "../../components/Alert";
import { AddPublicAPIKeyForm } from "../../components/forms/AddPublicAPIKeyForm";
const APIKeysPage = () => {
const { data: apiKeys } = useSWR("user/apikeys");
const { data: apiKeys = [], mutate: reloadKeys, error } = useSWR("user/apikeys");
const generalKeys = apiKeys.filter(({ public: isPublic }) => isPublic === "false");
const publicKeys = apiKeys.filter(({ public: isPublic }) => isPublic === "true");
const publicFormRef = useRef();
const generalFormRef = useRef();
const refreshState = useCallback(
(resetForms) => {
if (resetForms) {
publicFormRef.current?.reset();
generalFormRef.current?.reset();
}
reloadKeys();
},
[reloadKeys]
);
return (
<>
<div className="flex flex-col xl:flex-row">
<div className="flex flex-col gap-10 lg:shrink-0 lg:max-w-[576px] xl:max-w-[524px]">
<section>
<div>
<h4>API Keys</h4>
<p>
At vero eos et caritatem, quae sine metu contineret, saluti prospexit civium, qua. Laudem et dolorem
aspernari ut ad naturam aut fu.
<p className="leading-relaxed">There are two types of API keys that you can generate for your account.</p>
<p>Make sure to use the appropriate type.</p>
</div>
<hr />
<section className="flex flex-col gap-2">
<h5>Public keys</h5>
<p className="text-palette-500">
Public keys provide read access to a selected list of skylinks. You can share them publicly.
</p>
<div className="mt-4">
<AddPublicAPIKeyForm ref={publicFormRef} onSuccess={refreshState} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your API keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{publicKeys?.length > 0 ? (
<APIKeyList title="Your public keys" keys={publicKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No public API keys found.</Alert>
)}
</div>
)}
</section>
<hr />
<section className="flex flex-col gap-8">
<div className="grid sm:grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-end">
<TextInputBasic label="API Key Name" placeholder="my_applications_statistics" />
<div className="flex mt-2 sm:mt-0 justify-center">
<Button onClick={() => console.info("TODO: generate ky")}>Generate</Button>
</div>
<section className="flex flex-col gap-2">
<h5>General keys</h5>
<p className="text-palette-500">
These keys provide full access to <b>Accounts</b> service and are equivalent to using a JWT token.
</p>
<p className="underline">
This type of API keys needs to be kept secret and should never be shared with anyone.
</p>
<div className="mt-4">
<AddAPIKeyForm ref={generalFormRef} onSuccess={refreshState} type={APIKeyType.General} />
</div>
{error ? (
<Alert $variant="error" className="mt-4">
An error occurred while loading your API keys. Please try again later.
</Alert>
) : (
<div className="mt-4">
{generalKeys?.length > 0 ? (
<APIKeyList title="Your general keys" keys={generalKeys} reloadKeys={() => refreshState(true)} />
) : (
<Alert $variant="info">No general API keys found.</Alert>
)}
</div>
)}
</section>
{apiKeys?.length > 0 && (
<section className="mt-4">
<h6 className="text-palette-300">API Keys</h6>
<ul className="mt-4">
{apiKeys.map(({ id, name, createdAt }) => (
<li
key={id}
className="grid grid-cols-2 sm:grid-cols-[1fr_repeat(2,_max-content)] py-6 sm:py-4 px-4 gap-x-8 bg-white odd:bg-palette-100/50"
>
<span className="truncate col-span-2 sm:col-span-1">{name || id}</span>
<span className="col-span-2 my-4 border-t border-t-palette-200/50 sm:hidden" />
<span className="text-palette-400">{dayjs(createdAt).format("MMM DD, YYYY")}</span>
<span className="text-right">
<button
className="p-1 transition-colors hover:text-error"
onClick={() => window.confirm("TODO: confirmation modal")}
>
<TrashIcon size={14} />
</button>
</span>
</li>
))}
</ul>
</section>
)}
</div>
<div className="hidden xl:block w-full text-right pt-20 pr-6">
<img src="/images/import-export.svg" alt="" className="inline-block w-[200px]" />
<div className="hidden xl:block w-full text-right pt-16 pr-5">
<img src="/images/api-keys.svg" alt="" className="inline-block h-[150px]" />
</div>
</div>
</>

View File

@ -1,19 +1,42 @@
import * as React from "react";
import { useMedia } from "react-use";
import styled from "styled-components";
import { useCallback, useState } from "react";
import { navigate } from "gatsby";
import theme from "../../lib/theme";
import { useUser } from "../../contexts/user";
import UserSettingsLayout from "../../layouts/UserSettingsLayout";
import { TextInputBasic } from "../../components/TextInputBasic/TextInputBasic";
import { Button } from "../../components/Button";
import { AvatarUploader } from "../../components/AvatarUploader";
import { AccountSettingsForm } from "../../components/forms/AccountSettingsForm";
import { Modal } from "../../components/Modal/Modal";
import { AccountRemovalForm } from "../../components/forms/AccountRemovalForm";
import { Alert } from "../../components/Alert";
const FormGroup = styled.div.attrs({
className: "grid sm:grid-cols-[1fr_min-content] w-full gap-y-2 gap-x-4 items-end",
})``;
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
const AccountPage = () => {
const isLargeScreen = useMedia(`(min-width: ${theme.screens.xl})`);
const { user, mutate: reloadUser } = useUser();
const [state, setState] = useState(State.Pure);
const [removalInitiated, setRemovalInitiated] = useState(false);
const prompt = () => setRemovalInitiated(true);
const abort = () => setRemovalInitiated(false);
const onSettingsUpdated = useCallback(
async (updatedState) => {
try {
// Update state locally and request revalidation.
await reloadUser(updatedState);
} finally {
// If revalidation fails, we can't really do much. Request
// will be auto-retried by SWR, so we'll just show a message
// about the update request being successful.
setState(State.Success);
}
},
[reloadUser]
);
return (
<>
<div className="flex flex-col xl:flex-row">
@ -26,34 +49,12 @@ const AccountPage = () => {
</p>
</section>
<hr />
{!isLargeScreen && (
<section>
<AvatarUploader className="flex flex-col sm:flex-row gap-8 items-center" />
</section>
)}
<section className="flex flex-col gap-8">
<FormGroup>
<TextInputBasic label="Display name" placeholder="John Doe" />
<div className="flex mt-2 sm:mt-0 justify-center">
<Button>Update</Button>
</div>
</FormGroup>
<FormGroup>
<TextInputBasic label="Email" placeholder="john.doe@example.com" />
<div className="flex mt-2 sm:mt-0 justify-center">
<Button>Update</Button>
</div>
</FormGroup>
<FormGroup>
<TextInputBasic type="password" label="Password" placeholder="dbf3htf*efh4pcy@PXB" />
<div className="flex mt-2 sm:mt-0 justify-center order-last sm:order-none">
<Button>Update</Button>
</div>
<small className="text-palette-400">
The password must be at least 6 characters long. Significantly different from the email and old
password.
</small>
</FormGroup>
{state === State.Failure && (
<Alert $variant="error">There was an error processing your request. Please try again later.</Alert>
)}
{state === State.Success && <Alert $variant="success">Changes saved successfully.</Alert>}
<AccountSettingsForm user={user} onSuccess={onSettingsUpdated} onFailure={() => setState(State.Failure)} />
</section>
<hr />
<section>
@ -61,16 +62,18 @@ const AccountPage = () => {
<p>This will completely delete your account. This process can't be undone.</p>
<button
type="button"
onClick={() => window.confirm("TODO: confirmation modal")}
onClick={prompt}
className="text-error underline decoration-1 hover:decoration-dashed"
>
Delete account
</button>
</section>
</div>
<div className="flex w-full justify-start xl:justify-end">
{isLargeScreen && <AvatarUploader className="flex flex-col gap-4" />}
</div>
{removalInitiated && (
<Modal onClose={abort} className="text-center">
<AccountRemovalForm abort={abort} onSuccess={() => navigate("/auth/login")} />
</Modal>
)}
</div>
</>
);

View File

@ -0,0 +1,78 @@
import { useEffect, useState } from "react";
import { navigate } from "gatsby";
import { AllUsersAuthLayout } from "../../layouts/AuthLayout";
import HighlightedLink from "../../components/HighlightedLink";
import accountsService from "../../services/accountsService";
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
const EmailConfirmationPage = ({ location }) => {
const query = new URLSearchParams(location.search);
const token = query.get("token");
const [state, setState] = useState(State.Pure);
useEffect(() => {
const controller = new AbortController();
let timer;
async function confirm(token) {
try {
await accountsService.get("user/confirm", {
signal: controller.signal,
searchParams: { token },
});
timer = setTimeout(() => {
navigate("/");
}, 3000);
setState(State.Success);
} catch (err) {
// Don't show an error message if request was aborted due to `token` changing.
if (err.code !== DOMException.ABORT_ERR) {
setState(State.Failure);
}
}
}
if (token) {
confirm(token);
}
return () => {
controller.abort();
clearTimeout(timer);
};
}, [token]);
return (
<div className="bg-white px-8 py-10 md:py-32 lg:px-16 xl:px-28 min-h-screen">
<div className="mb-4 md:mb-16">
<img src="/images/logo-black-text.svg" alt="Skynet" />
</div>
<div className="text-center">
{state === State.Pure && <p>Please wait while we verify your account...</p>}
{state === State.Success && (
<>
<p className="text-primary font-semibold">All done!</p>
<p>You will be redirected to your dashboard shortly.</p>
<HighlightedLink to="/">Redirect now.</HighlightedLink>
</>
)}
{state === State.Failure && <p className="text-error">Something went wrong, please try again later.</p>}
</div>
</div>
);
};
EmailConfirmationPage.Layout = AllUsersAuthLayout;
export default EmailConfirmationPage;

View File

@ -0,0 +1,56 @@
import { useState } from "react";
import { navigate } from "gatsby";
import AuthLayout from "../../layouts/AuthLayout";
import { ResetPasswordForm } from "../../components/forms/ResetPasswordForm";
import HighlightedLink from "../../components/HighlightedLink";
const State = {
Pure: "PURE",
Success: "SUCCESS",
Failure: "FAILURE",
};
const RecoverPage = ({ location }) => {
const query = new URLSearchParams(location.search);
const token = query.get("token");
const [state, setState] = useState(State.Pure);
return (
<div className="bg-white px-8 py-10 md:py-32 lg:px-16 xl:px-28 min-h-screen">
<div className="mb-4 md:mb-16">
<img src="/images/logo-black-text.svg" alt="Skynet" />
</div>
{state !== State.Success && (
<ResetPasswordForm
token={token}
onSuccess={() => {
setState(State.Success);
navigate("/");
}}
onFailure={() => setState(State.Failure)}
/>
)}
{state === State.Success && (
<p className="text-primary text-center font-semibold">
All done! You will be redirected to your dashboard shortly.
</p>
)}
{state === State.Failure && (
<p className="text-error text-center">Something went wrong, please try again later.</p>
)}
<p className="text-sm text-center mt-8">
Suddenly remembered your old password? <HighlightedLink to="/auth/login">Sign in</HighlightedLink>
</p>
</div>
);
};
RecoverPage.Layout = AuthLayout;
export default RecoverPage;

View File

@ -0,0 +1,3 @@
import ky from "ky";
export default ky.create({ prefixUrl: "/api" });

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 173 131.5"><defs><style>.cls-1{fill:#00c65e;fill-rule:evenodd;}.cls-2{fill:none;stroke:#0d0d0d;stroke-width:2px;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M112,50.39V80.61L142,99.5l30-18.89V50.39L142.94,31.5Z"/><path class="cls-2" d="M125.61,89.18,142,99.5l30-18.89V50.39l-13.27-8.63"/><path class="cls-2" d="M78,19.5l16-10,16,10v16l-16,10-16-10Z"/><path class="cls-2" d="M110,19.5l-16,10-16-10m16.5,10v16m20,42v44M92,70h4M84,70h4m12,0h4m-6,61.5v-24a4,4,0,0,0-8,0v24M71,5.5H66c-13.24,8.55-20,21.07-20,38a48.29,48.29,0,0,0,28,44v44"/><path class="cls-2" d="M94,91.5a48,48,0,0,0,48-48c0-16.93-6.76-29.45-20-38h-4v36l-24,16-24-16V5.5"/><path class="cls-2" d="M118,11.5a40,40,0,1,1-64,32"/><path class="cls-2" d="M29,12H0m34,9H15M44,1H25"/></g></g></svg>

After

Width:  |  Height:  |  Size: 874 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="155" height="41" viewBox="0 0 155 41">
<defs>
<filter id="9uejgo84sa">
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0.050980 0 0 0 0 0.050980 0 0 0 0 0.050980 0 0 0 1.000000 0"/>
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<g>
<g>
<path fill="#00C65E" d="M0 8.762L43.44 31.68c1.14.596 1.347 2.137.406 3.012h-.022c-1.365 1.235-2.89 2.28-4.536 3.106C25.25 44.845 8.84 34.069 9.75 18.402l5.809 4.007c1.738 9.146 11.411 14.423 20.054 10.941L0 8.762zm17.563 6.392l29.176 11.52c.846.332 1.303 1.25 1.056 2.124v.037c-.313 1.09-1.558 1.61-2.554 1.065L17.563 15.154zM28.306.065c5.502-.472 12.377 1.596 16.474 6.872 3.45 4.453 4.162 9.894 3.919 15.513v-.015c-.097 1.192-1.32 1.95-2.432 1.511l-19.832-7.83 18.411 4.933c.316-3.24-.446-6.494-2.168-9.258-6.029-9.672-20.279-9.2-25.65.853-.308.579-.578 1.177-.809 1.791l-4.937-2.628c.14-.207.287-.402.435-.601.236-.49.49-.988.771-1.475C15.762 4.07 21.242.736 27.021.201z" transform="translate(-247 -96) translate(247 96)"/>
<g filter="url(#9uejgo84sa)" transform="translate(-247 -96) translate(247 96)">
<g>
<path fill="#333" fill-rule="nonzero" d="M7.758 19.892c1.552 0 2.901-.25 4.048-.75 1.146-.499 2.03-1.2 2.65-2.105.62-.904.931-1.95.931-3.139 0-1.12-.28-2.075-.84-2.867-.56-.792-1.35-1.421-2.366-1.886-1.018-.465-2.199-.75-3.543-.852l-1.656-.155c-.93-.07-1.612-.323-2.043-.763-.43-.439-.646-.951-.646-1.537 0-.482.12-.93.362-1.343.241-.413.603-.74 1.086-.982.483-.24 1.086-.361 1.81-.361.759 0 1.384.133 1.875.4.492.267.862.62 1.112 1.06.25.438.375.916.375 1.433h3.595c0-1.223-.284-2.286-.853-3.19-.57-.905-1.371-1.606-2.405-2.106C10.215.25 8.982 0 7.55 0 6.172 0 4.97.237 3.944.71c-1.026.474-1.823 1.146-2.392 2.015-.57.87-.854 1.908-.854 3.113 0 1.602.539 2.89 1.617 3.862 1.077.973 2.521 1.538 4.331 1.693l1.655.129c1.19.103 2.07.357 2.638.762.57.404.854.943.854 1.614 0 .534-.147 1.012-.44 1.434-.293.422-.741.762-1.345 1.02-.603.259-1.353.388-2.25.388-1 0-1.806-.146-2.418-.44-.612-.292-1.056-.675-1.332-1.149-.275-.473-.413-.96-.413-1.46H0c0 1.189.302 2.252.905 3.191.604.939 1.479 1.675 2.625 2.209 1.147.534 2.556.8 4.228.8zm14.51-.491v-6.562h1.526l4.706 6.562h4.06l-5.913-8.047 4.776-5.98h-3.75l-5.405 6.755V.543h-3.595V19.4h3.595zm14.225 5.528c1.155 0 2.125-.138 2.91-.413.784-.276 1.426-.728 1.926-1.356.5-.629.888-1.46 1.164-2.493l4.164-15.294h-3.44L40.389 16.25h-.55L36.417 5.373h-3.621l4.552 13.718h2.302l-.208.8c-.108.435-.259.792-.451 1.072l-.066.091c-.224.293-.504.5-.84.62-.337.12-.755.181-1.255.181h-2.637v3.074h2.301zm16.45-5.528V11.16c0-.913.27-1.64.814-2.183.543-.542 1.254-.814 2.134-.814.862 0 1.538.267 2.03.801.491.534.737 1.232.737 2.093V19.4h3.594v-8.034c0-2.136-.43-3.742-1.293-4.818-.862-1.077-2.129-1.615-3.801-1.615h-.155c-1.121 0-2.056.241-2.806.723-.75.483-1.315 1.202-1.694 2.158-.106.265-.196.549-.272.85l-.039.16V5.373h-2.844v14.028h3.594zm19.655.49c1.156 0 2.186-.197 3.09-.593.906-.397 1.652-.956 2.238-1.68.586-.723.982-1.567 1.19-2.531h-3.31c-.156.516-.51.951-1.061 1.304-.552.353-1.267.53-2.147.53-.879 0-1.603-.194-2.172-.581-.569-.388-.991-.922-1.267-1.602-.17-.419-.287-.875-.353-1.368l-.014-.117h10.556V11.96c0-1.24-.259-2.398-.776-3.475-.517-1.076-1.288-1.946-2.314-2.609s-2.298-.994-3.815-.994c-1.155 0-2.172.202-3.051.607-.88.404-1.617.947-2.211 1.627-.595.68-1.044 1.455-1.345 2.325-.302.87-.453 1.77-.453 2.7v.49c0 .896.151 1.774.453 2.636.301.86.75 1.64 1.345 2.337.594.698 1.344 1.253 2.25 1.667.905.413 1.96.62 3.167.62zm3.316-8.834h-7.058l.028-.15c.059-.284.135-.55.229-.8l.059-.148c.284-.68.702-1.201 1.254-1.563.552-.362 1.224-.543 2.017-.543.793 0 1.453.173 1.979.517.525.344.918.835 1.176 1.473.13.318.227.669.291 1.052l.025.162zm14.763 8.525v-3.075h-2.198c-.69 0-1.22-.185-1.59-.555-.371-.37-.557-.9-.557-1.589l.013-6.355h4.332V5.373H86.35l.008-3.952h-3.362l-.008 3.952h-2.242v2.635h2.237l-.013 6.123c0 1.292.187 2.332.56 3.12l.061.122c.414.8 1.039 1.37 1.875 1.705.836.336 1.9.504 3.194.504h2.017z" transform="translate(64 11)"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -54,6 +54,7 @@ module.exports = {
wiggle: "wiggle 3s ease-in-out infinite",
},
width: {
modal: "500px",
page: "100%",
"page-md": "640px",
"page-lg": "896px",
@ -64,6 +65,9 @@ module.exports = {
minWidth: {
button: "112px",
},
maxWidth: {
modal: "calc(100vw - 1rem)",
},
},
},
plugins: [

View File

@ -3373,6 +3373,11 @@
dependencies:
"@types/node" "*"
"@types/lodash@^4.14.175":
version "4.14.180"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670"
integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==
"@types/lodash@^4.14.92":
version "4.14.178"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
@ -6366,6 +6371,11 @@ deep-object-diff@^1.1.0:
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.7.tgz#348b3246f426427dd633eaa50e1ed1fc2eafc7e4"
integrity sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==
deepmerge@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
deepmerge@^4.0.0, deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@ -7951,6 +7961,19 @@ format@^0.2.0:
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
formik@^2.2.9:
version "2.2.9"
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0"
integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==
dependencies:
deepmerge "^2.1.1"
hoist-non-react-statics "^3.3.0"
lodash "^4.17.21"
lodash-es "^4.17.21"
react-fast-compare "^2.0.1"
tiny-warning "^1.0.2"
tslib "^1.10.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@ -10357,6 +10380,11 @@ klona@^2.0.4:
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
ky@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/ky/-/ky-0.30.0.tgz#a3d293e4f6c4604a9a4694eceb6ce30e73d27d64"
integrity sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==
language-subtag-registry@~0.3.2:
version "0.3.21"
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
@ -10510,6 +10538,11 @@ lock@^1.1.0:
resolved "https://registry.yarnpkg.com/lock/-/lock-1.1.0.tgz#53157499d1653b136ca66451071fca615703fa55"
integrity sha1-UxV0mdFlOxNspmRRBx/KYVcD+lU=
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash.clonedeep@4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
@ -11007,9 +11040,9 @@ minimatch@^3.0.2, minimatch@^3.0.4:
brace-expansion "^1.1.7"
minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
minipass-collect@^1.0.2:
version "1.0.2"
@ -11183,6 +11216,11 @@ nano-css@^5.3.1:
stacktrace-js "^2.0.2"
stylis "^4.0.6"
nanoclone@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
nanoid@^3.1.23, nanoid@^3.2.0, nanoid@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
@ -12647,6 +12685,11 @@ proper-lockfile@^4.1.2:
retry "^0.12.0"
signal-exit "^3.0.2"
property-expr@^2.0.4:
version "2.0.5"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4"
integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==
property-information@^5.0.0, property-information@^5.3.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
@ -12942,6 +12985,11 @@ react-error-overlay@^6.0.9:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6"
integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==
react-fast-compare@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-fast-compare@^3.0.1, react-fast-compare@^3.1.1, react-fast-compare@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
@ -14830,6 +14878,11 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tiny-warning@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tinycolor2@^1.4.1:
version "1.4.2"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
@ -14929,6 +14982,11 @@ token-types@^4.1.1:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
toposort@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -16036,6 +16094,19 @@ yoga-layout-prebuilt@^1.10.0:
dependencies:
"@types/yoga-layout" "1.9.2"
yup@^0.32.11:
version "0.32.11"
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"
integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/lodash" "^4.14.175"
lodash "^4.17.21"
lodash-es "^4.17.21"
nanoclone "^0.2.1"
property-expr "^2.0.4"
toposort "^2.0.2"
yurnalist@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/yurnalist/-/yurnalist-2.1.0.tgz#44cf7ea5a33a8fab4968cc8c2970489f93760902"

View File

@ -8,8 +8,8 @@
"start": "next start"
},
"dependencies": {
"@fontsource/sora": "4.5.3",
"@fontsource/source-sans-pro": "4.5.4",
"@fontsource/sora": "4.5.5",
"@fontsource/source-sans-pro": "4.5.6",
"@stripe/react-stripe-js": "1.7.0",
"@stripe/stripe-js": "1.25.0",
"classnames": "2.3.1",
@ -20,14 +20,14 @@
"formik": "2.2.9",
"http-status-codes": "2.2.0",
"ky": "0.30.0",
"next": "12.1.0",
"next": "12.1.1",
"normalize.css": "8.0.1",
"pretty-bytes": "6.0.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-toastify": "8.2.0",
"skynet-js": "3.0.2",
"stripe": "8.210.0",
"stripe": "8.212.0",
"swr": "1.2.2",
"yup": "0.32.11"
},
@ -35,10 +35,10 @@
"@tailwindcss/forms": "0.5.0",
"@tailwindcss/typography": "0.5.2",
"autoprefixer": "10.4.4",
"eslint": "8.11.0",
"eslint-config-next": "12.1.0",
"eslint": "8.12.0",
"eslint-config-next": "12.1.1",
"postcss": "8.4.12",
"prettier": "2.6.0",
"prettier": "2.6.1",
"tailwindcss": "3.0.23"
}
}

View File

@ -53,15 +53,15 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@fontsource/sora@4.5.3":
version "4.5.3"
resolved "https://registry.yarnpkg.com/@fontsource/sora/-/sora-4.5.3.tgz#987c9b43acb00c9e3fa5377ebcedfd4ec9b760a7"
integrity sha512-0ipYkroLonvChAyLajgIt6mImXMhvjrHwD5g7iX2ZR1eJ4hLDnwq6haW5pSeehe79lPjgp0BX6ZHivFIP0xR2g==
"@fontsource/sora@4.5.5":
version "4.5.5"
resolved "https://registry.yarnpkg.com/@fontsource/sora/-/sora-4.5.5.tgz#25e8e0a11d0a2a861ccce23a94383448c5beaa50"
integrity sha512-SeePOM5pvXaa2a1c9VuGPJGmHNcFSUBzVxfHMuQ/FrVeiXvF3XXyPbt68N6QmHK2rAH844BN1Y5zImxqa5ok6g==
"@fontsource/source-sans-pro@4.5.4":
version "4.5.4"
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-4.5.4.tgz#51510723ff40f446c7800f133e9ae604ae2f38d7"
integrity sha512-+YYw6HRvH9wYE+U2Hvxyossg+MHPApAj7VIjEqaXenNeNQa4U3uPD0e7pc+1Gic3srCQATN15O3S9WSFLXTmwQ==
"@fontsource/source-sans-pro@4.5.6":
version "4.5.6"
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-4.5.6.tgz#1c74aa0168f0ad851e7c69a5ff25914c7691b6cb"
integrity sha512-MPv0Ye2OmcRD4wQ/7lOkysQpXpWkDSqZ2QHdNNLj7CQVtG03BScLMYQXZbYNhNMbH518tdMFewqDSu+/ikAMTg==
"@humanwhocodes/config-array@^0.9.2":
version "0.9.2"
@ -77,72 +77,77 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@next/env@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314"
integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==
"@next/env@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.1.tgz#8a927397697ee9d94852feb5fed57a813d299979"
integrity sha512-VmTRkfo/IXOQCATndjW3OjKb8zmAuB07eDdzO9XvuXZP87SyvnCYw3jrhUuFhOe/FVsKiloafa5LJfToUpvjUQ==
"@next/eslint-plugin-next@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz#32586a11378b3ffa5a93ac40a3c44ad99d70e95a"
integrity sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g==
"@next/eslint-plugin-next@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.1.tgz#e3e51503e9d7f987a0e080344648bc84ac1e8eb8"
integrity sha512-5hd1VFWZzECADhvA+OE+g0CnrRBFZbPm03HbiUtpk7XeluNn7xVxBU6XvNQA+YrQ7qe5jCK9q7R8MbI9R55Y/Q==
dependencies:
glob "7.1.7"
"@next/swc-android-arm64@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39"
integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==
"@next/swc-android-arm-eabi@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.1.tgz#2b134efb6639a770db10688a93ce0d2a9362fc5e"
integrity sha512-phV9H6d1eK1oVC7nmKKcCXvgOWT4K7aLC/beyO6yvbFC4XtBLE21vPwVl7B4ybz5xjSa6TXoR3TMR6vkW6Mv+A==
"@next/swc-darwin-arm64@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135"
integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==
"@next/swc-android-arm64@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.1.tgz#4fc66990c71c066f99fc435c0e8a4b3191bdfb4a"
integrity sha512-X5qEz0YeeYT0Gz2wXPAEtRKEuAsLUIEgC/DDfS98t/5Idjv0S4aqIX+TQdzoXP5bwQkIr+mSg+MBIdLtbtnCsA==
"@next/swc-darwin-x64@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd"
integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==
"@next/swc-darwin-arm64@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.1.tgz#a5b4ea73ebf769f48dae97942b6b6fbeaf3f1dfc"
integrity sha512-bKKSNaTdnO3XPnfaR4NSpPcbs80fdbtOYC2lgtqLzA0bOMioupixMP5GrA/gfJHwh7GRH+A+sbgKQWsqSsYAqQ==
"@next/swc-linux-arm-gnueabihf@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7"
integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==
"@next/swc-darwin-x64@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.1.tgz#064d50c08d9eec0fc1ff76e190d4fe43184aa8b7"
integrity sha512-2VOsA6WLDuDBA6935djohWGGeUIKeQhXwDwu1CKx1b8+6YMMIvFr/y2dpPWoct+5/IjFz84a2MnbABwpoNB9YA==
"@next/swc-linux-arm64-gnu@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093"
integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==
"@next/swc-linux-arm-gnueabihf@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.1.tgz#6cd6edda7f17ad1ceb1cd242419d93a643b1de31"
integrity sha512-1urXtWwqjqbbpJBWeJYz5ATgelKacVNdKIdhfahbsmW+DZGoK5TYovgieyHFYUCyHdTuKeLTVR62ahIRUBv1YA==
"@next/swc-linux-arm64-musl@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566"
integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==
"@next/swc-linux-arm64-gnu@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.1.tgz#d480c71de4150728c0c67a363c7f17291db3f070"
integrity sha512-CDD9yFuknDvTOzzDnvfmb58USI5Vu6FUyzw96udKj7KA/n1YrNQ4K8X7KsDCRZoqfRWYceAyj1EpwHkfdiB7bg==
"@next/swc-linux-x64-gnu@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e"
integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==
"@next/swc-linux-arm64-musl@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.1.tgz#9578705269b746617c763c1ac5c5b26084f1e5ce"
integrity sha512-nxyjgmbOpZm7gGPj9EV5Cqosoujt+ih/8SO2XG+BetgfAk0+c15793DHVAljNuc8GF9wpzqQnjMMUZ211VmQsg==
"@next/swc-linux-x64-musl@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31"
integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==
"@next/swc-linux-x64-gnu@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.1.tgz#40f3c79b65b6cfc880e6131a25f7936716ded1b9"
integrity sha512-L8Cu8kH3Vn2dnRpvcvGGA1TlmDP2WXJ+qDwvjb/ikDXLdRdmFvJwHh45JUGiW2FHed3lGseOgNsuYiDvnT8Cdw==
"@next/swc-win32-arm64-msvc@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283"
integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==
"@next/swc-linux-x64-musl@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.1.tgz#922ea0306d36b5fd860d8cc5a6f4c53dca09395c"
integrity sha512-4RAb7L69MoRSggBqUfF3OrtBCUN2zPDi7asfL7bfxEhH10LGzyfil8dT0GVjPOPFz/SyLx3ORd6avGij2IlJUA==
"@next/swc-win32-ia32-msvc@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1"
integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==
"@next/swc-win32-arm64-msvc@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.1.tgz#be13af4e33c071e6ec97b2cfead51eb57ecf189b"
integrity sha512-zvkuNIgOxkAU3RbzWRGCcFasDxWJdhONt2YeRGe39dJERHhEFA1u4HgaZw/SFE/kfrNRUZbXjJNAg3OU/EpPZw==
"@next/swc-win32-x64-msvc@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064"
integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==
"@next/swc-win32-ia32-msvc@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.1.tgz#2a5adb993542fc54fedfd525c69593f75c055595"
integrity sha512-GsNDtZ//uKWNVjiwv3YKQYsDXuRWTz8jTmxopf5Ws3dK+zA77hn4o46LBQg0JPCNqTUO6eIOlUBjqSL6ejxmSQ==
"@next/swc-win32-x64-msvc@12.1.1":
version "12.1.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.1.tgz#21c12feb6acf75cac7c1ae4ca7f251aa1943f081"
integrity sha512-nH5osn/uK9wsjT8Jh1YxMtRrkN5hoCNLQjsEdvfUfb+loQXeYiBd3n/0DUJkf6Scjfv6/htfUTPP3AEa7AbBxQ==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@ -165,10 +170,10 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@rushstack/eslint-patch@^1.0.8":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323"
integrity sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==
"@rushstack/eslint-patch@1.0.8":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz#be3e914e84eacf16dbebd311c0d0b44aa1174c64"
integrity sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==
"@stripe/react-stripe-js@1.7.0":
version "1.7.0"
@ -218,48 +223,48 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@typescript-eslint/parser@^5.0.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.6.0.tgz#11677324659641400d653253c03dcfbed468d199"
integrity sha512-YVK49NgdUPQ8SpCZaOpiq1kLkYRPMv9U5gcMrywzI8brtwZjr/tG3sZpuHyODt76W/A0SufNjYt9ZOgrC4tLIQ==
"@typescript-eslint/parser@5.10.1":
version "5.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.1.tgz#4ce9633cc33fc70bc13786cb793c1a76fe5ad6bd"
integrity sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==
dependencies:
"@typescript-eslint/scope-manager" "5.6.0"
"@typescript-eslint/types" "5.6.0"
"@typescript-eslint/typescript-estree" "5.6.0"
"@typescript-eslint/scope-manager" "5.10.1"
"@typescript-eslint/types" "5.10.1"
"@typescript-eslint/typescript-estree" "5.10.1"
debug "^4.3.2"
"@typescript-eslint/scope-manager@5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.6.0.tgz#9dd7f007dc8f3a34cdff6f79f5eaab27ae05157e"
integrity sha512-1U1G77Hw2jsGWVsO2w6eVCbOg0HZ5WxL/cozVSTfqnL/eB9muhb8THsP0G3w+BB5xAHv9KptwdfYFAUfzcIh4A==
"@typescript-eslint/scope-manager@5.10.1":
version "5.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz#f0539c73804d2423506db2475352a4dec36cd809"
integrity sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==
dependencies:
"@typescript-eslint/types" "5.6.0"
"@typescript-eslint/visitor-keys" "5.6.0"
"@typescript-eslint/types" "5.10.1"
"@typescript-eslint/visitor-keys" "5.10.1"
"@typescript-eslint/types@5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.6.0.tgz#745cb1b59daadcc1f32f7be95f0f68accf38afdd"
integrity sha512-OIZffked7mXv4mXzWU5MgAEbCf9ecNJBKi+Si6/I9PpTaj+cf2x58h2oHW5/P/yTnPkKaayfjhLvx+crnl5ubA==
"@typescript-eslint/types@5.10.1":
version "5.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.1.tgz#dca9bd4cb8c067fc85304a31f38ec4766ba2d1ea"
integrity sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==
"@typescript-eslint/typescript-estree@5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.6.0.tgz#dfbb19c9307fdd81bd9c650c67e8397821d7faf0"
integrity sha512-92vK5tQaE81rK7fOmuWMrSQtK1IMonESR+RJR2Tlc7w4o0MeEdjgidY/uO2Gobh7z4Q1hhS94Cr7r021fMVEeA==
"@typescript-eslint/typescript-estree@5.10.1":
version "5.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz#b268e67be0553f8790ba3fe87113282977adda15"
integrity sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==
dependencies:
"@typescript-eslint/types" "5.6.0"
"@typescript-eslint/visitor-keys" "5.6.0"
"@typescript-eslint/types" "5.10.1"
"@typescript-eslint/visitor-keys" "5.10.1"
debug "^4.3.2"
globby "^11.0.4"
is-glob "^4.0.3"
semver "^7.3.5"
tsutils "^3.21.0"
"@typescript-eslint/visitor-keys@5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.6.0.tgz#3e36509e103fe9713d8f035ac977235fd63cb6e6"
integrity sha512-1p7hDp5cpRFUyE3+lvA74egs+RWSgumrBpzBCDzfTFv0aQ7lIeay80yU0hIxgAhwQ6PcasW35kaOCyDOv6O/Ng==
"@typescript-eslint/visitor-keys@5.10.1":
version "5.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz#29102de692f59d7d34ecc457ed59ab5fc558010b"
integrity sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==
dependencies:
"@typescript-eslint/types" "5.6.0"
"@typescript-eslint/types" "5.10.1"
eslint-visitor-keys "^3.0.0"
acorn-jsx@^5.3.1:
@ -638,7 +643,7 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
debug@^4.1.1, debug@^4.3.2:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
@ -781,22 +786,30 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-config-next@12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.0.tgz#8ace680dc5207e6ab6c915f3989adec122f582e7"
integrity sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA==
eslint-config-next@12.1.1:
version "12.1.1"
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.1.tgz#11f948c5f4959267c2157dff8f2c28d067e3e1d9"
integrity sha512-+Ql9F07Pafs+cDgy8Zp0F8FxCBq7ke02ZyC2/MMEiGAX+WlnuUCrboBDnfzmHJpAAkcBPjUthunu6LBnF9KWIQ==
dependencies:
"@next/eslint-plugin-next" "12.1.0"
"@rushstack/eslint-patch" "^1.0.8"
"@typescript-eslint/parser" "^5.0.0"
eslint-import-resolver-node "^0.3.4"
eslint-import-resolver-typescript "^2.4.0"
eslint-plugin-import "^2.25.2"
eslint-plugin-jsx-a11y "^6.5.1"
eslint-plugin-react "^7.27.0"
eslint-plugin-react-hooks "^4.3.0"
"@next/eslint-plugin-next" "12.1.1"
"@rushstack/eslint-patch" "1.0.8"
"@typescript-eslint/parser" "5.10.1"
eslint-import-resolver-node "0.3.4"
eslint-import-resolver-typescript "2.4.0"
eslint-plugin-import "2.25.2"
eslint-plugin-jsx-a11y "6.5.1"
eslint-plugin-react "7.29.1"
eslint-plugin-react-hooks "4.3.0"
eslint-import-resolver-node@^0.3.4, eslint-import-resolver-node@^0.3.6:
eslint-import-resolver-node@0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==
dependencies:
debug "^2.6.9"
resolve "^1.13.1"
eslint-import-resolver-node@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
@ -804,46 +817,45 @@ eslint-import-resolver-node@^0.3.4, eslint-import-resolver-node@^0.3.6:
debug "^3.2.7"
resolve "^1.20.0"
eslint-import-resolver-typescript@^2.4.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a"
integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==
eslint-import-resolver-typescript@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz#ec1e7063ebe807f0362a7320543aaed6fe1100e1"
integrity sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==
dependencies:
debug "^4.3.1"
glob "^7.1.7"
debug "^4.1.1"
glob "^7.1.6"
is-glob "^4.0.1"
resolve "^1.20.0"
resolve "^1.17.0"
tsconfig-paths "^3.9.0"
eslint-module-utils@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c"
integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==
eslint-module-utils@^2.7.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee"
integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==
dependencies:
debug "^3.2.7"
find-up "^2.1.0"
pkg-dir "^2.0.0"
eslint-plugin-import@^2.25.2:
version "2.25.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766"
integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg==
eslint-plugin-import@2.25.2:
version "2.25.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz#b3b9160efddb702fc1636659e71ba1d10adbe9e9"
integrity sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==
dependencies:
array-includes "^3.1.4"
array.prototype.flat "^1.2.5"
debug "^2.6.9"
doctrine "^2.1.0"
eslint-import-resolver-node "^0.3.6"
eslint-module-utils "^2.7.1"
eslint-module-utils "^2.7.0"
has "^1.0.3"
is-core-module "^2.8.0"
is-core-module "^2.7.0"
is-glob "^4.0.3"
minimatch "^3.0.4"
object.values "^1.1.5"
resolve "^1.20.0"
tsconfig-paths "^3.11.0"
eslint-plugin-jsx-a11y@^6.5.1:
eslint-plugin-jsx-a11y@6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8"
integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==
@ -861,27 +873,27 @@ eslint-plugin-jsx-a11y@^6.5.1:
language-tags "^1.0.5"
minimatch "^3.0.4"
eslint-plugin-react-hooks@^4.3.0:
eslint-plugin-react-hooks@4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172"
integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==
eslint-plugin-react@^7.27.0:
version "7.27.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz#469202442506616f77a854d91babaae1ec174b45"
integrity sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==
eslint-plugin-react@7.29.1:
version "7.29.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.1.tgz#6c40bc83142bb63d132a1b3565e2ea655411f800"
integrity sha512-WtzRpHMhsOX05ZrkyaaqmLl2uXGqmYooCfBxftJKlkYdsltiufGgfU7uuoHwR2lBam2Kh/EIVID4aU9e3kbCMA==
dependencies:
array-includes "^3.1.4"
array.prototype.flatmap "^1.2.5"
doctrine "^2.1.0"
estraverse "^5.3.0"
jsx-ast-utils "^2.4.1 || ^3.0.0"
minimatch "^3.0.4"
minimatch "^3.1.2"
object.entries "^1.1.5"
object.fromentries "^2.0.5"
object.hasown "^1.1.0"
object.values "^1.1.5"
prop-types "^15.7.2"
prop-types "^15.8.1"
resolve "^2.0.0-next.3"
semver "^6.3.0"
string.prototype.matchall "^4.0.6"
@ -911,10 +923,10 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@8.11.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37"
integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==
eslint@8.12.0:
version "8.12.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.12.0.tgz#c7a5bd1cfa09079aae64c9076c07eada66a46e8e"
integrity sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==
dependencies:
"@eslint/eslintrc" "^1.2.1"
"@humanwhocodes/config-array" "^0.9.2"
@ -1165,7 +1177,7 @@ glob@7.1.7:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.3, glob@^7.1.7:
glob@^7.1.3, glob@^7.1.6:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
@ -1340,14 +1352,7 @@ is-core-module@^2.2.0:
dependencies:
has "^1.0.3"
is-core-module@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548"
integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==
dependencies:
has "^1.0.3"
is-core-module@^2.8.1:
is-core-module@^2.7.0, is-core-module@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
@ -1646,10 +1651,17 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimist@^1.1.1, minimist@^1.2.0:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
ms@2.0.0:
version "2.0.0"
@ -1681,28 +1693,29 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
next@12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d"
integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==
next@12.1.1:
version "12.1.1"
resolved "https://registry.yarnpkg.com/next/-/next-12.1.1.tgz#f5e73d3a204da0632484a56040c23a4796958bc6"
integrity sha512-IOfEIAgroMtsoYz6HXpDS+b5WB9WZ+MH266COXGlcpIiYSgUyJf9xV6vF+zY2RPvBJFT4fUW0EVdVnoOmTloDw==
dependencies:
"@next/env" "12.1.0"
"@next/env" "12.1.1"
caniuse-lite "^1.0.30001283"
postcss "8.4.5"
styled-jsx "5.0.0"
styled-jsx "5.0.1"
use-subscription "1.5.1"
optionalDependencies:
"@next/swc-android-arm64" "12.1.0"
"@next/swc-darwin-arm64" "12.1.0"
"@next/swc-darwin-x64" "12.1.0"
"@next/swc-linux-arm-gnueabihf" "12.1.0"
"@next/swc-linux-arm64-gnu" "12.1.0"
"@next/swc-linux-arm64-musl" "12.1.0"
"@next/swc-linux-x64-gnu" "12.1.0"
"@next/swc-linux-x64-musl" "12.1.0"
"@next/swc-win32-arm64-msvc" "12.1.0"
"@next/swc-win32-ia32-msvc" "12.1.0"
"@next/swc-win32-x64-msvc" "12.1.0"
"@next/swc-android-arm-eabi" "12.1.1"
"@next/swc-android-arm64" "12.1.1"
"@next/swc-darwin-arm64" "12.1.1"
"@next/swc-darwin-x64" "12.1.1"
"@next/swc-linux-arm-gnueabihf" "12.1.1"
"@next/swc-linux-arm64-gnu" "12.1.1"
"@next/swc-linux-arm64-musl" "12.1.1"
"@next/swc-linux-x64-gnu" "12.1.1"
"@next/swc-linux-x64-musl" "12.1.1"
"@next/swc-win32-arm64-msvc" "12.1.1"
"@next/swc-win32-ia32-msvc" "12.1.1"
"@next/swc-win32-x64-msvc" "12.1.1"
node-releases@^2.0.2:
version "2.0.2"
@ -1884,13 +1897,6 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
pkg-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
dependencies:
find-up "^2.1.0"
postcss-js@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00"
@ -1950,10 +1956,10 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prettier@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4"
integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==
prettier@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17"
integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==
pretty-bytes@6.0.0:
version "6.0.0"
@ -1969,6 +1975,15 @@ prop-types@^15.7.2:
object-assign "^4.1.1"
react-is "^16.8.1"
prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.13.1"
property-expr@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910"
@ -2022,7 +2037,7 @@ react-fast-compare@^2.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-is@^16.7.0, react-is@^16.8.1:
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -2082,7 +2097,7 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.20.0, resolve@^1.22.0:
resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.0:
version "1.22.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
@ -2250,18 +2265,18 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
stripe@8.210.0:
version "8.210.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.210.0.tgz#6e5ede9abd36406d39b89d9129df86fb33f43db3"
integrity sha512-NDQNEInrr1avwSEDB7Phe7PYNejtU9EkhOBlPa90Aomc3WvSXgOuX8MpswQ1mTw+W/lGpePwVqMsUvrvhUjZfA==
stripe@8.212.0:
version "8.212.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.212.0.tgz#78dc8d2be770580be23a8e597641a9ace6fd1035"
integrity sha512-xQ2uPMRAmRyOiMZktw3hY8jZ8LFR9lEQRPEaQ5WcDcn51kMyn46GeikOikxiFTHEN8PeKRdwtpz4yNArAvu/Kg==
dependencies:
"@types/node" ">=8.1.0"
qs "^6.6.0"
styled-jsx@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77"
integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==
styled-jsx@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80"
integrity sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw==
supports-color@^5.3.0:
version "5.5.0"

View File

@ -8,6 +8,6 @@
"is-valid-domain": "^0.1.6"
},
"devDependencies": {
"prettier": "^2.6.0"
"prettier": "^2.6.1"
}
}

View File

@ -265,10 +265,10 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
prettier@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4"
integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==
prettier@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17"
integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==
proxy-addr@~2.0.7:
version "2.0.7"

View File

@ -10,6 +10,6 @@
"punycode": "^2.1.1"
},
"devDependencies": {
"prettier": "^2.6.0"
"prettier": "^2.6.1"
}
}

View File

@ -314,10 +314,10 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
prettier@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4"
integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==
prettier@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17"
integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==
proxy-addr@~2.0.7:
version "2.0.7"

View File

@ -19,6 +19,6 @@
},
"devDependencies": {
"jest": "^27.5.1",
"prettier": "^2.6.0"
"prettier": "^2.6.1"
}
}

View File

@ -2438,9 +2438,9 @@ minimatch@^3.0.4:
brace-expansion "^1.1.7"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
ms@2.0.0:
version "2.0.0"
@ -2643,10 +2643,10 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
prettier@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4"
integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==
prettier@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17"
integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==
pretty-format@^27.5.1:
version "27.5.1"

View File

@ -5,31 +5,31 @@
"version": "0.1.0",
"author": "Skynet Labs.",
"dependencies": {
"@fontsource/sora": "4.5.3",
"@fontsource/source-sans-pro": "4.5.4",
"@fontsource/sora": "4.5.5",
"@fontsource/source-sans-pro": "4.5.6",
"@svgr/webpack": "6.2.1",
"bytes": "3.1.2",
"classnames": "2.3.1",
"copy-text-to-clipboard": "3.0.1",
"crypto-browserify": "3.12.0",
"framer-motion": "6.2.8",
"gatsby": "4.10.1",
"gatsby": "4.10.3",
"gatsby-background-image": "1.6.0",
"gatsby-plugin-image": "2.10.0",
"gatsby-plugin-manifest": "4.10.1",
"gatsby-plugin-image": "2.10.1",
"gatsby-plugin-manifest": "4.10.2",
"gatsby-plugin-postcss": "5.10.0",
"gatsby-plugin-react-helmet": "5.10.0",
"gatsby-plugin-robots-txt": "1.7.0",
"gatsby-plugin-sharp": "4.10.1",
"gatsby-plugin-sitemap": "5.10.1",
"gatsby-plugin-sharp": "4.10.2",
"gatsby-plugin-sitemap": "5.10.2",
"gatsby-plugin-svgr": "3.0.0-beta.0",
"gatsby-source-filesystem": "4.10.0",
"gatsby-source-filesystem": "4.10.1",
"gatsby-transformer-sharp": "4.10.0",
"gatsby-transformer-yaml": "4.10.0",
"gbimage-bridge": "0.2.1",
"http-status-codes": "2.2.0",
"ms": "2.1.3",
"nanoid": "3.3.1",
"nanoid": "3.3.2",
"normalize.css": "8.0.1",
"path-browserify": "1.0.1",
"polished": "4.1.4",
@ -49,7 +49,7 @@
"autoprefixer": "10.4.4",
"cross-env": "7.0.3",
"cypress": "9.5.2",
"prettier": "2.6.0",
"prettier": "2.6.1",
"tailwindcss": "3.0.23"
},
"keywords": [

View File

@ -1098,15 +1098,15 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@fontsource/sora@4.5.3":
version "4.5.3"
resolved "https://registry.yarnpkg.com/@fontsource/sora/-/sora-4.5.3.tgz#987c9b43acb00c9e3fa5377ebcedfd4ec9b760a7"
integrity sha512-0ipYkroLonvChAyLajgIt6mImXMhvjrHwD5g7iX2ZR1eJ4hLDnwq6haW5pSeehe79lPjgp0BX6ZHivFIP0xR2g==
"@fontsource/sora@4.5.5":
version "4.5.5"
resolved "https://registry.yarnpkg.com/@fontsource/sora/-/sora-4.5.5.tgz#25e8e0a11d0a2a861ccce23a94383448c5beaa50"
integrity sha512-SeePOM5pvXaa2a1c9VuGPJGmHNcFSUBzVxfHMuQ/FrVeiXvF3XXyPbt68N6QmHK2rAH844BN1Y5zImxqa5ok6g==
"@fontsource/source-sans-pro@4.5.4":
version "4.5.4"
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-4.5.4.tgz#51510723ff40f446c7800f133e9ae604ae2f38d7"
integrity sha512-+YYw6HRvH9wYE+U2Hvxyossg+MHPApAj7VIjEqaXenNeNQa4U3uPD0e7pc+1Gic3srCQATN15O3S9WSFLXTmwQ==
"@fontsource/source-sans-pro@4.5.6":
version "4.5.6"
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-4.5.6.tgz#1c74aa0168f0ad851e7c69a5ff25914c7691b6cb"
integrity sha512-MPv0Ye2OmcRD4wQ/7lOkysQpXpWkDSqZ2QHdNNLj7CQVtG03BScLMYQXZbYNhNMbH518tdMFewqDSu+/ikAMTg==
"@gatsbyjs/parcel-namer-relative-to-cwd@0.0.2":
version "0.0.2"
@ -2436,7 +2436,7 @@
resolved "https://registry.yarnpkg.com/@types/get-port/-/get-port-3.2.0.tgz#f9e0a11443cc21336470185eae3dfba4495d29bc"
integrity sha512-TiNg8R1kjDde5Pub9F9vCwZA/BNW9HeXP5b9j7Qucqncy/McfPZ6xze/EyBdXS5FhMIGN6Fx3vg75l5KHy3V1Q==
"@types/glob@*", "@types/glob@^7.1.1":
"@types/glob@*":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
@ -3339,23 +3339,23 @@ babel-plugin-polyfill-regenerator@^0.3.0:
dependencies:
"@babel/helper-define-polyfill-provider" "^0.3.1"
babel-plugin-remove-graphql-queries@^4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-4.10.0.tgz#882ec7e75627ffd844e5c4c6d7ae50afe87bc415"
integrity sha512-vANJvjh03qC7o6O3huCKO+Jtmee9WPUJm4Nm+qn/ww+GOOQwz0Z0bSMeBhUkJbT/Y1b1JlysHoxTO3ZNH47EwA==
babel-plugin-remove-graphql-queries@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-4.10.1.tgz#09357b7afad1d1bf359d97701ba267bf968197f4"
integrity sha512-1Irx5lUoQtU56u1hMtrgipjlRsUF4Pz6WDCDNVyTuvcHqlKHQK6pna4igHrdoWhSSgEcbimlxSy6tTvP6bkplg==
dependencies:
"@babel/runtime" "^7.15.4"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
babel-plugin-transform-react-remove-prop-types@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a"
integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==
babel-preset-gatsby@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/babel-preset-gatsby/-/babel-preset-gatsby-2.10.0.tgz#83695f47dfbb5e6ea2ce2b504f1e8716d39a5535"
integrity sha512-rh8vXjNXzX8Hkt27R7L56EovUf1dZDQavaYiY8zMQno/0iK7XllgO18Cfkgg3yYro0u1jOBWpoderMmZBLaVgw==
babel-preset-gatsby@^2.10.1:
version "2.10.1"
resolved "https://registry.yarnpkg.com/babel-preset-gatsby/-/babel-preset-gatsby-2.10.1.tgz#a31fe2f4b4ed64204baca5ea8267639a00dd1a6b"
integrity sha512-wuEP2Tsf7MnblXIqJDdg+DOUXZhWQHBQYH/6cQ+HdEFiXu0gwskj/s5sPI6KoBgGK23EIZGr94HBtCq0D3aNRQ==
dependencies:
"@babel/plugin-proposal-class-properties" "^7.14.0"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5"
@ -3370,7 +3370,7 @@ babel-preset-gatsby@^2.10.0:
babel-plugin-dynamic-import-node "^2.3.3"
babel-plugin-macros "^2.8.0"
babel-plugin-transform-react-remove-prop-types "^0.4.24"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
gatsby-legacy-polyfills "^2.10.0"
backo2@^1.0.2, backo2@~1.0.2:
@ -4340,10 +4340,10 @@ create-ecdh@^4.0.0:
bn.js "^4.1.0"
elliptic "^6.5.3"
create-gatsby@^2.10.1:
version "2.10.1"
resolved "https://registry.yarnpkg.com/create-gatsby/-/create-gatsby-2.10.1.tgz#7172877693efe02bebf7bd8c15a4072e83dfb916"
integrity sha512-zkAtC6TLr6nNmZd2yKqnXuEMX2vX9ciu4VBtMY+4J/nQ/cV77xCAmoU9MCAjVDoLp2h8pNckYE1lneFxmhIgkQ==
create-gatsby@^2.10.2:
version "2.10.2"
resolved "https://registry.yarnpkg.com/create-gatsby/-/create-gatsby-2.10.2.tgz#ded9eb4066078845529a02bc966ef414a77b814a"
integrity sha512-OmuFey3Eoaek20m8Br/iIqEr76AUrl/ThCn7TBYJ6RvWVKezKV5UksQlFpdQgPK8kO9LogjkmiyA0k94VC3nXg==
dependencies:
"@babel/runtime" "^7.15.4"
@ -4808,20 +4808,6 @@ defined@^1.0.0:
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
del@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7"
integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==
dependencies:
globby "^10.0.1"
graceful-fs "^4.2.2"
is-glob "^4.0.1"
is-path-cwd "^2.2.0"
is-path-inside "^3.0.1"
p-map "^3.0.0"
rimraf "^3.0.0"
slash "^3.0.0"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@ -5807,7 +5793,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.9:
fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.9:
version "3.2.11"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
@ -6172,10 +6158,10 @@ gatsby-background-image@1.6.0:
short-uuid "^4.2.0"
sort-media-queries "^0.2.2"
gatsby-cli@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-cli/-/gatsby-cli-4.10.1.tgz#0c077a7c14de4a95a70edd32916a94580ff3dbda"
integrity sha512-bv1xJEAcv4vT3DKdOqT1W8qe4L0Yvw5sZoSXDSKX9K9YSGu/CnBJXPa31rNrv357mc4CyAf/Vdfnzvr0tNN+iQ==
gatsby-cli@^4.10.2:
version "4.10.2"
resolved "https://registry.yarnpkg.com/gatsby-cli/-/gatsby-cli-4.10.2.tgz#835a2c472d5cd1b9258e5208f7f7184e5fd1b97b"
integrity sha512-lA1zFuWZwE+VtKYgntls0EC5PG4QoqqeJFWP+83Lmh+9hoLLkmL1plALBQeI3JY/9KXgRiC4OoR94CQkI329TA==
dependencies:
"@babel/code-frame" "^7.14.0"
"@babel/core" "^7.15.5"
@ -6193,13 +6179,13 @@ gatsby-cli@^4.10.1:
common-tags "^1.8.2"
configstore "^5.0.1"
convert-hrtime "^3.0.0"
create-gatsby "^2.10.1"
create-gatsby "^2.10.2"
envinfo "^7.8.1"
execa "^5.1.1"
fs-exists-cached "^1.0.0"
fs-extra "^10.0.0"
gatsby-core-utils "^3.10.0"
gatsby-telemetry "^3.10.0"
gatsby-core-utils "^3.10.1"
gatsby-telemetry "^3.10.1"
hosted-git-info "^3.0.8"
is-valid-path "^0.1.1"
joi "^17.4.2"
@ -6223,10 +6209,10 @@ gatsby-cli@^4.10.1:
yoga-layout-prebuilt "^1.10.0"
yurnalist "^2.1.0"
gatsby-core-utils@^3.10.0, gatsby-core-utils@^3.8.2:
version "3.10.0"
resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-3.10.0.tgz#52be8a9a891d95686a7ee0c1cfef44f8e362232b"
integrity sha512-yaRI/uUsbIggPRfh0y6CH+fy2AqbFFLxCYndw5nrVByEY40+KaKs0wOF4yIgPRBZZUHOyfBJ+1AGo2JLHdY5lA==
gatsby-core-utils@^3.10.0, gatsby-core-utils@^3.10.1, gatsby-core-utils@^3.8.2:
version "3.10.1"
resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-3.10.1.tgz#0f5e6aeda5ef4b86218b27480a9766fe30af4393"
integrity sha512-WqNMm0u1CAZm6Q+UQ4dDHwIAt3l32NeIaPuSXmDX7QcMGR3FUUk8cl2Ym6gx1hfILm1aCexqfaSCLUXtaWKkbQ==
dependencies:
"@babel/runtime" "^7.15.4"
ci-info "2.0.0"
@ -6259,26 +6245,26 @@ gatsby-legacy-polyfills@^2.10.0:
"@babel/runtime" "^7.15.4"
core-js-compat "3.9.0"
gatsby-link@^4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/gatsby-link/-/gatsby-link-4.10.0.tgz#11565eca6df53e7952c44bc290791bb032fa88ce"
integrity sha512-icF3i58MWgk4hSLaI6s5xyUp/rPy6MK7F4hsETQgJR32aZzkhj5Ydd1Cqw5P0CYo8EdaYKtljFBVwai8TnY4MQ==
gatsby-link@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-link/-/gatsby-link-4.10.1.tgz#f68b04b900c9c81070913d65a6e4051a2266dc1b"
integrity sha512-U9Qry93Q+aXF9acs1YPH+wzipPlZELUa619CAhZnLv2sk75Iq5SttMZI/d0uhgF+X80PvoEKMkLb/VE4YiqVtw==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/reach__router" "^1.3.10"
gatsby-page-utils "^2.10.0"
gatsby-page-utils "^2.10.1"
prop-types "^15.7.2"
gatsby-page-utils@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/gatsby-page-utils/-/gatsby-page-utils-2.10.0.tgz#8d7729714ca2c5b416ed527eeaa59f5737d20888"
integrity sha512-xyWHrMAJCd/w4mCZ1anK4ADT7pKdVZgQhppBWkm9H604p7RqsoloaQ9XGbPUuypzyPQw78yV5oY6bsaDpbvtrg==
gatsby-page-utils@^2.10.1:
version "2.10.1"
resolved "https://registry.yarnpkg.com/gatsby-page-utils/-/gatsby-page-utils-2.10.1.tgz#39420dd41508ca2793f0c838816b16fd10bbc8d7"
integrity sha512-juhN7/cLKoFGPW37xBCN78D02yO1htxR3raO/f2iseCCsyDjXRnqGHgpdFu8g8s8bkukL5vscgALcyChQf7NtA==
dependencies:
"@babel/runtime" "^7.15.4"
bluebird "^3.7.2"
chokidar "^3.5.2"
fs-exists-cached "^1.0.0"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
glob "^7.2.0"
lodash "^4.17.21"
micromatch "^4.0.4"
@ -6306,50 +6292,50 @@ gatsby-parcel-config@^0.1.0:
"@parcel/transformer-raw" "^2.3.1"
"@parcel/transformer-react-refresh-wrap" "^2.3.1"
gatsby-plugin-image@2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/gatsby-plugin-image/-/gatsby-plugin-image-2.10.0.tgz#8b4bb0bb2113ab5fa542e52e83f75228ff577703"
integrity sha512-W6/RvRs27XG5TwxTEAhnY68g0LAqP6T2NDPPEzAe/V/x5PyPn5Zf/N1pPUoqqdJ2n48Wg2BSv3Asz5/4vHfRgw==
gatsby-plugin-image@2.10.1:
version "2.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-image/-/gatsby-plugin-image-2.10.1.tgz#7b9eda7334487dd0e4693996cf3664835e9c356e"
integrity sha512-ZFcaKkVz6h18jRzP9HHCcXzyIc/Gj57NZSycOWFxW9KLqkq3NZXVx3mkV9EQ+rup8r3RLiJu/AXEorZEy7iAhQ==
dependencies:
"@babel/code-frame" "^7.14.0"
"@babel/parser" "^7.15.5"
"@babel/runtime" "^7.15.4"
"@babel/traverse" "^7.15.4"
babel-jsx-utils "^1.1.0"
babel-plugin-remove-graphql-queries "^4.10.0"
babel-plugin-remove-graphql-queries "^4.10.1"
camelcase "^5.3.1"
chokidar "^3.5.2"
common-tags "^1.8.2"
fs-extra "^10.0.0"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
objectFitPolyfill "^2.3.5"
prop-types "^15.7.2"
gatsby-plugin-manifest@4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.10.1.tgz#01949125a70bac22f2d8946d5829b49f7f188ca2"
integrity sha512-D4WYQD1gDdyvWt8RYl4OC/i7thPkgtkm+kZW+d1JVpUTu+BrbdPYCIUMGdSrDyKxx3x0bhMmEf9hZW25acew0Q==
gatsby-plugin-manifest@4.10.2:
version "4.10.2"
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.10.2.tgz#117b44e0e219fa85d6b710d2ed985268d82a1e25"
integrity sha512-2QGrG3qFkUnp0IcxQ+uQS4/jPL4NMHShOEs/7JPsuIJSIdumnG7QYJ9xHbIBb8U5mIFLpeonADYJVbdTsnGtrg==
dependencies:
"@babel/runtime" "^7.15.4"
gatsby-core-utils "^3.10.0"
gatsby-plugin-utils "^3.4.1"
gatsby-core-utils "^3.10.1"
gatsby-plugin-utils "^3.4.2"
semver "^7.3.5"
sharp "^0.30.1"
gatsby-plugin-page-creator@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-4.10.1.tgz#6c5308daba53729225578ca67e52f117088502a5"
integrity sha512-tTN4qHH2n7CiV/MNx/FcnBBOmCWzlMkpIR6AtJfxdO5sncn3XIEwMKNxvp9WUm2bevUodlynxlER1dOtgj+DJA==
gatsby-plugin-page-creator@^4.10.2:
version "4.10.2"
resolved "https://registry.yarnpkg.com/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-4.10.2.tgz#05e6e3b0bdf800a4358573768987ed73d50eb158"
integrity sha512-6oVkPe69dGnq4ZiUgTuuIzTyOTmC/awXyfCdfkvQHQZRqhyzhHMW95aFgr3y/qNlOmCKDaxYmxeT0KOKxcyxWw==
dependencies:
"@babel/runtime" "^7.15.4"
"@babel/traverse" "^7.15.4"
"@sindresorhus/slugify" "^1.1.2"
chokidar "^3.5.2"
fs-exists-cached "^1.0.0"
gatsby-core-utils "^3.10.0"
gatsby-page-utils "^2.10.0"
gatsby-plugin-utils "^3.4.1"
gatsby-telemetry "^3.10.0"
gatsby-core-utils "^3.10.1"
gatsby-page-utils "^2.10.1"
gatsby-plugin-utils "^3.4.2"
gatsby-telemetry "^3.10.1"
globby "^11.0.4"
lodash "^4.17.21"
@ -6376,10 +6362,10 @@ gatsby-plugin-robots-txt@1.7.0:
"@babel/runtime" "^7.16.7"
generate-robotstxt "^8.0.3"
gatsby-plugin-sharp@4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-sharp/-/gatsby-plugin-sharp-4.10.1.tgz#d72145f7649b71a1e5051dcf7ed8f4d1f72bb041"
integrity sha512-Y+kB5BSfcxMYO5s13W3NDeDH28RCzSa8goo1yQNTISiGaC6iBO4EIEVbUiUBkoLA5nf46J71ve8A3sprZ66bog==
gatsby-plugin-sharp@4.10.2:
version "4.10.2"
resolved "https://registry.yarnpkg.com/gatsby-plugin-sharp/-/gatsby-plugin-sharp-4.10.2.tgz#253a49c452a7409ceece4e541e4770e61a306bcc"
integrity sha512-MWzPTYnu7HZ0kctHtkLbZOe6ZGUqSsNATO3lWlSBIFpeimxaPF5iHBiu1CX/ofz4pwt7VamtIzAV28VB6sjONw==
dependencies:
"@babel/runtime" "^7.15.4"
async "^3.2.3"
@ -6387,9 +6373,9 @@ gatsby-plugin-sharp@4.10.1:
debug "^4.3.3"
filenamify "^4.3.0"
fs-extra "^10.0.0"
gatsby-core-utils "^3.10.0"
gatsby-plugin-utils "^3.4.1"
gatsby-telemetry "^3.10.0"
gatsby-core-utils "^3.10.1"
gatsby-plugin-utils "^3.4.2"
gatsby-telemetry "^3.10.1"
got "^11.8.3"
lodash "^4.17.21"
mini-svg-data-uri "^1.4.3"
@ -6401,10 +6387,10 @@ gatsby-plugin-sharp@4.10.1:
svgo "1.3.2"
uuid "3.4.0"
gatsby-plugin-sitemap@5.10.1:
version "5.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-sitemap/-/gatsby-plugin-sitemap-5.10.1.tgz#297e9434802e1d829b257ab9627ba581f48cb154"
integrity sha512-EO5GWLhkN3gfOXLX8QlbIepu2Kq1kk4bBgU5M2CjZAyVQhahZAJxdBat7JgLTpKZL4lSJgVSCl9IkeKgHvm+pg==
gatsby-plugin-sitemap@5.10.2:
version "5.10.2"
resolved "https://registry.yarnpkg.com/gatsby-plugin-sitemap/-/gatsby-plugin-sitemap-5.10.2.tgz#208149b900b166c42aa88a5f5436f5c6bf6561e9"
integrity sha512-X6pVbytl/QfdfGrnXAEKPf5vc38WIbclmHYIfbgjXUYA9yckTxnfuYZqkS2YwCmbcUTHG1ugcmXMeBGVo77IBQ==
dependencies:
"@babel/runtime" "^7.15.4"
common-tags "^1.8.2"
@ -6416,10 +6402,10 @@ gatsby-plugin-svgr@3.0.0-beta.0:
resolved "https://registry.yarnpkg.com/gatsby-plugin-svgr/-/gatsby-plugin-svgr-3.0.0-beta.0.tgz#7e5315f51dae2663a447899322ea1487cef93dd6"
integrity sha512-oALTh6VwO6l3khgC/vGr706aqt38EkXwdr6iXVei/auOKGxpCLEuDCQVal1a4SpYXdjHjRsEyab6bxaHL2lzsA==
gatsby-plugin-typescript@^4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/gatsby-plugin-typescript/-/gatsby-plugin-typescript-4.10.0.tgz#ab518d6cca48cfc0b19ab56ea778ed57ba0e7e52"
integrity sha512-g6vbADv5+MRuNBkyvzpjzr2uwrkoNi/K+dAI2gcLdKQoITj6ZDTR9iB4yMF1s23XshlkwwN8+3/3VUiR1fguoQ==
gatsby-plugin-typescript@^4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-typescript/-/gatsby-plugin-typescript-4.10.1.tgz#1d94532f4cd97677c38bda3b4d92a50b3856e11d"
integrity sha512-yQZYRAZwtj20OrvsF8+xr9V0Jew//3HghM1LKvCB58wECvwLW9SXHzema92JubirFOk7VrdeQfVe3u4jCYDDYw==
dependencies:
"@babel/core" "^7.15.5"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5"
@ -6427,16 +6413,16 @@ gatsby-plugin-typescript@^4.10.0:
"@babel/plugin-proposal-optional-chaining" "^7.14.5"
"@babel/preset-typescript" "^7.15.0"
"@babel/runtime" "^7.15.4"
babel-plugin-remove-graphql-queries "^4.10.0"
babel-plugin-remove-graphql-queries "^4.10.1"
gatsby-plugin-utils@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/gatsby-plugin-utils/-/gatsby-plugin-utils-3.4.1.tgz#14c9cff75af32a32860575069af44bdabc8f65d9"
integrity sha512-sDMVGauxMgXyX8WGZDndZI2vIaolJzlXBMdKhgP7DIT+Qa5wjvyHWvZy34dxtVrT3IHPK/PRMgpE81Gr7gKveg==
gatsby-plugin-utils@^3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/gatsby-plugin-utils/-/gatsby-plugin-utils-3.4.2.tgz#bdc66fd52c052c91af443981e1facbc097429dc7"
integrity sha512-HkVR8BnEdc915pmY2L9wPwFgjq303ThMH9UtT+frDOPzn+GFjPlBF5+YGJ3tZaxH0ouwpnGTn3Pu37tcRZcctA==
dependencies:
"@babel/runtime" "^7.15.4"
fs-extra "^10.0.0"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
gatsby-sharp "^0.4.0"
graphql-compose "^9.0.7"
import-from "^4.0.0"
@ -6459,16 +6445,16 @@ gatsby-sharp@^0.4.0:
"@types/sharp" "^0.29.5"
sharp "^0.30.1"
gatsby-source-filesystem@4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-4.10.0.tgz#d51041c01b0e905a426f8ad81494175e2f2a1109"
integrity sha512-fRN7MFVnmxljVmZZCYswNNMdFygB0hfHmtTM6kdbBc8llkRPtr9tsvK6LHnWsxU2tzPHDTGvRORcSs/coT2v3A==
gatsby-source-filesystem@4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-4.10.1.tgz#c513fadb3cedb138ff28ba351ffd264524a0c28d"
integrity sha512-qdOWS234l6QyEN0M8tfdGQF530pK9nSiaT1JfSzZV7Bl9psX9SdsuOtfZ2AV0QVt1BQB7C53E/BNGaxMLCcnUg==
dependencies:
"@babel/runtime" "^7.15.4"
chokidar "^3.5.2"
file-type "^16.5.3"
fs-extra "^10.0.0"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
got "^9.6.0"
md5-file "^5.0.0"
mime "^2.5.2"
@ -6477,10 +6463,10 @@ gatsby-source-filesystem@4.10.0:
valid-url "^1.0.9"
xstate "^4.26.1"
gatsby-telemetry@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/gatsby-telemetry/-/gatsby-telemetry-3.10.0.tgz#917a4914e531d401ecf98ac87d29128b30bfab13"
integrity sha512-Oe2OShJbylKr5C4FTl2P/JUX/xRkpYb6IMfEoAd5inG7HNQ1fikON4NdwvJjOp++My4kWo+LLCu92TZBkyTtZw==
gatsby-telemetry@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/gatsby-telemetry/-/gatsby-telemetry-3.10.1.tgz#129c4309811cd9b2ba5e9d326ba47dddb59202ca"
integrity sha512-935quI1YsQfzYREuvPLNKBb7IUE2vX9p7WoS7Dc9TbV2xDZPTAzeOfX+HE56ZltkxMi8Zivp7mqe5+n//WL7EQ==
dependencies:
"@babel/code-frame" "^7.14.0"
"@babel/runtime" "^7.15.4"
@ -6490,7 +6476,7 @@ gatsby-telemetry@^3.10.0:
boxen "^4.2.0"
configstore "^5.0.1"
fs-extra "^10.0.0"
gatsby-core-utils "^3.10.0"
gatsby-core-utils "^3.10.1"
git-up "^4.0.5"
is-docker "^2.2.1"
lodash "^4.17.21"
@ -6520,18 +6506,18 @@ gatsby-transformer-yaml@4.10.0:
lodash "^4.17.21"
unist-util-select "^1.5.0"
gatsby-worker@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/gatsby-worker/-/gatsby-worker-1.10.0.tgz#19b958656c7a0ff2b52f24cf99b7c08131f5fa04"
integrity sha512-s4y3bI2ltbfm+hBI9OgDRkrtTIgiHW97DnbikzZtF0lgclqDh/7vhFvTjKGejObR/dbV28t+SAMJLzd9klrgog==
gatsby-worker@^1.10.1:
version "1.10.1"
resolved "https://registry.yarnpkg.com/gatsby-worker/-/gatsby-worker-1.10.1.tgz#d2549c9854effe3319bd4bd199be0a5c4d077af2"
integrity sha512-1bHZjWEKppJSLfuRGYIYhHZt9vYffptMPRs3zmrYBocmZcJvq0eNhTIXUvThbhP+2XHqAPHHBUcyKCwlWlMAzA==
dependencies:
"@babel/core" "^7.15.5"
"@babel/runtime" "^7.15.4"
gatsby@4.10.1:
version "4.10.1"
resolved "https://registry.yarnpkg.com/gatsby/-/gatsby-4.10.1.tgz#b00f0454baffebfe38de10aeb4db8aa6e39a5e4a"
integrity sha512-UCapYjofrdxa3bIjMUN0Y+EUIEHNX/srOonv03UeZRKHshwAPe6g1eZfXK4iFNy8LxNsH947D5hLN63pNK77zg==
gatsby@4.10.3:
version "4.10.3"
resolved "https://registry.yarnpkg.com/gatsby/-/gatsby-4.10.3.tgz#72d9620d3f9a90c96e568f4803f86458d1dc6a53"
integrity sha512-G6YYwQWrN99KhJgLQl/oy3oKRuTdUcW7Bgb9vRchjY/Apk0eTA2wq33W5L9y+npR3nxly6ZJ2pM8FHw7WX4tKg==
dependencies:
"@babel/code-frame" "^7.14.0"
"@babel/core" "^7.15.5"
@ -6558,8 +6544,8 @@ gatsby@4.10.1:
babel-plugin-add-module-exports "^1.0.4"
babel-plugin-dynamic-import-node "^2.3.3"
babel-plugin-lodash "^3.3.4"
babel-plugin-remove-graphql-queries "^4.10.0"
babel-preset-gatsby "^2.10.0"
babel-plugin-remove-graphql-queries "^4.10.1"
babel-preset-gatsby "^2.10.1"
better-opn "^2.1.1"
bluebird "^3.7.2"
body-parser "^1.19.0"
@ -6578,7 +6564,6 @@ gatsby@4.10.1:
date-fns "^2.25.0"
debug "^3.2.7"
deepmerge "^4.2.2"
del "^5.1.0"
detect-port "^1.3.0"
devcert "^1.2.0"
dotenv "^8.6.0"
@ -6602,20 +6587,21 @@ gatsby@4.10.1:
find-cache-dir "^3.3.2"
fs-exists-cached "1.0.0"
fs-extra "^10.0.0"
gatsby-cli "^4.10.1"
gatsby-core-utils "^3.10.0"
gatsby-cli "^4.10.2"
gatsby-core-utils "^3.10.1"
gatsby-graphiql-explorer "^2.10.0"
gatsby-legacy-polyfills "^2.10.0"
gatsby-link "^4.10.0"
gatsby-page-utils "^2.10.0"
gatsby-link "^4.10.1"
gatsby-page-utils "^2.10.1"
gatsby-parcel-config "^0.1.0"
gatsby-plugin-page-creator "^4.10.1"
gatsby-plugin-typescript "^4.10.0"
gatsby-plugin-utils "^3.4.1"
gatsby-plugin-page-creator "^4.10.2"
gatsby-plugin-typescript "^4.10.1"
gatsby-plugin-utils "^3.4.2"
gatsby-react-router-scroll "^5.10.0"
gatsby-telemetry "^3.10.0"
gatsby-worker "^1.10.0"
gatsby-telemetry "^3.10.1"
gatsby-worker "^1.10.1"
glob "^7.2.0"
globby "^11.1.0"
got "^11.8.2"
graphql "^15.7.2"
graphql-compose "^9.0.7"
@ -6908,21 +6894,7 @@ globby@11.0.3:
merge2 "^1.3.0"
slash "^3.0.0"
globby@^10.0.1:
version "10.0.2"
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==
dependencies:
"@types/glob" "^7.1.1"
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.0.3"
glob "^7.1.3"
ignore "^5.1.1"
merge2 "^1.2.3"
slash "^3.0.0"
globby@^11.0.3, globby@^11.0.4:
globby@^11.0.3, globby@^11.0.4, globby@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
@ -6968,7 +6940,7 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.9"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
@ -7301,7 +7273,7 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8, ignore@^5.2.0:
ignore@^5.1.4, ignore@^5.1.8, ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
@ -7661,12 +7633,7 @@ is-obj@^2.0.0:
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
is-path-cwd@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
is-path-inside@^3.0.1, is-path-inside@^3.0.2:
is-path-inside@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
@ -8562,7 +8529,7 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@ -8729,9 +8696,9 @@ minimist-options@4.1.0:
kind-of "^6.0.3"
minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
mitt@^1.2.0:
version "1.2.0"
@ -8831,10 +8798,10 @@ nano-css@^5.3.1:
stacktrace-js "^2.0.2"
stylis "^4.0.6"
nanoid@3.3.1, nanoid@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
nanoid@3.3.2, nanoid@^3.3.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557"
integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==
nanomatch@^1.2.9:
version "1.2.13"
@ -9306,13 +9273,6 @@ p-locate@^4.1.0:
dependencies:
p-limit "^2.2.0"
p-map@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d"
integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==
dependencies:
aggregate-error "^3.0.0"
p-map@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
@ -9940,10 +9900,10 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4"
integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==
prettier@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17"
integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==
pretty-bytes@^5.4.1, pretty-bytes@^5.6.0:
version "5.6.0"