mirror of
https://github.com/alexgo-io/redstone-node.git
synced 2026-01-12 22:43:31 +08:00
feat: request proxying implemented (#74)
* request proxying implemented * fix: error handling implemented * fix: fixes after review
This commit is contained in:
6
.prettierrc.json
Normal file
6
.prettierrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": false
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
"@types/node": "^14.14.31",
|
||||
"@types/promise-timeout": "^1.3.0",
|
||||
"@types/prompts": "^2.0.14",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/yargs": "^16.0.0",
|
||||
"ar-gql": "^0.0.6",
|
||||
@@ -82,6 +83,7 @@
|
||||
"plotly": "^1.0.6",
|
||||
"prompts": "^2.4.2",
|
||||
"replace-in-file": "^6.3.2",
|
||||
"supertest": "^6.2.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
"ts-node": "10.0.0",
|
||||
"typed-binary-json": "^1.17.4",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Consola } from "consola";
|
||||
import exprress from "express";
|
||||
import express from "express";
|
||||
import { setExpressRoutes } from "./routes/index";
|
||||
import { NodeConfig } from "./types";
|
||||
|
||||
const logger = require("./utils/logger")("express") as Consola;
|
||||
|
||||
@@ -12,14 +14,11 @@ const PORT = 8080;
|
||||
// This class will be extended in future and will be used for
|
||||
// communication between nodes
|
||||
export class ExpressAppRunner {
|
||||
private app: exprress.Application;
|
||||
private app: express.Application;
|
||||
|
||||
constructor() {
|
||||
this.app = exprress();
|
||||
|
||||
this.app.get("/", (_req, res) => {
|
||||
res.send("Hello App Runner. My name is RedStone node and I am doing good ;)");
|
||||
});
|
||||
constructor(private nodeConfig: NodeConfig) {
|
||||
this.app = express();
|
||||
setExpressRoutes(this.app, this.nodeConfig);
|
||||
}
|
||||
|
||||
run() {
|
||||
|
||||
@@ -85,7 +85,7 @@ export default class NodeRunner {
|
||||
// Running a simple web server
|
||||
// It should be called as early as possible
|
||||
// Otherwise App Runner crashes ¯\_(ツ)_/¯
|
||||
new ExpressAppRunner().run();
|
||||
new ExpressAppRunner(nodeConfig).run();
|
||||
|
||||
const arweave = new ArweaveProxy(jwk);
|
||||
const providerAddress = await arweave.getAddress();
|
||||
|
||||
@@ -3,20 +3,26 @@ import mode from "../../../mode";
|
||||
import { Broadcaster } from "../Broadcaster";
|
||||
import { PriceDataSigned, SignedPricePackage } from "../../types";
|
||||
import { Consola } from "consola";
|
||||
import { stringifyError } from "../../utils/error-stringifier";
|
||||
|
||||
const logger = require("../../utils/logger")("HttpBroadcaster") as Consola;
|
||||
|
||||
// TODO: add timeout to broadcasting
|
||||
|
||||
export class HttpBroadcaster implements Broadcaster {
|
||||
constructor(private readonly broadcasterURLs: string[] = [mode.broadcasterUrl]) {}
|
||||
constructor(
|
||||
private readonly broadcasterURLs: string[] = [mode.broadcasterUrl]
|
||||
) {}
|
||||
|
||||
async broadcast(prices: PriceDataSigned[]): Promise<void> {
|
||||
const promises = this.broadcasterURLs.map(url => {
|
||||
const promises = this.broadcasterURLs.map((url) => {
|
||||
logger.info(`Posting prices to ${url}`);
|
||||
return axios.post(url + '/prices', prices)
|
||||
return axios
|
||||
.post(url + "/prices", prices)
|
||||
.then(() => logger.info(`Broadcasting to ${url} completed`))
|
||||
.catch(e => logger.error(`Broadcasting to ${url} failed: ${errToString(e)}`));
|
||||
.catch((e) =>
|
||||
logger.error(`Broadcasting to ${url} failed: ${stringifyError(e)}`)
|
||||
);
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
@@ -24,31 +30,27 @@ export class HttpBroadcaster implements Broadcaster {
|
||||
|
||||
async broadcastPricePackage(
|
||||
signedData: SignedPricePackage,
|
||||
providerAddress: string): Promise<void> {
|
||||
const body = {
|
||||
signerAddress: signedData.signerAddress,
|
||||
liteSignature: signedData.liteSignature,
|
||||
provider: providerAddress,
|
||||
...signedData.pricePackage, // unpacking prices and timestamp
|
||||
};
|
||||
providerAddress: string
|
||||
): Promise<void> {
|
||||
const body = {
|
||||
signerAddress: signedData.signerAddress,
|
||||
liteSignature: signedData.liteSignature,
|
||||
provider: providerAddress,
|
||||
...signedData.pricePackage, // unpacking prices and timestamp
|
||||
};
|
||||
|
||||
const promises = this.broadcasterURLs.map(url => {
|
||||
logger.info(`Posting pacakages to ${url}`);
|
||||
return axios.post(url + '/packages', body)
|
||||
.then(() => logger.info(`Broadcasting package to ${url} completed`))
|
||||
.catch(e => logger.error(`Broadcasting package to ${url} failed: ${errToString(e)}`));
|
||||
});
|
||||
const promises = this.broadcasterURLs.map((url) => {
|
||||
logger.info(`Posting pacakages to ${url}`);
|
||||
return axios
|
||||
.post(url + "/packages", body)
|
||||
.then(() => logger.info(`Broadcasting package to ${url} completed`))
|
||||
.catch((e) =>
|
||||
logger.error(
|
||||
`Broadcasting package to ${url} failed: ${stringifyError(e)}`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maybe move this function to a separate module
|
||||
function errToString(e: any): string {
|
||||
const responseData = e?.response?.data;
|
||||
if (responseData) {
|
||||
return JSON.stringify(responseData);
|
||||
} else {
|
||||
return e.toString();
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
"evmAddress": "0xdBcC2C6c892C8d3e3Fe4D325fEc810B7376A5Ed6",
|
||||
"ecdsaPublicKey": "0x04feeab427d82702f95c9d1727e47c823c5841ea8b0864607ecb637ff4377604b8213dfd06628d01e624c30dfb20cfa0296be7c1481ce994adb6424d1a391493cf"
|
||||
},
|
||||
"redstone-custom-urls-on-demand-1": {
|
||||
"address": "nrwt7Pwyrt6l6nSTAUwBZW29lx_17-QKmKIsHlXsTPI",
|
||||
"publicKey": "9C8bFkHr4-rM6UpmQ0bNudZpNW5WB6L1G3Y09jl5IdQ5-vbU_QWxjHhLotmxvU8y9NqQiYSnRAdNk4-uBvmKeRw2uzzCeeE-lUZRJZQnFEy-n-YINXm5zwyUjdKQc4_Oq2rm6F6CSHSsJ8KU0tF5Q04Kv5x3jy2avBORyNDPNorbJR4BKhghG5YoASgcNCQWR_1-T9alrlnUg2LFUQoWyznbZCpDJq3zWNLFzgwY_lsTrTZ5Flm5rsEe_vUl_nrtIESV5Xc6prmNzmxovXja_5b21J28r0edozk_Loa1AJ1FcatvWprjE1umzM5DPFt5Ww4zXguHa1HMJYktccaOxHv0Sqc-k_midyzKo4MSlFaNZIu05uuSAByhw7pbcexLe-yr3vMu0JzVRC3XyLFYgHrXp3j3pcwyax5WdbqJ5Y_vcY6AC7Gvw3TXW26EQuyC6YdUohkTSwa-lCOYQ-iGv0xB1DxFzGmHSNnTTP6_PVBi_Ft7ngZiq51juBb6_32QXW84UeMTm9T5d-62hN4pBtwHHq6NgOgKXXKM2qKEvqfbiXtzfphFYxz4hiULin_LJKldbtvsKIC1igX7DGOHpN3sFrT3HKtmIMoFlUglySsS3qo-s2m63bOS-VfhFVbI_nKBQWyQtPpczHAXVk03DJaXzdPy60EgELR-f_U29_M",
|
||||
"evmAddress": "0x63b3Cc527bFD6e060EB65d4e902667Ae19aEcEC2",
|
||||
"ecdsaPublicKey": "0x04fb66f19666c62e0225a53d4b75e6bb74e4512d43936affb64d78fc498618b49b961047d91ed3241b7a03c1044df3fff9fa77a8ec768c776b7a24a3d3880dbace"
|
||||
},
|
||||
"redstone-twaps-1": {
|
||||
"address": "aw9F_2R2ogYPnM66TDsW1qtiiRflRcgZQG6OLySOSZE",
|
||||
"publicKey": "sa1_cjxXdrIZgZaxY_L_UkZVf7eL1Q9WTTFrvc9FuRES1ZXwZxXO1QTspji_KboxN54Z8-qFAFXqlun-dgYgGEoLOv57RgWchinlmMHb0QVp2WREe71cSOdioaYfmBhW7eQS8YbvrnFB8cUumdZMOI8UJ6qsBrWOFSDuhPXTdUd2hNZ7mQ9PQR2MMPglZAJnwl36mAb-kaa0cK5s6raeR8_MT0aSSPHQrwyIuM4kOBMvLGEH9XE4LVuNB581Y3h0gXEE_Os7rlhl8BpowkjZM-ZIKK-hUuco9VkjfooQC5CgBHAwivm1--PlIpNa7vtk48cEOkvNoEd0ixN80wfQ2YNgpNFIOV6InnRYyrdPuHBRqE9pCQE48e8VWSxlpgjCiT-bveww42RhRsw_cjVBFHHwMljNQ4bNzjPZLvAaADvSF_ViV4EBpCiqrgj71eyTb0xpUMDSNP7Ae3HcUYPEVC9-n3GjJCCz2akc2zBRxS3zVl13bfApg9JCKzS349Dyaeq9bW9f46CN2U87zLDjUUTJFu3904UumkePlUQjAyMO5YIooChmdoujNla3e_EN6j-SwwjnM3LjDJwNCESETZUrkgZ0Arj6qbENCuEbQLDYqwT7pCJMhXIepolfbloLNnTPTbdCpzyh9pmXwAHokF3hxwSdAzBH11vDSp6ZYvc",
|
||||
|
||||
91
src/routes/custom-url-requests.route.ts
Normal file
91
src/routes/custom-url-requests.route.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import express from "express";
|
||||
import { Consola } from "consola";
|
||||
import { ethers } from "ethers";
|
||||
import axios from "axios";
|
||||
import jp from "jsonpath";
|
||||
|
||||
import { fromBase64 } from "../utils/base64";
|
||||
import EvmPriceSigner from "../signers/EvmPriceSigner";
|
||||
import { NodeConfig } from "../types";
|
||||
import { stringifyError } from "../utils/error-stringifier";
|
||||
|
||||
const EVM_CHAIN_ID = 1;
|
||||
const QUERY_PARAM_NAME = "custom-url-request-config-base64";
|
||||
const DEFAULT_TIMEOUT_MILLISECONDS = 10000;
|
||||
const EVM_SIGNER_VERSION = "0.4";
|
||||
const logger = require("../utils/logger")(
|
||||
"custom-url-requests-route"
|
||||
) as Consola;
|
||||
const evmSigner = new EvmPriceSigner(EVM_SIGNER_VERSION, EVM_CHAIN_ID);
|
||||
|
||||
export default function (app: express.Application, nodeConfig: NodeConfig) {
|
||||
app.get("/custom-url-requests", async (req, res) => {
|
||||
try {
|
||||
// Parsing request details
|
||||
const customRequestConfig = parseCustomUrlDetails(
|
||||
req.query[QUERY_PARAM_NAME] as string
|
||||
);
|
||||
const { url, jsonpath } = customRequestConfig;
|
||||
|
||||
// Sending the request
|
||||
logger.info(`Fetching data from custom url: ${url}`);
|
||||
const response = await axios.get(url, {
|
||||
timeout: DEFAULT_TIMEOUT_MILLISECONDS,
|
||||
});
|
||||
const fetchedData = response.data;
|
||||
logger.info(
|
||||
`Fetched data from url: ${url}: ${JSON.stringify(fetchedData)}`
|
||||
);
|
||||
|
||||
// Extracting value
|
||||
logger.info(`Extracting data using jsonpath: ${jsonpath}`);
|
||||
const extractedValueArr = jp.query(fetchedData, jsonpath);
|
||||
if (extractedValueArr.length !== 1) {
|
||||
throw new Error(`Extracted value must be a single number`);
|
||||
}
|
||||
const extractedValue = extractedValueArr[0];
|
||||
if (isNaN(extractedValue)) {
|
||||
throw new Error(`Extracted value is not a number: ${extractedValue}`);
|
||||
}
|
||||
|
||||
// Preparing a signed data package
|
||||
const timestamp = Date.now();
|
||||
const symbol = getSymbol({ url, jsonpath });
|
||||
const dataPackage = {
|
||||
timestamp,
|
||||
prices: [{ symbol, value: extractedValue }],
|
||||
};
|
||||
const signedPackage = evmSigner.signPricePackage(
|
||||
dataPackage,
|
||||
nodeConfig.credentials.ethereumPrivateKey
|
||||
);
|
||||
|
||||
// Sending response
|
||||
return res.json({
|
||||
signerAddress: signedPackage.signerAddress,
|
||||
liteSignature: signedPackage.liteSignature,
|
||||
prices: dataPackage.prices,
|
||||
customRequestConfig,
|
||||
timestamp,
|
||||
});
|
||||
} catch (e) {
|
||||
const errText = stringifyError(e);
|
||||
// TODO: improve error catching later:
|
||||
// differentiate types of errors and
|
||||
// use appropriate HTTP error codes
|
||||
res.status(400).json({
|
||||
err: errText,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseCustomUrlDetails(customRequestParamBase64: string) {
|
||||
const stringifiedConfig = fromBase64(customRequestParamBase64);
|
||||
return JSON.parse(stringifiedConfig);
|
||||
}
|
||||
|
||||
function getSymbol(customRequestConfig: { url: string; jsonpath: string }) {
|
||||
const { url, jsonpath } = customRequestConfig;
|
||||
return ethers.utils.id(`${jsonpath}---${url}`).slice(0, 18);
|
||||
}
|
||||
9
src/routes/home.route.ts
Normal file
9
src/routes/home.route.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import express from "express";
|
||||
|
||||
export default function (app: express.Application) {
|
||||
app.get("/", (_req, res) => {
|
||||
res.send(
|
||||
"Hello App Runner. My name is RedStone node and I am doing good ;)"
|
||||
);
|
||||
});
|
||||
}
|
||||
12
src/routes/index.ts
Normal file
12
src/routes/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import express from "express";
|
||||
import { NodeConfig } from "../types";
|
||||
import setCustomUrlRequestsRoute from "./custom-url-requests.route";
|
||||
import setHomeRoute from "./home.route";
|
||||
|
||||
export function setExpressRoutes(
|
||||
app: express.Application,
|
||||
nodeConfig: NodeConfig
|
||||
) {
|
||||
setCustomUrlRequestsRoute(app, nodeConfig);
|
||||
setHomeRoute(app);
|
||||
}
|
||||
9
src/utils/base64.ts
Normal file
9
src/utils/base64.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function fromBase64(base64Str: string): string {
|
||||
const buff = Buffer.from(base64Str, "base64");
|
||||
return buff.toString("utf-8");
|
||||
}
|
||||
|
||||
export function toBase64(str: string): string {
|
||||
const buff = Buffer.from(str, "utf-8");
|
||||
return buff.toString("base64");
|
||||
}
|
||||
9
src/utils/error-stringifier.ts
Normal file
9
src/utils/error-stringifier.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function stringifyError(e: any) {
|
||||
if (e.response) {
|
||||
return JSON.stringify(e.response.data) + " | " + e.stack;
|
||||
} else if (e.toJSON) {
|
||||
return JSON.stringify(e.toJSON());
|
||||
} else {
|
||||
return e.stack || String(e);
|
||||
}
|
||||
}
|
||||
19
test/routes/_helpers.ts
Normal file
19
test/routes/_helpers.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import express from "express";
|
||||
import { setExpressRoutes } from "../../src/routes/index";
|
||||
|
||||
const MOCK_NODE_CONFIG = {
|
||||
arweaveKeysFile: "",
|
||||
credentials: {
|
||||
ethereumPrivateKey: "0x1111111111111111111111111111111111111111111111111111111111111111"
|
||||
},
|
||||
addEvmSignature: true,
|
||||
manifestFile: "",
|
||||
minimumArBalance: 0.2,
|
||||
enableStreamrBroadcaster: false,
|
||||
};
|
||||
|
||||
export function getApp() {
|
||||
const app = express();
|
||||
setExpressRoutes(app, MOCK_NODE_CONFIG);
|
||||
return app;
|
||||
}
|
||||
71
test/routes/custom-url-requests.route.spec.ts
Normal file
71
test/routes/custom-url-requests.route.spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import request from "supertest";
|
||||
import axios from "axios";
|
||||
import { toBase64 } from "../../src/utils/base64";
|
||||
import { getApp } from "./_helpers";
|
||||
|
||||
const app = getApp();
|
||||
|
||||
// Mock axios response
|
||||
const exampleResponse = {
|
||||
A: {
|
||||
B: {
|
||||
C: 42,
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock("axios");
|
||||
const mockedAxios = axios as jest.Mocked<typeof axios>;
|
||||
mockedAxios.get.mockResolvedValue({ data: exampleResponse });
|
||||
|
||||
// Mock current timestamp
|
||||
Date.now = jest.fn(() => 1652662184000);
|
||||
|
||||
describe("Custom URL requests route", () => {
|
||||
test("Should send correct response ", async () => {
|
||||
const customUrlRequestConfigBase64 = toBase64(
|
||||
JSON.stringify({
|
||||
url: "https://example-custom-data-source.com/hehe",
|
||||
jsonpath: "$.A.B.C",
|
||||
})
|
||||
);
|
||||
const response = await request(app)
|
||||
.get("/custom-url-requests")
|
||||
.query({
|
||||
"custom-url-request-config-base64": customUrlRequestConfigBase64,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).toEqual({
|
||||
signerAddress: "0x19E7E376E7C213B7E7e7e46cc70A5dD086DAff2A",
|
||||
liteSignature:
|
||||
"0x6a4fab1950cf3bfd0ff0df5ef50b87d1a39cd451476081d7ef50179562aacd940ac81ddbde946dcf4c45f4cf41eebe876f0e82cd343c9a0799943c95bbad1ee51c",
|
||||
prices: [{ symbol: "0x8edd634f1bbd8320", value: 42 }],
|
||||
customRequestConfig: {
|
||||
url: "https://example-custom-data-source.com/hehe",
|
||||
jsonpath: "$.A.B.C",
|
||||
},
|
||||
timestamp: 1652662184000,
|
||||
});
|
||||
});
|
||||
|
||||
test("Should handle invalid values correctly ", async () => {
|
||||
mockedAxios.get.mockResolvedValue({ data: { bad: "value" } });
|
||||
const customUrlRequestConfigBase64 = toBase64(
|
||||
JSON.stringify({
|
||||
url: "https://example-custom-data-source.com/hehe",
|
||||
jsonpath: "$.A.B.C",
|
||||
})
|
||||
);
|
||||
await request(app)
|
||||
.get("/custom-url-requests")
|
||||
.query({
|
||||
"custom-url-request-config-base64": customUrlRequestConfigBase64,
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
test("Should handle invalid request params ", async () => {
|
||||
mockedAxios.get.mockResolvedValue({ data: { bad: "value" } });
|
||||
await request(app).get("/custom-url-requests").expect(400);
|
||||
});
|
||||
});
|
||||
14
test/routes/home.route.spec.ts
Normal file
14
test/routes/home.route.spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import request from "supertest";
|
||||
import { getApp } from "./_helpers";
|
||||
|
||||
const app = getApp();
|
||||
|
||||
describe("Custom url requests route", () => {
|
||||
test("Should send correct response ", async () => {
|
||||
const response = await request(app)
|
||||
.get("/")
|
||||
.expect(200);
|
||||
|
||||
expect(response.text).toBe("Hello App Runner. My name is RedStone node and I am doing good ;)");
|
||||
});
|
||||
});
|
||||
92
yarn.lock
92
yarn.lock
@@ -1327,6 +1327,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.5.tgz#650820e95de346e1f84e30667d168c8fd25aa6e3"
|
||||
integrity sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==
|
||||
|
||||
"@types/cookiejar@*":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8"
|
||||
integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==
|
||||
|
||||
"@types/cookies@*":
|
||||
version "0.7.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81"
|
||||
@@ -1583,6 +1588,21 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/superagent@*":
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.15.tgz#63297de457eba5e2bc502a7609426c4cceab434a"
|
||||
integrity sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ==
|
||||
dependencies:
|
||||
"@types/cookiejar" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/supertest@^2.0.12":
|
||||
version "2.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc"
|
||||
integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==
|
||||
dependencies:
|
||||
"@types/superagent" "*"
|
||||
|
||||
"@types/through@*":
|
||||
version "0.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
|
||||
@@ -2088,6 +2108,11 @@ arweave@1.10.23, arweave@^1.10.13, arweave@^1.10.15, arweave@^1.10.16, arweave@^
|
||||
bignumber.js "^9.0.1"
|
||||
util "^0.12.4"
|
||||
|
||||
asap@^2.0.0:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
asn1.js@^5.4.1:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||
@@ -2909,7 +2934,7 @@ cookie@0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookiejar@^2.1.2:
|
||||
cookiejar@^2.1.2, cookiejar@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
|
||||
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
|
||||
@@ -3102,7 +3127,7 @@ debug@2.6.9, debug@^2.3.3:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3:
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
@@ -3273,6 +3298,14 @@ detect-newline@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||
|
||||
dezalgo@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
|
||||
integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=
|
||||
dependencies:
|
||||
asap "^2.0.0"
|
||||
wrappy "1"
|
||||
|
||||
diff-sequences@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
|
||||
@@ -4003,7 +4036,7 @@ fast-levenshtein@~2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
fast-safe-stringify@^2.0.7:
|
||||
fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
|
||||
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
|
||||
@@ -4158,6 +4191,16 @@ formidable@^1.1.1, formidable@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168"
|
||||
integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==
|
||||
|
||||
formidable@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff"
|
||||
integrity sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==
|
||||
dependencies:
|
||||
dezalgo "1.0.3"
|
||||
hexoid "1.0.0"
|
||||
once "1.4.0"
|
||||
qs "6.9.3"
|
||||
|
||||
forwarded@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
@@ -4476,6 +4519,11 @@ heap@^0.2.6:
|
||||
resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
|
||||
integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
|
||||
|
||||
hexoid@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
||||
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
||||
|
||||
hi-base32@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/hi-base32/-/hi-base32-0.5.1.tgz#1279f2ddae2673219ea5870c2121d2a33132857e"
|
||||
@@ -5995,7 +6043,7 @@ mime@1.6.0:
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mime@^2.4.6:
|
||||
mime@^2.4.6, mime@^2.5.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
|
||||
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
|
||||
@@ -6384,7 +6432,7 @@ on-finished@2.4.1, on-finished@^2.3.0:
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
@@ -6825,13 +6873,18 @@ pvutils@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3"
|
||||
integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==
|
||||
|
||||
qs@6.10.3, qs@^6.10.1, qs@^6.4.0, qs@^6.5.2, qs@^6.9.4:
|
||||
qs@6.10.3, qs@^6.10.1, qs@^6.10.3, qs@^6.4.0, qs@^6.5.2, qs@^6.9.4:
|
||||
version "6.10.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
|
||||
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@6.9.3:
|
||||
version "6.9.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
|
||||
integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
|
||||
@@ -7244,7 +7297,7 @@ seek-bzip@^1.0.5:
|
||||
dependencies:
|
||||
commander "^2.8.1"
|
||||
|
||||
semver@7.x, semver@^7.3.2, semver@^7.3.5:
|
||||
semver@7.x, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
|
||||
@@ -7763,11 +7816,36 @@ superagent@^6.1.0:
|
||||
readable-stream "^3.6.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
superagent@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.3.tgz#783ff8330e7c2dad6ad8f0095edc772999273b6b"
|
||||
integrity sha512-WA6et4nAvgBCS73lJvv1D0ssI5uk5Gh+TGN/kNe+B608EtcVs/yzfl+OLXTzDs7tOBDIpvgh/WUs1K2OK1zTeQ==
|
||||
dependencies:
|
||||
component-emitter "^1.3.0"
|
||||
cookiejar "^2.1.3"
|
||||
debug "^4.3.4"
|
||||
fast-safe-stringify "^2.1.1"
|
||||
form-data "^4.0.0"
|
||||
formidable "^2.0.1"
|
||||
methods "^1.1.2"
|
||||
mime "^2.5.0"
|
||||
qs "^6.10.3"
|
||||
readable-stream "^3.6.0"
|
||||
semver "^7.3.7"
|
||||
|
||||
superstruct@^0.14.2:
|
||||
version "0.14.2"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b"
|
||||
integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==
|
||||
|
||||
supertest@^6.2.3:
|
||||
version "6.2.3"
|
||||
resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.2.3.tgz#291b220126e5faa654d12abe1ada3658757c8c67"
|
||||
integrity sha512-3GSdMYTMItzsSYjnIcljxMVZKPW1J9kYHZY+7yLfD0wpPwww97GeImZC1oOk0S5+wYl2niJwuFusBJqwLqYM3g==
|
||||
dependencies:
|
||||
methods "^1.1.2"
|
||||
superagent "^7.1.3"
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
|
||||
Reference in New Issue
Block a user