Implement the actual proxy
This commit is contained in:
parent
2086648c87
commit
8aa5675ba2
@ -17,6 +17,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/adm-zip": "^0.4.32",
|
"@types/adm-zip": "^0.4.32",
|
||||||
"@types/fs-extra": "^8.0.1",
|
"@types/fs-extra": "^8.0.1",
|
||||||
|
"@types/http-proxy": "^1.17.4",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
"@types/node": "^12.12.7",
|
"@types/node": "^12.12.7",
|
||||||
"@types/parcel-bundler": "^1.12.1",
|
"@types/parcel-bundler": "^1.12.1",
|
||||||
@ -52,13 +53,14 @@
|
|||||||
"@coder/logger": "1.1.11",
|
"@coder/logger": "1.1.11",
|
||||||
"adm-zip": "^0.4.14",
|
"adm-zip": "^0.4.14",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
|
"http-proxy": "^1.18.0",
|
||||||
"httpolyglot": "^0.1.2",
|
"httpolyglot": "^0.1.2",
|
||||||
"node-pty": "^0.9.0",
|
"node-pty": "^0.9.0",
|
||||||
"pem": "^1.14.2",
|
"pem": "^1.14.2",
|
||||||
"safe-compare": "^1.1.4",
|
"safe-compare": "^1.1.4",
|
||||||
"semver": "^7.1.3",
|
"semver": "^7.1.3",
|
||||||
"tar": "^6.0.1",
|
|
||||||
"ssh2": "^0.8.7",
|
"ssh2": "^0.8.7",
|
||||||
|
"tar": "^6.0.1",
|
||||||
"tar-fs": "^2.0.0",
|
"tar-fs": "^2.0.0",
|
||||||
"ws": "^7.2.0"
|
"ws": "^7.2.0"
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,8 @@ export class ApiHttpProvider extends HttpProvider {
|
|||||||
|
|
||||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||||
this.ensureAuthenticated(request)
|
this.ensureAuthenticated(request)
|
||||||
if (route.requestPath !== "/index.html") {
|
// Only serve root pages.
|
||||||
|
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,8 @@ export class DashboardHttpProvider extends HttpProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||||
if (route.requestPath !== "/index.html") {
|
// Only serve root pages.
|
||||||
|
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ interface LoginPayload {
|
|||||||
*/
|
*/
|
||||||
export class LoginHttpProvider extends HttpProvider {
|
export class LoginHttpProvider extends HttpProvider {
|
||||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||||
if (this.options.auth !== AuthType.Password || route.requestPath !== "/index.html") {
|
// Only serve root pages and only if password authentication is enabled.
|
||||||
|
if (this.options.auth !== AuthType.Password || (route.requestPath && route.requestPath !== "/index.html")) {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
}
|
}
|
||||||
switch (route.base) {
|
switch (route.base) {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import * as http from "http"
|
import * as http from "http"
|
||||||
|
import proxy from "http-proxy"
|
||||||
|
import * as net from "net"
|
||||||
import { HttpCode, HttpError } from "../../common/http"
|
import { HttpCode, HttpError } from "../../common/http"
|
||||||
import { HttpProvider, HttpProviderOptions, HttpProxyProvider, HttpResponse, Route } from "../http"
|
import { HttpProvider, HttpProviderOptions, HttpProxyProvider, HttpResponse, Route } from "../http"
|
||||||
|
|
||||||
@ -10,6 +12,7 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
|||||||
* Proxy domains are stored here without the leading `*.`
|
* Proxy domains are stored here without the leading `*.`
|
||||||
*/
|
*/
|
||||||
public readonly proxyDomains: string[]
|
public readonly proxyDomains: string[]
|
||||||
|
private readonly proxy = proxy.createProxyServer({})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domains can be provided in the form `coder.com` or `*.coder.com`. Either
|
* Domains can be provided in the form `coder.com` or `*.coder.com`. Either
|
||||||
@ -20,15 +23,20 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
|||||||
this.proxyDomains = proxyDomains.map((d) => d.replace(/^\*\./, "")).filter((d, i, arr) => arr.indexOf(d) === i)
|
this.proxyDomains = proxyDomains.map((d) => d.replace(/^\*\./, "")).filter((d, i, arr) => arr.indexOf(d) === i)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
public async handleRequest(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
response: http.ServerResponse,
|
||||||
|
): Promise<HttpResponse> {
|
||||||
if (!this.authenticated(request)) {
|
if (!this.authenticated(request)) {
|
||||||
if (route.requestPath === "/index.html") {
|
// Only redirect from the root. Other requests get an unauthorized error.
|
||||||
return { redirect: "/login", query: { to: route.fullPath } }
|
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||||
|
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
|
||||||
}
|
}
|
||||||
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
|
return { redirect: "/login", query: { to: route.fullPath } }
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = this.proxy(route.base.replace(/^\//, ""))
|
const payload = this.doProxy(route.requestPath, request, response, route.base.replace(/^\//, ""))
|
||||||
if (payload) {
|
if (payload) {
|
||||||
return payload
|
return payload
|
||||||
}
|
}
|
||||||
@ -36,6 +44,16 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
|||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async handleWebSocket(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
socket: net.Socket,
|
||||||
|
head: Buffer,
|
||||||
|
): Promise<void> {
|
||||||
|
this.ensureAuthenticated(request)
|
||||||
|
this.doProxy(route.requestPath, request, socket, head, route.base.replace(/^\//, ""))
|
||||||
|
}
|
||||||
|
|
||||||
public getCookieDomain(host: string): string {
|
public getCookieDomain(host: string): string {
|
||||||
let current: string | undefined
|
let current: string | undefined
|
||||||
this.proxyDomains.forEach((domain) => {
|
this.proxyDomains.forEach((domain) => {
|
||||||
@ -46,7 +64,26 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
|||||||
return current || host
|
return current || host
|
||||||
}
|
}
|
||||||
|
|
||||||
public maybeProxy(request: http.IncomingMessage): HttpResponse | undefined {
|
public maybeProxyRequest(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
response: http.ServerResponse,
|
||||||
|
): HttpResponse | undefined {
|
||||||
|
const port = this.getPort(request)
|
||||||
|
return port ? this.doProxy(route.fullPath, request, response, port) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
public maybeProxyWebSocket(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
socket: net.Socket,
|
||||||
|
head: Buffer,
|
||||||
|
): HttpResponse | undefined {
|
||||||
|
const port = this.getPort(request)
|
||||||
|
return port ? this.doProxy(route.fullPath, request, socket, head, port) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPort(request: http.IncomingMessage): string | undefined {
|
||||||
// No proxy until we're authenticated. This will cause the login page to
|
// No proxy until we're authenticated. This will cause the login page to
|
||||||
// show as well as let our assets keep loading normally.
|
// show as well as let our assets keep loading normally.
|
||||||
if (!this.authenticated(request)) {
|
if (!this.authenticated(request)) {
|
||||||
@ -67,26 +104,58 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.proxy(port)
|
return port
|
||||||
}
|
}
|
||||||
|
|
||||||
private proxy(portStr: string): HttpResponse {
|
private doProxy(
|
||||||
if (!portStr) {
|
path: string,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
response: http.ServerResponse,
|
||||||
|
portStr: string,
|
||||||
|
): HttpResponse
|
||||||
|
private doProxy(
|
||||||
|
path: string,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
socket: net.Socket,
|
||||||
|
head: Buffer,
|
||||||
|
portStr: string,
|
||||||
|
): HttpResponse
|
||||||
|
private doProxy(
|
||||||
|
path: string,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
responseOrSocket: http.ServerResponse | net.Socket,
|
||||||
|
headOrPortStr: Buffer | string,
|
||||||
|
portStr?: string,
|
||||||
|
): HttpResponse {
|
||||||
|
const _portStr = typeof headOrPortStr === "string" ? headOrPortStr : portStr
|
||||||
|
if (!_portStr) {
|
||||||
return {
|
return {
|
||||||
code: HttpCode.BadRequest,
|
code: HttpCode.BadRequest,
|
||||||
content: "Port must be provided",
|
content: "Port must be provided",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const port = parseInt(portStr, 10)
|
|
||||||
|
const port = parseInt(_portStr, 10)
|
||||||
if (isNaN(port)) {
|
if (isNaN(port)) {
|
||||||
return {
|
return {
|
||||||
code: HttpCode.BadRequest,
|
code: HttpCode.BadRequest,
|
||||||
content: `"${portStr}" is not a valid number`,
|
content: `"${_portStr}" is not a valid number`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
code: HttpCode.Ok,
|
const options: proxy.ServerOptions = {
|
||||||
content: `will proxy this to ${port}`,
|
autoRewrite: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
ignorePath: true,
|
||||||
|
target: `http://127.0.0.1:${port}${path}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (responseOrSocket instanceof net.Socket) {
|
||||||
|
this.proxy.ws(request, responseOrSocket, headOrPortStr, options)
|
||||||
|
} else {
|
||||||
|
this.proxy.web(request, responseOrSocket, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { handled: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,8 @@ export class UpdateHttpProvider extends HttpProvider {
|
|||||||
this.ensureAuthenticated(request)
|
this.ensureAuthenticated(request)
|
||||||
this.ensureMethod(request)
|
this.ensureMethod(request)
|
||||||
|
|
||||||
if (route.requestPath !== "/index.html") {
|
// Only serve root pages.
|
||||||
|
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,8 @@ export class VscodeHttpProvider extends HttpProvider {
|
|||||||
|
|
||||||
switch (route.base) {
|
switch (route.base) {
|
||||||
case "/":
|
case "/":
|
||||||
if (route.requestPath !== "/index.html") {
|
// Only serve this at the root.
|
||||||
|
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
} else if (!this.authenticated(request)) {
|
} else if (!this.authenticated(request)) {
|
||||||
return { redirect: "/login", query: { to: this.options.base } }
|
return { redirect: "/login", query: { to: this.options.base } }
|
||||||
|
@ -77,6 +77,10 @@ export interface HttpResponse<T = string | Buffer | object> {
|
|||||||
* `undefined` to remove a query variable.
|
* `undefined` to remove a query variable.
|
||||||
*/
|
*/
|
||||||
query?: Query
|
query?: Query
|
||||||
|
/**
|
||||||
|
* Indicates the request was handled and nothing else needs to be done.
|
||||||
|
*/
|
||||||
|
handled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,10 +108,26 @@ export interface HttpServerOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Route {
|
export interface Route {
|
||||||
|
/**
|
||||||
|
* Base path part (in /test/path it would be "/test").
|
||||||
|
*/
|
||||||
base: string
|
base: string
|
||||||
|
/**
|
||||||
|
* Remaining part of the route (in /test/path it would be "/path"). It can be
|
||||||
|
* blank.
|
||||||
|
*/
|
||||||
requestPath: string
|
requestPath: string
|
||||||
|
/**
|
||||||
|
* Query variables included in the request.
|
||||||
|
*/
|
||||||
query: querystring.ParsedUrlQuery
|
query: querystring.ParsedUrlQuery
|
||||||
|
/**
|
||||||
|
* Normalized version of `originalPath`.
|
||||||
|
*/
|
||||||
fullPath: string
|
fullPath: string
|
||||||
|
/**
|
||||||
|
* Original path of the request without any modifications.
|
||||||
|
*/
|
||||||
originalPath: string
|
originalPath: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +172,11 @@ export abstract class HttpProvider {
|
|||||||
/**
|
/**
|
||||||
* Handle requests to the registered endpoint.
|
* Handle requests to the registered endpoint.
|
||||||
*/
|
*/
|
||||||
public abstract handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse>
|
public abstract handleRequest(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
response: http.ServerResponse,
|
||||||
|
): Promise<HttpResponse>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base relative to the provided route. For each slash we need to go
|
* Get the base relative to the provided route. For each slash we need to go
|
||||||
@ -403,7 +427,21 @@ export interface HttpProxyProvider {
|
|||||||
* For example if `coder.com` is specified `8080.coder.com` will be proxied
|
* For example if `coder.com` is specified `8080.coder.com` will be proxied
|
||||||
* but `8080.test.coder.com` and `test.8080.coder.com` will not.
|
* but `8080.test.coder.com` and `test.8080.coder.com` will not.
|
||||||
*/
|
*/
|
||||||
maybeProxy(request: http.IncomingMessage): HttpResponse | undefined
|
maybeProxyRequest(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
response: http.ServerResponse,
|
||||||
|
): HttpResponse | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same concept as `maybeProxyRequest` but for web sockets.
|
||||||
|
*/
|
||||||
|
maybeProxyWebSocket(
|
||||||
|
route: Route,
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
socket: net.Socket,
|
||||||
|
head: Buffer,
|
||||||
|
): HttpResponse | undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the domain that should be used for setting a cookie. This will allow
|
* Get the domain that should be used for setting a cookie. This will allow
|
||||||
@ -584,12 +622,11 @@ export class HttpServer {
|
|||||||
try {
|
try {
|
||||||
const payload =
|
const payload =
|
||||||
this.maybeRedirect(request, route) ||
|
this.maybeRedirect(request, route) ||
|
||||||
(this.proxy && this.proxy.maybeProxy(request)) ||
|
(this.proxy && this.proxy.maybeProxyRequest(route, request, response)) ||
|
||||||
(await route.provider.handleRequest(route, request))
|
(await route.provider.handleRequest(route, request, response))
|
||||||
if (!payload) {
|
if (!payload.handled) {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
write(payload)
|
||||||
}
|
}
|
||||||
write(payload)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let e = error
|
let e = error
|
||||||
if (error.code === "ENOENT" || error.code === "EISDIR") {
|
if (error.code === "ENOENT" || error.code === "EISDIR") {
|
||||||
@ -662,7 +699,9 @@ export class HttpServer {
|
|||||||
throw new HttpError("Not found", HttpCode.NotFound)
|
throw new HttpError("Not found", HttpCode.NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
await route.provider.handleWebSocket(route, request, await this.socketProvider.createProxy(socket), head)
|
if (!this.proxy || !this.proxy.maybeProxyWebSocket(route, request, socket, head)) {
|
||||||
|
await route.provider.handleWebSocket(route, request, await this.socketProvider.createProxy(socket), head)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
socket.destroy(error)
|
socket.destroy(error)
|
||||||
logger.warn(`discarding socket connection: ${error.message}`)
|
logger.warn(`discarding socket connection: ${error.message}`)
|
||||||
@ -684,7 +723,6 @@ export class HttpServer {
|
|||||||
// Happens if it's a plain `domain.com`.
|
// Happens if it's a plain `domain.com`.
|
||||||
base = "/"
|
base = "/"
|
||||||
}
|
}
|
||||||
requestPath = requestPath || "/index.html"
|
|
||||||
return { base, requestPath }
|
return { base, requestPath }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
yarn.lock
35
yarn.lock
@ -871,6 +871,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/http-proxy@^1.17.4":
|
||||||
|
version "1.17.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.4.tgz#e7c92e3dbe3e13aa799440ff42e6d3a17a9d045b"
|
||||||
|
integrity sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/json-schema@^7.0.3":
|
"@types/json-schema@^7.0.3":
|
||||||
version "7.0.4"
|
version "7.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
|
||||||
@ -2240,7 +2247,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@3.2.6:
|
debug@3.2.6, debug@^3.0.0:
|
||||||
version "3.2.6"
|
version "3.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||||
@ -2745,6 +2752,11 @@ etag@~1.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
|
eventemitter3@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
|
||||||
|
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
|
||||||
|
|
||||||
events@^3.0.0:
|
events@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
|
resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
|
||||||
@ -2980,6 +2992,13 @@ flatted@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
|
||||||
integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
|
integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
|
||||||
|
|
||||||
|
follow-redirects@^1.0.0:
|
||||||
|
version "1.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"
|
||||||
|
integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==
|
||||||
|
dependencies:
|
||||||
|
debug "^3.0.0"
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
@ -3403,6 +3422,15 @@ http-errors@~1.7.2:
|
|||||||
statuses ">= 1.5.0 < 2"
|
statuses ">= 1.5.0 < 2"
|
||||||
toidentifier "1.0.0"
|
toidentifier "1.0.0"
|
||||||
|
|
||||||
|
http-proxy@^1.18.0:
|
||||||
|
version "1.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
|
||||||
|
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
|
||||||
|
dependencies:
|
||||||
|
eventemitter3 "^4.0.0"
|
||||||
|
follow-redirects "^1.0.0"
|
||||||
|
requires-port "^1.0.0"
|
||||||
|
|
||||||
http-signature@~1.2.0:
|
http-signature@~1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||||
@ -5894,6 +5922,11 @@ require-main-filename@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||||
|
|
||||||
|
requires-port@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||||
|
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||||
|
|
||||||
resolve-from@^3.0.0:
|
resolve-from@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
|
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
|
||||||
|
Reference in New Issue
Block a user