tx-manager flow control & smart rbf retry

master
cyhhao 2 years ago
parent 5f956a76c9
commit e974f4693a

@ -9,6 +9,7 @@
"bin": "dist/git3/index.js",
"dependencies": {
"@ethersproject/experimental": "^5.7.0",
"axios": "^1.3.2",
"bip39": "^3.0.4",
"buffer-split": "^1.0.0",
"colors-cli": "^1.0.29",
@ -16,6 +17,7 @@
"debug": "^4.3.4",
"eth-ens-namehash": "^2.0.8",
"ethers": "^5.7.2",
"form-data": "^4.0.0",
"git-config-path": "^2.0.0",
"inquirer": "^9.1.4",
"js-sha3": "^0.8.0",
@ -29,7 +31,7 @@
"clean": "rm -rf ./dist ./bin",
"build:git3": "esbuild src/git3/index.ts --bundle --platform=node --outfile=dist/git3.cjs --format=cjs",
"build:git-remote": "esbuild src/git-remote-git3/index.ts --bundle --platform=node --outfile=dist/git-remote-git3.cjs --format=cjs",
"build": "yarn clean && yarn build:git3 & yarn build:git-remote & wait",
"build": "yarn clean && ( yarn build:git3 & yarn build:git-remote & wait)",
"pkg:git3": "pkg dist/git3.cjs -c git3.pkg.json",
"pkg:git-remote": "pkg dist/git-remote-git3.cjs -c git-remote-git3.pkg.json",
"pkg:all": "yarn pkg:git3 && yarn pkg:git-remote",
@ -48,4 +50,4 @@
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
}
}
}

File diff suppressed because one or more lines are too long

@ -2,42 +2,72 @@
const evmNetworks: Record<number, any> = {
1: {
"name": "ethereum",
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
name: "ethereum",
nativeCurrency: {
name: "Ether",
symbol: "ETH",
decimals: 18,
},
"rpc": ["https://rpc.flashbots.net", "https://singapore.rpc.blxrbdn.com", "https://rpc.ankr.com/eth"],
"explorers": [
rpc: [
"https://rpc.flashbots.net",
"https://singapore.rpc.blxrbdn.com",
"https://rpc.ankr.com/eth",
],
explorers: [
{
"name": "etherscan",
"url": "https://etherscan.io",
"standard": "EIP3091"
}
name: "etherscan",
url: "https://etherscan.io",
standard: "EIP3091",
},
],
"contracts": { "git3": "" }
txConst: {
blockTimeSec: 15,
},
contracts: { git3: "" },
},
3334: {
"name": "Web3Q Galileo",
"nativeCurrency": {
"name": "Web3Q",
"symbol": "W3Q",
"decimals": 18
name: "Web3Q Galileo",
nativeCurrency: {
name: "Web3Q",
symbol: "W3Q",
decimals: 18,
},
rpc: ["https://galileo.web3q.io:8545"],
explorers: [
{
name: "w3q-galileo",
url: "https://explorer.galileo.web3q.io",
standard: "EIP3091",
},
],
txConst: {
blockTimeSec: 6,
},
"rpc": [
"https://galileo.web3q.io:8545"
contracts: { git3: "0x59ef6b2dbfE86CcAaD84E2d8e78177f528521Da9" },
},
3141: {
name: "Filecoin - Hyperspace testnet",
nativeCurrency: {
name: "testnet filecoin",
symbol: "tFIL",
decimals: 18,
},
rpc: [
"https://api.hyperspace.node.glif.io/rpc/v1",
// "https://filecoin-hyperspace.chainstacklabs.com/rpc/v1",
],
"explorers": [
explorers: [
{
"name": "w3q-galileo",
"url": "https://explorer.galileo.web3q.io",
"standard": "EIP3091"
}
name: "Filfox - Hyperspace",
url: "https://hyperspace.filfox.info/en",
standard: "none",
},
],
"contracts": { "git3": "0x59ef6b2dbfE86CcAaD84E2d8e78177f528521Da9" }
}
txConst: {
blockTimeSec: 30,
},
contracts: { git3: "0xF56A1dd941667911896B9B872AC79E56cfc6a3dB" },
},
}
export default evmNetworks
export default evmNetworks

@ -0,0 +1,8 @@
export default {
gateways: [
"https://ipfs.io/ipfs/",
"https://cloudflare-ipfs.com/ipfs/",
"https://ipfs.fleek.co/ipfs/",
"https://gateway.ipfs.io/ipfs/",
],
}

@ -1,12 +1,16 @@
const ns: Record<string, any> = {
"eth": {
"chainId": 1,
"resolver": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
eth: {
chainId: 1,
resolver: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
},
w3q: {
chainId: 3334,
resolver: "0x076B3e04dd300De7db95Ba3F5db1eD31f3139aE0",
},
fvm: {
chainId: 3141,
resolver: "",
},
"w3q": {
"chainId": 3334,
"resolver": "0x076B3e04dd300De7db95Ba3F5db1eD31f3139aE0"
}
}
export default ns;
export default ns

@ -1,8 +1,8 @@
import { log } from './log.js'
import { superpathjoin as join } from 'superpathjoin'
import { ApiBaseParams } from './git-remote-helper.js'
import { Ref, Status, Storage } from '../storage/storage.js'
import { GitUtils } from './git-utils.js'
import { log } from "./log.js"
import { superpathjoin as join } from "superpathjoin"
import { ApiBaseParams } from "./git-remote-helper.js"
import { Ref, Status, Storage } from "../storage/storage.js"
import { GitUtils } from "./git-utils.js"
class Git {
gitdir: string
remoteName: string
@ -12,7 +12,6 @@ class Git {
pushed: Map<string, string> = new Map()
head: string | null
constructor(info: ApiBaseParams, storage: Storage) {
this.gitdir = info.gitdir
this.remoteName = info.remoteName
@ -30,40 +29,45 @@ class Git {
if (ref.ref == "HEAD") {
if (!forPush) outLines.push(`@${ref.sha} HEAD\n`)
this.head = ref.sha
}
else {
} else {
outLines.push(`${ref.sha} ${ref.ref}\n`)
this.refs.set(ref.ref, ref.sha)
}
}
return outLines.join("") + "\n"
}
async doFetch(refs: { ref: string, oid: string }[]) {
async doFetch(refs: { ref: string; oid: string }[]) {
for (let ref of refs) {
await this.fetch(ref.oid)
}
return "\n\n"
}
async doPush(refs: {
src: string
dst: string
force: boolean
}[]): Promise<string> {
async doPush(
refs: {
src: string
dst: string
force: boolean
}[]
): Promise<string> {
let outLines: string[] = []
// let remoteHead = null
let hasError = false
for (let ref of refs) {
if (!await this.storage.hasPermission(ref.dst)) {
return `error ${ref.dst} refusing to push to remote ${this.remoteUrl} (permission denied)` + "\n\n"
if (!(await this.storage.hasPermission(ref.dst))) {
return (
`error ${ref.dst} refusing to push to remote ${this.remoteUrl} (permission denied)` +
"\n\n"
)
}
if (ref.src == "") {
if (this.refs.get("HEAD") == ref.dst) {
return `error ${ref.dst} refusing to delete the current branch: ${ref.dst}` + "\n\n"
return (
`error ${ref.dst} refusing to delete the current branch: ${ref.dst}` +
"\n\n"
)
}
log("deleting ref", ref.dst)
this.storage.removeRef(ref.dst)
@ -84,7 +88,6 @@ class Git {
}
}
return outLines.join("") + "\n\n"
}
async fetch(oid: string) {
@ -98,12 +101,10 @@ class Git {
for (let sha of GitUtils.referencedObjects(oid)) {
fetching.push(this.fetch(sha))
}
}
else {
} else {
log("already downloaded", oid)
}
}
else {
} else {
let error = await this.download(oid)
if (!error) {
for (let sha of GitUtils.referencedObjects(oid)) {
@ -112,7 +113,6 @@ class Git {
} else {
fetching.push(this.fetch(oid))
}
}
await Promise.all(fetching)
}
@ -125,8 +125,7 @@ class Git {
if (computedSha != sha) {
return new Error(`sha mismatch ${computedSha} != ${sha}`)
}
}
else {
} else {
return new Error(`download failed ${sha}`)
}
return null
@ -162,7 +161,11 @@ class Git {
}
}
async wirteRef(newSha: string, dst: string, force: boolean): Promise<string | null> {
async wirteRef(
newSha: string,
dst: string,
force: boolean
): Promise<string | null> {
let sha = this.refs.get(dst)
if (sha) {
if (!GitUtils.objectExists(sha)) {
@ -175,26 +178,25 @@ class Git {
}
let status
if (dst == "HEAD") {
status = await this.storage.setRef(`HEAD:${newSha}`, "0000000000000000000000000000000000001ead")
status = await this.storage.setRef(
`HEAD:${newSha}`,
"0000000000000000000000000000000000001ead"
)
} else {
status = await this.storage.setRef(dst, newSha)
}
if (status == Status.SUCCEED) {
return null
} else {
return "set ref error"
}
else {
return 'set ref error'
}
}
async putObject(sha: string): Promise<string> {
let data = GitUtils.encodeObject(sha)
let path = this.objectPath(sha)
log("writing...", path)
let status = await this.storage.upload(path, data)
log("status", status)
return status
}
@ -214,9 +216,6 @@ class Git {
}
return refs
}
}
export default Git
export default Git

@ -1,12 +1,12 @@
import GitRemoteHelper from "./git-remote-helper.js"
import { ApiBaseParams } from "./git-remote-helper.js"
import Git from "./git.js"
import { ETHStorage } from "../storage/ETHStorage.js"
import GitRemoteHelper from './git-remote-helper.js'
import { ApiBaseParams } from './git-remote-helper.js'
import Git from './git.js'
import { ETHStorage } from '../storage/ETHStorage.js'
import nameServices from '../config/name-services.js'
import nameServices from "../config/name-services.js"
import { SLIStorage } from "../storage/SLIStorage.js"
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
let git: Git;
let git: Git
GitRemoteHelper({
env: process.env,
stdin: process.stdin,
@ -16,7 +16,7 @@ GitRemoteHelper({
const url = new URL(p.remoteUrl)
let repoName
let git3Address
let chainId = url.port ? parseInt(url.port) : 3334
let chainId = url.port ? parseInt(url.port) : null
if (url.hostname.indexOf(".") < 0) {
if (url.hostname.startsWith("0x")) {
git3Address = url.hostname
@ -24,10 +24,11 @@ GitRemoteHelper({
} else {
// use Default git3Address
git3Address = null
repoName = url.hostname.startsWith("/") ? url.hostname.slice(1) : url.hostname
repoName = url.hostname.startsWith("/")
? url.hostname.slice(1)
: url.hostname
}
}
else {
} else {
let nsSuffix = url.hostname.split(".")[1] // Todo: support sub domain
let ns = nameServices[nsSuffix]
if (!ns) throw new Error("invalid name service")
@ -37,8 +38,21 @@ GitRemoteHelper({
chainId = chainId || ns.chainId
repoName = url.pathname.slice(1)
}
chainId = chainId || 3334
let sender = url.username || null
let storage = new ETHStorage(repoName, chainId, { git3Address, sender })
let storage
if (chainId == 3334) {
storage = new ETHStorage(repoName, chainId, {
git3Address,
sender,
})
} else {
storage = new SLIStorage(repoName, chainId, {
git3Address,
sender,
})
}
git = new Git(p, storage)
return
},
@ -57,7 +71,7 @@ GitRemoteHelper({
gitdir: string
remoteName: string
remoteUrl: string
refs: { ref: string, oid: string }[]
refs: { ref: string; oid: string }[]
}) => {
// log("fetch", p)
let out = await git.doFetch(p.refs)
@ -81,6 +95,6 @@ GitRemoteHelper({
},
},
}).catch((error: any) => {
console.error("wtf");
console.error(error);
});
console.error("wtf")
console.error(error)
})

@ -0,0 +1,175 @@
import { Ref, Status, Storage } from "./storage.js"
import { getWallet } from "../wallet/index.js"
import { TxManager } from "../wallet/tx-manager.js"
import { ethers, Signer } from "ethers"
import { NonceManager } from "@ethersproject/experimental"
import abis from "../config/abis.js"
import network from "../config/evm-network.js"
import ipfsConf from "../config/ipfs.js"
import axios from "axios"
export class SLIStorage implements Storage {
repoName: string
wallet: Signer
contract: ethers.Contract
provider: ethers.providers.JsonRpcProvider
auth: string
txManager: TxManager
constructor(
repoName: string,
chainId: number,
options: { git3Address: string | null; sender: string | null }
) {
let net = network[chainId]
if (!net) throw new Error("chainId not supported")
this.repoName = repoName
this.wallet = getWallet(options.sender)
let rpc = net.rpc[Math.floor(Math.random() * net.rpc.length)] //random get rpc
this.provider = new ethers.providers.JsonRpcProvider(rpc)
this.wallet = this.wallet.connect(this.provider)
// this.wallet = new NonceManager(this.wallet)
let repoAddress = options.git3Address || net.contracts.git3
this.contract = new ethers.Contract(
repoAddress,
abis.SLIStorage,
this.wallet
)
this.auth =
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweGFEQTdCOWFlQTdGNTc2ZDI5NzM0ZWUxY0Q2ODVFMzc2OWNCM2QwRDEiLCJpc3MiOiJuZnQtc3RvcmFnZSIsImlhdCI6MTY3NTQ5NDYwMDkzMiwibmFtZSI6ImZ2bS1oYWNrc29uIn0.YBqfsj_LTZSJPKc0OH586avnQNqove_Htzl5rrToXTk"
this.txManager = new TxManager(this.contract, chainId, net.txConst)
}
async repoRoles(): Promise<string[]> {
let owner = await this.contract.repoNameToOwner(
Buffer.from(this.repoName)
)
if (owner === ethers.constants.AddressZero) return []
return [owner]
}
async hasPermission(ref: string): Promise<boolean> {
let member = await this.repoRoles()
return member.indexOf(await this.wallet.getAddress()) >= 0
}
async download(path: string): Promise<[Status, Buffer]> {
const res = await this.contract.download(
Buffer.from(this.repoName),
Buffer.from(path)
)
const buffer = Buffer.from(res[0].slice(2), "hex")
console.error("buffer", buffer, buffer.toString(), res[0])
const cid = buffer.toString("utf8")
for (let i = 0; i < ipfsConf.gateways.length; i++) {
let gateway =
ipfsConf.gateways[
Math.floor(Math.random() * ipfsConf.gateways.length)
] //random get rpc
try {
let response = await axios.get(gateway + cid)
if (response.status === 200) {
console.error(`=== download file ${path} succeed ===`)
return [Status.SUCCEED, Buffer.from(response.data)]
}
} catch (e) {
//pass
}
}
console.error(`=== download file ${cid} failed ===`)
// console.error(buffer.toString('utf-8'))
return [Status.FAILED, Buffer.from("")]
}
async upload(path: string, file: Buffer): Promise<Status> {
try {
console.error(`=== uploading file ${path} ===`)
const cid = await this.storeIPFS(file)
await this.txManager.SendCall("upload", [
Buffer.from(this.repoName),
Buffer.from(path),
Buffer.from(cid),
])
console.error(`=== upload ${path} ${cid} succeed ===`)
return Status.SUCCEED
} catch (error: any) {
this.txManager.CancelAll()
console.error(`upload failed: ${error}`)
return Status.FAILED
}
}
remove(path: string): Promise<Status> {
throw new Error("Method not implemented.")
}
async listRefs(): Promise<Ref[]> {
const res: string[][] = await this.contract.listRefs(
Buffer.from(this.repoName)
)
let refs = res.map((i) => ({
ref: Buffer.from(i[1].slice(2), "hex")
.toString("utf8")
.slice(this.repoName.length + 1),
sha: i[0].slice(2),
}))
return refs
}
async setRef(path: string, sha: string): Promise<Status> {
try {
console.error(`=== setting ref ${path} ===`)
await this.txManager.SendCall("setRef", [
Buffer.from(this.repoName),
Buffer.from(path),
"0x" + sha,
])
console.error(`ref set succeed ${path}`)
return Status.SUCCEED
} catch (error: any) {
console.error(`ref set failed ${error} : ${path}`)
return Status.FAILED
}
}
async removeRef(path: string): Promise<Status> {
await this.contract.delRef(
Buffer.from(this.repoName),
Buffer.from(path)
)
return Status.SUCCEED
}
async storeIPFS(data: Buffer): Promise<string> {
let response
for (let i = 0; i < 10; i++) {
// Todo: add timeout
try {
response = await axios.post(
"https://api.nft.storage/upload",
data,
{
headers: {
"Content-Type": "application/octet-stream",
Authorization: this.auth,
},
}
)
if (response.status == 200) {
return response.data.value.cid
}
} catch (e) {
//pass
}
}
throw new Error(`store ipfs failed: ${response?.status}`)
}
}

@ -1,4 +1,3 @@
export enum Status {
SUCCEED = "SUCCEED",
TIMEOUT = "TIMEOUT",
@ -8,7 +7,6 @@ export enum Status {
export type Ref = {
ref: string
sha: string
}
export interface Storage {
@ -21,5 +19,4 @@ export interface Storage {
listRefs(): Promise<Ref[]>
setRef(path: string, sha: string): Promise<Status>
removeRef(path: string): Promise<Status>
}

@ -0,0 +1,23 @@
import axios from "axios"
import Form from "form-data"
let form = new Form()
form.append("file", Buffer.from("hello world"), {
filename: "",
contentType: "image/*",
})
const response = await axios.post(
"https://api.nft.storage/upload",
Buffer.from("hello world"),
{
headers: {
"Content-Type": "application/octet-stream",
Authorization:
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweGFEQTdCOWFlQTdGNTc2ZDI5NzM0ZWUxY0Q2ODVFMzc2OWNCM2QwRDEiLCJpc3MiOiJuZnQtc3RvcmFnZSIsImlhdCI6MTY3NTQ5NDYwMDkzMiwibmFtZSI6ImZ2bS1oYWNrc29uIn0.YBqfsj_LTZSJPKc0OH586avnQNqove_Htzl5rrToXTk",
},
}
)
console.log(response.status)
console.log(response.headers)
console.log(response.data)

@ -0,0 +1,199 @@
import { ethers } from "ethers"
export class TxManager {
contract: ethers.Contract
chainId: number
price: ethers.providers.FeeData | null
cancel: boolean
blockTimeSec: number
gasLimitRatio: number
minNonce: number = -1
queueCurrNonce: number = -1
highestNonce: number = -1
rbfTimes: number
boardcastTimes: number
waitDistance: number
minRBFRatio: number
constructor(
contract: ethers.Contract,
chainId: number,
constOptions: {
blockTimeSec?: number
gasLimitRatio?: number
rbfTimes?: number
boardcastTimes?: number
waitDistance?: number
minRBFRatio?: number
}
) {
this.chainId = chainId
this.contract = contract
this.price = null
this.cancel = false
this.blockTimeSec = constOptions.blockTimeSec || 3
this.gasLimitRatio = constOptions.gasLimitRatio || 1.2
this.rbfTimes = constOptions.rbfTimes || 3
this.boardcastTimes = constOptions.boardcastTimes || 3
this.waitDistance = constOptions.waitDistance || 10
this.minRBFRatio = constOptions.minRBFRatio || 1.3
}
async FreshBaseGas(): Promise<ethers.providers.FeeData | null> {
this.price = await this.contract.provider.getFeeData()
return this.price
}
CancelAll() {
this.cancel = true
// TODO: cancel all tx sended
}
async SendCall(_method: string, _args: any[]): Promise<any> {
let unsignedTx = await this.contract.populateTransaction[_method](
..._args
)
unsignedTx.chainId = this.chainId
if (this.highestNonce < 0) {
this.highestNonce = await this.contract.signer.getTransactionCount()
}
const nonce = this.highestNonce
unsignedTx.nonce = nonce
this.highestNonce += 1
// estimateGas check
let gasLimit = await this.contract.provider.estimateGas(unsignedTx)
unsignedTx.gasLimit = gasLimit
.mul((this.gasLimitRatio * 100) | 0)
.div(100)
let retryRBF = this.rbfTimes
let lastPrice = null
while (retryRBF > 0 && !this.cancel) {
// set gas price
let price
try {
price = await this.FreshBaseGas()
} catch (e) {
price = this.price
} finally {
if (
!price ||
!price.maxFeePerGas ||
!price.maxPriorityFeePerGas
) {
throw new Error("get fee data failed")
}
}
if (lastPrice) {
let maxFeePerGasMin = lastPrice
.maxFeePerGas!.mul((this.minRBFRatio * 100) | 0)
.div(100)
if (price.maxFeePerGas.lt(maxFeePerGasMin)) {
price.maxFeePerGas = maxFeePerGasMin
}
let maxPriorityFeePerGasMin = lastPrice
.maxPriorityFeePerGas!.mul((this.minRBFRatio * 100) | 0)
.div(100)
if (price.maxPriorityFeePerGas.lt(maxPriorityFeePerGasMin)) {
price.maxPriorityFeePerGas = maxPriorityFeePerGasMin
}
}
lastPrice = price
if (price && price.maxFeePerGas && price.maxPriorityFeePerGas) {
unsignedTx.type = 2
unsignedTx.maxFeePerGas = price.maxFeePerGas
unsignedTx.maxPriorityFeePerGas = price.maxPriorityFeePerGas
} else {
throw new Error("get fee data failed")
}
// sign
let signedTx = await this.contract.signer.signTransaction(
unsignedTx
)
let retryBoardcast = this.boardcastTimes
let txRes = null
while (retryBoardcast > 0 && !this.cancel) {
if (
this.queueCurrNonce < 0 ||
this.queueCurrNonce + 1 == nonce
) {
// Arrive in line
retryBoardcast--
} else if (nonce - this.queueCurrNonce > this.waitDistance) {
// Too far away don't boardcast, waitTime = int(distance / groupSize) * blockTime + 1s
const waitTime =
(((nonce - this.queueCurrNonce) / this.waitDistance) |
0) *
this.blockTimeSec *
1000 +
1000
await new Promise((r) => setTimeout(r, waitTime))
continue
} else {
// Broadcast first anyway
}
try {
// send
txRes = await this.contract.provider.sendTransaction(
signedTx
)
await new Promise((r) =>
setTimeout(r, (this.blockTimeSec / 2) * 1000)
)
} catch (e: Error | any) {
if (e.code == ethers.errors.NONCE_EXPIRED) {
// ignore if tx already in mempool
} else {
console.error(
"[tx-manager] sendTransaction",
nonce,
e.code,
e.message
)
}
}
if (txRes) {
// wait
try {
let receipt =
await this.contract.provider.waitForTransaction(
txRes.hash,
1,
this.blockTimeSec * 1000 + 1000
)
if (receipt) {
this.queueCurrNonce =
txRes.nonce > this.queueCurrNonce
? txRes.nonce
: this.queueCurrNonce
return receipt
}
} catch (e: Error | any) {
if (e.code == ethers.errors.TIMEOUT) {
// ignore timeout
} else {
console.error(
"[tx-manager] waitForTransaction",
nonce,
txRes.hash,
e.code,
e.reason
)
}
}
} else {
await new Promise((r) => setTimeout(r, 1000))
}
}
retryRBF--
}
throw new Error("send tx failed")
}
}

@ -1,71 +1,69 @@
{
"compilerOptions": {
/* Added */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"preserveConstEnums": true, /* Do not erase const enum declarations in generated code. */
"resolveJsonModule": true, /* Include modules imported with '.json' extension. Requires TypeScript version 2.9 or later. */
/* Basic Options */
"target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": [
"es5",
"es6"
], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "dist", /* Redirect output structure to the directory. */
"rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "nodenext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "src", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [
"node_modules/@types",
"types"
], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"exclude": [
"node_modules",
"test",
"dist",
"bin",
"types",
]
}
"compilerOptions": {
/* Added */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"preserveConstEnums": true /* Do not erase const enum declarations in generated code. */,
"resolveJsonModule": true /* Include modules imported with '.json' extension. Requires TypeScript version 2.9 or later. */,
/* Basic Options */
"target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
"module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"lib": [
"es5",
"es6"
] /* Specify library files to be included in the compilation. */,
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"declaration": true /* Generates corresponding '.d.ts' file. */,
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true /* Generates corresponding '.map' file. */,
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "dist" /* Redirect output structure to the directory. */,
"rootDir": "src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
// "composite": true, /* Enable project compilation */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"strictNullChecks": true /* Enable strict null checks. */,
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */,
// "noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "nodenext" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
// "baseUrl": "src", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [
"node_modules/@types",
"types"
] /* List of folders to include type definitions from. */,
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"exclude": ["node_modules", "test", "dist", "bin", "types"],
"ts-node": {
// Tell ts-node CLI to install the --loader automatically
"esm": true
}
}

@ -727,11 +727,25 @@ array-union@^2.1.0:
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
at-least-node@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
axios@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.2.tgz#7ac517f0fa3ec46e0e636223fd973713a09c72b3"
integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -904,6 +918,13 @@ colors-cli@^1.0.29:
resolved "https://registry.yarnpkg.com/colors-cli/-/colors-cli-1.0.29.tgz#121346c8e3f57344c3df38ee4641b6e4824718bd"
integrity sha512-S3wbmkI5Hmw7V9zM+xwxqLQAm2Z21epMtfEzErzhA/ToGNSpMOMA1Y842O56tiH5La9ZGMasAGVUKFYfb9RG9g==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1"
@ -973,6 +994,11 @@ defaults@^1.0.3:
dependencies:
clone "^1.0.2"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@ -1159,6 +1185,20 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
from2@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
@ -1483,6 +1523,18 @@ micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@ -1706,6 +1758,11 @@ progress@^2.0.3:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"

Loading…
Cancel
Save