mirror of
https://github.com/dumbasPL/deluge-windscribe-ephemeral-port.git
synced 2024-11-10 01:02:17 +01:00
commit
50febacf59
17 changed files with 975 additions and 458 deletions
|
@ -1,6 +1,8 @@
|
|||
.vscode
|
||||
.env
|
||||
.git
|
||||
.gitignore
|
||||
.github
|
||||
node_modules
|
||||
dist
|
||||
Dockerfile
|
45
.github/workflows/ci.yml
vendored
Normal file
45
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name: Docker build
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
jobs:
|
||||
docker-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
|
||||
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
|
19
.github/workflows/docker-publish.yml
vendored
19
.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
|
||||
|
@ -44,9 +39,9 @@ jobs:
|
|||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
- uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -8,4 +8,5 @@ dist/
|
|||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
!.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
|
||||
|
||||
|
|
62
README.md
62
README.md
|
@ -8,36 +8,74 @@ This project was designed to work along side containers like [kabe0/deluge-winds
|
|||
It will not help you configure windscribe to use a vpn!
|
||||
It will only update the port that deluge listens on to the same port that's configured on windscribe website.
|
||||
|
||||
**I strongly advise against using a "restart on error" policy since windscribe will temporary block your ip address after a few failed login attempts.**
|
||||
# Configuration
|
||||
|
||||
Configuration is done using environment variables
|
||||
|
||||
| Variable | Description | Required | Default |
|
||||
| :-: | :-: | :-: | :-: |
|
||||
| WINDSCRIBE_USERNAME | username you use to login at windscribe.com/login | YES | |
|
||||
| WINDSCRIBE_PASSWORD | password you use to login at windscribe.com/login | YES | |
|
||||
| DELUGE_URL | The base URL for the deluge web UI | YES | |
|
||||
| DELUGE_PASSWORD | The password for the deluge web UI | YES | |
|
||||
| CRON_SCHEDULE | An extra cron schedule used to periodically validate and update the port if needed. Disabled if left empty | NO | |
|
||||
| DELUGE_HOST_ID | The internal host id to connect to in the deluge web UI. It will be printed in stdout after the first successful connection to deluge | Only if you have more then one connection configured in connection manager | If you have multiple configured in deluge web ui the app will print them out and crash. If you have only one that one will be used and you don't need to specify it explicitly |
|
||||
| WINDSCRIBE_RETRY_DELAY | how long to wait (in milliseconds) before retrying after a windscribe error. For example a failed login. | NO | 3600000 (1 hour) |
|
||||
| WINDSCRIBE_EXTRA_DELAY | how long to wait (in milliseconds) after the ephemeral port expires before trying to create a new one. | NO | 60000 (1 minute) |
|
||||
| DELUGE_RETRY_DELAY | how long to wait (in milliseconds) before retrying after a deluge error. For example a failed login. | NO | 300000 (5 minutes) |
|
||||
| CACHE_DIR | A directory where to store cached data like windscribe session cookies | YES | `/cache` in the docker container and `./cache` everywhere else |
|
||||
|
||||
# Running
|
||||
|
||||
## Using docker (and docker compose in this example)
|
||||
```yml
|
||||
version: '3'
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
deluge-windscribe-ephemeral-port:
|
||||
image: dumbaspl/deluge-windscribe-ephemeral-port
|
||||
image: dumbaspl/deluge-windscribe-ephemeral-port:2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- windscribe-cache:/cache
|
||||
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>
|
||||
|
||||
# optional
|
||||
# - DELUGE_HOST_ID=
|
||||
# - DELUGE_RETRY_DELAY=300000
|
||||
# - WINDSCRIBE_RETRY_DELAY=3600000
|
||||
# - WINDSCRIBE_EXTRA_DELAY=60000
|
||||
# - CRON_SCHEDULE=
|
||||
# - CACHE_DIR=/cache
|
||||
volumes:
|
||||
windscribe-cache:
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
**This project requires Node.js version 16 or newer**
|
||||
**This project uses [yarn](https://classic.yarnpkg.com/) to manage dependencies, make sure you have it installed first.**
|
||||
|
||||
1. clone this repository
|
||||
2. Install dependencies by running `yarn install`
|
||||
3. Create a `.env` file in the root of the project with the necessary configuration
|
||||
```shell
|
||||
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
|
||||
# optional
|
||||
# DELUGE_HOST_ID=
|
||||
# DELUGE_RETRY_DELAY=300000
|
||||
# WINDSCRIBE_RETRY_DELAY=3600000
|
||||
# WINDSCRIBE_EXTRA_DELAY=60000
|
||||
# CRON_SCHEDULE=
|
||||
# CACHE_DIR=./cache
|
||||
```
|
||||
4. Build and start using `yarn install`
|
||||
|
||||
Tip: you can use tools like pm2 to manage nodejs applications
|
||||
|
|
|
@ -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",
|
||||
"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