mirror of
https://github.com/dumbasPL/deluge-windscribe-ephemeral-port.git
synced 2024-11-10 01:02:17 +01:00
rewrite everything
This commit is contained in:
parent
7fb033b8ea
commit
80ad78ac87
16 changed files with 926 additions and 475 deletions
48
.github/workflows/ci.yml
vendored
Normal file
48
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
name: Docker build
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
jobs:
|
||||
docker-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm/v6,linux/arm64/v8
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 17.x, 18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- run: yarn install --pure-lockfile
|
||||
|
||||
- run: yarn build
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- run: yarn install --pure-lockfile
|
||||
|
||||
- run: yarn lint
|
16
.github/workflows/docker-build.yml
vendored
16
.github/workflows/docker-build.yml
vendored
|
@ -1,16 +0,0 @@
|
|||
name: Docker build
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build image
|
||||
uses: docker/build-push-action@v2
|
18
.github/workflows/docker-publish.yml
vendored
18
.github/workflows/docker-publish.yml
vendored
|
@ -11,15 +11,11 @@ jobs:
|
|||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
|
@ -28,15 +24,14 @@ jobs:
|
|||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to GitHub Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
- uses: docker/metadata-action@v4
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
dumbasPL/deluge-windscribe-ephemeral-port
|
||||
|
@ -45,8 +40,7 @@ jobs:
|
|||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
- uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,3 +9,4 @@ dist/
|
|||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
cache/
|
|
@ -14,6 +14,9 @@ FROM node:16-alpine
|
|||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV CACHE_DIR=/cache
|
||||
|
||||
RUN mkdir -p $CACHE_DIR
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
30
README.md
30
README.md
|
@ -12,32 +12,4 @@ It will only update the port that deluge listens on to the same port that's conf
|
|||
|
||||
# Running
|
||||
|
||||
## Using docker (and docker compose in this example)
|
||||
```yml
|
||||
version: '3'
|
||||
services:
|
||||
deluge-windscribe-ephemeral-port:
|
||||
image: dumbaspl/deluge-windscribe-ephemeral-port
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- WINDSCRIBE_USERNAME=<your windscribe username>
|
||||
- WINDSCRIBE_PASSWORD=<your windscribe password>
|
||||
- DELUGE_URL=<url of your Deluge Web UI>
|
||||
- DELUGE_PASSWORD=<password for the Deluge Web UI>
|
||||
```
|
||||
|
||||
## Using nodejs
|
||||
Tested on node 16 but should work on node 14 as well.
|
||||
This project uses [yarn](https://classic.yarnpkg.com/) to manage dependencies, make sure you have it installed first.
|
||||
|
||||
1. Install dependencies by running `yarn`
|
||||
2. Create a `.env` file with the necessary configuration
|
||||
```
|
||||
WINDSCRIBE_USERNAME=<your windscribe username>
|
||||
WINDSCRIBE_PASSWORD=<your windscribe password>
|
||||
DELUGE_URL=<url of your Deluge Web UI>
|
||||
DELUGE_PASSWORD=<password for the Deluge Web UI>
|
||||
```
|
||||
3. Start using `yarn start`
|
||||
|
||||
Tip: you can use tools like [pm2](https://www.npmjs.com/package/pm2) to manage nodejs applications
|
||||
TODO
|
|
@ -3,7 +3,7 @@
|
|||
"ignore": [".git", "node_modules/", "dist/"],
|
||||
"watch": ["src/"],
|
||||
"execMap": {
|
||||
"ts": "node -r ts-node/register"
|
||||
"ts": "node --loader ts-node/esm"
|
||||
},
|
||||
"env": {
|
||||
"NODE_ENV": "development"
|
||||
|
|
25
package.json
25
package.json
|
@ -1,34 +1,41 @@
|
|||
{
|
||||
"name": "project",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0-rc1",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"license": "MIT",
|
||||
"author": "nezu",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "ts-node src/index.ts",
|
||||
"start": "node --loader ts-node/esm src/index.ts",
|
||||
"dev": "nodemon --config nodemon.json src/index.ts",
|
||||
"dev:debug": "nodemon --config nodemon.json --inspect-brk src/index.ts",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.7",
|
||||
"@types/async-lock": "^1.1.5",
|
||||
"@types/node": "^18.7.16",
|
||||
"@types/node-cron": "^3.0.4",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/set-cookie-parser": "^2.4.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"eslint": "^8.22.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"nodemon": "^2.0.19",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.7.4"
|
||||
"typescript": "^4.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ctrl/deluge": "^4.1.0",
|
||||
"@ctrl/deluge": "^4.2.0",
|
||||
"async-lock": "^1.3.2",
|
||||
"axios": "^0.27.2",
|
||||
"dotenv": "^16.0.1",
|
||||
"dotenv": "^16.0.2",
|
||||
"keyv": "^4.5.0",
|
||||
"keyv-file": "^0.2.0",
|
||||
"node-cron": "^3.0.2",
|
||||
"qs": "^6.11.0",
|
||||
"set-cookie-parser": "^2.5.1"
|
||||
}
|
||||
|
|
120
src/DelugeClient.ts
Normal file
120
src/DelugeClient.ts
Normal file
|
@ -0,0 +1,120 @@
|
|||
import {Deluge} from '@ctrl/deluge';
|
||||
|
||||
export class DelugeClient {
|
||||
|
||||
private deluge: Deluge;
|
||||
private currentHost?: string;
|
||||
|
||||
constructor(
|
||||
url: string,
|
||||
password: string,
|
||||
private defaultHostId?: string,
|
||||
) {
|
||||
this.deluge = new Deluge({
|
||||
baseUrl: url,
|
||||
password: password,
|
||||
});
|
||||
}
|
||||
|
||||
async updateConnection(): Promise<{ hostId: string; version: string; }> {
|
||||
// session check
|
||||
if (!await this.deluge.checkSession()) {
|
||||
// login if not logged in already
|
||||
if (!await this.deluge.login()) {
|
||||
throw new Error('Failed to connect to deluge');
|
||||
}
|
||||
}
|
||||
|
||||
// connection check
|
||||
if (!this.currentHost || !await this.deluge.connected()) {
|
||||
const {result: hosts, error} = await this.deluge.getHosts();
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Deluge getHosts error: ${error}`);
|
||||
}
|
||||
|
||||
if (hosts.length == 0) {
|
||||
throw new Error('No deluge hosts available');
|
||||
}
|
||||
|
||||
let hostId = this.currentHost || this.defaultHostId;
|
||||
if (hostId) {
|
||||
// make sure the host actually exists
|
||||
if (!hosts.some(host => host[0] == hostId)) {
|
||||
throw new Error(`Deluge host with id ${hostId} does not exist`);
|
||||
}
|
||||
} else {
|
||||
if (hosts.length == 1) {
|
||||
// if we have a single host, just use it
|
||||
hostId = hosts[0][0];
|
||||
console.log(`Selecting the only available deluge host: ${hostId}`);
|
||||
} else {
|
||||
console.log(
|
||||
`Found ${hosts.length} deluge hosts(id: host:port - status): \n` +
|
||||
hosts
|
||||
.map(host => `\t${host[0]}: ${host[1]}:${host[2]} - ${host[3]}`)
|
||||
.join('\n')
|
||||
);
|
||||
throw new Error(`Found more than one deluge host, select one via DELUGE_HOST_ID env variable`);
|
||||
}
|
||||
}
|
||||
|
||||
// try to connect if not connected already
|
||||
await this.deluge.connect(hostId);
|
||||
this.currentHost = hostId;
|
||||
}
|
||||
|
||||
// check the status of the current host
|
||||
const {result: {
|
||||
[0]: hostId,
|
||||
[1]: status,
|
||||
[2]: version,
|
||||
}, error} = await this.deluge.getHostStatus(this.currentHost);
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Deluge getHostStatus error: ${error}`);
|
||||
}
|
||||
|
||||
// this should never fail in theory
|
||||
if (status != 'Connected') {
|
||||
throw new Error('Not connected to deluge');
|
||||
}
|
||||
|
||||
// report status
|
||||
return {
|
||||
hostId,
|
||||
version,
|
||||
};
|
||||
}
|
||||
|
||||
async getPort() {
|
||||
// make sure we are connected
|
||||
await this.updateConnection();
|
||||
|
||||
const {error, result: config} = await this.deluge.getConfig();
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Deluge getConfig error: ${error}`);
|
||||
}
|
||||
|
||||
return config.random_port ? 0 : config.listen_ports[0];
|
||||
}
|
||||
|
||||
async updatePort(port: number): Promise<void> {
|
||||
// make sure we are connected
|
||||
await this.updateConnection();
|
||||
|
||||
// update port
|
||||
const {error} = await this.deluge.setConfig({
|
||||
listen_ports: [port, port],
|
||||
random_port: false, // turn of random port as well
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Deluge setConfig error: ${error}`);
|
||||
}
|
||||
|
||||
console.log('Deluge port successfully updated');
|
||||
}
|
||||
|
||||
}
|
272
src/WindscribeClient.ts
Normal file
272
src/WindscribeClient.ts
Normal file
|
@ -0,0 +1,272 @@
|
|||
import AsyncLock from 'async-lock';
|
||||
import {AxiosResponse, default as axios} from 'axios';
|
||||
import {Store, default as Keyv} from 'keyv';
|
||||
import {Cookie, parse as parseCookie} from 'set-cookie-parser';
|
||||
import qs from 'qs';
|
||||
|
||||
|
||||
const lock = new AsyncLock();
|
||||
|
||||
const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36';
|
||||
|
||||
interface CsrfInfo {
|
||||
csrfTime: number;
|
||||
csrfToken: string;
|
||||
}
|
||||
|
||||
interface PortForwardingInfo {
|
||||
epfExpires: number;
|
||||
ports: number[];
|
||||
}
|
||||
|
||||
export interface WindscribePort {
|
||||
port: number,
|
||||
expires: Date,
|
||||
}
|
||||
|
||||
export class WindscribeClient {
|
||||
|
||||
private cache: Keyv<string>;
|
||||
|
||||
constructor(
|
||||
private username: string,
|
||||
private password: string,
|
||||
cache?: Store<any>,
|
||||
) {
|
||||
this.cache = new Keyv({
|
||||
store: cache,
|
||||
namespace: 'windscribe',
|
||||
});
|
||||
}
|
||||
|
||||
async updatePort(): Promise<WindscribePort> {
|
||||
// get csrf token and time to pass on to future requests
|
||||
// this will also verify if we are logged in and login if not
|
||||
const csrfToken = await this.getMyAccountCsrfToken();
|
||||
|
||||
// check for current status
|
||||
let portForwardingInfo = await this.getPortForwardingInfo();
|
||||
|
||||
// check for mismatched ports if any present
|
||||
if (portForwardingInfo.ports.length == 2 && portForwardingInfo.ports[0] != portForwardingInfo.ports[1]) {
|
||||
console.log('Detected mismatched ports, removing existing ports');
|
||||
await this.removeEphemeralPort(csrfToken);
|
||||
|
||||
// update data to match current state
|
||||
portForwardingInfo.ports = [];
|
||||
portForwardingInfo.epfExpires = 0;
|
||||
await this.cache.delete('port');
|
||||
}
|
||||
|
||||
// request new port if we don't have any
|
||||
if (portForwardingInfo.epfExpires == 0) {
|
||||
console.log('No windscribe port configured, requesting new matching ephemeral port');
|
||||
portForwardingInfo = await this.requestMatchingEphemeralPort(csrfToken);
|
||||
} else {
|
||||
console.log(`Using existing windscribe ephemeral port: ${portForwardingInfo.ports[0]}`);
|
||||
}
|
||||
|
||||
const ret = {
|
||||
port: portForwardingInfo.ports[0],
|
||||
expires: new Date((portForwardingInfo.epfExpires + 86400 * 7) * 1000),
|
||||
};
|
||||
|
||||
await this.cache.set('port', ret.port.toString(), ret.expires.getTime() - Date.now());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
async getPort(): Promise<WindscribePort | null> {
|
||||
const cachedPort = await this.cache.get('port', {raw: true});
|
||||
return cachedPort == undefined ? null : {
|
||||
port: parseInt(cachedPort.value),
|
||||
expires: new Date(cachedPort.expires),
|
||||
};
|
||||
}
|
||||
|
||||
private async getSession(forceLogin: boolean = false): Promise<string> {
|
||||
return lock.acquire('getSession', async () => {
|
||||
if (forceLogin) {
|
||||
// force clear the session
|
||||
await this.cache.delete('sessionCookie');
|
||||
} else {
|
||||
// try to get cached value
|
||||
const cachedCookie = await this.cache.get('sessionCookie');
|
||||
if (cachedCookie != undefined) {
|
||||
return cachedCookie;
|
||||
}
|
||||
}
|
||||
|
||||
// get a new session
|
||||
console.log(`Invalid/missing session cookie, logging into windscribe`);
|
||||
const sessionCookie = await this.login();
|
||||
await this.cache.set('sessionCookie', sessionCookie.value, sessionCookie.expires.getTime() - Date.now());
|
||||
console.log(`Successfully logged into windscribe, session expires in ${Math.floor((sessionCookie.expires.getTime() - Date.now()) / (100 * 60)) / 10} minutes`);
|
||||
|
||||
return sessionCookie.value;
|
||||
});
|
||||
}
|
||||
|
||||
private async login(): Promise<Cookie> {
|
||||
try {
|
||||
// get csrf token and time
|
||||
const {data: csrfData} = await axios.post<{csrf_token: string, csrf_time: number}>('https://res.windscribe.com/res/logintoken', null, {
|
||||
headers: {'User-Agent': userAgent},
|
||||
});
|
||||
|
||||
// log in
|
||||
const res = await axios.post('https://windscribe.com/login', qs.stringify({
|
||||
login: '1',
|
||||
upgrade: '0',
|
||||
csrf_time: csrfData.csrf_time,
|
||||
csrf_token: csrfData.csrf_token,
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
code: ''
|
||||
}), {
|
||||
headers: {'content-type': 'application/x-www-form-urlencoded', 'User-Agent': userAgent},
|
||||
maxRedirects: 0,
|
||||
validateStatus: status => status == 302,
|
||||
});
|
||||
|
||||
// extract the cookie
|
||||
return parseCookie(res.headers['set-cookie'], {map: true, decodeValues: true})['ws_session_auth_hash'];
|
||||
} catch (error) {
|
||||
// try to extract windscribe message
|
||||
if (error.response) {
|
||||
const response = error.response as AxiosResponse<string>;
|
||||
const errorMessage = /<div class="content_message error">.*>(.*)<\/div/.exec(response.data);
|
||||
if (response.status == 200 && errorMessage && errorMessage[1]) {
|
||||
throw new Error(`Failed to log into windscribe: ${errorMessage[1]}`);
|
||||
}
|
||||
}
|
||||
|
||||
// or throw a generic error if windscribe message not found
|
||||
throw new Error(`Failed to log into windscribe: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async getMyAccountCsrfToken(forceLogin: boolean = false): Promise<CsrfInfo> {
|
||||
try {
|
||||
const sessionCookie = await this.getSession(forceLogin);
|
||||
|
||||
// get page
|
||||
const res = await axios.get<string>('https://windscribe.com/myaccount', {
|
||||
headers: {
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
},
|
||||
maxRedirects: 0,
|
||||
validateStatus: status => [302, 200].includes(status),
|
||||
});
|
||||
|
||||
if (res.status == 302) {
|
||||
// force to login again as the current session is invalid
|
||||
return await this.getMyAccountCsrfToken(true);
|
||||
}
|
||||
|
||||
// extract csrf tokena and time from page content
|
||||
const csrfTime = /csrf_time = (\d+);/.exec(res.data)[1];
|
||||
const csrfToken = /csrf_token = '(\w+)';/.exec(res.data)[1];
|
||||
|
||||
return {
|
||||
csrfTime: +csrfTime,
|
||||
csrfToken: csrfToken,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get csrf token from my account page: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async getPortForwardingInfo(): Promise<PortForwardingInfo> {
|
||||
try {
|
||||
const sessionCookie = await this.getSession();
|
||||
|
||||
// load sub page
|
||||
const res = await axios.get<string>('https://windscribe.com/staticips/load', {
|
||||
headers: {
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
}
|
||||
});
|
||||
|
||||
// extract data from page
|
||||
const epfExpires = res.data.match(/epfExpires = (\d+);/)[1]; // this is always present. set to 0 if no port is active
|
||||
const ports = [...res.data.matchAll(/<span>(?<port>\d+)<\/span>/g)].map(x => +x[1]); // this will return an empty array when there are not pots forwarded
|
||||
|
||||
return {
|
||||
epfExpires: +epfExpires,
|
||||
ports,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get port forwarding info: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async removeEphemeralPort(csrfInfo: CsrfInfo): Promise<void> {
|
||||
try {
|
||||
const sessionCookie = await this.getSession();
|
||||
|
||||
// remove port
|
||||
const res = await axios.post<{success: number, epf: boolean, message?: string}>('https://windscribe.com/staticips/deleteEphPort', qs.stringify({
|
||||
ctime: csrfInfo.csrfTime,
|
||||
ctoken: csrfInfo.csrfToken
|
||||
}), {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
}
|
||||
});
|
||||
|
||||
// check for errors
|
||||
if (res.data.success == 0) {
|
||||
throw new Error(`success = 0; ${res.data.message ?? 'No message'}`);
|
||||
}
|
||||
|
||||
// make sure we actually removed it
|
||||
if (res.data.epf == false) {
|
||||
console.warn('Tried to remove a non-existent ephemeral port, ignoring');
|
||||
} else {
|
||||
console.log('Deleted ephemeral port');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to delete ephemeral port: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async requestMatchingEphemeralPort(csrfInfo: CsrfInfo): Promise<PortForwardingInfo> {
|
||||
try {
|
||||
const sessionCookie = await this.getSession();
|
||||
|
||||
// request new port
|
||||
const res = await axios.post<{success: number, message?: string, epf?: {ext: number, int: number, start_ts: number}}>('https://windscribe.com/staticips/postEphPort', qs.stringify({
|
||||
ctime: csrfInfo.csrfTime,
|
||||
ctoken: csrfInfo.csrfToken,
|
||||
port: '', // empty string for a matching port
|
||||
}), {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
}
|
||||
});
|
||||
|
||||
// check for errors
|
||||
if (res.data.success == 0) {
|
||||
throw new Error(`success = 0; ${res.data.message ?? 'No message'}`);
|
||||
}
|
||||
|
||||
// epf should be present by this point
|
||||
const epf = res.data.epf!;
|
||||
console.log(`Created new matching ephemeral port: ${epf.ext}`);
|
||||
return {
|
||||
epfExpires: epf.start_ts,
|
||||
ports: [epf.ext, epf.int],
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
112
src/config.ts
Normal file
112
src/config.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
|
||||
interface ConfigTemplate<T extends string | number> {
|
||||
envVariableName: string,
|
||||
type: T extends string ? typeof String : typeof Number,
|
||||
}
|
||||
|
||||
interface ConfigTemplateRequiredEntry<T extends string | number> extends ConfigTemplate<T> {
|
||||
required: true,
|
||||
}
|
||||
|
||||
interface ConfigTemplateOptionalEntry<T extends string | number> extends ConfigTemplate<T> {
|
||||
required: false,
|
||||
default?: string,
|
||||
}
|
||||
|
||||
const configTemplate = {
|
||||
delugeUrl: {
|
||||
envVariableName: 'DELUGE_URL',
|
||||
required: true,
|
||||
type: String,
|
||||
} as ConfigTemplateRequiredEntry<string>,
|
||||
delugePassword: {
|
||||
envVariableName: 'DELUGE_PASSWORD',
|
||||
required: true,
|
||||
type: String,
|
||||
} as ConfigTemplateRequiredEntry<string>,
|
||||
delugeHostId: {
|
||||
envVariableName: 'DELUGE_HOST_ID',
|
||||
required: false,
|
||||
type: String,
|
||||
} as ConfigTemplateOptionalEntry<string>,
|
||||
delugeRetryDelay: {
|
||||
envVariableName: 'DELUGE_RETRY_DELAY',
|
||||
required: false,
|
||||
default: `${5 * 60 * 1000}`, // 5 minutes
|
||||
type: Number,
|
||||
} as ConfigTemplateOptionalEntry<number>,
|
||||
windscribeUsername: {
|
||||
envVariableName: 'WINDSCRIBE_USERNAME',
|
||||
required: true,
|
||||
type: String,
|
||||
} as ConfigTemplateRequiredEntry<string>,
|
||||
windscribePassword: {
|
||||
envVariableName: 'WINDSCRIBE_PASSWORD',
|
||||
required: true,
|
||||
type: String,
|
||||
} as ConfigTemplateRequiredEntry<string>,
|
||||
windscribeRetryDelay: {
|
||||
envVariableName: 'WINDSCRIBE_RETRY_DELAY',
|
||||
required: false,
|
||||
default: `${60 * 60 * 1000}`, // one hour
|
||||
type: Number,
|
||||
} as ConfigTemplateOptionalEntry<number>,
|
||||
windscribeExtraDelay: {
|
||||
envVariableName: 'WINDSCRIBE_EXTRA_DELAY',
|
||||
required: false,
|
||||
default: `${60 * 1000}`, // one minute
|
||||
type: Number,
|
||||
} as ConfigTemplateOptionalEntry<number>,
|
||||
cronSchedule: {
|
||||
envVariableName: 'CRON_SCHEDULE',
|
||||
required: false,
|
||||
type: String,
|
||||
} as ConfigTemplateOptionalEntry<string>,
|
||||
cacheDir: {
|
||||
envVariableName: 'CACHE_DIR',
|
||||
required: false,
|
||||
default: './cache',
|
||||
type: String,
|
||||
} as ConfigTemplateOptionalEntry<string>,
|
||||
};
|
||||
|
||||
type entryType =
|
||||
ConfigTemplateRequiredEntry<string> |
|
||||
ConfigTemplateOptionalEntry<string> |
|
||||
ConfigTemplateRequiredEntry<number> |
|
||||
ConfigTemplateOptionalEntry<number>;
|
||||
|
||||
type configTemplateType = typeof configTemplate;
|
||||
|
||||
type Config =
|
||||
{[key in keyof configTemplateType as configTemplateType[key] extends ConfigTemplateRequiredEntry<string> ? key : never]: string} &
|
||||
{[key in keyof configTemplateType as configTemplateType[key] extends ConfigTemplateOptionalEntry<string> ? key : never]?: string} &
|
||||
{[key in keyof configTemplateType as configTemplateType[key] extends ConfigTemplateOptionalEntry<number> ? key : never]?: number} &
|
||||
{[key in keyof configTemplateType as configTemplateType[key] extends ConfigTemplateOptionalEntry<number> ? key : never]?: number};
|
||||
|
||||
export function getConfig(): Config {
|
||||
const entries = Object.entries(configTemplate).map(([name, entry]: [string, entryType]) => {
|
||||
let value = process.env[entry.envVariableName];
|
||||
|
||||
// this needs an explicit `== true` check because typescript
|
||||
if (entry.required == true) {
|
||||
if (!value || value.length == 0) {
|
||||
throw new Error(`Missing environment variable ${entry.envVariableName}`);
|
||||
}
|
||||
} else {
|
||||
value = value || entry.default || '';
|
||||
}
|
||||
|
||||
if (entry.type == Number) {
|
||||
const intValue = parseInt(value);
|
||||
if (isNaN(intValue)) {
|
||||
throw new Error(`Environment variable ${entry.envVariableName}`);
|
||||
}
|
||||
return [name, intValue];
|
||||
}
|
||||
|
||||
return [name, value ? value : null];
|
||||
});
|
||||
|
||||
return Object.fromEntries(entries);
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
import {Deluge} from '@ctrl/deluge';
|
||||
|
||||
// env variables
|
||||
|
||||
const url = process.env.DELUGE_URL;
|
||||
const password = process.env.DELUGE_PASSWORD;
|
||||
|
||||
if (!url || url.length == 0) {
|
||||
console.log('Missing environment variable DELUGE_URL');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!password || password.length == 0) {
|
||||
console.log('Missing environment variable DELUGE_PASSWORD');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// functions
|
||||
|
||||
export async function testDelugeConnection() {
|
||||
try {
|
||||
const client = new Deluge({
|
||||
baseUrl: url,
|
||||
password: password,
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to connect to deluge: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateDelugePort(port: number) {
|
||||
try {
|
||||
const client = new Deluge({
|
||||
baseUrl: url,
|
||||
password: password,
|
||||
});
|
||||
|
||||
const res = await client.setConfig({
|
||||
listen_ports: [port, port],
|
||||
random_port: false, // turn of random port as well
|
||||
});
|
||||
|
||||
if (res.error) {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
console.log('Deluge port successfully updated');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to update deluge port: ${error.message}`);
|
||||
}
|
||||
}
|
164
src/index.ts
164
src/index.ts
|
@ -1,54 +1,130 @@
|
|||
import 'dotenv/config';
|
||||
import {testDelugeConnection, updateDelugePort} from './deluge';
|
||||
import {getMyAccountCsrfToken, getPortForwardingInfo, login, removeEphemeralPort, requestMatchingEphemeralPort} from './windscribe';
|
||||
import path from 'path';
|
||||
import {KeyvFile} from 'keyv-file';
|
||||
import {getConfig} from './config.js';
|
||||
import {DelugeClient} from './DelugeClient.js';
|
||||
import {WindscribeClient, WindscribePort} from './WindscribeClient.js';
|
||||
import {schedule} from 'node-cron';
|
||||
|
||||
async function run() {
|
||||
// load config
|
||||
const config = getConfig();
|
||||
|
||||
// init cache (if configured)
|
||||
const cache = !config.cacheDir ? undefined : new KeyvFile({
|
||||
filename: path.join(config.cacheDir, 'cache.json'),
|
||||
});
|
||||
|
||||
// inti windscribe client
|
||||
const windscribe = new WindscribeClient(config.windscribeUsername, config.windscribePassword, cache);
|
||||
|
||||
// init deluge client
|
||||
const deluge = new DelugeClient(config.delugeUrl, config.delugePassword, config.delugeHostId);
|
||||
|
||||
// init schedule if configured
|
||||
const scheduledTask = !config.cronSchedule ? null :
|
||||
schedule(config.cronSchedule, () => run('schedule'), {scheduled: false});
|
||||
|
||||
async function update() {
|
||||
let nextRetry: Date = null;
|
||||
let nextRun: Date = null;
|
||||
|
||||
let portInfo: WindscribePort;
|
||||
try {
|
||||
// try to connect to deluge once before we start spamming windscribe with pointless requests
|
||||
await testDelugeConnection();
|
||||
// try to update ephemeral port
|
||||
portInfo = await windscribe.updatePort();
|
||||
|
||||
// get a new session each time
|
||||
const sessionCookie = await login();
|
||||
|
||||
// get csrf token and time to pass on to future requests
|
||||
const csrfToken = await getMyAccountCsrfToken(sessionCookie);
|
||||
|
||||
// check for current status
|
||||
let portForwardingInfo = await getPortForwardingInfo(sessionCookie);
|
||||
|
||||
// check for mismatched ports if any present
|
||||
if (portForwardingInfo.ports.length == 2 && portForwardingInfo.ports[0] != portForwardingInfo.ports[1]) {
|
||||
console.log('detected mismatched ports, removing existing ports');
|
||||
await removeEphemeralPort(sessionCookie, csrfToken);
|
||||
|
||||
// update data to match current state
|
||||
portForwardingInfo.ports = [];
|
||||
portForwardingInfo.epfExpires = 0;
|
||||
}
|
||||
|
||||
// request new port if we don't have any
|
||||
if (portForwardingInfo.epfExpires == 0) {
|
||||
console.log('no port configured, Requesting new matching ephemeral port');
|
||||
portForwardingInfo = await requestMatchingEphemeralPort(sessionCookie, csrfToken);
|
||||
} else {
|
||||
console.log(`Using existing ephemeral port: ${portForwardingInfo.ports[0]}`);
|
||||
}
|
||||
|
||||
// update deluge with new port
|
||||
console.log('Updating deluge');
|
||||
await updateDelugePort(portForwardingInfo.ports[0]);
|
||||
|
||||
// schedule next run in 7 days since the time of creation (+ 1 minute just to be sure)
|
||||
// (this code is copied form the windscribe website btw)
|
||||
const expiresAt = new Date((portForwardingInfo.epfExpires + 86400 * 7) * 1000 + 60000);
|
||||
const diff = expiresAt.getTime() - new Date().getTime(); // time difference in milliseconds
|
||||
setTimeout(run, diff);
|
||||
console.log(`Port expires in ${Math.floor(diff/1000)} seconds. Next run scheduled at ${expiresAt.toLocaleString()}`);
|
||||
const windscribeExtraDelay = config.windscribeExtraDelay || (60 * 1000);
|
||||
nextRun = new Date(portInfo.expires.getTime() + windscribeExtraDelay);
|
||||
} catch (error) {
|
||||
console.error('Windscribe update failed: ', error);
|
||||
|
||||
// if failed, retry after some delay
|
||||
const windscribeRetryDelay = config.windscribeRetryDelay || (60 * 60 * 1000);
|
||||
nextRetry = new Date(Date.now() + windscribeRetryDelay);
|
||||
|
||||
// get cached info if available
|
||||
portInfo = await windscribe.getPort();
|
||||
}
|
||||
|
||||
try {
|
||||
let currentPort = await deluge.getPort();
|
||||
if (portInfo) {
|
||||
if (currentPort == portInfo.port) {
|
||||
// no need to update
|
||||
console.log(`Current deluge port (${currentPort}) already matches windscribe port`);
|
||||
} else {
|
||||
// update port to a new one
|
||||
console.log(`Current deluge port (${currentPort}) does not match windscribe port (${portInfo.port})`);
|
||||
await deluge.updatePort(portInfo.port);
|
||||
|
||||
// double check
|
||||
currentPort = await deluge.getPort();
|
||||
if (currentPort != portInfo.port) {
|
||||
throw new Error(`Unable to set deluge port! Current deluge port: ${currentPort}`);
|
||||
}
|
||||
console.log('Deluge port updated');
|
||||
}
|
||||
} else {
|
||||
console.log(`Windscribe port is unknown, current deluge port is ${currentPort}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Deluge update failed', error);
|
||||
|
||||
// if failed, retry after some delay
|
||||
const delugeRetryDelay = config.delugeRetryDelay || (5 * 60 * 1000);
|
||||
nextRetry = new Date(Date.now() + delugeRetryDelay);
|
||||
}
|
||||
|
||||
return {
|
||||
nextRun,
|
||||
nextRetry,
|
||||
};
|
||||
}
|
||||
|
||||
let timeoutId: NodeJS.Timeout; // next run/retry timer
|
||||
async function run(trigger: string) {
|
||||
console.log(`starting update, trigger type: ${trigger}`);
|
||||
|
||||
// clear any previous timeouts (relevant when triggered by schedule)
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// the magic
|
||||
const {nextRun, nextRetry} = await update().catch(error => {
|
||||
// in theory this should never throw, if it does we have bigger problems
|
||||
console.error(error);
|
||||
// just kill the process on error
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// reties always take priority since they block normal runs from the retry delay
|
||||
if (nextRetry) {
|
||||
// disable schedule if present
|
||||
scheduledTask?.stop();
|
||||
|
||||
// calculate delay
|
||||
const delay = nextRetry.getTime() - Date.now();
|
||||
console.log(`Next retry scheduled for ${nextRetry.toLocaleString()} (in ${Math.floor(delay / 100) / 10} seconds)`);
|
||||
|
||||
// set timer
|
||||
timeoutId = setTimeout(() => run('retry'), delay);
|
||||
} else if (nextRun) {
|
||||
// re-enable schedule if present
|
||||
scheduledTask?.start();
|
||||
|
||||
// calculate delay
|
||||
const delay = nextRun.getTime() - Date.now();
|
||||
console.log(`Next normal run scheduled for ${nextRun.toLocaleString()} (in ${Math.floor(delay / 100) / 10} seconds)`);
|
||||
if (scheduledTask != null) {
|
||||
console.log('Cron schedule is configured, there might be runs happening sooner!');
|
||||
}
|
||||
|
||||
// set timer
|
||||
timeoutId = setTimeout(() => run('normal'), delay);
|
||||
} else {
|
||||
// in theory this should never happen
|
||||
console.error('Invalid state, no next retry/run date present');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
// always run on start
|
||||
run('initial');
|
||||
|
|
|
@ -1,183 +0,0 @@
|
|||
import axios, {AxiosResponse} from 'axios';
|
||||
import * as qs from 'qs';
|
||||
import {parse as parseCookie} from 'set-cookie-parser';
|
||||
|
||||
// env variables
|
||||
|
||||
const username = process.env.WINDSCRIBE_USERNAME;
|
||||
const password = process.env.WINDSCRIBE_PASSWORD;
|
||||
|
||||
if (!username || username.length == 0) {
|
||||
console.log('Missing environment variable WINDSCRIBE_USERNAME');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!password || password.length == 0) {
|
||||
console.log('Missing environment variable WINDSCRIBE_PASSWORD');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// interfaces
|
||||
|
||||
interface CsrfInfo {
|
||||
csrfTime: number;
|
||||
csrfToken: string;
|
||||
}
|
||||
|
||||
interface PortForwardingInfo {
|
||||
epfExpires: number;
|
||||
ports: number[];
|
||||
}
|
||||
|
||||
// constants
|
||||
|
||||
const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36';
|
||||
|
||||
// functions
|
||||
|
||||
export async function login(): Promise<string> {
|
||||
try {
|
||||
// get csrf token and time
|
||||
const {data: csrfData} = await axios.post<{csrf_token: string, csrf_time: number}>('https://res.windscribe.com/res/logintoken', null, {
|
||||
headers: {'User-Agent': userAgent},
|
||||
});
|
||||
|
||||
// log in
|
||||
const res = await axios.post('https://windscribe.com/login', qs.stringify({
|
||||
login: '1',
|
||||
upgrade: '0',
|
||||
csrf_time: csrfData.csrf_time,
|
||||
csrf_token: csrfData.csrf_token,
|
||||
username: username,
|
||||
password: password,
|
||||
code: ''
|
||||
}), {
|
||||
headers: {'content-type': 'application/x-www-form-urlencoded', 'User-Agent': userAgent},
|
||||
maxRedirects: 0,
|
||||
validateStatus: status => status == 302,
|
||||
});
|
||||
|
||||
// extract the cookie
|
||||
return parseCookie(res.headers['set-cookie'], {map: true, decodeValues: true})['ws_session_auth_hash'].value;
|
||||
} catch (error) {
|
||||
// try to extract windscribe message
|
||||
if (error.response) {
|
||||
const response = error.response as AxiosResponse<string>;
|
||||
const errorMessage = /<div class="content_message error">.*>(.*)<\/div/.exec(response.data);
|
||||
if (response.status == 200 && errorMessage && errorMessage[1]) {
|
||||
throw new Error(`Failed to log into windscribe: ${errorMessage[1]}`);
|
||||
}
|
||||
}
|
||||
|
||||
// or throw a generic error if windscribe message not found
|
||||
throw new Error(`Failed to log into windscribe: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMyAccountCsrfToken(sessionCookie: string): Promise<CsrfInfo> {
|
||||
try {
|
||||
// get page
|
||||
const res = await axios.get<string>('https://windscribe.com/myaccount', {
|
||||
headers: {
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
},
|
||||
});
|
||||
|
||||
// extract csrf tokena and time from page content
|
||||
const csrfTime = /csrf_time = (\d+);/.exec(res.data)[1];
|
||||
const csrfToken = /csrf_token = '(\w+)';/.exec(res.data)[1];
|
||||
|
||||
return {
|
||||
csrfTime: +csrfTime,
|
||||
csrfToken: csrfToken,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get csrf token from my account page: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPortForwardingInfo(sessionCookie: string): Promise<PortForwardingInfo> {
|
||||
try {
|
||||
// load sub page
|
||||
const res = await axios.get<string>('https://windscribe.com/staticips/load', {
|
||||
headers: {
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
}
|
||||
});
|
||||
|
||||
// extract data from page
|
||||
const epfExpires = res.data.match(/epfExpires = (\d+);/)[1]; // this is always present. set to 0 if no port is active
|
||||
const ports = [...res.data.matchAll(/<span>(?<port>\d+)<\/span>/g)].map(x => +x[1]); // this will return an empty array when there are not pots forwarded
|
||||
|
||||
return {
|
||||
epfExpires: +epfExpires,
|
||||
ports,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get port forwarding info: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeEphemeralPort(sessionCookie: string, csrfInfo: CsrfInfo): Promise<void> {
|
||||
try {
|
||||
// remove port
|
||||
const res = await axios.post<{success: number, epf: boolean, message?: string}>('https://windscribe.com/staticips/deleteEphPort', qs.stringify({
|
||||
ctime: csrfInfo.csrfTime,
|
||||
ctoken: csrfInfo.csrfToken
|
||||
}), {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
}
|
||||
});
|
||||
|
||||
// check for errors
|
||||
if (res.data.success == 0) {
|
||||
throw new Error(`success = 0; ${res.data.message ?? 'No message'}`);
|
||||
}
|
||||
|
||||
// make sure we actually removed it
|
||||
if (res.data.epf == false) {
|
||||
console.log('Tried to remove a non-existent ephemeral port, ignoring');
|
||||
} else {
|
||||
console.log('Deleted ephemeral port');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to delete ephemeral port: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function requestMatchingEphemeralPort(sessionCookie: string, csrfInfo: CsrfInfo): Promise<PortForwardingInfo> {
|
||||
try {
|
||||
// request new port
|
||||
const res = await axios.post<{success: number, message?: string, epf?: {ext: number, int: number, start_ts: number}}>('https://windscribe.com/staticips/postEphPort', qs.stringify({
|
||||
ctime: csrfInfo.csrfTime,
|
||||
ctoken: csrfInfo.csrfToken,
|
||||
port: '', // empty string for a matching port
|
||||
}), {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'Cookie': `ws_session_auth_hash=${sessionCookie};`,
|
||||
'User-Agent': userAgent,
|
||||
}
|
||||
});
|
||||
|
||||
// check for errors
|
||||
if (res.data.success == 0) {
|
||||
throw new Error(`success = 0; ${res.data.message ?? 'No message'}`);
|
||||
}
|
||||
|
||||
// epf should be present by this point
|
||||
const epf = res.data.epf!;
|
||||
console.log(`Created new matching ephemeral port: ${epf.ext}`);
|
||||
return {
|
||||
epfExpires: epf.start_ts,
|
||||
ports: [epf.ext, epf.int],
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error;
|
||||
}
|
||||
}
|
|
@ -1,21 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es5",
|
||||
"es6",
|
||||
"ES2016",
|
||||
"ES2018",
|
||||
],
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "ES2017",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"outDir": "./dist",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"downlevelIteration": true
|
||||
"skipLibCheck": true, // needed until https://github.com/sindresorhus/got/issues/2051 gets resolved
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"ts-node": {
|
||||
"esm": true
|
||||
},
|
||||
}
|
341
yarn.lock
341
yarn.lock
|
@ -9,17 +9,17 @@
|
|||
dependencies:
|
||||
"@jridgewell/trace-mapping" "0.3.9"
|
||||
|
||||
"@ctrl/deluge@^4.1.0":
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/deluge/-/deluge-4.1.0.tgz#f9f1404162903a32b879bdcccc4cd6d3da38d391"
|
||||
integrity sha512-U/iTUZ6tHsd+aesQncaTkzqIZSs+Zap7zRpU4OO8UVg4BHiU63RVc4Z9Vh6/kvknoM7E1JCBXg8kuA8fLSK4JQ==
|
||||
"@ctrl/deluge@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/deluge/-/deluge-4.2.0.tgz#7aaa183d6e6c974fa697f9b439bbf922784619b7"
|
||||
integrity sha512-vmhrVuuaq/OdIGLBRFunVmmgLrJq5mdO8RMl1IY5hN/x2aDP5bSN2/MdYysiz4MrQBEacClI8E87/s8XJEkIoA==
|
||||
dependencies:
|
||||
"@ctrl/magnet-link" "^3.1.1"
|
||||
"@ctrl/shared-torrent" "^4.1.1"
|
||||
"@ctrl/url-join" "^2.0.0"
|
||||
formdata-node "^4.3.2"
|
||||
got "^12.1.0"
|
||||
tough-cookie "^4.0.0"
|
||||
"@ctrl/shared-torrent" "^4.2.0"
|
||||
"@ctrl/url-join" "^2.0.2"
|
||||
formdata-node "^5.0.0"
|
||||
got "^12.3.1"
|
||||
tough-cookie "^4.1.2"
|
||||
|
||||
"@ctrl/magnet-link@^3.1.1":
|
||||
version "3.1.1"
|
||||
|
@ -28,31 +28,31 @@
|
|||
dependencies:
|
||||
"@ctrl/ts-base32" "^2.1.1"
|
||||
|
||||
"@ctrl/shared-torrent@^4.1.1":
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/shared-torrent/-/shared-torrent-4.1.1.tgz#0dc627a718ee078b682a36f58c19226a1c9ee340"
|
||||
integrity sha512-gDCV02liPbajkOWYxaeFkFDwnv6bmNm/v4OEvGOXrQcwKAZN7HzD4zoGb2Etnp4GO916HUMSMyZQzjRwtUy7tg==
|
||||
"@ctrl/shared-torrent@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/shared-torrent/-/shared-torrent-4.2.0.tgz#d7d817e1125d09007d016304b78ffa0ec265e718"
|
||||
integrity sha512-MvhC3xavvpiEc0Qd328Qe8Cc9rteUq/hIKy8paO7TZwfEUfCQYwUShvSTWy5aIUWZqHNg9S0fSdhDTc+LPT6Og==
|
||||
dependencies:
|
||||
got "^12.1.0"
|
||||
got "^12.3.1"
|
||||
|
||||
"@ctrl/ts-base32@^2.1.1":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/ts-base32/-/ts-base32-2.1.1.tgz#e84abee414044462ff06e61f2afeddd516ea690d"
|
||||
integrity sha512-lQ6Q0hZyhGgzAn/+7h0mseWhMh5d8nS4fxlhjhJrVSc+3U7qvITBvcLRDk2p71OcPgU55ZXVYfTfr2FqjDljGQ==
|
||||
|
||||
"@ctrl/url-join@^2.0.0":
|
||||
"@ctrl/url-join@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/url-join/-/url-join-2.0.2.tgz#c65fdc0ca61c36248566dffe78c4636a274330bd"
|
||||
integrity sha512-V/QZoIgCB7kLCkqWoTImtXyzD8TnDqBfAzg167bTLp7CNbHHqpum9hLezpaBolhZK+wIZjSCHqFB7ajXVaIHMw==
|
||||
|
||||
"@eslint/eslintrc@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
|
||||
integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
|
||||
"@eslint/eslintrc@^1.3.1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.1.tgz#de0807bfeffc37b964a7d0400e0c348ce5a2543d"
|
||||
integrity sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==
|
||||
dependencies:
|
||||
ajv "^6.12.4"
|
||||
debug "^4.3.2"
|
||||
espree "^9.3.2"
|
||||
espree "^9.4.0"
|
||||
globals "^13.15.0"
|
||||
ignore "^5.2.0"
|
||||
import-fresh "^3.2.1"
|
||||
|
@ -74,6 +74,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d"
|
||||
integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==
|
||||
|
||||
"@humanwhocodes/module-importer@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
|
||||
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||
|
||||
"@humanwhocodes/object-schema@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||
|
@ -150,6 +155,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
|
||||
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
|
||||
|
||||
"@types/async-lock@^1.1.5":
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.5.tgz#a82f33e09aef451d6ded7bffae73f9d254723124"
|
||||
integrity sha512-A9ClUfmj6wwZMLRz0NaYzb98YH1exlHdf/cdDSKBfMQJnPOdO8xlEW0Eh2QsTTntGzOFWURcEjYElkZ1IY4GCQ==
|
||||
|
||||
"@types/cacheable-request@^6.0.2":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
|
||||
|
@ -177,22 +187,27 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node-cron@^3.0.4":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.4.tgz#ade755125a5b9e409ba9598e04c7132a05c108db"
|
||||
integrity sha512-A2H+uz5ry4hohYjRe5mQSE/8Dx/HGw4WZ728JxhKUZ7z8CMvRuG2tpbzGHRGQCuQzz5aCNB1iXzPZYHd4BPHvw==
|
||||
|
||||
"@types/node@*":
|
||||
version "17.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.4.tgz#fec0ce0526abb6062fd206d72a642811b887a111"
|
||||
integrity sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==
|
||||
|
||||
"@types/node@^18.7.7":
|
||||
version "18.7.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.7.tgz#2f7e3443fb434315ff594c49043486fe49937182"
|
||||
integrity sha512-sTKYCtQmaUpsAT+AbUTKg0Ya0dYyh20t3TBQebWrGXQHFmkrEyeedok2/IpTthlJopPSbUoc1hAKoK6UeFRCZw==
|
||||
"@types/node@^18.7.16":
|
||||
version "18.7.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.16.tgz#0eb3cce1e37c79619943d2fd903919fc30850601"
|
||||
integrity sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==
|
||||
|
||||
"@types/qs@^6.9.7":
|
||||
version "6.9.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
|
||||
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
|
||||
|
||||
"@types/responselike@*", "@types/responselike@^1.0.0":
|
||||
"@types/responselike@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
||||
integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||
|
@ -206,14 +221,14 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.1.tgz#c0a480d05211660221eda963cc844732fe9b1714"
|
||||
integrity sha512-S1iZIxrTvKkU3+m63YUOxYPKaP+yWDQrdhxTglVDVEVBf+aCSw85+BmJnyUaQQsk5TXFG/LpBu9fa+LrAQ91fQ==
|
||||
"@typescript-eslint/eslint-plugin@^5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.36.2.tgz#6df092a20e0f9ec748b27f293a12cb39d0c1fe4d"
|
||||
integrity sha512-OwwR8LRwSnI98tdc2z7mJYgY60gf7I9ZfGjN5EjCwwns9bdTuQfAXcsjSB2wSQ/TVNYSGKf4kzVXbNGaZvwiXw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.33.1"
|
||||
"@typescript-eslint/type-utils" "5.33.1"
|
||||
"@typescript-eslint/utils" "5.33.1"
|
||||
"@typescript-eslint/scope-manager" "5.36.2"
|
||||
"@typescript-eslint/type-utils" "5.36.2"
|
||||
"@typescript-eslint/utils" "5.36.2"
|
||||
debug "^4.3.4"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
ignore "^5.2.0"
|
||||
|
@ -221,69 +236,70 @@
|
|||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/parser@^5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.1.tgz#e4b253105b4d2a4362cfaa4e184e2d226c440ff3"
|
||||
integrity sha512-IgLLtW7FOzoDlmaMoXdxG8HOCByTBXrB1V2ZQYSEV1ggMmJfAkMWTwUjjzagS6OkfpySyhKFkBw7A9jYmcHpZA==
|
||||
"@typescript-eslint/parser@^5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.36.2.tgz#3ddf323d3ac85a25295a55fcb9c7a49ab4680ddd"
|
||||
integrity sha512-qS/Kb0yzy8sR0idFspI9Z6+t7mqk/oRjnAYfewG+VN73opAUvmYL3oPIMmgOX6CnQS6gmVIXGshlb5RY/R22pA==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.33.1"
|
||||
"@typescript-eslint/types" "5.33.1"
|
||||
"@typescript-eslint/typescript-estree" "5.33.1"
|
||||
"@typescript-eslint/scope-manager" "5.36.2"
|
||||
"@typescript-eslint/types" "5.36.2"
|
||||
"@typescript-eslint/typescript-estree" "5.36.2"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.1.tgz#8d31553e1b874210018ca069b3d192c6d23bc493"
|
||||
integrity sha512-8ibcZSqy4c5m69QpzJn8XQq9NnqAToC8OdH/W6IXPXv83vRyEDPYLdjAlUx8h/rbusq6MkW4YdQzURGOqsn3CA==
|
||||
"@typescript-eslint/scope-manager@5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.36.2.tgz#a75eb588a3879ae659514780831370642505d1cd"
|
||||
integrity sha512-cNNP51L8SkIFSfce8B1NSUBTJTu2Ts4nWeWbFrdaqjmn9yKrAaJUBHkyTZc0cL06OFHpb+JZq5AUHROS398Orw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.33.1"
|
||||
"@typescript-eslint/visitor-keys" "5.33.1"
|
||||
"@typescript-eslint/types" "5.36.2"
|
||||
"@typescript-eslint/visitor-keys" "5.36.2"
|
||||
|
||||
"@typescript-eslint/type-utils@5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.1.tgz#1a14e94650a0ae39f6e3b77478baff002cec4367"
|
||||
integrity sha512-X3pGsJsD8OiqhNa5fim41YtlnyiWMF/eKsEZGsHID2HcDqeSC5yr/uLOeph8rNF2/utwuI0IQoAK3fpoxcLl2g==
|
||||
"@typescript-eslint/type-utils@5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.36.2.tgz#752373f4babf05e993adf2cd543a763632826391"
|
||||
integrity sha512-rPQtS5rfijUWLouhy6UmyNquKDPhQjKsaKH0WnY6hl/07lasj8gPaH2UD8xWkePn6SC+jW2i9c2DZVDnL+Dokw==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "5.33.1"
|
||||
"@typescript-eslint/typescript-estree" "5.36.2"
|
||||
"@typescript-eslint/utils" "5.36.2"
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/types@5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.1.tgz#3faef41793d527a519e19ab2747c12d6f3741ff7"
|
||||
integrity sha512-7K6MoQPQh6WVEkMrMW5QOA5FO+BOwzHSNd0j3+BlBwd6vtzfZceJ8xJ7Um2XDi/O3umS8/qDX6jdy2i7CijkwQ==
|
||||
"@typescript-eslint/types@5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.36.2.tgz#a5066e500ebcfcee36694186ccc57b955c05faf9"
|
||||
integrity sha512-9OJSvvwuF1L5eS2EQgFUbECb99F0mwq501w0H0EkYULkhFa19Qq7WFbycdw1PexAc929asupbZcgjVIe6OK/XQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.1.tgz#a573bd360790afdcba80844e962d8b2031984f34"
|
||||
integrity sha512-JOAzJ4pJ+tHzA2pgsWQi4804XisPHOtbvwUyqsuuq8+y5B5GMZs7lI1xDWs6V2d7gE/Ez5bTGojSK12+IIPtXA==
|
||||
"@typescript-eslint/typescript-estree@5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.36.2.tgz#0c93418b36c53ba0bc34c61fe9405c4d1d8fe560"
|
||||
integrity sha512-8fyH+RfbKc0mTspfuEjlfqA4YywcwQK2Amcf6TDOwaRLg7Vwdu4bZzyvBZp4bjt1RRjQ5MDnOZahxMrt2l5v9w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.33.1"
|
||||
"@typescript-eslint/visitor-keys" "5.33.1"
|
||||
"@typescript-eslint/types" "5.36.2"
|
||||
"@typescript-eslint/visitor-keys" "5.36.2"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/utils@5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.1.tgz#171725f924fe1fe82bb776522bb85bc034e88575"
|
||||
integrity sha512-uphZjkMaZ4fE8CR4dU7BquOV6u0doeQAr8n6cQenl/poMaIyJtBu8eys5uk6u5HiDH01Mj5lzbJ5SfeDz7oqMQ==
|
||||
"@typescript-eslint/utils@5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.36.2.tgz#b01a76f0ab244404c7aefc340c5015d5ce6da74c"
|
||||
integrity sha512-uNcopWonEITX96v9pefk9DC1bWMdkweeSsewJ6GeC7L6j2t0SJywisgkr9wUTtXk90fi2Eljj90HSHm3OGdGRg==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@typescript-eslint/scope-manager" "5.33.1"
|
||||
"@typescript-eslint/types" "5.33.1"
|
||||
"@typescript-eslint/typescript-estree" "5.33.1"
|
||||
"@typescript-eslint/scope-manager" "5.36.2"
|
||||
"@typescript-eslint/types" "5.36.2"
|
||||
"@typescript-eslint/typescript-estree" "5.36.2"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.33.1":
|
||||
version "5.33.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.1.tgz#0155c7571c8cd08956580b880aea327d5c34a18b"
|
||||
integrity sha512-nwIxOK8Z2MPWltLKMLOEZwmfBZReqUdbEoHQXeCpa+sRVARe5twpJGHCB4dk9903Yaf0nMAlGbQfaAH92F60eg==
|
||||
"@typescript-eslint/visitor-keys@5.36.2":
|
||||
version "5.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.36.2.tgz#2f8f78da0a3bad3320d2ac24965791ac39dace5a"
|
||||
integrity sha512-BtRvSR6dEdrNt7Net2/XDjbYKU5Ml6GqJgVfXT0CxTCJlnIqK7rAGreuWKMT2t8cFUT2Msv5oxw0GMRD7T5J7A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.33.1"
|
||||
"@typescript-eslint/types" "5.36.2"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
abbrev@1:
|
||||
|
@ -356,6 +372,11 @@ 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==
|
||||
|
||||
async-lock@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.3.2.tgz#56668613f91c1c55432b4db73e65c9ced664e789"
|
||||
integrity sha512-phnXdS3RP7PPcmP6NWWzWMU0sLTeyvtZCxBPpZdkYE3seGLKSQZs9FrmVO/qwypq98FUtWWUEYxziLkdGk5nnA==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
|
@ -555,10 +576,10 @@ doctrine@^3.0.0:
|
|||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dotenv@^16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d"
|
||||
integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==
|
||||
dotenv@^16.0.2:
|
||||
version "16.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.2.tgz#0b0f8652c016a3858ef795024508cddc4bffc5bf"
|
||||
integrity sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
version "1.4.4"
|
||||
|
@ -610,14 +631,15 @@ eslint-visitor-keys@^3.3.0:
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
||||
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||
|
||||
eslint@^8.22.0:
|
||||
version "8.22.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48"
|
||||
integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==
|
||||
eslint@^8.23.0:
|
||||
version "8.23.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.0.tgz#a184918d288820179c6041bb3ddcc99ce6eea040"
|
||||
integrity sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==
|
||||
dependencies:
|
||||
"@eslint/eslintrc" "^1.3.0"
|
||||
"@eslint/eslintrc" "^1.3.1"
|
||||
"@humanwhocodes/config-array" "^0.10.4"
|
||||
"@humanwhocodes/gitignore-to-minimatch" "^1.0.2"
|
||||
"@humanwhocodes/module-importer" "^1.0.1"
|
||||
ajv "^6.10.0"
|
||||
chalk "^4.0.0"
|
||||
cross-spawn "^7.0.2"
|
||||
|
@ -627,7 +649,7 @@ eslint@^8.22.0:
|
|||
eslint-scope "^7.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
espree "^9.3.3"
|
||||
espree "^9.4.0"
|
||||
esquery "^1.4.0"
|
||||
esutils "^2.0.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
|
@ -653,12 +675,11 @@ eslint@^8.22.0:
|
|||
strip-ansi "^6.0.1"
|
||||
strip-json-comments "^3.1.0"
|
||||
text-table "^0.2.0"
|
||||
v8-compile-cache "^2.0.3"
|
||||
|
||||
espree@^9.3.2, espree@^9.3.3:
|
||||
version "9.3.3"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d"
|
||||
integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==
|
||||
espree@^9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a"
|
||||
integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==
|
||||
dependencies:
|
||||
acorn "^8.8.0"
|
||||
acorn-jsx "^5.3.2"
|
||||
|
@ -766,10 +787,10 @@ follow-redirects@^1.14.9:
|
|||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
|
||||
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
|
||||
|
||||
form-data-encoder@^2.0.1:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.0.tgz#ee9afc735186a2c897005431c13b624cede616da"
|
||||
integrity sha512-njK60LnfhfDWy+AEUIf9ZQNRAcmXCdDfiNOm2emuPtzwh7U9k/mo9F3S54aPiaZ3vhqUjikVLfcPg2KuBddskQ==
|
||||
form-data-encoder@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.2.tgz#5996b7c236e8c418d08316055a2235226c5e4061"
|
||||
integrity sha512-FCaIOVTRA9E0siY6FeXid7D5yrCqpsErplUkE2a1BEiKj1BE9z6FbKB4ntDTwC4NVLie9p+4E9nX4mWwEOT05A==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
|
@ -780,13 +801,22 @@ form-data@^4.0.0:
|
|||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formdata-node@^4.3.2:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.0.tgz#7dda5b9c2959c8c312fd69ef2b10a9c7ea40ca29"
|
||||
integrity sha512-Q1LZsZggMqcmiAv1VIf4qSo1jxROFYUtuwNbJxman4UurKp3kdcDhtIK2+rKKqa0Xgqlga0qea3s+qH4I4v5kw==
|
||||
formdata-node@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-5.0.0.tgz#7b4d23f8d823b88d29130366f33ff9ac58dfa7fe"
|
||||
integrity sha512-zrGsVVS56jJo+htsVv7ffXuzie91a2NrU1cPamvtPaSyRX++SH+4KXlGoOt+ncgDJ4bFA2SAQ+QGA+p4l1vciw==
|
||||
dependencies:
|
||||
node-domexception "1.0.0"
|
||||
web-streams-polyfill "4.0.0-beta.2"
|
||||
web-streams-polyfill "4.0.0-beta.3"
|
||||
|
||||
fs-extra@^4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
|
||||
integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -874,24 +904,28 @@ globby@^11.1.0:
|
|||
merge2 "^1.4.1"
|
||||
slash "^3.0.0"
|
||||
|
||||
got@^12.1.0:
|
||||
version "12.3.1"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-12.3.1.tgz#79d6ebc0cb8358c424165698ddb828be56e74684"
|
||||
integrity sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw==
|
||||
got@^12.3.1:
|
||||
version "12.4.1"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-12.4.1.tgz#8598311b42591dfd2ed3ca4cdb9a591e2769a0bd"
|
||||
integrity sha512-Sz1ojLt4zGNkcftIyJKnulZT/yEDvifhUjccHA8QzOuTgPs/+njXYNMFE3jR4/2OODQSSbH8SdnoLCkbh41ieA==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^5.2.0"
|
||||
"@szmarczak/http-timer" "^5.0.1"
|
||||
"@types/cacheable-request" "^6.0.2"
|
||||
"@types/responselike" "^1.0.0"
|
||||
cacheable-lookup "^6.0.4"
|
||||
cacheable-request "^7.0.2"
|
||||
decompress-response "^6.0.0"
|
||||
form-data-encoder "^2.0.1"
|
||||
form-data-encoder "^2.1.0"
|
||||
get-stream "^6.0.1"
|
||||
http2-wrapper "^2.1.10"
|
||||
lowercase-keys "^3.0.0"
|
||||
p-cancelable "^3.0.0"
|
||||
responselike "^2.0.0"
|
||||
responselike "^3.0.0"
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
||||
grapheme-splitter@^1.0.4:
|
||||
version "1.0.4"
|
||||
|
@ -1020,6 +1054,22 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
keyv-file@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv-file/-/keyv-file-0.2.0.tgz#3442b07a00c1d7bd0242f4a91bcf498afbd6ea6a"
|
||||
integrity sha512-zUQ11eZRmilEUpV1gJSj8mBAHjyXpleQo1iCS0khb+GFRhiPfwavWgn4eDUKNlOyMZzmExnISl8HE1hNbim0gw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
fs-extra "^4.0.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
keyv@^4.0.0:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.4.tgz#f040b236ea2b06ed15ed86fbef8407e1a1c8e376"
|
||||
|
@ -1027,6 +1077,13 @@ keyv@^4.0.0:
|
|||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
|
||||
keyv@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.0.tgz#dbce9ade79610b6e641a9a65f2f6499ba06b9bc6"
|
||||
integrity sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==
|
||||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
|
||||
levn@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
|
||||
|
@ -1133,6 +1190,13 @@ natural-compare@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
node-cron@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-3.0.2.tgz#bb0681342bd2dfb568f28e464031280e7f06bd01"
|
||||
integrity sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==
|
||||
dependencies:
|
||||
uuid "8.3.2"
|
||||
|
||||
node-domexception@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||
|
@ -1281,6 +1345,11 @@ qs@^6.11.0:
|
|||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
querystringify@^2.1.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
|
@ -1303,6 +1372,11 @@ regexpp@^3.2.0:
|
|||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
|
||||
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
||||
|
||||
resolve-alpn@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
|
||||
|
@ -1320,6 +1394,13 @@ responselike@^2.0.0:
|
|||
dependencies:
|
||||
lowercase-keys "^2.0.0"
|
||||
|
||||
responselike@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626"
|
||||
integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==
|
||||
dependencies:
|
||||
lowercase-keys "^3.0.0"
|
||||
|
||||
reusify@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||
|
@ -1439,14 +1520,15 @@ touch@^3.1.0:
|
|||
dependencies:
|
||||
nopt "~1.0.10"
|
||||
|
||||
tough-cookie@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
|
||||
integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==
|
||||
tough-cookie@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874"
|
||||
integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==
|
||||
dependencies:
|
||||
psl "^1.1.33"
|
||||
punycode "^2.1.1"
|
||||
universalify "^0.1.2"
|
||||
universalify "^0.2.0"
|
||||
url-parse "^1.5.3"
|
||||
|
||||
ts-node@^10.9.1:
|
||||
version "10.9.1"
|
||||
|
@ -1467,7 +1549,7 @@ ts-node@^10.9.1:
|
|||
v8-compile-cache-lib "^3.0.1"
|
||||
yn "3.1.1"
|
||||
|
||||
tslib@^1.8.1:
|
||||
tslib@^1.8.1, tslib@^1.9.3:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
@ -1491,21 +1573,26 @@ type-fest@^0.20.2:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
typescript@^4.7.4:
|
||||
version "4.7.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
|
||||
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
|
||||
typescript@^4.8.2:
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790"
|
||||
integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==
|
||||
|
||||
undefsafe@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
||||
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
||||
|
||||
universalify@^0.1.2:
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
universalify@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
|
||||
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
|
@ -1513,20 +1600,28 @@ uri-js@^4.2.2:
|
|||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-parse@^1.5.3:
|
||||
version "1.5.10"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
||||
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
uuid@8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
v8-compile-cache-lib@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
|
||||
|
||||
v8-compile-cache@^2.0.3:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
|
||||
|
||||
web-streams-polyfill@4.0.0-beta.2:
|
||||
version "4.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.2.tgz#a534d1a8a4bbbb8d2bba07eb27722fde8ee8d077"
|
||||
integrity sha512-UHhhnoe2M40uh2r0KVdJTN7qjFytm6o0Yp3VcjwV3bfo6rz8uqvxNoE5yNmGF0y3eFfXaFeb6M09MDSwwLmq4w==
|
||||
web-streams-polyfill@4.0.0-beta.3:
|
||||
version "4.0.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38"
|
||||
integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
|
|
Loading…
Reference in a new issue