This commit is contained in:
Morten Olsen
2022-03-23 22:00:27 +01:00
parent e30e3c8d2d
commit 90fdaeb406
26 changed files with 337 additions and 134 deletions

102
:w
View File

@@ -1,102 +0,0 @@
import React from 'react';
import styled from 'styled-components';
import { Profile } from '../../data/profile';
import { HeroBackground } from './background';
type Props = {
profile: Profile;
}
const Wrapper = styled.div`
position: relative;
min-height: 900px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30px;
`;
const Avatar = styled.div<{src: string}>`
background-image: url('${({ src }) => src}');
max-width: 400px;
width: 100%;
border-radius: 50%;
background-size: cover;
border: solid 10px #fff;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
transform: scaleX(-1);
`;
const AvatarSpacer = styled.div`
padding-bottom: 100%;
`;
const Name = styled.h1`
font-size: 4rem;
line-height: 4rem;
font-weight: 400;
color: #fff;
margin: 0;
text-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
text-align: center;
`;
const Tagline = styled.h2`
font-size: 2rem;
font-weight: 100;
color: #fff;
margin: 0;
text-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
text-align: center;
`;
const Social = styled.div`
margin-top: 40px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
`;
const SocialItem = styled.a`
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: #000;
`;
const SocialText = styled.div`
margin-left: 20px;
`;
const SocialLogo = styled.div<{ src: string }>`
background-image: url('${({ src }) => src}');
width: 50px;
height: 50px;
background-size: cover;
`
const Hero: React.FC<Props> = ({ profile }) => (
<Wrapper>
<Avatar src={profile.avatar}>
<AvatarSpacer />
</Avatar>
<Name>
{profile.name}
</Name>
<Tagline>{profile.tagline}</Tagline>
<Social>
{profile.social.map((social) => (
<SocialItem href={social.link} target="_blank">
<SocialLogo src={social.logo} />
<SocialText>{social.name}</SocialText>
</SocialItem>
))}
</Social>
<HeroBackground />
</Wrapper>
);
export { Hero };

View File

@@ -1,4 +1,5 @@
title: How to hire engineers, by an engineer
cover: cover.png
published: 2021-03-15
parts:
- main.md

View File

@@ -0,0 +1,10 @@
we will need a way for our devices to identify with one and another, and since we might not have access to any particular node in the system at the time of authentication, this needs to work without a trusted third party at the time of connection.
The best way I can think of here is to use a signing authority.
Our authentication will consist of two main concept; a passport and a passport authority.
Each device will create a passport, which contains some various information but most important is a public key coresponding to a private key stored on the device. A device then has to go through a "claim process" where a user assign it as their device. this happens by that uses passport authority uses a private key to sign the device's passport, and giving it the authorities public key.
Now our device has a signed passport, and the oublic key of the authority, so when two devices needs to connect they can now go through an authentication process to verify each others passport. If both devices verifies the other device's passport as valid the connection can be established.

View File

@@ -0,0 +1,6 @@
What does hyper-connectivity mean? Well, a common interpretation is to be available on multiple different channels. We al know that one guy whom you contact on text message only to get a reply on Signal, just to get a follow up using email.
Then what does it mean for devices to be hyper connected? In this case it means using all available forms of communication in order to get data from one place to another, which is what this article is about.
Today, when a developer want a system to send data from one place to another he needs to pick a transport protocol such as http. Then connect

View File

@@ -0,0 +1,25 @@
title: Creating a hyper connectivity framework
parts:
- title: definition
file: definition.md
- title: reason
- title: target
- title: design
notes: |
onion based stream
4 way streams
- title: connection
notes: |
peerjs, tcp, ble(?)
session id
- title: reconnection
- title: authentication
file: authentication.md
notes: |
passport service
- title: security
notes: |
diff-hellman
- title: transport-nodes
- title: proof-of-concept
- title: conclusion

View File

@@ -1,4 +1,5 @@
title: My home runs Redux
cover: cover.png
published: 2021-03-15
parts:
- main.md

View File

@@ -0,0 +1,3 @@
title: NPM safety
parts:
- main.md

View File

@@ -0,0 +1,12 @@
The NPM eco system has hade a long list of stories about major dependencies turning evil lately, either by direct involvment from the package maintainer or due to the maintainer being compromised. What ever the reason, it does bring in to focus the dangers of package managers!
The threat landscaoe being what it is it is tine that we as developers start ti apply a little extra operational security to mitigate some of the risk that comes from installing unaudited oackages, which are in essence just arbitrary executing code which can conceil a viper virus or a remote access token as easily as a random email attachment.
So what can we do to protect ourselfs? we can split the attacks into to groups, one which attacks the users of our application and one that attacks our selves and our CI/CD pipelines
Combating the former is hard and here the only thing short of manually auditing every change in every depebdebcy is to set up monitoring of dependencies with vulnerabilities and freeze dependencies versions to known good versions
The later attack type is the one this article is going to focus on.
A NPM oackage has the ability to run post install hooks, which essentially allow it to execute any code as the current user upon installing the dependency. This allows a malicius oackage to infect your system upon installation by also installing malware or running malicious commands. This can be mitigated by disable install hooks, but some packages have legitinate use cases for these install hooks.

View File

@@ -30,6 +30,7 @@
"@types/yaml": "^1.9.7",
"babel-plugin-styled-components": "^2.0.6",
"file-loader": "^6.2.0",
"framer-motion": "^6.2.8",
"fs-extra": "^10.0.1",
"marked": "^4.0.12",
"next-compose-plugins": "^2.2.1",

View File

@@ -0,0 +1,51 @@
import { motion, useAnimation } from 'framer-motion';
import React, { useEffect, useMemo, useRef } from 'react';
import { useOnScreen } from '../../hooks/animation';
const SlideIn: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
children,
...props
}) => {
const ref = useRef<HTMLDivElement>();
const controls = useAnimation();
const onScreen = useOnScreen(ref);
const slideTime = useMemo(
() => 1,
[],
);
useEffect(
() => {
if (!onScreen) {
return;
}
controls.start({
y: 0,
opacity: 1,
scale: 1,
transition: {
duration: slideTime,
ease: "easeOut"
}
})
},
[controls, onScreen],
);
return (
<motion.div
{...props as any}
ref={ref}
initial={{
y: 50,
opacity: 0,
scale: 1.3,
}}
animate={controls}
>
{children}
</motion.div>
)
}
export { SlideIn };

View File

@@ -0,0 +1,57 @@
import { useEffect, useState } from "react"
import { motion, useAnimation } from 'framer-motion';
const Transition: React.FC = ({ children }) => {
return <>{children}</>
// const [displayChildren, setDisplayChildren] = useState<React.ReactNode>(children);
// const [inTransition, setInTransition] = useState(false);
// const controls = useAnimation();
//
// useEffect(() => {
// if (children !== displayChildren) {
// setInTransition(true);
// }
// }, [children, setDisplayChildren, displayChildren]);
//
// useEffect(() => {
// if (!inTransition || displayChildren === children) {
// return;
// }
// controls.start({
// opacity: 1,
// y: 0,
// transition: {
// duration: 0.3,
// ease: "easeOut"
// }
// }).then(() => {
// setInTransition(false);
// setDisplayChildren(children);
// });
// }, [inTransition, children, displayChildren])
//
// return (
// <>
// {inTransition && (
// <motion.div
// animate={controls}
// initial={{
// opacity: 0,
// background: '#fff',
// position: 'fixed',
// zIndex: 10,
// top: 0,
// left: 0,
// right: 0,
// bottom: 0,
// }}
// >
// {children}
// </motion.div>
// )}
// <div>{displayChildren}</div>
// </>
// );
}
export { Transition };

View File

@@ -1,7 +1,8 @@
import React, { useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
import styled from "styled-components";
import { Experience } from "../../data/experiences"
import { SlideIn } from '../animations/slide-in';
type Props = {
experiences: Experience[];
@@ -44,7 +45,7 @@ const ExperienceWrapper = styled.div`
}
`;
const Inner = styled.div`
const Inner = styled(SlideIn)`
border: solid 1px #eee;
padding: 20px;
`;
@@ -73,25 +74,31 @@ const More = styled.div`
margin-top: 10px;
`;
const Hidden = styled.div`
overflow: hidden;
transition: all 0.3s ease-out;
`;
const ExperienceRow = ({ experience }) => {
const [visible, setVisible] = useState(false);
return (
<Outer>
<ExperienceWrapper key={experience.id}>
<ExperienceWrapper>
<Inner>
<CompanyName>{experience.company.name}</CompanyName>
<JobTitle>{experience.title}</JobTitle>
<Time>{experience.startDate} - {experience.endDate}</Time>
{visible ? (
<>
<ReactMarkdown>
{experience.content}
</ReactMarkdown>
<More onClick={() => setVisible(false)}>Show less</More>
</>
) : (
<More onClick={() => setVisible(true)}>Show more</More>
)}
<Hidden
style={{
maxHeight: visible ? 1000 : 0,
opacity: visible ? 1 : 0,
}}
>
<ReactMarkdown>
{experience.content}
</ReactMarkdown>
</Hidden>
<More onClick={() => setVisible(!visible)}>Show {visible ? 'less' : 'more'}</More>
</Inner>
</ExperienceWrapper>
</Outer>

View File

@@ -1,5 +1,6 @@
import React, { ReactNode } from 'react';
import styled from 'styled-components';
import { SlideIn } from '../animations/slide-in';
type Props = {
title: string;

View File

@@ -1,6 +1,6 @@
import React, { Suspense, useMemo, useRef, useState } from 'react';
import { Canvas, useFrame, useLoader } from '@react-three/fiber';
import { Euler, Vector3 } from 'three';
import { Clock, Euler, Vector3 } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader'
import styled from 'styled-components';
const { default: smoke } = require('./smoke.png');
@@ -14,6 +14,24 @@ const Wrapper = styled.div`
z-index: -1;
`;
const FrameLimiter = () => {
const [clock] = React.useState(new Clock());
const [fps] = React.useState(10);
useFrame((state: any) => {
state.ready = false;
const timeUntilNextFrame = (1000 / fps) - clock.getDelta();
setTimeout(() => {
state.ready = true;
state.invalidate();
}, Math.max(0, timeUntilNextFrame));
});
return <></>;
};
const Cloud = ({ texture }) => {
const ref = useRef<any>();
const width = window.innerWidth / 2;
@@ -21,7 +39,7 @@ const Cloud = ({ texture }) => {
useFrame(() => {
if (!ref.current) return;
ref.current.rotation.z -= 0.001;
ref.current.rotation.z -= 0.003;
});
return (
@@ -45,7 +63,7 @@ const Clouds = () => {
return (
<>
{items.map((_, i) => (
<Cloud texture={colorMap} />
<Cloud key={i} texture={colorMap} />
))}
</>
)
@@ -69,6 +87,7 @@ const Scene = () => {
rotation: [1.16, -0.12, 0.27],
}}
>
<FrameLimiter />
<scene>
<perspectiveCamera
args={[60,window.innerWidth / window.innerHeight,1,1000]}

View File

@@ -2,6 +2,7 @@ import React from 'react';
import styled from 'styled-components';
import { Profile } from '../../data/profile';
import { HeroBackground } from './background';
import { SlideIn } from '../animations/slide-in';
type Props = {
profile: Profile;
@@ -101,10 +102,12 @@ const Hero: React.FC<Props> = ({ profile }) => (
<Tagline>{profile.tagline}</Tagline>
<Social>
{profile.social.map((social) => (
<SocialItem href={social.link} target="_blank">
<SlideIn key={social.name}>
<SocialItem key={social.name} href={social.link} target="_blank">
<SocialLogo src={social.logo} />
<SocialText>{social.name}</SocialText>
</SocialItem>
</SlideIn>
))}
</Social>
<HeroBackground />

View File

@@ -3,6 +3,10 @@ import React from "react";
import styled from "styled-components";
import { Content } from "../content";
type Props = {
name: string;
};
const Menu = styled.aside`
font-size: 2rem;
padding: 1rem 0;
@@ -13,10 +17,10 @@ const Menu = styled.aside`
const MenuItem = styled(Link)`
`;
const Navigation: React.FC<{}> = () => (
const Navigation: React.FC<Props> = ({ name }) => (
<Menu>
<Content>
by <MenuItem href="/">Morten Olsen</MenuItem>
by <MenuItem href="/">{name}</MenuItem>
</Content>
</Menu>
);

View File

@@ -2,12 +2,13 @@ import Link from 'next/link';
import React from 'react';
import styled from 'styled-components';
import { Article } from '../../../data/articles';
import { SlideIn } from '../../animations/slide-in';
type Props = {
article: Article;
}
const Wrapper = styled.div`
const Wrapper = styled(SlideIn)`
cursor: pointer;
display: flex;
padding: 15px;
@@ -58,11 +59,13 @@ const ArticleTile: React.FC<Props> = ({ article }) => (
<Header>
{article.title}
</Header>
<Published>3 days ago</Published>
<Published>{article.published}</Published>
</Inner>
<ImageWrapper>
<Image src={article.cover} />
</ImageWrapper>
{article.cover && (
<ImageWrapper>
<Image src={article.cover} />
</ImageWrapper>
)}
</Wrapper>
</Link>
);

7
src/config.ts Normal file
View File

@@ -0,0 +1,7 @@
class Config {
public get dev() {
return process && process.env.NODE_ENV === 'development';
}
}
export const config = new Config();

View File

@@ -5,6 +5,7 @@ import { remark } from 'remark';
import remarkHtml from 'remark-html';
import behead from 'remark-behead';
import { visit } from 'unist-util-visit';
import { config } from '../config';
const imageModules = (require as any).context(
'../../articles',
@@ -18,7 +19,6 @@ const images = imageModules.keys().map((key) => ({
const replaceImages = ({ id }) => (tree: any) => {
visit(tree, 'image', (node) => {
console.log(id);
if (!node.url.startsWith('./')) return;
const correctedUrl = `.${path.resolve('/', id, node.url)}`;
const image = images.find((i: any) => i.key === correctedUrl);
@@ -113,7 +113,10 @@ export class Articles {
const articles = await Promise.all(articleLocations.map(
(location) => this.#loadArticle(path.join(rootLocation, location)),
));
return articles;
return articles
.sort((a, b) => new Date(b.published || 0).getTime() - new Date(a.published || 0).getTime())
.filter(a => a.published || config.dev)
}
public getImage = (article: string, name: string) => {

View File

@@ -18,7 +18,6 @@ const images = imageModules.keys().map((key) => ({
const replaceImages = ({ id }) => (tree: any) => {
visit(tree, 'image', (node) => {
console.log(id);
if (!node.url.startsWith('./')) return;
const correctedUrl = `.${path.resolve('/', id, node.url)}`;
const image = images.find((i: any) => i.key === correctedUrl);

View File

@@ -44,7 +44,6 @@ export class ProfileDB {
structure.avatar = image.url;
}
structure.social = structure.social.map((social) => {
console.log(social.logo, images)
const image = images.find((i: any) => i.key === `.${path.resolve('/', social.logo)}`)
return {
...social,

28
src/hooks/animation.ts Normal file
View File

@@ -0,0 +1,28 @@
import { MutableRefObject, useEffect, useState } from "react";
export const useOnScreen = (ref: MutableRefObject<any>, rootMargin = '0px') => {
const [isIntersecting, setIntersecting] = useState(false);
useEffect(() => {
const { current } = ref;
const observer = new IntersectionObserver(
([entry]) => {
setIntersecting(entry.isIntersecting);
},
{
rootMargin
}
);
if (current) {
observer.observe(current);
}
return () => {
if (current) {
observer.unobserve(current);
}
};
}, [ref]);
return isIntersecting;
}

View File

@@ -5,6 +5,7 @@ import Head from 'next/head';
import "@fontsource/merriweather";
import "@fontsource/pacifico";
import "@fontsource/fredoka";
import { Transition } from '../components/animations/transition';
const GlobalStyle = createGlobalStyle`
* {
@@ -42,7 +43,9 @@ export default function App({ Component, pageProps }: AppProps) {
<Head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<Component {...pageProps} />
<Transition>
<Component {...pageProps} />
</Transition>
</>
);
};

View File

@@ -4,15 +4,19 @@ import { Article, articleDB } from '../../data/articles';
import { Content } from '../../components/content';
import ReactMarkdown, { Components } from 'react-markdown';
import { Navigation } from '../../components/navigation';
import Head from 'next/head';
import { Profile, profileDB } from '../../data/profile';
type Props = {
article: Article;
profile: Profile;
};
const Image = styled.img`
`;
const ImageWrapper = styled.div`
const ImageWrapper = styled.span`
display: block;
text-align: center;
`;
@@ -67,10 +71,14 @@ const components: Components = {
const ArticleView: React.FC<Props> = ({
article,
profile,
}) => {
return (
<>
<Navigation />
<Head>
<title>{profile.name} - {article.title}</title>
</Head>
<Navigation name={profile.name} />
<Content>
<Wrapper>
<h1>{article.title}</h1>
@@ -96,9 +104,11 @@ export async function getStaticPaths() {
export async function getStaticProps({ params }: any) {
const { id } = params;
const article = await articleDB.get(id);
const profile = await profileDB.get();
return {
props: {
article,
profile,
}
}
}

View File

@@ -1,3 +1,4 @@
import Head from 'next/head';
import React from 'react';
import { Content } from '../components/content';
import { Experiences } from '../components/experiences';
@@ -17,6 +18,9 @@ type Props = {
const Home: React.FC<Props> = ({ articles, profile, experiences }) => (
<>
<Head>
<title>{profile.name} - {profile.tagline}</title>
</Head>
<Hero profile={profile} />
<Content>
<Featured title="Latest articles">

View File

@@ -155,7 +155,7 @@
resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-10.1.2.tgz#d2fb7b968141139e5c2419553e5295382c265e7d"
integrity sha512-bbZIpW6fdyf7FMaeDmw3cBbkTqsecxEkwlVKgVfqqXWBPLH6azxhPA2V9F7OhoZSVrsnMYw7QuyK6qutXPjEew==
"@emotion/is-prop-valid@^0.8.8":
"@emotion/is-prop-valid@^0.8.2", "@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
@@ -1025,6 +1025,26 @@ file-loader@^6.2.0:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
framer-motion@^6.2.8:
version "6.2.8"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.2.8.tgz#02abb529191af7e2df444185fe27e932215b715d"
integrity sha512-4PtBWFJ6NqR350zYVt9AsFDtISTqsdqna79FvSYPfYDXuuqFmiKtZdkTnYPslnsOMedTW0pEvaQ7eqjD+sA+HA==
dependencies:
framesync "6.0.1"
hey-listen "^1.0.8"
popmotion "11.0.3"
style-value-types "5.0.0"
tslib "^2.1.0"
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"
framesync@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20"
integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==
dependencies:
tslib "^2.1.0"
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -1156,6 +1176,11 @@ hast-util-whitespace@^2.0.0:
resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c"
integrity sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg==
hey-listen@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
@@ -1986,6 +2011,16 @@ picomatch@^2.3.0:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
popmotion@11.0.3:
version "11.0.3"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9"
integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==
dependencies:
framesync "6.0.1"
hey-listen "^1.0.8"
style-value-types "5.0.0"
tslib "^2.1.0"
postcss-value-parser@^4.0.2:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
@@ -2517,6 +2552,14 @@ style-to-object@^0.3.0:
dependencies:
inline-style-parser "0.1.1"
style-value-types@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad"
integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==
dependencies:
hey-listen "^1.0.8"
tslib "^2.1.0"
styled-components@^5.3.3:
version "5.3.3"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.3.tgz#312a3d9a549f4708f0fb0edc829eb34bde032743"
@@ -2644,6 +2687,11 @@ trough@^2.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876"
integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==
tslib@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"