add dialog to ui-toolkit, add functionality to provider to render connect dialog and connectors ui

This commit is contained in:
Matthew Russell 2022-02-21 22:40:55 -08:00
parent 37294b3174
commit ff24a4a3ba
17 changed files with 549 additions and 6 deletions

View File

@ -11,6 +11,10 @@ const nextConfig = {
svgr: false,
},
pageExtensions: ['page.tsx', 'page.jsx'],
experimental: {
// https://github.com/vercel/next.js/issues/32360
esmExternals: false,
},
};
module.exports = withNx(nextConfig);

View File

@ -5,6 +5,7 @@ import { useCallback, useMemo } from 'react';
import { Navbar } from '../components/navbar';
import { createClient } from '../lib/apollo-client';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/react-helpers';
import './styles.css';
function VegaTradingApp({ Component, pageProps }: AppProps) {
@ -38,6 +39,7 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60">
<div className="flex items-center border-b-[7px] border-vega-yellow">
<Navbar />
<VegaWalletButton />
<ThemeSwitcher onToggle={setTheme} className="ml-auto mr-8 -my-2" />
</div>
<main>
@ -48,4 +50,23 @@ function VegaTradingApp({ Component, pageProps }: AppProps) {
);
}
const VegaWalletButton = () => {
const { setConnectDialog, disconnect, publicKeys } = useVegaWallet();
const isConnected = publicKeys !== null;
const handleClick = () => {
if (isConnected) {
disconnect();
} else {
setConnectDialog();
}
};
return (
<button onClick={handleClick}>
{isConnected ? 'Disconnect' : 'Connect Vega wallet'}
</button>
);
};
export default VegaTradingApp;

View File

@ -1,11 +1,7 @@
import { Callout, Button } from '@vegaprotocol/ui-toolkit';
export function Index() {
/*
* Replace the elements below with your own.
*
* Note: The corresponding styles are in the ./index.scss file.
*/
const vegaWallet = useVegaWallet();
return (
<div className="m-24 ">
<Callout
@ -21,6 +17,7 @@ export function Index() {
</Button>
</div>
</Callout>
<pre>{JSON.stringify(vegaWallet, null, 2)}</pre>
</div>
);
}

View File

@ -1,2 +1,3 @@
export * from './lib/react-helpers';
export * from './lib/trading';
export * from './lib/vega-wallet';

View File

@ -0,0 +1,63 @@
import { useState } from 'react';
import { Dialog } from '@vegaprotocol/ui-toolkit';
import {
InjectedConnector,
RestConnector,
VegaConnector,
} from './vega-wallet-connectors';
import { RestConnectorForm } from './rest-connector-form';
import { useEffect } from 'react';
export const Connectors = {
injected: new InjectedConnector(),
rest: new RestConnector(),
};
interface ConnectDialogProps {
dialogOpen: boolean;
setDialogOpen: (isOpen: boolean) => void;
connect: (connector: VegaConnector) => void;
}
export function ConnectDialog({
dialogOpen,
setDialogOpen,
connect,
}: ConnectDialogProps) {
const [isRestConnector, setIsRestConnector] = useState(false);
useEffect(() => {
if (!dialogOpen) {
setIsRestConnector(false);
}
}, [dialogOpen]);
return (
<Dialog open={dialogOpen} setOpen={setDialogOpen}>
<div className="bg-black p-5 w-100 text-white">
{isRestConnector ? (
<RestConnectorForm setDialogOpen={setDialogOpen} />
) : (
<div className="flex gap-5 flex-col">
{Object.entries(Connectors).map(([key, connector]) => (
<button
key={key}
onClick={() => {
if (key === 'rest') {
// show form so that we can get an authentication token before 'connecting'
setIsRestConnector(true);
} else {
connect(connector);
setDialogOpen(false);
}
}}
>
{key} provider
</button>
))}
</div>
)}
</div>
</Dialog>
);
}

View File

@ -0,0 +1,10 @@
import { useContext } from 'react';
import { VegaWalletContext } from '.';
export function useVegaWallet() {
const context = useContext(VegaWalletContext);
if (context === undefined) {
throw new Error('useVegaWallet must be used within VegaWalletProvider');
}
return context;
}

View File

@ -0,0 +1,3 @@
export * from './vega-wallet-provider';
export * from './vega-wallet-context';
export * from './hooks';

View File

@ -0,0 +1,57 @@
import { useForm } from 'react-hook-form';
import { useVegaWallet } from '.';
import { Connectors } from './connect-dialog';
interface FormFields {
wallet: string;
passphrase: string;
}
interface RestConnectorFormProps {
setDialogOpen: (isOpen: boolean) => void;
}
export function RestConnectorForm({ setDialogOpen }: RestConnectorFormProps) {
const { connect } = useVegaWallet();
const { register, handleSubmit } = useForm<FormFields>();
async function onSubmit(fields: FormFields) {
try {
const success = await Connectors.rest.authenticate({
wallet: fields.wallet,
passphrase: fields.passphrase,
});
if (success) {
connect(Connectors.rest);
setDialogOpen(false);
} else {
throw new Error('Authentication failed');
}
} catch (err) {
console.error(err);
}
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="vega-wallet-form">
<div className="mb-5">
<input
{...register('wallet')}
type="text"
placeholder="Wallet"
className="text-black"
/>
</div>
<div className="mb-5">
<input
{...register('passphrase')}
type="text"
placeholder="Passphrase"
className="text-black"
/>
</div>
<button type="submit">Connect</button>
</form>
);
}

View File

@ -0,0 +1,86 @@
import {
DefaultApi,
createConfiguration,
Configuration,
VegaKey,
} from '@vegaprotocol/vegawallet-service-api-client';
import { LocalStorage } from '@vegaprotocol/storage';
export interface VegaConnector {
connect(): Promise<VegaKey[]>;
disconnect(): Promise<void>;
}
export class RestConnector implements VegaConnector {
apiConfig: Configuration;
service: DefaultApi;
constructor() {
this.apiConfig = createConfiguration({
authMethods: {
bearer: `Bearer ${LocalStorage.getItem('vega_wallet_token')}`,
},
promiseMiddleware: [
{
pre: async (requestContext) => {
const headers = requestContext.getHeaders();
if (
'Authorization' in headers &&
headers['Authorization'] === 'Bearer null'
) {
console.log('first login: getting and setting auth header');
requestContext.setHeaderParam(
'Authorization',
`Bearer ${LocalStorage.getItem('vega_wallet_token')}`
);
}
return requestContext;
},
post: async (requestContext) => {
return requestContext;
},
},
],
});
this.service = new DefaultApi(this.apiConfig);
}
async authenticate(params: { wallet: string; passphrase: string }) {
try {
const tokenRes = await this.service.authTokenPost(params);
LocalStorage.setItem('vega_wallet_token', tokenRes.token);
return true;
} catch (err) {
console.error(err);
return false;
}
}
async connect() {
const res = await this.service.keysGet();
return res.keys;
}
async disconnect() {
await this.service.authTokenDelete();
LocalStorage.removeItem('vega_wallet_token');
}
}
export class InjectedConnector implements VegaConnector {
async connect() {
return [
{
index: 9,
pub: '0x123',
algorithm: { name: 'algo', version: 2 },
tainted: false,
meta: [],
},
];
}
async disconnect() {
return;
}
}

View File

@ -0,0 +1,16 @@
import { VegaKey } from '@vegaprotocol/vegawallet-service-api-client';
import { createContext } from 'react';
import { VegaConnector } from './vega-wallet-connectors';
interface VegaWalletContextShape {
publicKey: VegaKey | null;
publicKeys: VegaKey[] | null;
setConnectDialog: (isOpen?: boolean) => void;
connect: (connector: VegaConnector) => Promise<void>;
disconnect: () => Promise<void>;
connector: VegaConnector | null;
}
export const VegaWalletContext = createContext<
VegaWalletContextShape | undefined
>(undefined);

View File

@ -0,0 +1,67 @@
import { VegaKey } from '@vegaprotocol/vegawallet-service-api-client';
import { ReactNode, useCallback, useMemo, useRef, useState } from 'react';
import { ConnectDialog } from './connect-dialog';
import { VegaConnector } from './vega-wallet-connectors';
import { VegaWalletContext } from './vega-wallet-context';
interface VegaWalletProviderProps {
children: ReactNode;
}
export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
const [dialogOpen, setDialogOpen] = useState(false);
const [publicKey, setPublicKey] = useState<VegaKey | null>(null);
const [publicKeys, setPublicKeys] = useState<VegaKey[] | null>(null);
const connector = useRef<VegaConnector | null>(null);
const setConnectDialog = useCallback((isOpen?: boolean) => {
setDialogOpen((curr) => {
if (isOpen === undefined) return !curr;
return isOpen;
});
}, []);
const connect = useCallback(async (c: VegaConnector) => {
connector.current = c;
try {
const res = await c.connect();
setPublicKeys(res);
} catch (err) {
console.log('connect failed');
}
}, []);
const disconnect = useCallback(async () => {
if (!connector.current) return;
try {
await connector.current.disconnect();
setPublicKeys(null);
setPublicKey(null);
connector.current = null;
} catch (err) {
console.log('disconnect failed', err);
}
}, []);
const contextValue = useMemo(() => {
return {
publicKey,
publicKeys,
setConnectDialog,
connect,
disconnect,
connector: connector.current,
};
}, [publicKey, publicKeys, connect, disconnect, setConnectDialog, connector]);
return (
<VegaWalletContext.Provider value={contextValue}>
{children}
<ConnectDialog
dialogOpen={dialogOpen}
setDialogOpen={setDialogOpen}
connect={connect}
/>
</VegaWalletContext.Provider>
);
};

28
libs/storage/src/index.ts Normal file
View File

@ -0,0 +1,28 @@
// TODO: fine for now however will leak state between tests (we don't really have) in future. Ideally should use a provider
export const LocalStorage = {
getItem: (key: string) => {
if (typeof window === 'undefined') return null;
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : null;
} catch (error) {
console.error(error);
}
},
setItem: (key: string, value: any) => {
if (typeof window === 'undefined') return;
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
},
removeItem: (key: string) => {
if (typeof window === 'undefined') return;
try {
window.localStorage.removeItem(key);
} catch (error) {
console.error(error);
}
},
};

View File

@ -0,0 +1,24 @@
import * as DialogPrimitives from '@radix-ui/react-dialog';
import { ReactNode } from 'react';
interface DialogProps {
children: ReactNode;
open: boolean;
setOpen: (isOpen: boolean) => void;
}
export function Dialog({ children, open, setOpen }: DialogProps) {
return (
<DialogPrimitives.Root open={open} onOpenChange={(x) => setOpen(x)}>
<DialogPrimitives.Portal>
<DialogPrimitives.Overlay className="bg-gray/75 fixed inset-0" />
<DialogPrimitives.Content
style={{ width: 300, top: 40, left: 'calc(50% - 150px)' }}
className="fixed inset-0"
>
{children}
</DialogPrimitives.Content>
</DialogPrimitives.Portal>
</DialogPrimitives.Root>
);
}

View File

@ -10,3 +10,4 @@ export { InputError } from './components/input-error';
export { Select } from './components/select';
export { TextArea } from './components/text-area';
export { ThemeSwitcher } from './components/theme-switcher';
export { Dialog } from './components/dialog';

View File

@ -23,6 +23,8 @@
"apollo": "^2.33.9",
"autoprefixer": "^10.4.2",
"classnames": "^2.3.1",
"@radix-ui/react-dialog": "^0.1.5",
"@vegaprotocol/vegawallet-service-api-client": "^0.3.0",
"core-js": "^3.6.5",
"env-cmd": "^10.1.0",
"graphql": "^15.7.2",
@ -35,6 +37,7 @@
"react-syntax-highlighter": "^15.4.5",
"react-use-websocket": "^3.0.0",
"react-virtualized-auto-sizer": "^1.0.6",
"react-hook-form": "^7.27.0",
"regenerator-runtime": "0.13.7",
"tailwindcss": "^3.0.23",
"tslib": "^2.0.0",

View File

@ -19,7 +19,8 @@
"@vegaprotocol/tailwindcss-config": [
"libs/tailwindcss-config/src/index.js"
],
"@vegaprotocol/ui-toolkit": ["libs/ui-toolkit/src/index.ts"]
"@vegaprotocol/ui-toolkit": ["libs/ui-toolkit/src/index.ts"],
"@vegaprotocol/storage": ["libs/storage/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]

161
yarn.lock
View File

@ -2721,6 +2721,158 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-0.1.0.tgz#6206b97d379994f0d1929809db035733b337e543"
integrity sha512-tqxZKybwN5Fa3VzZry4G6mXAAb9aAqKmPtnVbZpL0vsBwvOHTBwsjHVPXylocYLwEtBY9SCe665bYnNB515uoA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz#cff6e780a0f73778b976acff2c2a5b6551caab95"
integrity sha512-eyclbh+b77k+69Dk72q3694OHrn9B3QsoIRx7ywX341U9RK1ThgQjMFZoPtmZNQTksXHLNEiefR8hGVeFyInGg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-context@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-0.1.1.tgz#06996829ea124d9a1bc1dbe3e51f33588fab0875"
integrity sha512-PkyVX1JsLBioeu0jB9WvRpDBBLtLZohVDT3BB5CTSJqActma8S8030P57mWZb4baZifMvN7KKWPAA40UmWKkQg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-dialog@^0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.1.5.tgz#4310659607f5ad0b8796623d5f7490dc47d3d295"
integrity sha512-WftvXcQSszUphCTLQkkpBIkrYYU0IYqgIvACLQady4BN4YHDgdNlrwdg2ti9QrXgq1PZ+0S/6BIaA1dmSuRQ2g==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-dismissable-layer" "0.1.3"
"@radix-ui/react-focus-guards" "0.1.0"
"@radix-ui/react-focus-scope" "0.1.3"
"@radix-ui/react-id" "0.1.4"
"@radix-ui/react-portal" "0.1.3"
"@radix-ui/react-presence" "0.1.1"
"@radix-ui/react-primitive" "0.1.3"
"@radix-ui/react-slot" "0.1.2"
"@radix-ui/react-use-controllable-state" "0.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "^2.4.0"
"@radix-ui/react-dismissable-layer@0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.1.3.tgz#d427c7520c3799d2b957e40e7d67045d96120356"
integrity sha512-3veE7M8K13Qb+6+tC3DHWmWV9VMuuRoZvRLdrvz7biSraK/qkGBN4LbKZDaTdw2D2HS7RNpSd/sF8pFd3TaAgA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-primitive" "0.1.3"
"@radix-ui/react-use-body-pointer-events" "0.1.0"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-escape-keydown" "0.1.0"
"@radix-ui/react-focus-guards@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-0.1.0.tgz#ba3b6f902cba7826569f8edc21ff8223dece7def"
integrity sha512-kRx/swAjEfBpQ3ns7J3H4uxpXuWCqN7MpALiSDOXiyo2vkWv0L9sxvbpZeTulINuE3CGMzicVMuNc/VWXjFKOg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-focus-scope@0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-0.1.3.tgz#b1cc825b6190001d731417ed90d192d13b41bce1"
integrity sha512-bKi+lw14SriQqYWMBe13b/wvxSqYMC+3FylMUEwOKA6JrBoldpkhX5XffGDdpDRTTpjbncdH3H7d1PL5Bs7Ikg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-primitive" "0.1.3"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-id@0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-0.1.4.tgz#4cd6126e6ac8a43ebe6d52948a068b797cc9ad71"
integrity sha512-/hq5m/D0ZfJWOS7TLF+G0l08KDRs87LBE46JkAvgKkg1fW4jkucx9At9D9vauIPSbdNmww5kXEp566hMlA8eXA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-portal@0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-0.1.3.tgz#56826e789b3d4e37983f6d23666e3f1b1b9ee358"
integrity sha512-DrV+sPYLs0HhmX5/b7yRT6nLM9Nl6FtQe2KUG+46kiCOKQ+0XzNMO5hmeQtyq0mRf/qlC02rFu6OMsWpIqVsJg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "0.1.3"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-presence@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-0.1.1.tgz#2088dec6f4f8042f83dd2d6bf9e8ef09dadbbc15"
integrity sha512-LsL+NcWDpFUAYCmXeH02o4pgqcSLpwxP84UIjCtpIKrsPe2vLuhcp79KC/jZJeXz+of2lUpMAxpM+eCpxFZtlg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-primitive@0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.1.3.tgz#585c35ef2ec06bab0ea9e0fc5c916e556661b881"
integrity sha512-fcyADaaAx2jdqEDLsTs6aX50S3L1c9K9CC6XMpJpuXFJCU4n9PGTFDZRtY2gAoXXoRCPIBsklCopSmGb6SsDjQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-slot" "0.1.2"
"@radix-ui/react-slot@0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-0.1.2.tgz#e6f7ad9caa8ce81cc8d532c854c56f9b8b6307c8"
integrity sha512-ADkqfL+agEzEguU3yS26jfB50hRrwf7U4VTwAOZEmi/g+ITcBWe12yM46ueS/UCIMI9Py+gFUaAdxgxafFvY2Q==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-use-body-pointer-events@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.0.tgz#29b211464493f8ca5149ce34b96b95abbc97d741"
integrity sha512-svPyoHCcwOq/vpWNEvdH/yD91vN9p8BtiozNQbjVmJRxQ/vS12zqk70AxTGWe+2ZKHq2sggpEQNTv1JHyVFlnQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-use-callback-ref@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-0.1.0.tgz#934b6e123330f5b3a6b116460e6662cbc663493f"
integrity sha512-Va041McOFFl+aV+sejvl0BS2aeHx86ND9X/rVFmEFQKTXCp6xgUK0NGUAGcgBlIjnJSbMYPGEk1xKSSlVcN2Aw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-controllable-state@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-0.1.0.tgz#4fced164acfc69a4e34fb9d193afdab973a55de1"
integrity sha512-zv7CX/PgsRl46a52Tl45TwqwVJdmqnlQEQhaYMz/yBOD2sx2gCkCFSoF/z9mpnYWmS6DTLNTg5lIps3fV6EnXg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-escape-keydown@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-0.1.0.tgz#dc80cb3753e9d1bd992adbad9a149fb6ea941874"
integrity sha512-tDLZbTGFmvXaazUXXv8kYbiCcbAE8yKgng9s95d8fCO+Eundv0Jngbn/hKPhDDs4jj9ChwRX5cDDnlaN+ugYYQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-layout-effect@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz#ebf71bd6d2825de8f1fbb984abf2293823f0f223"
integrity sha512-+wdeS51Y+E1q1Wmd+1xSSbesZkpVj4jsg0BojCbopWvgq5iBvixw5vgemscdh58ep98BwUbsFYnrywFhV9yrVg==
dependencies:
"@babel/runtime" "^7.13.10"
"@rollup/plugin-babel@^5.3.0":
version "5.3.0"
resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
@ -5069,6 +5221,15 @@
"@typescript-eslint/types" "5.10.2"
eslint-visitor-keys "^3.0.0"
"@vegaprotocol/vegawallet-service-api-client@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.3.0.tgz#93ef25f94a2fb3280928053eceed945d00bb0a3a"
integrity sha512-JQHdKI/B6hvnqdXQpWi+zsU2tiJJzkNaZE/aOFFbrdl9gUooF94piDzc5PPBx3YO4NA7mWKwaf4VqRckzjVKXw==
dependencies:
es6-promise "^4.2.4"
url-parse "^1.4.3"
whatwg-fetch "^3.0.0"
"@webassemblyjs/ast@1.11.1":
version "1.11.1"
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz"