Finally fixed 400 by updating URLs, improved logging
This commit is contained in:
parent
8ae655a34e
commit
4baf4ea1a7
17 changed files with 174 additions and 267 deletions
|
@ -1,52 +1,11 @@
|
|||
"""
|
||||
Unofficial Aternos API module written in Python.
|
||||
It uses Aternos' private API and html parsing"""
|
||||
"""Init"""
|
||||
|
||||
from .atclient import Client
|
||||
from .atserver import AternosServer
|
||||
from .atserver import Edition
|
||||
from .atserver import Status
|
||||
from .atconnect import AternosConnect
|
||||
from .atplayers import PlayersList
|
||||
from .atplayers import Lists
|
||||
from .atconf import AternosConfig
|
||||
from .atconf import ServerOpts
|
||||
from .atconf import WorldOpts
|
||||
from .atconf import WorldRules
|
||||
from .atconf import Gamemode
|
||||
from .atconf import Difficulty
|
||||
from .atwss import AternosWss
|
||||
from .atwss import Streams
|
||||
from .atfm import FileManager
|
||||
from .atfile import AternosFile
|
||||
from .atfile import FileType
|
||||
from .aterrors import AternosError
|
||||
from .aterrors import CloudflareError
|
||||
from .aterrors import CredentialsError
|
||||
from .aterrors import TokenError
|
||||
from .aterrors import ServerError
|
||||
from .aterrors import ServerStartError
|
||||
from .aterrors import FileError
|
||||
from .aterrors import AternosPermissionError
|
||||
from .atjsparse import Js2PyInterpreter
|
||||
from .atjsparse import NodeInterpreter
|
||||
|
||||
__all__ = [
|
||||
|
||||
'atclient', 'atserver', 'atconnect',
|
||||
'atplayers', 'atconf', 'atwss',
|
||||
'atfm', 'atfile',
|
||||
'aterrors', 'atjsparse',
|
||||
|
||||
'Client', 'AternosServer', 'AternosConnect',
|
||||
'PlayersList', 'AternosConfig', 'AternosWss',
|
||||
'FileManager', 'AternosFile', 'AternosError',
|
||||
'CloudflareError', 'CredentialsError', 'TokenError',
|
||||
'ServerError', 'ServerStartError', 'FileError',
|
||||
'AternosPermissionError',
|
||||
'Js2PyInterpreter', 'NodeInterpreter',
|
||||
|
||||
'Edition', 'Status', 'Lists',
|
||||
'ServerOpts', 'WorldOpts', 'WorldRules',
|
||||
'Gamemode', 'Difficulty', 'Streams', 'FileType',
|
||||
]
|
||||
|
|
|
@ -21,6 +21,7 @@ if TYPE_CHECKING:
|
|||
from .atclient import Client
|
||||
|
||||
|
||||
ACCOUNT_URL = f'{AJAX_URL}/account'
|
||||
email_re = re.compile(
|
||||
r'^[A-Za-z0-9\-_+.]+@[A-Za-z0-9\-_+.]+\.[A-Za-z0-9\-]+$|^$'
|
||||
)
|
||||
|
@ -116,7 +117,7 @@ class AternosAccount:
|
|||
"""
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/username.php',
|
||||
f'{ACCOUNT_URL}/username',
|
||||
'POST', data={'username': value},
|
||||
sendtoken=True,
|
||||
)
|
||||
|
@ -136,7 +137,7 @@ class AternosAccount:
|
|||
raise ValueError('Invalid e-mail')
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/email.php',
|
||||
f'{ACCOUNT_URL}/email',
|
||||
'POST', data={'email': value},
|
||||
sendtoken=True,
|
||||
)
|
||||
|
@ -165,7 +166,7 @@ class AternosAccount:
|
|||
"""
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/password.php',
|
||||
f'{ACCOUNT_URL}/password',
|
||||
'POST', data={
|
||||
'oldpassword': old,
|
||||
'newpassword': new,
|
||||
|
@ -178,7 +179,7 @@ class AternosAccount:
|
|||
a QR code for enabling 2FA"""
|
||||
|
||||
return self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/secret.php',
|
||||
f'{ACCOUNT_URL}/secret',
|
||||
'GET', sendtoken=True,
|
||||
).json()
|
||||
|
||||
|
@ -205,7 +206,7 @@ class AternosAccount:
|
|||
"""
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/twofactor.php',
|
||||
f'{ACCOUNT_URL}/twofactor',
|
||||
'POST', data={'code': code},
|
||||
sendtoken=True,
|
||||
)
|
||||
|
@ -218,7 +219,7 @@ class AternosAccount:
|
|||
"""
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/disbaleTwofactor.php',
|
||||
f'{ACCOUNT_URL}/disbaleTwofactor',
|
||||
'POST', data={'code': code},
|
||||
sendtoken=True,
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import os
|
|||
import re
|
||||
from typing import Optional, Type
|
||||
|
||||
from .atlog import log
|
||||
from .atlog import log, is_debug, set_debug
|
||||
from .atmd5 import md5encode
|
||||
|
||||
from .ataccount import AternosAccount
|
||||
|
@ -28,12 +28,11 @@ class Client:
|
|||
def __init__(self) -> None:
|
||||
|
||||
# Config
|
||||
self.debug = False
|
||||
self.sessions_dir = '~'
|
||||
self.js: Type[Interpreter] = Js2PyInterpreter
|
||||
# ###
|
||||
|
||||
self.saved_session = ''
|
||||
self.saved_session = '~/.aternos' # will be rewritten by login()
|
||||
self.atconn = AternosConnect()
|
||||
self.account = AternosAccount(self)
|
||||
|
||||
|
@ -101,7 +100,7 @@ class Client:
|
|||
credentials['code'] = str(code)
|
||||
|
||||
loginreq = self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/login.php',
|
||||
f'{AJAX_URL}/account/login',
|
||||
'POST', data=credentials, sendtoken=True,
|
||||
)
|
||||
|
||||
|
@ -123,18 +122,18 @@ class Client:
|
|||
"""Log out from the Aternos account"""
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
f'{AJAX_URL}/account/logout.php',
|
||||
f'{AJAX_URL}/account/logout',
|
||||
'GET', sendtoken=True,
|
||||
)
|
||||
|
||||
self.remove_session(self.saved_session)
|
||||
|
||||
def restore_session(self, filename: str = '~/.aternos') -> None:
|
||||
def restore_session(self, file: str = '~/.aternos') -> None:
|
||||
"""Restores ATERNOS_SESSION cookie and,
|
||||
if included, servers list, from a session file
|
||||
|
||||
Args:
|
||||
filename (str, optional): Filename
|
||||
file (str, optional): Filename
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the file cannot be found
|
||||
|
@ -142,13 +141,13 @@ class Client:
|
|||
(or the file at all) has incorrect format
|
||||
"""
|
||||
|
||||
filename = os.path.expanduser(filename)
|
||||
log.debug('Restoring session from %s', filename)
|
||||
file = os.path.expanduser(file)
|
||||
log.debug('Restoring session from %s', file)
|
||||
|
||||
if not os.path.exists(filename):
|
||||
if not os.path.exists(file):
|
||||
raise FileNotFoundError()
|
||||
|
||||
with open(filename, 'rt', encoding='utf-8') as f:
|
||||
with open(file, 'rt', encoding='utf-8') as f:
|
||||
saved = f.read() \
|
||||
.strip() \
|
||||
.replace('\r\n', '\n') \
|
||||
|
@ -164,7 +163,7 @@ class Client:
|
|||
self.account.refresh_servers(saved[1:])
|
||||
|
||||
self.atconn.session.cookies['ATERNOS_SESSION'] = session
|
||||
self.saved_session = filename
|
||||
self.saved_session = file
|
||||
|
||||
def save_session(
|
||||
self,
|
||||
|
@ -231,3 +230,11 @@ class Client:
|
|||
)
|
||||
|
||||
return f'{sessions_dir}/.at_{secure}'
|
||||
|
||||
@property
|
||||
def debug(self) -> bool:
|
||||
return is_debug()
|
||||
|
||||
@debug.setter
|
||||
def debug(self, state: bool) -> None:
|
||||
return set_debug(state)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
"""Stores API session and sends requests"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
|
@ -16,7 +15,7 @@ import requests
|
|||
|
||||
from cloudscraper import CloudScraper
|
||||
|
||||
from .atlog import log
|
||||
from .atlog import log, is_debug
|
||||
|
||||
from . import atjsparse
|
||||
from .aterrors import TokenError
|
||||
|
@ -163,7 +162,8 @@ class AternosConnect:
|
|||
headers: Optional[Dict[Any, Any]] = None,
|
||||
reqcookies: Optional[Dict[Any, Any]] = None,
|
||||
sendtoken: bool = False,
|
||||
retry: int = 5) -> requests.Response:
|
||||
retries: int = 5,
|
||||
timeout: int = 4) -> requests.Response:
|
||||
"""Sends a request to Aternos API bypass Cloudflare
|
||||
|
||||
Args:
|
||||
|
@ -177,8 +177,9 @@ class AternosConnect:
|
|||
Cookies only for this request
|
||||
sendtoken (bool, optional): If the ajax and SEC token
|
||||
should be sent
|
||||
retry (int, optional): How many times parser must retry
|
||||
retries (int, optional): How many times parser must retry
|
||||
connection to API bypass Cloudflare
|
||||
timeout (int, optional): Request timeout in seconds
|
||||
|
||||
Raises:
|
||||
CloudflareError: When the parser has exceeded retries count
|
||||
|
@ -188,7 +189,7 @@ class AternosConnect:
|
|||
API response
|
||||
"""
|
||||
|
||||
if retry <= 0:
|
||||
if retries <= 0:
|
||||
raise CloudflareError('Unable to bypass Cloudflare protection')
|
||||
|
||||
try:
|
||||
|
@ -217,7 +218,7 @@ class AternosConnect:
|
|||
reqcookies['ATERNOS_SESSION'] = self.atcookie
|
||||
del self.session.cookies['ATERNOS_SESSION']
|
||||
|
||||
if log.level == logging.DEBUG:
|
||||
if is_debug():
|
||||
|
||||
reqcookies_dbg = {
|
||||
k: str(v or '')[:3]
|
||||
|
@ -240,18 +241,19 @@ class AternosConnect:
|
|||
sendreq = partial(
|
||||
self.session.post,
|
||||
params=params,
|
||||
data=data
|
||||
data=data,
|
||||
)
|
||||
else:
|
||||
sendreq = partial(
|
||||
self.session.get,
|
||||
params={**params, **data}
|
||||
params={**params, **data},
|
||||
)
|
||||
|
||||
req = sendreq(
|
||||
url,
|
||||
headers=headers,
|
||||
cookies=reqcookies
|
||||
cookies=reqcookies,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
resp_type = req.headers.get('content-type', '')
|
||||
|
@ -265,7 +267,7 @@ class AternosConnect:
|
|||
url, method,
|
||||
params, data,
|
||||
headers, reqcookies,
|
||||
sendtoken, retry - 1
|
||||
sendtoken, retries - 1
|
||||
)
|
||||
|
||||
log.debug('AternosConnect received: %s', req.text[:65])
|
||||
|
|
|
@ -84,6 +84,7 @@ class NodeInterpreter(Interpreter):
|
|||
server_js = file_dir / 'data' / 'server.js'
|
||||
|
||||
self.url = f'http://{host}:{port}'
|
||||
self.timeout = 2
|
||||
|
||||
# pylint: disable=consider-using-with
|
||||
self.proc = subprocess.Popen(
|
||||
|
@ -100,11 +101,11 @@ class NodeInterpreter(Interpreter):
|
|||
log.debug('Received from server.js: %s', ok_msg)
|
||||
|
||||
def exec_js(self, func: str) -> None:
|
||||
resp = requests.post(self.url, data=func)
|
||||
resp = requests.post(self.url, data=func, timeout=self.timeout)
|
||||
resp.raise_for_status()
|
||||
|
||||
def get_var(self, name: str) -> Any:
|
||||
resp = requests.post(self.url, data=name)
|
||||
resp = requests.post(self.url, data=name, timeout=self.timeout)
|
||||
resp.raise_for_status()
|
||||
log.debug('NodeJS response: %s', resp.content)
|
||||
return json.loads(resp.content)
|
||||
|
|
|
@ -1,4 +1,31 @@
|
|||
"""Creates a logger"""
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
log = logging.getLogger('aternos')
|
||||
handler = logging.StreamHandler()
|
||||
fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
||||
|
||||
handler.setFormatter(fmt)
|
||||
log.addHandler(handler)
|
||||
|
||||
|
||||
def is_debug() -> bool:
|
||||
"""Is debug logging enabled"""
|
||||
|
||||
return log.level == logging.DEBUG
|
||||
|
||||
|
||||
def set_debug(state: bool) -> None:
|
||||
"""Enable debug logging"""
|
||||
|
||||
if state:
|
||||
set_level(logging.DEBUG)
|
||||
else:
|
||||
set_level(logging.WARNING)
|
||||
|
||||
|
||||
def set_level(level: int) -> None:
|
||||
log.setLevel(level)
|
||||
handler.setLevel(level)
|
||||
|
|
|
@ -4,11 +4,8 @@ import re
|
|||
import json
|
||||
|
||||
import enum
|
||||
|
||||
from typing import Optional
|
||||
from typing import List, Dict, Any
|
||||
|
||||
import requests
|
||||
from typing import List
|
||||
from functools import partial
|
||||
|
||||
from .atconnect import BASE_URL, AJAX_URL
|
||||
from .atconnect import AternosConnect
|
||||
|
@ -24,6 +21,7 @@ from .aterrors import AternosError
|
|||
from .aterrors import ServerStartError
|
||||
|
||||
|
||||
SERVER_URL = f'{AJAX_URL}/server'
|
||||
status_re = re.compile(
|
||||
r'<script>\s*var lastStatus\s*?=\s*?(\{.+?\});?\s*<\/script>'
|
||||
)
|
||||
|
@ -58,27 +56,35 @@ class Status(enum.IntEnum):
|
|||
|
||||
|
||||
class AternosServer:
|
||||
|
||||
"""Class for controlling your Aternos Minecraft server"""
|
||||
|
||||
def __init__(
|
||||
self, servid: str,
|
||||
atconn: AternosConnect,
|
||||
reqinfo: bool = False) -> None:
|
||||
autofetch: bool = False) -> None:
|
||||
"""Class for controlling your Aternos Minecraft server
|
||||
|
||||
Args:
|
||||
servid (str): Unique server IDentifier
|
||||
atconn (AternosConnect):
|
||||
AternosConnect instance with initialized Aternos session
|
||||
reqinfo (bool, optional): Automatically call
|
||||
autofetch (bool, optional): Automatically call
|
||||
`fetch()` to get all info
|
||||
"""
|
||||
|
||||
self.servid = servid
|
||||
self.atconn = atconn
|
||||
|
||||
if reqinfo:
|
||||
self._info = {}
|
||||
|
||||
self.atserver_request = partial(
|
||||
self.atconn.request_cloudflare,
|
||||
reqcookies={
|
||||
'ATERNOS_SERVER': self.servid,
|
||||
}
|
||||
)
|
||||
|
||||
if autofetch:
|
||||
self.fetch()
|
||||
|
||||
def fetch(self) -> None:
|
||||
|
@ -113,6 +119,7 @@ class AternosServer:
|
|||
def start(
|
||||
self,
|
||||
headstart: bool = False,
|
||||
access_credits: bool = False,
|
||||
accepteula: bool = True) -> None:
|
||||
"""Starts a server
|
||||
|
||||
|
@ -120,6 +127,9 @@ class AternosServer:
|
|||
headstart (bool, optional): Start a server in
|
||||
the headstart mode which allows
|
||||
you to skip all queue
|
||||
access_credits (bool, optional):
|
||||
Some new parameter in Aternos API,
|
||||
I don't know what it is
|
||||
accepteula (bool, optional):
|
||||
Automatically accept the Mojang EULA
|
||||
|
||||
|
@ -129,9 +139,12 @@ class AternosServer:
|
|||
"""
|
||||
|
||||
startreq = self.atserver_request(
|
||||
f'{AJAX_URL}/start.php',
|
||||
'GET', params={'headstart': int(headstart)},
|
||||
sendtoken=True
|
||||
f'{SERVER_URL}/start',
|
||||
'GET', params={
|
||||
'headstart': int(headstart),
|
||||
'access-credits': int(access_credits),
|
||||
},
|
||||
sendtoken=True,
|
||||
)
|
||||
startresult = startreq.json()
|
||||
|
||||
|
@ -151,40 +164,40 @@ class AternosServer:
|
|||
"""Confirms server launching"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/confirm.php',
|
||||
'GET', sendtoken=True
|
||||
f'{SERVER_URL}/confirm',
|
||||
'GET', sendtoken=True,
|
||||
)
|
||||
|
||||
def stop(self) -> None:
|
||||
"""Stops the server"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/stop.php',
|
||||
'GET', sendtoken=True
|
||||
f'{SERVER_URL}/stop',
|
||||
'GET', sendtoken=True,
|
||||
)
|
||||
|
||||
def cancel(self) -> None:
|
||||
"""Cancels server launching"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/cancel.php',
|
||||
'GET', sendtoken=True
|
||||
f'{SERVER_URL}/cancel',
|
||||
'GET', sendtoken=True,
|
||||
)
|
||||
|
||||
def restart(self) -> None:
|
||||
"""Restarts the server"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/restart.php',
|
||||
'GET', sendtoken=True
|
||||
f'{SERVER_URL}/restart',
|
||||
'GET', sendtoken=True,
|
||||
)
|
||||
|
||||
def eula(self) -> None:
|
||||
"""Accepts the Mojang EULA"""
|
||||
"""Sends a request to accept the Mojang EULA"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/eula.php',
|
||||
'GET', sendtoken=True
|
||||
f'{SERVER_URL}/accept-eula',
|
||||
'GET', sendtoken=True,
|
||||
)
|
||||
|
||||
def files(self) -> FileManager:
|
||||
|
@ -222,43 +235,40 @@ class AternosServer:
|
|||
|
||||
return PlayersList(lst, self)
|
||||
|
||||
def atserver_request(
|
||||
self, url: str, method: str,
|
||||
params: Optional[Dict[Any, Any]] = None,
|
||||
data: Optional[Dict[Any, Any]] = None,
|
||||
headers: Optional[Dict[Any, Any]] = None,
|
||||
sendtoken: bool = False) -> requests.Response:
|
||||
"""Sends a request to Aternos API
|
||||
with server IDenitfier parameter
|
||||
def set_subdomain(self, value: str) -> None:
|
||||
"""Set a new subdomain for your server
|
||||
(the part before `.aternos.me`)
|
||||
|
||||
Args:
|
||||
url (str): Request URL
|
||||
method (str): Request method, must be GET or POST
|
||||
params (Optional[Dict[Any, Any]], optional): URL parameters
|
||||
data (Optional[Dict[Any, Any]], optional): POST request data,
|
||||
if the method is GET, this dict
|
||||
will be combined with params
|
||||
headers (Optional[Dict[Any, Any]], optional): Custom headers
|
||||
sendtoken (bool, optional): If the ajax and SEC token should be sent
|
||||
|
||||
Returns:
|
||||
API response
|
||||
value (str): Subdomain
|
||||
"""
|
||||
|
||||
return self.atconn.request_cloudflare(
|
||||
url=url, method=method,
|
||||
params=params, data=data,
|
||||
headers=headers,
|
||||
reqcookies={
|
||||
'ATERNOS_SERVER': self.servid
|
||||
},
|
||||
sendtoken=sendtoken
|
||||
self.atserver_request(
|
||||
f'{SERVER_URL}/options/set-subdomain',
|
||||
'GET', params={'subdomain': value},
|
||||
sendtoken=True,
|
||||
)
|
||||
|
||||
def set_motd(self, value: str) -> None:
|
||||
"""Set new Message of the Day
|
||||
(shown below the name in the Minecraft servers list).
|
||||
Formatting with "paragraph sign + code" is supported,
|
||||
see https://minecraft.tools/color-code.php
|
||||
|
||||
Args:
|
||||
value (str): MOTD
|
||||
"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{SERVER_URL}/options/set-motd',
|
||||
'POST', data={'motd': value},
|
||||
sendtoken=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def subdomain(self) -> str:
|
||||
"""Server subdomain
|
||||
(the part of domain before `.aternos.me`)
|
||||
"""Get the server subdomain
|
||||
(the part before `.aternos.me`)
|
||||
|
||||
Returns:
|
||||
Subdomain
|
||||
|
@ -267,25 +277,10 @@ class AternosServer:
|
|||
atdomain = self.domain
|
||||
return atdomain[:atdomain.find('.')]
|
||||
|
||||
@subdomain.setter
|
||||
def subdomain(self, value: str) -> None:
|
||||
"""Set a new subdomain for your server
|
||||
|
||||
Args:
|
||||
value (str): Subdomain
|
||||
"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/options/subdomain.php',
|
||||
'GET', params={'subdomain': value},
|
||||
sendtoken=True
|
||||
)
|
||||
|
||||
@property
|
||||
def motd(self) -> str:
|
||||
"""Server message of the day
|
||||
which is shown below its name
|
||||
in the Minecraft servers list
|
||||
"""Get the server message of the day
|
||||
(shown below its name in Minecraft servers list)
|
||||
|
||||
Returns:
|
||||
MOTD
|
||||
|
@ -293,20 +288,6 @@ class AternosServer:
|
|||
|
||||
return self._info['motd']
|
||||
|
||||
@motd.setter
|
||||
def motd(self, value: str) -> None:
|
||||
"""Set a new message of the day
|
||||
|
||||
Args:
|
||||
value (str): New MOTD
|
||||
"""
|
||||
|
||||
self.atserver_request(
|
||||
f'{AJAX_URL}/options/motd.php',
|
||||
'POST', data={'motd': value},
|
||||
sendtoken=True
|
||||
)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
"""Full server address
|
||||
|
@ -392,11 +373,11 @@ class AternosServer:
|
|||
|
||||
@property
|
||||
def css_class(self) -> str:
|
||||
"""CSS class for
|
||||
server status block
|
||||
on official web site
|
||||
(offline, loading,
|
||||
loading starting, queueing)
|
||||
"""CSS class for the server status element
|
||||
on official web site: offline, online, loading, etc.
|
||||
See https://aternos.dc09.ru/howto/server/#server-info
|
||||
|
||||
In most cases you need `AternosServer.status` instead of this
|
||||
|
||||
Returns:
|
||||
CSS class
|
||||
|
|
13
python_aternos/data/package-lock.json
generated
13
python_aternos/data/package-lock.json
generated
|
@ -4,6 +4,7 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "data",
|
||||
"dependencies": {
|
||||
"vm2": "^3.9.13"
|
||||
}
|
||||
|
@ -28,9 +29,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vm2": {
|
||||
"version": "3.9.13",
|
||||
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz",
|
||||
"integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==",
|
||||
"version": "3.9.19",
|
||||
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz",
|
||||
"integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==",
|
||||
"dependencies": {
|
||||
"acorn": "^8.7.0",
|
||||
"acorn-walk": "^8.2.0"
|
||||
|
@ -55,9 +56,9 @@
|
|||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
|
||||
},
|
||||
"vm2": {
|
||||
"version": "3.9.13",
|
||||
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.13.tgz",
|
||||
"integrity": "sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==",
|
||||
"version": "3.9.19",
|
||||
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz",
|
||||
"integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==",
|
||||
"requires": {
|
||||
"acorn": "^8.7.0",
|
||||
"acorn-walk": "^8.2.0"
|
||||
|
|
Reference in a new issue