From 8a2fb27b817396d0b07047d154c6456fb85578dd Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 23 Apr 2020 15:46:17 -0700 Subject: [PATCH 01/75] refactor: construct bucket paths with service prefixes BREAKING CHANGES: GCSPaths must include a path scheme Before GCSPath would convert `/bucket-name/file` into `gs://bucket-name/file` internally. This leads to ambiguity *(thanks @honnibal) when dealing with regular file-system paths and bucket paths together. Does the path `/foo/bar` point to an absolute file path or a GCS bucket named foo? Now paths **must be** constructed with a path scheme. This allows GCSPath to deal with both types of paths, and is needed for the CLI apps to come. --- gcspath/api.py | 150 +++++++++--------- gcspath/base.py | 67 ++++---- gcspath/file.py | 56 +++---- gcspath/gcs.py | 23 +-- tests/conftest.py | 55 ++++++- tests/test_api.py | 356 ++++++++++++++++++++----------------------- tests/test_base.py | 108 ++++++------- tests/test_client.py | 9 +- 8 files changed, 422 insertions(+), 402 deletions(-) diff --git a/gcspath/api.py b/gcspath/api.py index 03c5c4d..06b9b2f 100644 --- a/gcspath/api.py +++ b/gcspath/api.py @@ -1,3 +1,4 @@ +from typing import cast import os import shutil import tempfile @@ -12,7 +13,7 @@ from google.cloud import storage from . import gcs -from .base import PureGCSPath +from .base import PureGCSPath, PathType from .client import ( BucketClient, BucketEntry, @@ -117,27 +118,29 @@ class GCSPath(Path, PureGCSPath): __slots__ = () _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" - def __truediv__(self, key) -> "GCSPath": + def __truediv__(self: PathType, key: Union[str, PathType]) -> PathType: return super().__truediv__(key) - def __rtruediv__(self, key) -> "GCSPath": - return super().__rtruediv__(key) + def __rtruediv__(self: PathType, key: Union[str, PathType]) -> PathType: + return cast(GCSPath, super().__rtruediv__(key)) - def _init(self, template=None): - super()._init(template) + def _init(self: PathType, template=None): + super()._init(template) # type:ignore if template is None: self._accessor = _gcs_accessor else: self._accessor = template._accessor @classmethod - def from_bucket(cls, bucket_name: str) -> "GCSPath": + def from_bucket(cls: PathType, bucket_name: str) -> "GCSPath": """Helper to convert a bucket name into a GCSPath without needing to add the leading and trailing slashes""" - return GCSPath(f"/{bucket_name}/") + return GCSPath(f"gs://{bucket_name}/") @classmethod - def to_local(cls, blob_path: Union["GCSPath", str], recurse: bool = True) -> Path: + def to_local( + cls: PathType, blob_path: Union["GCSPath", str], recurse: bool = True + ) -> Path: """Get a bucket blob and return a local file cached version of it. The cache is sensitive to the file updated time, and downloads new blobs as they become available.""" @@ -151,9 +154,9 @@ def to_local(cls, blob_path: Union["GCSPath", str], recurse: bool = True) -> Pat if isinstance(blob_path, str): blob_path = GCSPath(blob_path) - cache_blob: Path = cache_folder.absolute() / blob_path.bucket_name / blob_path.key + cache_blob: Path = cache_folder.absolute() / blob_path.root / blob_path.key cache_time: Path = ( - cache_folder.absolute() / blob_path.bucket_name / f"{blob_path.key}.time" + cache_folder.absolute() / blob_path.root / f"{blob_path.key}.time" ) # Keep a cache of downloaded files. Fetch new ones when: # - the file isn't in the cache @@ -184,17 +187,17 @@ def to_local(cls, blob_path: Union["GCSPath", str], recurse: bool = True) -> Pat GCSPath.to_local(blob, recurse=False) return cache_blob - def stat(self): + def stat(self: PathType) -> BucketStat: """ Returns information about this path. The result is looked up at each call to this method """ self._absolute_path_validation() if not self.key: - return None - return super().stat() + raise ValueError("cannot stat a bucket without a key") + return cast(BucketStat, super().stat()) - def exists(self): + def exists(self: PathType) -> bool: """ Whether the path points to an existing Bucket, key or key prefix. """ @@ -203,7 +206,7 @@ def exists(self): return True return self._accessor.exists(self) - def is_dir(self): + def is_dir(self: PathType) -> bool: """ Returns True if the path points to a Bucket or a key prefix, False if it points to a full key path. False is returned if the path doesn’t exist. @@ -214,7 +217,7 @@ def is_dir(self): return True return self._accessor.is_dir(self) - def is_file(self): + def is_file(self: PathType) -> bool: """ Returns True if the path points to a Bucket key, False if it points to Bucket or a key prefix. False is returned if the path doesn’t exist. @@ -228,7 +231,7 @@ def is_file(self): except (gcs_errors.ClientError, FileNotFoundError): return False - def iterdir(self): + def iterdir(self: PathType) -> Generator[PathType, None, None]: """ When the path points to a Bucket or a key prefix, yield path objects of the directory contents @@ -236,14 +239,14 @@ def iterdir(self): self._absolute_path_validation() yield from super().iterdir() - def glob(self, pattern): + def glob(self: PathType, pattern) -> Generator[PathType, None, None]: """ Glob the given relative pattern in the Bucket / key prefix represented by this path, yielding all matching files (of any kind) """ yield from super().glob(pattern) - def rglob(self, pattern): + def rglob(self: PathType, pattern) -> Generator[PathType, None, None]: """ This is like calling GCSPath.glob with "**/" added in front of the given relative pattern. @@ -251,7 +254,7 @@ def rglob(self, pattern): yield from super().rglob(pattern) def open( - self, + self: PathType, mode="r", buffering=DEFAULT_BUFFER_SIZE, encoding=None, @@ -285,7 +288,7 @@ def open( newline=newline, ) - def owner(self): + def owner(self: PathType) -> Optional[str]: """ Returns the name of the user owning the Bucket or key. Similarly to boto3's ObjectSummary owner attribute @@ -295,7 +298,7 @@ def owner(self): raise FileNotFoundError(str(self)) return self._accessor.owner(self) - def resolve(self): + def resolve(self: PathType) -> PathType: """ Return a copy of this path. All paths are absolute in buckets, so no transformation is applied. @@ -303,7 +306,7 @@ def resolve(self): self._absolute_path_validation() return self._accessor.resolve(self) - def rename(self, target): + def rename(self: PathType, target: Union[str, PathType]) -> None: """ Renames this file or Bucket / key prefix / key to the given target. If target exists and is a file, it will be replaced silently if the user @@ -312,20 +315,21 @@ def rename(self, target): another GCSPath object. """ self._absolute_path_validation() - if not isinstance(target, type(self)): - target = type(self)(target) - target._absolute_path_validation() - return super().rename(target) + self_type = type(self) + if not isinstance(target, self_type): + target = self_type(target) + target._absolute_path_validation() # type:ignore + super().rename(target) - def replace(self, target): + def replace(self: PathType, target: Union[str, PathType]) -> None: """ Renames this Bucket / key prefix / key to the given target. If target points to an existing Bucket / key prefix / key, it will be unconditionally replaced. """ - return self.rename(target) + self.rename(target) - def rmdir(self): + def rmdir(self: PathType) -> None: """ Removes this Bucket / key prefix. The Bucket / key prefix must be empty """ @@ -334,9 +338,9 @@ def rmdir(self): raise NotADirectoryError() if not self.is_dir(): raise FileNotFoundError() - return super().rmdir() + super().rmdir() - def samefile(self, other_path): + def samefile(self: PathType, other_path: PathType) -> bool: """ Returns whether this path points to the same Bucket key as other_path, Which can be either a Path object, or a string @@ -348,7 +352,7 @@ def samefile(self, other_path): self.bucket == other_path.bucket and self.key == self.key and self.is_file() ) - def touch(self, mode=0o666, exist_ok=True): + def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True): """ Creates a key at this given path. @@ -360,7 +364,9 @@ def touch(self, mode=0o666, exist_ok=True): raise FileExistsError() self.write_text("") - def mkdir(self, mode=0o777, parents=False, exist_ok=False): + def mkdir( + self: PathType, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + ) -> None: """ Create a path bucket. GCS Service doesn't support folders, therefore the mkdir method will @@ -391,22 +397,22 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False): if not exist_ok: raise - def is_mount(self): + def is_mount(self: PathType) -> bool: return False - def is_symlink(self): + def is_symlink(self: PathType) -> bool: return False - def is_socket(self): + def is_socket(self: PathType) -> bool: return False - def is_fifo(self): + def is_fifo(self: PathType) -> bool: return False # Unsupported operations below here @classmethod - def cwd(cls): + def cwd(cls: PathType): """ cwd class method is unsupported on GCS service GCS don't have this file system action concept @@ -415,7 +421,7 @@ def cwd(cls): raise NotImplementedError(message) @classmethod - def home(cls): + def home(cls: PathType): """ home class method is unsupported on GCS service GCS don't have this file system action concept @@ -423,7 +429,7 @@ def home(cls): message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.home.__qualname__) raise NotImplementedError(message) - def chmod(self, mode): + def chmod(self: PathType, mode): """ chmod method is unsupported on GCS service GCS don't have this file system action concept @@ -431,7 +437,7 @@ def chmod(self, mode): message = self._NOT_SUPPORTED_MESSAGE.format(method=self.chmod.__qualname__) raise NotImplementedError(message) - def expanduser(self): + def expanduser(self: PathType): """ expanduser method is unsupported on GCS service GCS don't have this file system action concept @@ -441,7 +447,7 @@ def expanduser(self): ) raise NotImplementedError(message) - def lchmod(self, mode): + def lchmod(self: PathType, mode): """ lchmod method is unsupported on GCS service GCS don't have this file system action concept @@ -449,7 +455,7 @@ def lchmod(self, mode): message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lchmod.__qualname__) raise NotImplementedError(message) - def group(self): + def group(self: PathType): """ group method is unsupported on GCS service GCS don't have this file system action concept @@ -457,7 +463,7 @@ def group(self): message = self._NOT_SUPPORTED_MESSAGE.format(method=self.group.__qualname__) raise NotImplementedError(message) - def is_block_device(self): + def is_block_device(self: PathType): """ is_block_device method is unsupported on GCS service GCS don't have this file system action concept @@ -467,7 +473,7 @@ def is_block_device(self): ) raise NotImplementedError(message) - def is_char_device(self): + def is_char_device(self: PathType): """ is_char_device method is unsupported on GCS service GCS don't have this file system action concept @@ -477,7 +483,7 @@ def is_char_device(self): ) raise NotImplementedError(message) - def lstat(self): + def lstat(self: PathType): """ lstat method is unsupported on GCS service GCS don't have this file system action concept @@ -485,7 +491,7 @@ def lstat(self): message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lstat.__qualname__) raise NotImplementedError(message) - def symlink_to(self, *args, **kwargs): + def symlink_to(self: PathType, *args, **kwargs): """ symlink_to method is unsupported on GCS service GCS don't have this file system action concept @@ -499,7 +505,7 @@ def symlink_to(self, *args, **kwargs): class BucketsAccessor(_Accessor): """Access data from blob buckets""" - _client: BucketClient + _client: Optional[BucketClient] @property def client(self) -> BucketClient: @@ -509,15 +515,15 @@ def client(self) -> BucketClient: assert self._client is not None, "neither GCS or FS clients are enabled" return self._client - def __init__(self, **kwargs): + def __init__(self, **kwargs) -> None: try: self._client = gcs.BucketClientGCS() except DefaultCredentialsError: self._client = None - def get_blob(self, path: "GCSPath") -> Optional[ClientBlob]: + def get_blob(self, path: PathType) -> Optional[ClientBlob]: """Get the blob associated with a path or return None""" - if not path.bucket_name: + if not path.root: return None bucket = self.client.lookup_bucket(path) if bucket is None: @@ -525,30 +531,30 @@ def get_blob(self, path: "GCSPath") -> Optional[ClientBlob]: key_name = str(path.key) return bucket.get_blob(key_name) - def unlink(self, path: "GCSPath"): + def unlink(self, path: PathType) -> None: """Delete a link to a blob in a bucket.""" bucket = self.client.get_bucket(path) blob: Optional[ClientBlob] = bucket.get_blob(str(path.key)) if blob is None: raise FileNotFoundError(path) - return blob.delete() + blob.delete() - def stat(self, path: "GCSPath"): + def stat(self, path: PathType) -> BucketStat: bucket = self.client.get_bucket(path) blob: Optional[ClientBlob] = bucket.get_blob(str(path.key)) if blob is None: raise FileNotFoundError(path) return BucketStat(size=blob.size, last_modified=blob.updated) - def is_dir(self, path: "GCSPath"): + def is_dir(self, path: PathType) -> bool: if str(path) == path.root: return True if self.get_blob(path) is not None: return False return self.client.is_dir(path) - def exists(self, path: "GCSPath") -> bool: - if not path.bucket_name: + def exists(self, path: PathType) -> bool: + if not path.root: return any(self.client.list_buckets()) try: bucket = self.client.lookup_bucket(path) @@ -565,15 +571,16 @@ def exists(self, path: "GCSPath") -> bool: # Determine if the path exists according to the current adapter return self.client.exists(path) - def scandir(self, path: "GCSPath") -> Generator[BucketEntry, None, None]: + def scandir(self, path: PathType) -> Generator[BucketEntry, None, None]: return self.client.scandir(path, prefix=path.prefix) - def listdir(self, path: "GCSPath") -> List[str]: - return [entry.name for entry in self.scandir(path)] + def listdir(self, path: PathType) -> Generator[str, None, None]: + for entry in self.scandir(path): + yield entry.name def open( - self, - path: "GCSPath", + self: PathType, + path: PathType, *, mode="r", buffering=-1, @@ -590,14 +597,15 @@ def open( newline=newline, ) - def owner(self, path: "GCSPath") -> Optional[str]: + def owner(self, path: PathType) -> Optional[str]: blob: Optional[ClientBlob] = self.get_blob(path) return blob.owner if blob is not None else None - def resolve(self, path: "GCSPath", strict=False): - return GCSPath(os.path.abspath(str(path))) + def resolve(self, path: PathType, strict: bool = False) -> PathType: + path_parts = str(path).replace(path.drive, "") + return GCSPath(f"{path.drive}{os.path.abspath(path_parts)}") - def rename(self, path: "GCSPath", target: "GCSPath"): + def rename(self, path: PathType, target: PathType) -> None: bucket: ClientBucket = self.client.get_bucket(path) target_bucket: ClientBucket = self.client.get_bucket(target) @@ -621,10 +629,10 @@ def rename(self, path: "GCSPath", target: "GCSPath"): for blob in blobs: bucket.delete_blob(blob) - def replace(self, path: "GCSPath", target: "GCSPath"): + def replace(self, path: PathType, target: PathType) -> None: return self.rename(path, target) - def rmdir(self, path: "GCSPath") -> None: + def rmdir(self, path: PathType) -> None: key_name = str(path.key) if path.key is not None else None bucket = self.client.get_bucket(path) blobs = list(self.client.list_blobs(path, prefix=key_name)) @@ -632,7 +640,7 @@ def rmdir(self, path: "GCSPath") -> None: if self.client.is_dir(path): self.client.rmdir(path) - def mkdir(self, path: "GCSPath", mode) -> None: + def mkdir(self, path: PathType, mode) -> None: if not self.client.lookup_bucket(path): self.client.create_bucket(path) diff --git a/gcspath/base.py b/gcspath/base.py index 84c7b05..97455c9 100644 --- a/gcspath/base.py +++ b/gcspath/base.py @@ -1,5 +1,6 @@ from pathlib import PurePath, _PosixFlavour # noqa -from typing import Iterable, Optional, Union, List, Generator +from typing import TypeVar, List, Tuple +import os try: import google.cloud.storage # noqa @@ -9,11 +10,20 @@ has_gcs = False +PathType = TypeVar("PathType") + + class _GCSFlavour(_PosixFlavour): is_supported = bool(has_gcs) def parse_parts(self, parts): drv, root, parsed = super().parse_parts(parts) + if len(parsed) and parsed[0] == "gs:": + if len(parsed) < 2: + raise ValueError("need atleast two parts") + # Restore the + drv = parsed[0] # gs: + root = parsed[1] # bucket_name for part in parsed[1:]: if part == "..": index = parsed.index(part) @@ -39,19 +49,6 @@ class PureGCSPath(PurePath): _flavour = _gcs_flavour __slots__ = () - @classmethod - def from_uri(cls, uri): - """ - from_uri class method create a class instance from url - - >> from gcspath import PureGCSPath - >> PureGCSPath.from_url('gs:///') - << PureGCSPath('/') - """ - if not uri.startswith("gs://"): - raise ValueError("...") - return cls(uri[4:]) - @property def bucket(self): """ @@ -59,46 +56,44 @@ def bucket(self): return a new instance of only the bucket path """ self._absolute_path_validation() - if not self.is_absolute(): - raise ValueError("relative path don't have bucket") - try: - _, bucket, *_ = self.parts - except ValueError: - return None - return type(self)(self._flavour.sep, bucket) + return type(self)(f"{self.drive}//{self.root}") @property def key(self): """ - bucket property + key property return a new instance of only the key path """ self._absolute_path_validation() key = self._flavour.sep.join(self.parts[2:]) - if not key: + if not key or len(self.parts) < 2: return None return type(self)(key) - def as_uri(self): - """ - Return the path as a 'gs' URI. - """ - return super().as_uri() - - @property - def bucket_name(self) -> str: - return str(self.bucket)[1:] - @property def prefix(self) -> str: sep = self._flavour.sep - if not self.key: + a = str(self) + key = self.key + if not key: return "" - key_name = str(self.key) + key_name = str(key) if not key_name.endswith(sep): return key_name + sep return key_name def _absolute_path_validation(self): if not self.is_absolute(): - raise ValueError("relative path has no bucket/key specification") + raise ValueError("relative paths has no bucket/key specification") + + @classmethod + def _format_parsed_parts(cls, drv, root, parts): + # Bucket path "gs://foo/bar" + if drv and root: + return f"{drv}//{root}/" + cls._flavour.join(parts[2:]) + # Absolute path + elif drv or root: + return drv + root + cls._flavour.join(parts[1:]) + else: + # Relative path + return cls._flavour.join(parts) diff --git a/gcspath/file.py b/gcspath/file.py index 38c870e..6e6891a 100644 --- a/gcspath/file.py +++ b/gcspath/file.py @@ -72,10 +72,14 @@ class BucketClientFS(BucketClient): # Root to store file-system buckets as children of root: pathlib.Path + def make_uri(self, path: PureGCSPath): + uri = super().make_uri(path) + return uri.replace("gs://", "file:///") + def full_path(self, path: PureGCSPath) -> pathlib.Path: - if path.bucket_name is None: + if path.root is None: raise ValueError(f"Invalid bucket name for path: {path}") - full_path = self.root.absolute() / path.bucket_name + full_path = self.root.absolute() / path.root if path.key is not None: full_path = full_path / path.key return full_path @@ -102,9 +106,7 @@ def open( newline=None, ): if self.lookup_bucket(path) is None: - raise ClientError( - message=f'bucket "{path.bucket_name}" does not exist', code=404 - ) + raise ClientError(message=f'bucket "{path.root}" does not exist', code=404) full_path = self.full_path(path) if not full_path.exists(): @@ -121,41 +123,39 @@ def open( ) def make_uri(self, path: PureGCSPath) -> str: - if not path.bucket_name: - raise ValueError( - f"cannot make a URI to an invalid bucket: {path.bucket_name}" - ) - result = f"file://{self.root.absolute() / path.bucket_name / path.key}" + if not path.root: + raise ValueError(f"cannot make a URI to an invalid bucket: {path.root}") + result = f"file://{self.root.absolute() / path.root / path.key}" return result def create_bucket(self, path: PureGCSPath) -> ClientBucket: - if not path.bucket_name: - raise ValueError(f"Invalid bucket name: {path.bucket_name}") - bucket_path: pathlib.Path = self.root / path.bucket_name + if not path.root: + raise ValueError(f"Invalid bucket name: {path.root}") + bucket_path: pathlib.Path = self.root / path.root if bucket_path.exists(): raise FileExistsError(f"Bucket already exists at: {bucket_path}") bucket_path.mkdir(parents=True, exist_ok=True) - return ClientBucketFS(str(path.bucket_name), bucket=bucket_path) + return ClientBucketFS(str(path.root), bucket=bucket_path) def delete_bucket(self, path: PureGCSPath) -> None: - bucket_path: pathlib.Path = self.root / str(path.bucket_name) + bucket_path: pathlib.Path = self.root / str(path.root) if bucket_path.exists(): shutil.rmtree(bucket_path) def lookup_bucket(self, path: PureGCSPath) -> Optional[ClientBucketFS]: - if path.bucket_name: - bucket_path: pathlib.Path = self.root / path.bucket_name + if path.root: + bucket_path: pathlib.Path = self.root / path.root if bucket_path.exists(): - return ClientBucketFS(str(path.bucket_name), bucket=bucket_path) + return ClientBucketFS(str(path.root), bucket=bucket_path) return None def get_bucket(self, path: PureGCSPath) -> ClientBucketFS: - if not path.bucket_name: - raise ValueError(f"path has an invalid bucket_name: {path.bucket_name}") - bucket_path: pathlib.Path = self.root / path.bucket_name + if not path.root: + raise ValueError(f"path has an invalid bucket_name: {path.root}") + bucket_path: pathlib.Path = self.root / path.root if bucket_path.is_dir(): - return ClientBucketFS(str(path.bucket_name), bucket=bucket_path) - raise FileNotFoundError(f"Bucket {path.bucket_name} does not exist!") + return ClientBucketFS(str(path.root), bucket=bucket_path) + raise FileNotFoundError(f"Bucket {path.root} does not exist!") def list_buckets(self, **kwargs) -> Generator[ClientBucketFS, None, None]: for f in self.root.glob("*"): @@ -168,13 +168,13 @@ def scandir( prefix: Optional[str] = None, delimiter: Optional[str] = None, ) -> Generator[BucketEntryFS, None, None]: - if path is None or not path.bucket_name: + if path is None or not path.root: for bucket in self.list_buckets(): yield BucketEntryFS(bucket.name, is_dir=True, raw=None) return assert path is not None - assert path.bucket_name is not None - scan_path = self.root / path.bucket_name + assert path.root is not None + scan_path = self.root / path.root if prefix is not None: scan_path = scan_path / prefix for dir_entry in scan_path.glob("*"): @@ -208,9 +208,9 @@ def list_blobs( delimiter: Optional[str] = None, include_dirs: bool = False, ) -> Generator[ClientBlobFS, None, None]: - assert path.bucket_name is not None + assert path.root is not None bucket = self.get_bucket(path) - scan_path = self.root / path.bucket_name + scan_path = self.root / path.root if prefix is not None: scan_path = scan_path / prefix elif prefix is not None: diff --git a/gcspath/gcs.py b/gcspath/gcs.py index 2b6e482..363d49b 100644 --- a/gcspath/gcs.py +++ b/gcspath/gcs.py @@ -71,28 +71,31 @@ def delete_blobs(self, blobs: List[ClientBlobGCS]) -> None: class BucketClientGCS(BucketClient): client: storage.Client = field(default_factory=lambda: storage.Client()) + def make_uri(self, path: PureGCSPath): + return str(path) + def create_bucket(self, path: PureGCSPath) -> ClientBucket: - return self.client.create_bucket(path.bucket_name) + return self.client.create_bucket(path.root) def delete_bucket(self, path: PureGCSPath) -> None: - bucket = self.client.get_bucket(path.bucket_name) + bucket = self.client.get_bucket(path.root) bucket.delete() def lookup_bucket(self, path: PureGCSPath) -> Optional[ClientBucketGCS]: try: - native_bucket = self.client.lookup_bucket(path.bucket_name) + native_bucket = self.client.lookup_bucket(path.root) if native_bucket is not None: - return ClientBucketGCS(str(path.bucket_name), bucket=native_bucket) + return ClientBucketGCS(str(path.root), bucket=native_bucket) except gcs_errors.ClientError: pass return None def get_bucket(self, path: PureGCSPath) -> ClientBucketGCS: try: - native_bucket = self.client.lookup_bucket(path.bucket_name) + native_bucket = self.client.lookup_bucket(path.root) if native_bucket is not None: - return ClientBucketGCS(str(path.bucket_name), bucket=native_bucket) - raise FileNotFoundError(f"Bucket {path.bucket_name} does not exist!") + return ClientBucketGCS(str(path.root), bucket=native_bucket) + raise FileNotFoundError(f"Bucket {path.root} does not exist!") except gcs_errors.ClientError as e: raise ClientError(message=e.message, code=e.code) @@ -107,7 +110,7 @@ def scandir( include_raw: bool = False, ) -> Generator[BucketEntryGCS, None, None]: continuation_token = None - if path is None or not path.bucket_name: + if path is None or not path.root: for bucket in self.list_buckets(): yield BucketEntryGCS(bucket.name, is_dir=True, raw=None) return @@ -161,14 +164,14 @@ def list_blobs( while True: if continuation_token: response = self.client.list_blobs( - path.bucket_name, + path.root, prefix=prefix, delimiter=delimiter, page_token=continuation_token, ) else: response = self.client.list_blobs( - path.bucket_name, prefix=prefix, delimiter=delimiter + path.root, prefix=prefix, delimiter=delimiter ) for page in response.pages: for item in page: diff --git a/tests/conftest.py b/tests/conftest.py index 1d6f71b..8658804 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,27 @@ -import pytest -from pathlib import Path -import tempfile +import os import shutil -from gcspath import use_fs +import tempfile +from pathlib import Path + +import pytest +from gcspath import GCSPath, use_fs, use_fs_cache + + +# TODO: set this up with a service account for the CI +has_credentials = "CI" not in os.environ + +# Which adapters to use +TEST_ADAPTERS = ["gcs", "fs"] if has_credentials else ["fs"] + + +@pytest.fixture() +def bucket() -> str: + return "gcspath-tests-1" + + +@pytest.fixture() +def other_bucket() -> str: + return "gcspath-tests-2" @pytest.fixture() @@ -17,3 +36,31 @@ def with_fs(temp_folder): yield temp_folder # Turn off FS adapter use_fs(False) + + +@pytest.fixture() +def with_adapter(adapter: str, bucket: str, other_bucket: str): + tmp_dir = None + if adapter == "gcs": + # Use GCS (with system credentials) + use_fs(False) + elif adapter == "fs": + # Use local file-system in a temp folder + tmp_dir = tempfile.mkdtemp() + use_fs(tmp_dir) + bucket_one = GCSPath.from_bucket(bucket) + if not bucket_one.exists(): + bucket_one.mkdir() + bucket_two = GCSPath.from_bucket(other_bucket) + if not bucket_two.exists(): + bucket_two.mkdir() + else: + raise ValueError("invalid adapter, nothing is configured") + # execute the test + yield + + if adapter == "fs" and tmp_dir is not None: + # Cleanup fs temp folder + shutil.rmtree(tmp_dir) + use_fs(False) + use_fs_cache(False) diff --git a/tests/test_api.py b/tests/test_api.py index 7d12b57..785c288 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,7 +1,3 @@ -import os -import shutil -import sys -import tempfile from pathlib import Path from time import sleep from uuid import uuid4 @@ -10,7 +6,8 @@ import pytest import spacy from google.auth.exceptions import DefaultCredentialsError -from google.cloud import storage + +from .conftest import TEST_ADAPTERS from gcspath import ( BucketClientFS, @@ -19,7 +16,6 @@ GCSPath, PureGCSPath, clear_fs_cache, - get_fs_cache, get_fs_client, use_fs, use_fs_cache, @@ -29,63 +25,21 @@ # todo: test security and boto config changes # todo: test open method check R/W bytes/unicode # todo(jd): replace global test-bucket with mock or generate buckets and call these e2e tests -bucket = "gcspath-tests-1" -other_bucket = "gcspath-tests-2" - -# TODO: set this up with a service account for the CI -has_credentials = "CI" not in os.environ - -# Which adapters to use -TEST_ADAPTERS = ["gcs", "fs"] if has_credentials else ["fs"] - - -@pytest.fixture() -def bucket_(): - bucket = f"gcspath-test-{uuid4().hex}" - yield bucket - client = storage.Client() - bucket = client.lookup_bucket(bucket) - bucket.delete() - - -def test_path_support(): - assert PureGCSPath in GCSPath.mro() - assert Path in GCSPath.mro() - - -@pytest.fixture() -def with_adapter(adapter: str): - if adapter == "gcs": - # Use GCS (with system credentials) - use_fs(False) - elif adapter == "fs": - # Use local file-system in a temp folder - tmp_dir = tempfile.mkdtemp() - use_fs(tmp_dir) - bucket_one = GCSPath(f"/{bucket}/") - if not bucket_one.exists(): - bucket_one.mkdir() - bucket_two = GCSPath(f"/{other_bucket}/") - if not bucket_two.exists(): - bucket_two.mkdir() - # execute the test - yield - - if adapter == "fs": - # Cleanup fs temp folder - shutil.rmtree(tmp_dir) - use_fs(False) - use_fs_cache(False) + + +def test_api_path_support(): + assert PureGCSPath in GCSPath.mro() # type: ignore + assert Path in GCSPath.mro() # type: ignore @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_is_path_instance(with_adapter): - blob = GCSPath("/fake/blob") +def test_api_is_path_instance(with_adapter): + blob = GCSPath("gs://fake/blob") assert isinstance(blob, Path) @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_path_to_local(with_adapter): +def test_api_path_to_local(with_adapter, bucket: str): root: GCSPath = GCSPath.from_bucket(bucket) / "to_local" foo_blob: GCSPath = root / "foo" foo_blob.write_text("---") @@ -105,8 +59,8 @@ def test_path_to_local(with_adapter): for i in range(3): folder = f"folder_{i}" for j in range(2): - iter_blob: GCSPath = complex_folder / folder / f"file_{j}.txt" - iter_blob.write_text("---") + gcs_blob: GCSPath = complex_folder / folder / f"file_{j}.txt" + gcs_blob.write_text("---") cached_folder: Path = GCSPath.to_local(complex_folder) assert isinstance(cached_folder, Path) @@ -116,7 +70,7 @@ def test_path_to_local(with_adapter): for i in range(3): folder = f"folder_{i}" for j in range(2): - iter_blob: GCSPath = cached_folder / folder / f"file_{j}.txt" + iter_blob: Path = cached_folder / folder / f"file_{j}.txt" assert iter_blob.exists() assert iter_blob.read_text() == "---" @@ -125,8 +79,8 @@ def test_path_to_local(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_use_fs_cache(with_adapter, with_fs: str): - path = GCSPath(f"/{bucket}/directory/foo.txt") +def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): + path = GCSPath(f"gs://{bucket}/directory/foo.txt") path.write_text("---") assert isinstance(path, GCSPath) with pytest.raises(ValueError): @@ -155,42 +109,45 @@ def test_use_fs_cache(with_adapter, with_fs: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_stat(with_adapter): +def test_api_stat(with_adapter, bucket: str): path = GCSPath("fake-bucket-1234-0987/fake-key") with pytest.raises(ValueError): path.stat() - path = GCSPath(f"/{bucket}/foo.txt") + path = GCSPath(f"gs://{bucket}/foo.txt") path.write_text("a-a-a-a-a-a-a") stat = path.stat() assert isinstance(stat, BucketStat) assert stat.size > 0 assert stat.last_modified > 0 - assert GCSPath("/test-bucket").stat() is None + with pytest.raises(ValueError): + assert GCSPath(f"gs://{bucket}").stat() + with pytest.raises(FileNotFoundError): + assert GCSPath(f"gs://{bucket}/nonexistant_file.txt").stat() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_resolve(with_adapter): - path = GCSPath("/fake-bucket/fake-key") +def test_api_resolve(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/fake-key") assert path.resolve() == path - path = GCSPath("/fake-bucket/dir/../fake-key") - assert path.resolve() == GCSPath("/fake-bucket/fake-key") + path = GCSPath(f"gs://{bucket}/dir/../fake-key") + assert path.resolve() == GCSPath(f"gs://{bucket}/fake-key") @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_exists(with_adapter): +def test_api_exists(with_adapter, bucket: str): path = GCSPath("./fake-key") with pytest.raises(ValueError): path.exists() # GCS buckets are globally unique, "test-bucket" exists so this # raises an access error. - assert GCSPath("/test-bucket/fake-key").exists() is False + assert GCSPath("gs://test-bucket/fake-key").exists() is False # invalid bucket name - assert GCSPath("/unknown-bucket-name-123987519875419").exists() is False + assert GCSPath("gs://unknown-bucket-name-123987519875419").exists() is False # valid bucket with invalid object - assert GCSPath(f"/{bucket}/not_found_lol_nice.txt").exists() is False + assert GCSPath(f"gs://{bucket}/not_found_lol_nice.txt").exists() is False - path = GCSPath(f"/{bucket}/directory/foo.txt") + path = GCSPath(f"gs://{bucket}/directory/foo.txt") path.write_text("---") assert path.exists() for parent in path.parents: @@ -198,44 +155,44 @@ def test_exists(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_glob(with_adapter): +def test_api_glob(with_adapter, bucket: str): for i in range(3): - path = GCSPath(f"/{bucket}/glob/{i}.file") + path = GCSPath(f"gs://{bucket}/glob/{i}.file") path.write_text("---") for i in range(2): - path = GCSPath(f"/{bucket}/glob/{i}/dir/file.txt") + path = GCSPath(f"gs://{bucket}/glob/{i}/dir/file.txt") path.write_text("---") - assert list(GCSPath(f"/{bucket}/glob/").glob("*.test")) == [] - assert sorted(list(GCSPath(f"/{bucket}/glob/").glob("*.file"))) == [ - GCSPath(f"/{bucket}/glob/0.file"), - GCSPath(f"/{bucket}/glob/1.file"), - GCSPath(f"/{bucket}/glob/2.file"), + assert list(GCSPath(f"gs://{bucket}/glob/").glob("*.test")) == [] + assert sorted(list(GCSPath(f"gs://{bucket}/glob/").glob("*.file"))) == [ + GCSPath(f"gs://{bucket}/glob/0.file"), + GCSPath(f"gs://{bucket}/glob/1.file"), + GCSPath(f"gs://{bucket}/glob/2.file"), ] - assert list(GCSPath(f"/{bucket}/glob/0/").glob("*/*.txt")) == [ - GCSPath(f"/{bucket}/glob/0/dir/file.txt"), + assert list(GCSPath(f"gs://{bucket}/glob/0/").glob("*/*.txt")) == [ + GCSPath(f"gs://{bucket}/glob/0/dir/file.txt"), ] - assert sorted(GCSPath.from_uri(f"gs://{bucket}").glob("*lob/")) == [ - GCSPath(f"/{bucket}/glob"), + assert sorted(GCSPath(f"gs://{bucket}").glob("*lob/")) == [ + GCSPath(f"gs://{bucket}/glob"), ] # Recursive matches - assert sorted(list(GCSPath(f"/{bucket}/glob/").glob("**/*.txt"))) == [ - GCSPath(f"/{bucket}/glob/0/dir/file.txt"), - GCSPath(f"/{bucket}/glob/1/dir/file.txt"), + assert sorted(list(GCSPath(f"gs://{bucket}/glob/").glob("**/*.txt"))) == [ + GCSPath(f"gs://{bucket}/glob/0/dir/file.txt"), + GCSPath(f"gs://{bucket}/glob/1/dir/file.txt"), ] # rglob adds the **/ for you - assert sorted(list(GCSPath(f"/{bucket}/glob/").rglob("*.txt"))) == [ - GCSPath(f"/{bucket}/glob/0/dir/file.txt"), - GCSPath(f"/{bucket}/glob/1/dir/file.txt"), + assert sorted(list(GCSPath(f"gs://{bucket}/glob/").rglob("*.txt"))) == [ + GCSPath(f"gs://{bucket}/glob/0/dir/file.txt"), + GCSPath(f"gs://{bucket}/glob/1/dir/file.txt"), ] @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_unlink_path(with_adapter): - path = GCSPath(f"/{bucket}/unlink/404.txt") +def test_api_unlink_path(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/unlink/404.txt") with pytest.raises(FileNotFoundError): path.unlink() - path = GCSPath(f"/{bucket}/unlink/foo.txt") + path = GCSPath(f"gs://{bucket}/unlink/foo.txt") path.write_text("---") assert path.exists() path.unlink() @@ -243,8 +200,8 @@ def test_unlink_path(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_is_dir(with_adapter): - path = GCSPath(f"/{bucket}/is_dir/subfolder/another/my.file") +def test_api_is_dir(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/is_dir/subfolder/another/my.file") path.write_text("---") assert path.is_dir() is False for parent in path.parents: @@ -252,8 +209,8 @@ def test_is_dir(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_is_file(with_adapter): - path = GCSPath(f"/{bucket}/is_file/subfolder/another/my.file") +def test_api_is_file(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/is_file/subfolder/another/my.file") path.write_text("---") # The full file is a file assert path.is_file() is True @@ -263,36 +220,37 @@ def test_is_file(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_iterdir(with_adapter): +def test_api_iterdir(with_adapter, bucket: str): # (n) files in a folder for i in range(2): - path = GCSPath(f"/{bucket}/iterdir/{i}.file") + path = GCSPath(f"gs://{bucket}/iterdir/{i}.file") path.write_text("---") # 1 file in a subfolder - path = GCSPath(f"/{bucket}/iterdir/sub/file.txt") + path = GCSPath(f"gs://{bucket}/iterdir/sub/file.txt") path.write_text("---") - path = GCSPath(f"/{bucket}/iterdir/") - assert sorted(path.iterdir()) == [ - GCSPath(f"/{bucket}/iterdir/0.file"), - GCSPath(f"/{bucket}/iterdir/1.file"), - GCSPath(f"/{bucket}/iterdir/sub"), + path = GCSPath(f"gs://{bucket}/iterdir/") + check = sorted(path.iterdir()) + assert check == [ + GCSPath(f"gs://{bucket}/iterdir/0.file"), + GCSPath(f"gs://{bucket}/iterdir/1.file"), + GCSPath(f"gs://{bucket}/iterdir/sub"), ] @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_iterdir_pipstore(with_adapter): - path = GCSPath.from_bucket(bucket) / "iterdir/pipstore/prodigy/prodigy.whl" +def test_api_iterdir_pipstore(with_adapter, bucket: str): + path = GCSPath.from_bucket(bucket) / "iterdir_pipstore/prodigy/prodigy.whl" path.write_bytes(b"---") - path = GCSPath.from_bucket(bucket) / "iterdir/pipstore" + path = GCSPath.from_bucket(bucket) / "iterdir_pipstore" res = [e.name for e in sorted(path.iterdir())] assert res == ["prodigy"] @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_open_for_read(with_adapter): - path = GCSPath(f"/{bucket}/read/file.txt") +def test_api_open_for_read(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/read/file.txt") path.write_text("---") with path.open() as file_obj: assert file_obj.read() == "---" @@ -300,19 +258,19 @@ def test_open_for_read(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_open_for_write(with_adapter): - path = GCSPath(f"/{bucket}/write/file.txt") +def test_api_open_for_write(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/write/file.txt") with path.open(mode="w") as file_obj: file_obj.write("---") file_obj.writelines(["---"]) - path = GCSPath(f"/{bucket}/write/file.txt") + path = GCSPath(f"gs://{bucket}/write/file.txt") with path.open() as file_obj: assert file_obj.read() == "------" @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_open_binary_read(with_adapter): - path = GCSPath(f"/{bucket}/read_binary/file.txt") +def test_api_open_binary_read(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/read_binary/file.txt") path.write_bytes(b"---") with path.open(mode="rb") as file_obj: assert file_obj.readlines() == [b"---"] @@ -322,8 +280,8 @@ def test_open_binary_read(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_readwrite_text(with_adapter): - path = GCSPath(f"/{bucket}/write_text/file.txt") +def test_api_readwrite_text(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/write_text/file.txt") path.write_text("---") with path.open() as file_obj: assert file_obj.read() == "---" @@ -331,15 +289,15 @@ def test_readwrite_text(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_readwrite_bytes(with_adapter): - path = GCSPath(f"/{bucket}/write_bytes/file.txt") +def test_api_readwrite_bytes(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/write_bytes/file.txt") path.write_bytes(b"---") assert path.read_bytes() == b"---" @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_readwrite_lines(with_adapter): - path = GCSPath(f"/{bucket}/write_text/file.txt") +def test_api_readwrite_lines(with_adapter, bucket: str): + path = GCSPath(f"gs://{bucket}/write_text/file.txt") with path.open("w") as file_obj: file_obj.writelines(["---"]) with path.open("r") as file_obj: @@ -349,12 +307,12 @@ def test_readwrite_lines(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_owner(with_adapter): +def test_api_owner(with_adapter, bucket: str): # Raises for invalid file with pytest.raises(FileNotFoundError): - GCSPath(f"/{bucket}/write_text/not_a_valid_blob").owner() + GCSPath(f"gs://{bucket}/write_text/not_a_valid_blob").owner() - path = GCSPath(f"/{bucket}/write_text/file.txt") + path = GCSPath(f"gs://{bucket}/write_text/file.txt") path.write_text("---") # TODO: How to set file owner to non-None in GCS? Then assert here. # @@ -368,130 +326,140 @@ def test_owner(with_adapter): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_rename_files_in_bucket(with_adapter): +def test_api_rename_files_in_bucket(with_adapter, bucket: str): # Rename a single file - GCSPath(f"/{bucket}/rename/file.txt").write_text("---") - GCSPath(f"/{bucket}/rename/file.txt").rename(f"/{bucket}/rename/other.txt") - assert not GCSPath(f"/{bucket}/rename/file.txt").exists() - assert GCSPath(f"/{bucket}/rename/other.txt").is_file() + GCSPath(f"gs://{bucket}/rename/file.txt").write_text("---") + GCSPath(f"gs://{bucket}/rename/file.txt").rename(f"gs://{bucket}/rename/other.txt") + assert not GCSPath(f"gs://{bucket}/rename/file.txt").exists() + assert GCSPath(f"gs://{bucket}/rename/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_rename_files_across_buckets(with_adapter): +def test_api_rename_files_across_buckets(with_adapter, bucket: str, other_bucket: str): # Rename a single file across buckets - GCSPath(f"/{bucket}/rename/file.txt").write_text("---") - GCSPath(f"/{bucket}/rename/file.txt").rename(f"/{other_bucket}/rename/other.txt") - assert not GCSPath(f"/{bucket}/rename/file.txt").exists() - assert GCSPath(f"/{other_bucket}/rename/other.txt").is_file() + GCSPath(f"gs://{bucket}/rename/file.txt").write_text("---") + GCSPath(f"gs://{bucket}/rename/file.txt").rename( + f"gs://{other_bucket}/rename/other.txt" + ) + assert not GCSPath(f"gs://{bucket}/rename/file.txt").exists() + assert GCSPath(f"gs://{other_bucket}/rename/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_rename_folders_in_bucket(with_adapter): +def test_api_rename_folders_in_bucket(with_adapter, bucket: str): # Rename a folder in the same bucket - GCSPath(f"/{bucket}/rename/folder/one.txt").write_text("---") - GCSPath(f"/{bucket}/rename/folder/two.txt").write_text("---") - path = GCSPath(f"/{bucket}/rename/folder/") - new_path = GCSPath(f"/{bucket}/rename/other/") + GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") + GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") + path = GCSPath(f"gs://{bucket}/rename/folder/") + new_path = GCSPath(f"gs://{bucket}/rename/other/") path.rename(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"/{bucket}/rename/other/one.txt").is_file() - assert GCSPath(f"/{bucket}/rename/other/two.txt").is_file() + assert GCSPath(f"gs://{bucket}/rename/other/one.txt").is_file() + assert GCSPath(f"gs://{bucket}/rename/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_rename_folders_across_buckets(with_adapter): +def test_api_rename_folders_across_buckets( + with_adapter, bucket: str, other_bucket: str +): # Rename a folder across buckets - GCSPath(f"/{bucket}/rename/folder/one.txt").write_text("---") - GCSPath(f"/{bucket}/rename/folder/two.txt").write_text("---") - path = GCSPath(f"/{bucket}/rename/folder/") - new_path = GCSPath(f"/{other_bucket}/rename/other/") + GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") + GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") + path = GCSPath(f"gs://{bucket}/rename/folder/") + new_path = GCSPath(f"gs://{other_bucket}/rename/other/") path.rename(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"/{other_bucket}/rename/other/one.txt").is_file() - assert GCSPath(f"/{other_bucket}/rename/other/two.txt").is_file() + assert GCSPath(f"gs://{other_bucket}/rename/other/one.txt").is_file() + assert GCSPath(f"gs://{other_bucket}/rename/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_replace_files_in_bucket(with_adapter): +def test_api_replace_files_in_bucket(with_adapter, bucket: str): # replace a single file - GCSPath(f"/{bucket}/replace/file.txt").write_text("---") - GCSPath(f"/{bucket}/replace/file.txt").replace(f"/{bucket}/replace/other.txt") - assert not GCSPath(f"/{bucket}/replace/file.txt").exists() - assert GCSPath(f"/{bucket}/replace/other.txt").is_file() + GCSPath(f"gs://{bucket}/replace/file.txt").write_text("---") + GCSPath(f"gs://{bucket}/replace/file.txt").replace( + f"gs://{bucket}/replace/other.txt" + ) + assert not GCSPath(f"gs://{bucket}/replace/file.txt").exists() + assert GCSPath(f"gs://{bucket}/replace/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_replace_files_across_buckets(with_adapter): +def test_api_replace_files_across_buckets(with_adapter, bucket: str, other_bucket: str): # Rename a single file across buckets - GCSPath(f"/{bucket}/replace/file.txt").write_text("---") - GCSPath(f"/{bucket}/replace/file.txt").replace(f"/{other_bucket}/replace/other.txt") - assert not GCSPath(f"/{bucket}/replace/file.txt").exists() - assert GCSPath(f"/{other_bucket}/replace/other.txt").is_file() + GCSPath(f"gs://{bucket}/replace/file.txt").write_text("---") + GCSPath(f"gs://{bucket}/replace/file.txt").replace( + f"gs://{other_bucket}/replace/other.txt" + ) + assert not GCSPath(f"gs://{bucket}/replace/file.txt").exists() + assert GCSPath(f"gs://{other_bucket}/replace/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_replace_folders_in_bucket(with_adapter): +def test_api_replace_folders_in_bucket(with_adapter, bucket: str): # Rename a folder in the same bucket - GCSPath(f"/{bucket}/replace/folder/one.txt").write_text("---") - GCSPath(f"/{bucket}/replace/folder/two.txt").write_text("---") - path = GCSPath(f"/{bucket}/replace/folder/") - new_path = GCSPath(f"/{bucket}/replace/other/") + GCSPath(f"gs://{bucket}/replace/folder/one.txt").write_text("---") + GCSPath(f"gs://{bucket}/replace/folder/two.txt").write_text("---") + path = GCSPath(f"gs://{bucket}/replace/folder/") + new_path = GCSPath(f"gs://{bucket}/replace/other/") path.replace(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"/{bucket}/replace/other/one.txt").is_file() - assert GCSPath(f"/{bucket}/replace/other/two.txt").is_file() + assert GCSPath(f"gs://{bucket}/replace/other/one.txt").is_file() + assert GCSPath(f"gs://{bucket}/replace/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_replace_folders_across_buckets(with_adapter): +def test_api_replace_folders_across_buckets( + with_adapter, bucket: str, other_bucket: str +): # Rename a folder across buckets - GCSPath(f"/{bucket}/replace/folder/one.txt").write_text("---") - GCSPath(f"/{bucket}/replace/folder/two.txt").write_text("---") - path = GCSPath(f"/{bucket}/replace/folder/") - new_path = GCSPath(f"/{other_bucket}/replace/other/") + GCSPath(f"gs://{bucket}/replace/folder/one.txt").write_text("---") + GCSPath(f"gs://{bucket}/replace/folder/two.txt").write_text("---") + path = GCSPath(f"gs://{bucket}/replace/folder/") + new_path = GCSPath(f"gs://{other_bucket}/replace/other/") path.replace(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"/{other_bucket}/replace/other/one.txt").is_file() - assert GCSPath(f"/{other_bucket}/replace/other/two.txt").is_file() + assert GCSPath(f"gs://{other_bucket}/replace/other/one.txt").is_file() + assert GCSPath(f"gs://{other_bucket}/replace/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_rmdir(with_adapter): - GCSPath(f"/{bucket}/rmdir/one.txt").write_text("---") - GCSPath(f"/{bucket}/rmdir/folder/two.txt").write_text("---") - path = GCSPath(f"/{bucket}/rmdir/") +def test_api_rmdir(with_adapter, bucket: str): + GCSPath(f"gs://{bucket}/rmdir/one.txt").write_text("---") + GCSPath(f"gs://{bucket}/rmdir/folder/two.txt").write_text("---") + path = GCSPath(f"gs://{bucket}/rmdir/") path.rmdir() - assert not GCSPath(f"/{bucket}/rmdir/one.txt").is_file() - assert not GCSPath(f"/{bucket}/rmdir/other/two.txt").is_file() + assert not GCSPath(f"gs://{bucket}/rmdir/one.txt").is_file() + assert not GCSPath(f"gs://{bucket}/rmdir/other/two.txt").is_file() assert not path.exists() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_mkdir(with_adapter): +def test_api_mkdir(with_adapter, bucket: str): bucket_name = f"gcspath-e2e-test-{uuid4().hex}" # Create a bucket - GCSPath(f"/{bucket_name}/").mkdir() + GCSPath(f"gs://{bucket_name}/").mkdir() # Does not assert if it already exists - GCSPath(f"/{bucket_name}/").mkdir(exist_ok=True) + GCSPath(f"gs://{bucket_name}/").mkdir(exist_ok=True) with pytest.raises(FileExistsError): - GCSPath(f"/{bucket_name}/").mkdir(exist_ok=False) + GCSPath(f"gs://{bucket_name}/").mkdir(exist_ok=False) # with pytest.raises(FileNotFoundError): # GCSPath("/test-second-bucket/test-directory/file.name").mkdir() # GCSPath("/test-second-bucket/test-directory/file.name").mkdir(parents=True) - assert GCSPath(f"/{bucket_name}/").exists() + assert GCSPath(f"gs://{bucket_name}/").exists() # remove the bucket # client = storage.Client() # bucket = client.lookup_bucket(bucket_name) # bucket.delete() - GCSPath(f"/{bucket_name}/").rmdir() + GCSPath(f"gs://{bucket_name}/").rmdir() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_ignore_extension(with_adapter): +def test_api_ignore_extension(with_adapter, bucket: str): """The smart_open library does automatic decompression based on the filename. We disable that to avoid errors, e.g. if you have a .tar.gz file that isn't gzipped.""" @@ -502,7 +470,7 @@ def test_ignore_extension(with_adapter): assert again is not None -def test_use_fs(with_fs: Path): +def test_api_use_fs(with_fs: Path): assert get_fs_client() is None # Use the default path use_fs() @@ -531,7 +499,7 @@ def test_use_fs(with_fs: Path): @mock.patch("gcspath.gcs.BucketClientGCS", side_effect=DefaultCredentialsError()) -def test_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): +def test_api_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): accessor = BucketsAccessor() # Accessing the client lazily throws with no GCS or FS adapters configured with pytest.raises(AssertionError): @@ -542,18 +510,18 @@ def test_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): assert isinstance(accessor.client, BucketClientFS) -def test_export_spacy_model(temp_folder): +def test_api_export_spacy_model(temp_folder): """spaCy model loading is one of the things we need to support""" use_fs(temp_folder) - bucket = GCSPath("/my-bucket/") + bucket = GCSPath("gs://my-bucket/") bucket.mkdir(exist_ok=True) model = spacy.blank("en") - output_path = GCSPath("/my-bucket/models/my_model") + output_path = GCSPath("gs://my-bucket/models/my_model") model.to_disk(output_path) sorted_entries = sorted([str(p) for p in output_path.glob("*")]) expected_entries = [ - "/my-bucket/models/my_model/meta.json", - "/my-bucket/models/my_model/tokenizer", - "/my-bucket/models/my_model/vocab", + "gs://my-bucket/models/my_model/meta.json", + "gs://my-bucket/models/my_model/tokenizer", + "gs://my-bucket/models/my_model/vocab", ] assert sorted_entries == expected_entries diff --git a/tests/test_base.py b/tests/test_base.py index c129bd6..541de78 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -7,81 +7,81 @@ from gcspath import GCSPath, PureGCSPath -def test_not_supported(monkeypatch): +def test_base_not_supported(monkeypatch): monkeypatch.setattr(GCSPath._flavour, "is_supported", False) with pytest.raises(NotImplementedError): GCSPath() -def test_cwd(): +def test_base_cwd(): with pytest.raises(NotImplementedError): GCSPath.cwd() -def test_home(): +def test_base_home(): with pytest.raises(NotImplementedError): GCSPath.home() -def test_chmod(): +def test_base_chmod(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.chmod(0o666) -def test_lchmod(): +def test_base_lchmod(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.lchmod(0o666) -def test_group(): +def test_base_group(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.group() -def test_is_mount(): +def test_base_is_mount(): assert not GCSPath("/fake-bucket/fake-key").is_mount() -def test_is_symlink(): +def test_base_is_symlink(): assert not GCSPath("/fake-bucket/fake-key").is_symlink() -def test_is_socket(): +def test_base_is_socket(): assert not GCSPath("/fake-bucket/fake-key").is_socket() -def test_is_fifo(): +def test_base_is_fifo(): assert not GCSPath("/fake-bucket/fake-key").is_fifo() -def test_is_block_device(): +def test_base_is_block_device(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.is_block_device() -def test_is_char_device(): +def test_base_is_char_device(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.is_char_device() -def test_lstat(): +def test_base_lstat(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.lstat() -def test_symlink_to(): +def test_base_symlink_to(): path = GCSPath("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.symlink_to("file_name") -def test_paths_of_a_different_flavour(): +def test_base_paths_of_a_different_flavour(): with pytest.raises(TypeError): PureGCSPath("/bucket/key") < PurePosixPath("/bucket/key") @@ -89,35 +89,37 @@ def test_paths_of_a_different_flavour(): PureWindowsPath("/bucket/key") > PureGCSPath("/bucket/key") -def test_repr(): - assert repr(PureGCSPath("setup.py")) == "PureGCSPath('setup.py')" - assert str(PureGCSPath("setup.py")) == "setup.py" - assert bytes(PureGCSPath("setup.py")) == b"setup.py" - assert PureGCSPath("/usr/bin").as_posix() == "/usr/bin" +def test_base_repr(): + a = PureGCSPath("/var/tests/fake") + f = a.prefix + assert a.as_posix() == "/var/tests/fake" + assert repr(PureGCSPath("fake_file.txt")) == "PureGCSPath('fake_file.txt')" + assert str(PureGCSPath("fake_file.txt")) == "fake_file.txt" + assert bytes(PureGCSPath("fake_file.txt")) == b"fake_file.txt" @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") -def test_fspath(): - assert os.fspath(PureGCSPath("/usr/bin")) == "/usr/bin" +def test_base_fspath(): + assert os.fspath(PureGCSPath("/var/tests/fake")) == "/var/tests/fake" -def test_join_strs(): +def test_base_join_strs(): assert PureGCSPath("foo", "some/path", "bar") == PureGCSPath("foo/some/path/bar") -def test_join_paths(): +def test_base_join_paths(): assert PureGCSPath(Path("foo"), Path("bar")) == PureGCSPath("foo/bar") -def test_empty(): +def test_base_empty(): assert PureGCSPath() == PureGCSPath(".") -def test_absolute_paths(): +def test_base_absolute_paths(): assert PureGCSPath("/etc", "/usr", "lib64") == PureGCSPath("/usr/lib64") -def test_slashes_single_double_dots(): +def test_base_slashes_single_double_dots(): assert PureGCSPath("foo//bar") == PureGCSPath("foo/bar") assert PureGCSPath("foo/./bar") == PureGCSPath("foo/bar") assert PureGCSPath("foo/../bar") == PureGCSPath("bar") @@ -125,23 +127,23 @@ def test_slashes_single_double_dots(): assert PureGCSPath("foo", "../bar") == PureGCSPath("bar") -def test_operators(): +def test_base_operators(): assert PureGCSPath("/etc") / "init.d" / "apache2" == PureGCSPath( "/etc/init.d/apache2" ) - assert "/usr" / PureGCSPath("bin") == PureGCSPath("/usr/bin") + assert "/var" / PureGCSPath("tests") / "fake" == PureGCSPath("/var/tests/fake") -def test_parts(): +def test_base_parts(): + assert PureGCSPath("../bar").parts == ("..", "bar") assert PureGCSPath("foo//bar").parts == ("foo", "bar") assert PureGCSPath("foo/./bar").parts == ("foo", "bar") assert PureGCSPath("foo/../bar").parts == ("bar",) - assert PureGCSPath("../bar").parts == ("..", "bar") assert PureGCSPath("foo", "../bar").parts == ("bar",) assert PureGCSPath("/foo/bar").parts == ("/", "foo", "bar") -def test_drive(): +def test_base_drive(): assert PureGCSPath("foo//bar").drive == "" assert PureGCSPath("foo/./bar").drive == "" assert PureGCSPath("foo/../bar").drive == "" @@ -150,7 +152,7 @@ def test_drive(): assert PureGCSPath("/foo/bar").drive == "" -def test_root(): +def test_base_root(): assert PureGCSPath("foo//bar").root == "" assert PureGCSPath("foo/./bar").root == "" assert PureGCSPath("foo/../bar").root == "" @@ -159,7 +161,7 @@ def test_root(): assert PureGCSPath("/foo/bar").root == "/" -def test_anchor(): +def test_base_anchor(): assert PureGCSPath("foo//bar").anchor == "" assert PureGCSPath("foo/./bar").anchor == "" assert PureGCSPath("foo/../bar").anchor == "" @@ -168,7 +170,7 @@ def test_anchor(): assert PureGCSPath("/foo/bar").anchor == "/" -def test_parents(): +def test_base_parents(): assert tuple(PureGCSPath("foo//bar").parents) == ( PureGCSPath("foo"), PureGCSPath("."), @@ -186,7 +188,7 @@ def test_parents(): ) -def test_parent(): +def test_base_parent(): assert PureGCSPath("foo//bar").parent == PureGCSPath("foo") assert PureGCSPath("foo/./bar").parent == PureGCSPath("foo") assert PureGCSPath("foo/../bar").parent == PureGCSPath(".") @@ -197,45 +199,45 @@ def test_parent(): assert PureGCSPath("/").parent == PureGCSPath("/") -def test_name(): - assert PureGCSPath("my/library/setup.py").name == "setup.py" +def test_base_name(): + assert PureGCSPath("my/library/fake_file.txt").name == "fake_file.txt" -def test_suffix(): - assert PureGCSPath("my/library/setup.py").suffix == ".py" +def test_base_suffix(): + assert PureGCSPath("my/library/fake_file.txt").suffix == ".txt" assert PureGCSPath("my/library.tar.gz").suffix == ".gz" assert PureGCSPath("my/library").suffix == "" -def test_suffixes(): +def test_base_suffixes(): assert PureGCSPath("my/library.tar.gar").suffixes == [".tar", ".gar"] assert PureGCSPath("my/library.tar.gz").suffixes == [".tar", ".gz"] assert PureGCSPath("my/library").suffixes == [] -def test_stem(): +def test_base_stem(): assert PureGCSPath("my/library.tar.gar").stem == "library.tar" assert PureGCSPath("my/library.tar").stem == "library" assert PureGCSPath("my/library").stem == "library" -def test_uri(): +def test_base_uri(): assert PureGCSPath("/etc/passwd").as_uri() == "gs://etc/passwd" assert PureGCSPath("/etc/init.d/apache2").as_uri() == "gs://etc/init.d/apache2" assert PureGCSPath("/bucket/key").as_uri() == "gs://bucket/key" -def test_absolute(): +def test_base_absolute(): assert PureGCSPath("/a/b").is_absolute() assert not PureGCSPath("a/b").is_absolute() -def test_reserved(): +def test_base_reserved(): assert not PureGCSPath("/a/b").is_reserved() assert not PureGCSPath("a/b").is_reserved() -def test_joinpath(): +def test_base_joinpath(): assert PureGCSPath("/etc").joinpath("passwd") == PureGCSPath("/etc/passwd") assert PureGCSPath("/etc").joinpath(PureGCSPath("passwd")) == PureGCSPath( "/etc/passwd" @@ -245,7 +247,7 @@ def test_joinpath(): ) -def test_match(): +def test_base_match(): assert PureGCSPath("a/b.py").match("*.py") assert PureGCSPath("/a/b/c.py").match("b/*.py") assert not PureGCSPath("/a/b/c.py").match("a/*.py") @@ -254,7 +256,7 @@ def test_match(): assert not PureGCSPath("a/b.py").match("*.Py") -def test_relative_to(): +def test_base_relative_to(): gcs_path = PureGCSPath("/etc/passwd") assert gcs_path.relative_to("/") == PureGCSPath("etc/passwd") assert gcs_path.relative_to("/etc") == PureGCSPath("passwd") @@ -262,15 +264,17 @@ def test_relative_to(): gcs_path.relative_to("/usr") -def test_with_name(): +def test_base_with_name(): gcs_path = PureGCSPath("/Downloads/pathlib.tar.gz") - assert gcs_path.with_name("setup.py") == PureGCSPath("/Downloads/setup.py") + assert gcs_path.with_name("fake_file.txt") == PureGCSPath( + "/Downloads/fake_file.txt" + ) gcs_path = PureGCSPath("/") with pytest.raises(ValueError): - gcs_path.with_name("setup.py") + gcs_path.with_name("fake_file.txt") -def test_with_suffix(): +def test_base_with_suffix(): gcs_path = PureGCSPath("/Downloads/pathlib.tar.gz") assert gcs_path.with_suffix(".bz2") == PureGCSPath("/Downloads/pathlib.tar.bz2") gcs_path = PureGCSPath("README") diff --git a/tests/test_client.py b/tests/test_client.py index 59db353..2e09259 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,18 +1,13 @@ -import os -import shutil -import tempfile from pathlib import Path -from typing import Generator -import pytest from gcspath import PureGCSPath -from gcspath.file import BucketClientFS, ClientBlobFS, ClientBucketFS +from gcspath.file import BucketClientFS def test_client_create_bucket(temp_folder: Path): bucket_target = temp_folder / "foo" assert bucket_target.exists() is False cl = BucketClientFS(temp_folder) - cl.create_bucket(PureGCSPath("/foo/")) + cl.create_bucket(PureGCSPath("gs://foo/")) assert bucket_target.exists() is True From 98760fcfc0cb62891b7f2aac81a74fef088fdf78 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 23 Apr 2020 15:53:20 -0700 Subject: [PATCH 02/75] feat(cli): add pathy executable with cp and mv commands - add "pathy" entry point to the gcspath package - add Typer requirement - add typer CLI app for copying and moving files - basic test --- gcspath/cli.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 +- setup.py | 2 +- tests/test_cli.py | 39 +++++++++++++++++++++++ 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 gcspath/cli.py create mode 100644 tests/test_cli.py diff --git a/gcspath/cli.py b/gcspath/cli.py new file mode 100644 index 0000000..3309aea --- /dev/null +++ b/gcspath/cli.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3.7 +from pathlib import Path +from typing import Union + +import typer +from gcspath import GCSPath + +app = typer.Typer() + + +@app.command() +def cp(from_location: str, to_location: str): + """ + Copy a blob or folder of blobs from one bucket to another. + """ + from_path: Union[Path, GCSPath] + to_path: Union[Path, GCSPath] + + try: + from_path = GCSPath(from_location) + except ValueError: + from_path = Path(from_location) + if not from_path.exists(): + raise ValueError(f"from_path is not an existing Path or GCSPath: {from_path}") + + try: + to_path = GCSPath(to_location) + except ValueError: + to_path = Path(to_location) + + if from_path.is_dir(): + to_path.mkdir(parents=True, exist_ok=True) + for blob in from_path.rglob("*"): + if not blob.is_file(): + continue + to_blob = to_path / str(blob.relative_to(from_path)) + to_blob.write_bytes(blob.read_bytes()) + elif from_path.is_file(): + to_path.parent.mkdir(parents=True, exist_ok=True) + to_path.write_bytes(from_path.read_bytes()) + + +@app.command() +def mv(from_location: str, to_location: str): + """ + Move a blob or folder of blobs from one path to another. + """ + from_path: Union[Path, GCSPath] + to_path: Union[Path, GCSPath] + + try: + from_path = GCSPath(from_location) + except ValueError: + from_path = Path(from_location) + if not from_path.exists(): + raise ValueError(f"from_path is not an existing Path or GCSPath: {from_path}") + + try: + to_path = GCSPath(to_location) + except ValueError: + to_path = Path(to_location) + + if from_path.is_dir(): + to_path.mkdir(parents=True, exist_ok=True) + to_unlink = [] + for blob in from_path.rglob("*"): + if not blob.is_file(): + continue + to_blob = to_path / str(blob.relative_to(from_path)) + to_blob.write_bytes(blob.read_bytes()) + to_unlink.append(blob) + for blob in to_unlink: + blob.unlink() + elif from_path.is_file(): + to_path.parent.mkdir(parents=True, exist_ok=True) + to_path.write_bytes(from_path.read_bytes()) + from_path.unlink() + + +if __name__ == "__main__": + app() diff --git a/requirements.txt b/requirements.txt index db4c8e3..0c040f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ google-cloud-storage>=1.26.0,<2.0.0 -smart-open==1.10.0 \ No newline at end of file +smart-open==1.10.0 +typer>=0.1.0,<0.2.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 041f601..47dfac7 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ def setup_package(): install_requires=requirements, entry_points=""" [console_scripts] - prodigyteams=gcspath.cli:main + pathy=pathy.cli:main """, classifiers=[ "Development Status :: 4 - Beta", diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..e687cb5 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,39 @@ +import pytest +from gcspath import GCSPath +from gcspath.cli import app +from typer.testing import CliRunner + + +from .conftest import TEST_ADAPTERS + +runner = CliRunner() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cp_files(with_adapter): + source = "gs://gcspath-tests-1/cli_cp.txt" + destination = "gs://gcspath-tests-2/cli_cp.txt" + content = "hi" + dest_path = GCSPath(destination) + source_path = GCSPath(source) + source_path.write_text(content) + assert runner.invoke(app, ["cp", source, destination]).exit_code == 0 + assert dest_path.is_file() + # Both files exist + assert source_path.read_text() == content + assert dest_path.read_text() == content + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cp_folders(with_adapter): + source = "gs://gcspath-tests-1/cli_cp.txt" + destination = "gs://gcspath-tests-2/cli_cp.txt" + content = "hi" + dest_path = GCSPath(destination) + source_path = GCSPath(source) + source_path.write_text(content) + assert runner.invoke(app, ["cp", source, destination]).exit_code == 0 + assert dest_path.is_file() + # Both files exist + assert source_path.read_text() == content + assert dest_path.read_text() == content From 3393226bc7f390f696d109bfac5f44e59a8b5151 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 23 Apr 2020 17:34:36 -0700 Subject: [PATCH 03/75] feat: add FluidPath and GCSPath.fluid method GCSPath wants to work with many kinds of paths, and it's not always clear upfront what kind of path a string represents. If you're on a local file system, the path "/usr/bin/something" may be totally valid, but as a GCSPath it isn't valid because there's no service scheme attached to it, e.g. "gs://bucket/usr/bin/something" FluidPath is a Union of pathlib.Path and GCSPath which allows type-checking of the paths without needing explicit knowledge of what kind of path it is, until that knowledge is needed. *note* I originally thought of using "UnionPath" instead of "FluidPath" but the intellisense for completing "GCSPath.union" was very crowded, and a helper should be easy to type with completion. --- gcspath/__init__.py | 1 + gcspath/api.py | 25 +++++++++++++++++++++++++ tests/test_api.py | 11 +++++++++++ 3 files changed, 37 insertions(+) diff --git a/gcspath/__init__.py b/gcspath/__init__.py index 2ee9ec4..46f444c 100644 --- a/gcspath/__init__.py +++ b/gcspath/__init__.py @@ -6,6 +6,7 @@ ClientError, GCSPath, PureGCSPath, + FluidPath, clear_fs_cache, get_fs_cache, get_fs_client, diff --git a/gcspath/api.py b/gcspath/api.py index 06b9b2f..e9e82a8 100644 --- a/gcspath/api.py +++ b/gcspath/api.py @@ -109,6 +109,9 @@ def clear_fs_cache(force: bool = False) -> None: shutil.rmtree(str(resolved)) +FluidPath = Union["GCSPath", Path] + + class GCSPath(Path, PureGCSPath): """Path subclass for GCS service. @@ -131,6 +134,28 @@ def _init(self: PathType, template=None): else: self._accessor = template._accessor + @classmethod + def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: + """Helper to infer a pathlib.Path or GCSPath from an input path or string. + + The returned type is a union of the potential `FluidPath` types and will + type-check correctly against the minimum overlapping APIs of all the input + types. + + If you need to use specific implementation details of a type, you + will need to cast the return of this function to the desired type, e.g. + + # Narrow the type a specific class + assert isinstance(path, GCSPath), "must be GCSPath" + # Use a member specific to that class + print(path.prefix) + + """ + from_path: FluidPath = GCSPath(path_candidate) + if from_path.root in ["/", ""]: + from_path = Path(path_candidate) + return from_path + @classmethod def from_bucket(cls: PathType, bucket_name: str) -> "GCSPath": """Helper to convert a bucket name into a GCSPath without needing diff --git a/tests/test_api.py b/tests/test_api.py index 785c288..1b74505 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -13,6 +13,7 @@ BucketClientFS, BucketsAccessor, BucketStat, + FluidPath, GCSPath, PureGCSPath, clear_fs_cache, @@ -38,6 +39,16 @@ def test_api_is_path_instance(with_adapter): assert isinstance(blob, Path) +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_fluid(with_adapter, bucket: str): + path: FluidPath = GCSPath.fluid(f"gs://{bucket}/fake-key") + assert isinstance(path, GCSPath) + path: FluidPath = GCSPath.fluid(f"foo/bar.txt") + assert isinstance(path, Path) + path: FluidPath = GCSPath.fluid(f"/dev/null") + assert isinstance(path, Path) + + @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_path_to_local(with_adapter, bucket: str): root: GCSPath = GCSPath.from_bucket(bucket) / "to_local" From 25c58a312131bbc31c17c5fc7f37905074ded5d9 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 23 Apr 2020 17:35:02 -0700 Subject: [PATCH 04/75] chore: fix bad entry_point in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 47dfac7..2ed5da0 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ def setup_package(): install_requires=requirements, entry_points=""" [console_scripts] - pathy=pathy.cli:main + pathy=gcspath.cli:app """, classifiers=[ "Development Status :: 4 - Beta", From 7b85ca15e9f6deee941e934d42d57562aab7e5d9 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 23 Apr 2020 17:36:49 -0700 Subject: [PATCH 05/75] test(cli): add tests for cp/mv files and folders --- gcspath/cli.py | 46 ++++++++----------- tests/test_cli.py | 115 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 110 insertions(+), 51 deletions(-) diff --git a/gcspath/cli.py b/gcspath/cli.py index 3309aea..5b3e2d0 100644 --- a/gcspath/cli.py +++ b/gcspath/cli.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3.7 from pathlib import Path -from typing import Union import typer -from gcspath import GCSPath +from gcspath import GCSPath, FluidPath app = typer.Typer() @@ -13,21 +12,10 @@ def cp(from_location: str, to_location: str): """ Copy a blob or folder of blobs from one bucket to another. """ - from_path: Union[Path, GCSPath] - to_path: Union[Path, GCSPath] - - try: - from_path = GCSPath(from_location) - except ValueError: - from_path = Path(from_location) + from_path: FluidPath = GCSPath.fluid(from_location) if not from_path.exists(): raise ValueError(f"from_path is not an existing Path or GCSPath: {from_path}") - - try: - to_path = GCSPath(to_location) - except ValueError: - to_path = Path(to_location) - + to_path: FluidPath = GCSPath.fluid(to_location) if from_path.is_dir(): to_path.mkdir(parents=True, exist_ok=True) for blob in from_path.rglob("*"): @@ -36,6 +24,12 @@ def cp(from_location: str, to_location: str): to_blob = to_path / str(blob.relative_to(from_path)) to_blob.write_bytes(blob.read_bytes()) elif from_path.is_file(): + # Copy prefix from the source if the to_path has none. + # + # e.g. "cp ./file.txt gs://bucket-name/" writes "gs://bucket-name/file.txt" + if isinstance(to_path, GCSPath) and to_path.prefix == "": + to_path = to_path / from_path + to_path.parent.mkdir(parents=True, exist_ok=True) to_path.write_bytes(from_path.read_bytes()) @@ -45,21 +39,10 @@ def mv(from_location: str, to_location: str): """ Move a blob or folder of blobs from one path to another. """ - from_path: Union[Path, GCSPath] - to_path: Union[Path, GCSPath] - - try: - from_path = GCSPath(from_location) - except ValueError: - from_path = Path(from_location) + from_path: FluidPath = GCSPath.fluid(from_location) if not from_path.exists(): raise ValueError(f"from_path is not an existing Path or GCSPath: {from_path}") - - try: - to_path = GCSPath(to_location) - except ValueError: - to_path = Path(to_location) - + to_path: FluidPath = GCSPath.fluid(to_location) if from_path.is_dir(): to_path.mkdir(parents=True, exist_ok=True) to_unlink = [] @@ -71,7 +54,14 @@ def mv(from_location: str, to_location: str): to_unlink.append(blob) for blob in to_unlink: blob.unlink() + if from_path.is_dir(): + from_path.rmdir() elif from_path.is_file(): + # Copy prefix from the source if the to_path has none. + # + # e.g. "cp ./file.txt gs://bucket-name/" writes "gs://bucket-name/file.txt" + if isinstance(to_path, GCSPath) and to_path.prefix == "": + to_path = to_path / from_path to_path.parent.mkdir(parents=True, exist_ok=True) to_path.write_bytes(from_path.read_bytes()) from_path.unlink() diff --git a/tests/test_cli.py b/tests/test_cli.py index e687cb5..0894695 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -10,30 +10,99 @@ @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_cp_files(with_adapter): - source = "gs://gcspath-tests-1/cli_cp.txt" - destination = "gs://gcspath-tests-2/cli_cp.txt" - content = "hi" - dest_path = GCSPath(destination) - source_path = GCSPath(source) - source_path.write_text(content) +def test_cli_cp_file(with_adapter, bucket: str): + source = f"gs://{bucket}/cli_cp_file/file.txt" + destination = f"gs://{bucket}/cli_cp_file/other.txt" + GCSPath(source).write_text("---") assert runner.invoke(app, ["cp", source, destination]).exit_code == 0 - assert dest_path.is_file() - # Both files exist - assert source_path.read_text() == content - assert dest_path.read_text() == content + assert GCSPath(source).exists() + assert GCSPath(destination).is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_cp_folders(with_adapter): - source = "gs://gcspath-tests-1/cli_cp.txt" - destination = "gs://gcspath-tests-2/cli_cp.txt" - content = "hi" - dest_path = GCSPath(destination) - source_path = GCSPath(source) - source_path.write_text(content) - assert runner.invoke(app, ["cp", source, destination]).exit_code == 0 - assert dest_path.is_file() - # Both files exist - assert source_path.read_text() == content - assert dest_path.read_text() == content +def test_cli_cp_folder(with_adapter, bucket: str): + root = GCSPath.from_bucket(bucket) + source = root / "cli_cp_folder" + destination = root / "cli_cp_folder_other" + for i in range(2): + for j in range(2): + (source / f"{i}" / f"{j}").write_text("---") + assert runner.invoke(app, ["cp", str(source), str(destination)]).exit_code == 0 + assert GCSPath(source).exists() + assert GCSPath(destination).is_dir() + for i in range(2): + for j in range(2): + assert (destination / f"{i}" / f"{j}").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_mv_folder(with_adapter, bucket: str): + root = GCSPath.from_bucket(bucket) + source = root / "cli_mv_folder" + destination = root / "cli_mv_folder_other" + for i in range(2): + for j in range(2): + (source / f"{i}" / f"{j}").write_text("---") + assert runner.invoke(app, ["mv", str(source), str(destination)]).exit_code == 0 + assert not GCSPath(source).exists() + assert GCSPath(destination).is_dir() + # Ensure source files are gone + for i in range(2): + for j in range(2): + assert not (source / f"{i}" / f"{j}").is_file() + # And dest files exist + for i in range(2): + for j in range(2): + assert (destination / f"{i}" / f"{j}").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_mv_file(with_adapter, bucket: str): + source = f"gs://{bucket}/cli_mv_file/file.txt" + destination = f"gs://{bucket}/cli_mv_file/other.txt" + GCSPath(source).write_text("---") + assert GCSPath(source).exists() + assert runner.invoke(app, ["mv", source, destination]).exit_code == 0 + assert not GCSPath(source).exists() + assert GCSPath(destination).is_file() + + +# @pytest.mark.parametrize("adapter", TEST_ADAPTERS) +# def test_cli_rename_files_across_buckets(with_adapter, bucket: str, other_bucket: str): +# # Rename a single file across buckets +# GCSPath(f"gs://{bucket}/rename/file.txt").write_text("---") +# GCSPath(f"gs://{bucket}/rename/file.txt").rename( +# f"gs://{other_bucket}/rename/other.txt" +# ) +# assert not GCSPath(f"gs://{bucket}/rename/file.txt").exists() +# assert GCSPath(f"gs://{other_bucket}/rename/other.txt").is_file() + + +# @pytest.mark.parametrize("adapter", TEST_ADAPTERS) +# def test_cli_rename_folders_in_bucket(with_adapter, bucket: str): +# # Rename a folder in the same bucket +# GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") +# GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") +# path = GCSPath(f"gs://{bucket}/rename/folder/") +# new_path = GCSPath(f"gs://{bucket}/rename/other/") +# path.rename(new_path) +# assert not path.exists() +# assert new_path.exists() +# assert GCSPath(f"gs://{bucket}/rename/other/one.txt").is_file() +# assert GCSPath(f"gs://{bucket}/rename/other/two.txt").is_file() + + +# @pytest.mark.parametrize("adapter", TEST_ADAPTERS) +# def test_cli_rename_folders_across_buckets( +# with_adapter, bucket: str, other_bucket: str +# ): +# # Rename a folder across buckets +# GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") +# GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") +# path = GCSPath(f"gs://{bucket}/rename/folder/") +# new_path = GCSPath(f"gs://{other_bucket}/rename/other/") +# path.rename(new_path) +# assert not path.exists() +# assert new_path.exists() +# assert GCSPath(f"gs://{other_bucket}/rename/other/one.txt").is_file() +# assert GCSPath(f"gs://{other_bucket}/rename/other/two.txt").is_file() From 31cea9156d99d9d465569c20c566943d4238c5dd Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 24 Apr 2020 07:49:11 -0700 Subject: [PATCH 06/75] feat(cli): add rm [path] command - removes files or folders - add tests --- gcspath/cli.py | 18 ++++++++++ tests/test_cli.py | 92 +++++++++++++++++++++++++++-------------------- 2 files changed, 71 insertions(+), 39 deletions(-) diff --git a/gcspath/cli.py b/gcspath/cli.py index 5b3e2d0..774e9d3 100644 --- a/gcspath/cli.py +++ b/gcspath/cli.py @@ -67,5 +67,23 @@ def mv(from_location: str, to_location: str): from_path.unlink() +@app.command() +def rm(location: str, strict: bool = False): + """ + Remove a blob or folder of blobs from a given location. + """ + path: FluidPath = GCSPath.fluid(location) + if not path.exists() and strict: + raise ValueError(f"from_path is not an existing Path or GCSPath: {path}") + if path.is_dir(): + to_unlink = [b for b in path.rglob("*") if b.is_file()] + for blob in to_unlink: + blob.unlink() + if path.exists(): + path.rmdir() + elif path.is_file(): + path.unlink() + + if __name__ == "__main__": app() diff --git a/tests/test_cli.py b/tests/test_cli.py index 0894695..44e05e4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -67,42 +67,56 @@ def test_cli_mv_file(with_adapter, bucket: str): assert GCSPath(destination).is_file() -# @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -# def test_cli_rename_files_across_buckets(with_adapter, bucket: str, other_bucket: str): -# # Rename a single file across buckets -# GCSPath(f"gs://{bucket}/rename/file.txt").write_text("---") -# GCSPath(f"gs://{bucket}/rename/file.txt").rename( -# f"gs://{other_bucket}/rename/other.txt" -# ) -# assert not GCSPath(f"gs://{bucket}/rename/file.txt").exists() -# assert GCSPath(f"gs://{other_bucket}/rename/other.txt").is_file() - - -# @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -# def test_cli_rename_folders_in_bucket(with_adapter, bucket: str): -# # Rename a folder in the same bucket -# GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") -# GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") -# path = GCSPath(f"gs://{bucket}/rename/folder/") -# new_path = GCSPath(f"gs://{bucket}/rename/other/") -# path.rename(new_path) -# assert not path.exists() -# assert new_path.exists() -# assert GCSPath(f"gs://{bucket}/rename/other/one.txt").is_file() -# assert GCSPath(f"gs://{bucket}/rename/other/two.txt").is_file() - - -# @pytest.mark.parametrize("adapter", TEST_ADAPTERS) -# def test_cli_rename_folders_across_buckets( -# with_adapter, bucket: str, other_bucket: str -# ): -# # Rename a folder across buckets -# GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") -# GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") -# path = GCSPath(f"gs://{bucket}/rename/folder/") -# new_path = GCSPath(f"gs://{other_bucket}/rename/other/") -# path.rename(new_path) -# assert not path.exists() -# assert new_path.exists() -# assert GCSPath(f"gs://{other_bucket}/rename/other/one.txt").is_file() -# assert GCSPath(f"gs://{other_bucket}/rename/other/two.txt").is_file() +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_mv_file_across_buckets(with_adapter, bucket: str, other_bucket: str): + source = f"gs://{bucket}/cli_mv_file_across_buckets/file.txt" + destination = f"gs://{other_bucket}/cli_mv_file_across_buckets/other.txt" + GCSPath(source).write_text("---") + assert GCSPath(source).exists() + assert runner.invoke(app, ["mv", source, destination]).exit_code == 0 + assert not GCSPath(source).exists() + assert GCSPath(destination).is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_mv_folder_across_buckets(with_adapter, bucket: str, other_bucket: str): + source = GCSPath.from_bucket(bucket) / "cli_mv_folder_across_buckets" + destination = GCSPath.from_bucket(other_bucket) / "cli_mv_folder_across_buckets" + for i in range(2): + for j in range(2): + (source / f"{i}" / f"{j}").write_text("---") + assert runner.invoke(app, ["mv", str(source), str(destination)]).exit_code == 0 + assert not GCSPath(source).exists() + assert GCSPath(destination).is_dir() + # Ensure source files are gone + for i in range(2): + for j in range(2): + assert not (source / f"{i}" / f"{j}").is_file() + # And dest files exist + for i in range(2): + for j in range(2): + assert (destination / f"{i}" / f"{j}").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_rm_folder(with_adapter, bucket: str): + root = GCSPath.from_bucket(bucket) + source = root / "cli_rm_folder" + for i in range(2): + for j in range(2): + (source / f"{i}" / f"{j}").write_text("---") + assert runner.invoke(app, ["rm", str(source)]).exit_code == 0 + assert not GCSPath(source).exists() + # Ensure source files are gone + for i in range(2): + for j in range(2): + assert not (source / f"{i}" / f"{j}").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_rm_file(with_adapter, bucket: str): + source = f"gs://{bucket}/cli_rm_file/file.txt" + GCSPath(source).write_text("---") + assert GCSPath(source).exists() + assert runner.invoke(app, ["rm", source]).exit_code == 0 + assert not GCSPath(source).exists() From 17cab1d8b96d92ca79e18512ac7e8a42aa136066 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 24 Apr 2020 08:10:14 -0700 Subject: [PATCH 07/75] feat(cli): add ls [path] command - prints the full paths of files found in the location --- gcspath/cli.py | 13 +++++++++++++ tests/test_cli.py | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/gcspath/cli.py b/gcspath/cli.py index 774e9d3..3232f0c 100644 --- a/gcspath/cli.py +++ b/gcspath/cli.py @@ -85,5 +85,18 @@ def rm(location: str, strict: bool = False): path.unlink() +@app.command() +def ls(location: str): + """ + List the blobs that exist at a given location. + """ + path: FluidPath = GCSPath.fluid(location) + if not path.exists() or path.is_file(): + typer.echo(f"ls: {path}: No such file or directory") + return + for file in path.iterdir(): + typer.echo(file) + + if __name__ == "__main__": app() diff --git a/tests/test_cli.py b/tests/test_cli.py index 44e05e4..e3597eb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -120,3 +120,19 @@ def test_cli_rm_file(with_adapter, bucket: str): assert GCSPath(source).exists() assert runner.invoke(app, ["rm", source]).exit_code == 0 assert not GCSPath(source).exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_ls(with_adapter, bucket: str): + root = GCSPath.from_bucket(bucket) / "cli_ls" + one = str(root / f"file.txt") + two = str(root / f"other.txt") + three = str(root / f"folder/file.txt") + GCSPath(one).write_text("---") + GCSPath(two).write_text("---") + GCSPath(three).write_text("---") + result = runner.invoke(app, ["ls", str(root)]) + assert result.exit_code == 0 + assert one in result.output + assert two in result.output + assert str(root / "folder") in result.output From c62b14da2aba25024af647e29df09ee57a13f6bd Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 24 Apr 2020 08:21:29 -0700 Subject: [PATCH 08/75] feat(pathy): rename library to be more generic - it does more than just GCS at this point --- CHANGELOG.md | 64 ++++---- MANIFEST.in | 2 +- Makefile | 4 +- README.md | 10 +- package-lock.json | 2 +- package.json | 10 +- {gcspath => pathy}/__init__.py | 2 +- {gcspath => pathy}/about.py | 4 +- {gcspath => pathy}/api.py | 34 ++-- {gcspath => pathy}/base.py | 0 {gcspath => pathy}/cli.py | 24 +-- {gcspath => pathy}/client.py | 0 {gcspath => pathy}/data/.gitkeep | 0 {gcspath => pathy}/file.py | 0 {gcspath => pathy}/gcs.py | 0 {gcspath => pathy}/py.typed | 0 requirements-dev.txt | 2 +- setup.py | 4 +- tests/conftest.py | 10 +- tests/test_api.py | 258 +++++++++++++++---------------- tests/test_base.py | 32 ++-- tests/test_cli.py | 64 ++++---- tests/test_client.py | 4 +- tests/test_package.py | 2 +- tools/ci-set-build-version.ts | 8 +- tools/test.sh | 2 +- 26 files changed, 271 insertions(+), 271 deletions(-) rename {gcspath => pathy}/__init__.py (97%) rename {gcspath => pathy}/about.py (70%) rename {gcspath => pathy}/api.py (95%) rename {gcspath => pathy}/base.py (100%) rename {gcspath => pathy}/cli.py (83%) rename {gcspath => pathy}/client.py (100%) rename {gcspath => pathy}/data/.gitkeep (100%) rename {gcspath => pathy}/file.py (100%) rename {gcspath => pathy}/gcs.py (100%) rename {gcspath => pathy}/py.typed (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb3f41..a284323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,107 +1,107 @@ -## [0.0.17](https://github.com/justindujardin/gcspath/compare/v0.0.16...v0.0.17) (2020-04-17) +## [0.0.17](https://github.com/justindujardin/pathy/compare/v0.0.16...v0.0.17) (2020-04-17) ### Bug Fixes -* do not de/compress opened files based on extension ([22d14e7](https://github.com/justindujardin/gcspath/commit/22d14e7d4919f16ca54bf28e685c221f7c96f8d3)) +* do not de/compress opened files based on extension ([22d14e7](https://github.com/justindujardin/pathy/commit/22d14e7d4919f16ca54bf28e685c221f7c96f8d3)) -## [0.0.16](https://github.com/justindujardin/gcspath/compare/v0.0.15...v0.0.16) (2020-04-16) +## [0.0.16](https://github.com/justindujardin/pathy/compare/v0.0.15...v0.0.16) (2020-04-16) ### Features -* **typing:** expose library python types to mypy ([53cf348](https://github.com/justindujardin/gcspath/commit/53cf34845399e1d31538dc02e462d7e02bcd32a6)) +* **typing:** expose library python types to mypy ([53cf348](https://github.com/justindujardin/pathy/commit/53cf34845399e1d31538dc02e462d7e02bcd32a6)) -## [0.0.15](https://github.com/justindujardin/gcspath/compare/v0.0.14...v0.0.15) (2020-04-16) +## [0.0.15](https://github.com/justindujardin/pathy/compare/v0.0.14...v0.0.15) (2020-04-16) ### Bug Fixes -* **requirements:** remove typer dependency ([08e8fa0](https://github.com/justindujardin/gcspath/commit/08e8fa0baa186b710a6adf2205b0a51bbd39fe37)) +* **requirements:** remove typer dependency ([08e8fa0](https://github.com/justindujardin/pathy/commit/08e8fa0baa186b710a6adf2205b0a51bbd39fe37)) -## [0.0.14](https://github.com/justindujardin/gcspath/compare/v0.0.13...v0.0.14) (2020-04-16) +## [0.0.14](https://github.com/justindujardin/pathy/compare/v0.0.13...v0.0.14) (2020-04-16) ### Bug Fixes -* **iterdir:** don't return empty results ([2a8b870](https://github.com/justindujardin/gcspath/commit/2a8b870c2ca232431c65808050363e8faff60ba2)) +* **iterdir:** don't return empty results ([2a8b870](https://github.com/justindujardin/pathy/commit/2a8b870c2ca232431c65808050363e8faff60ba2)) -## [0.0.13](https://github.com/justindujardin/gcspath/compare/v0.0.12...v0.0.13) (2020-04-16) +## [0.0.13](https://github.com/justindujardin/pathy/compare/v0.0.12...v0.0.13) (2020-04-16) ### Bug Fixes -* **to_local:** issue where files without extensions would not be cached ([3d543a8](https://github.com/justindujardin/gcspath/commit/3d543a88a81604d13f8e401422d59803d9bb3943)) +* **to_local:** issue where files without extensions would not be cached ([3d543a8](https://github.com/justindujardin/pathy/commit/3d543a88a81604d13f8e401422d59803d9bb3943)) -## [0.0.12](https://github.com/justindujardin/gcspath/compare/v0.0.11...v0.0.12) (2020-04-15) +## [0.0.12](https://github.com/justindujardin/pathy/compare/v0.0.11...v0.0.12) (2020-04-15) ### Bug Fixes -* recursion error when copying blob folders ([8b6e01c](https://github.com/justindujardin/gcspath/commit/8b6e01c3e8c35a78deee60d45563b27b7a732e9a)) +* recursion error when copying blob folders ([8b6e01c](https://github.com/justindujardin/pathy/commit/8b6e01c3e8c35a78deee60d45563b27b7a732e9a)) -## [0.0.11](https://github.com/justindujardin/gcspath/compare/v0.0.10...v0.0.11) (2020-04-15) +## [0.0.11](https://github.com/justindujardin/pathy/compare/v0.0.10...v0.0.11) (2020-04-15) ### Features -* **to_local:** support caching folders ([cc56f6e](https://github.com/justindujardin/gcspath/commit/cc56f6eab21f850f0521013749589ad0736e261d)) +* **to_local:** support caching folders ([cc56f6e](https://github.com/justindujardin/pathy/commit/cc56f6eab21f850f0521013749589ad0736e261d)) -## [0.0.10](https://github.com/justindujardin/gcspath/compare/v0.0.9...v0.0.10) (2020-04-14) +## [0.0.10](https://github.com/justindujardin/pathy/compare/v0.0.9...v0.0.10) (2020-04-14) ### Features -* add `use_fs_caching` and `GCSPath.to_local` for caching ([2894360](https://github.com/justindujardin/gcspath/commit/2894360d48e3ac4b28ecb4627eb562f9e65b3c93)) +* add `use_fs_caching` and `Pathy.to_local` for caching ([2894360](https://github.com/justindujardin/pathy/commit/2894360d48e3ac4b28ecb4627eb562f9e65b3c93)) -## [0.0.9](https://github.com/justindujardin/gcspath/compare/v0.0.8...v0.0.9) (2020-04-08) +## [0.0.9](https://github.com/justindujardin/pathy/compare/v0.0.8...v0.0.9) (2020-04-08) ### Features -* add `resolve` method ([7cebc69](https://github.com/justindujardin/gcspath/commit/7cebc69bfc88b1a522defdce1637f5159c37def6)) +* add `resolve` method ([7cebc69](https://github.com/justindujardin/pathy/commit/7cebc69bfc88b1a522defdce1637f5159c37def6)) -## [0.0.8](https://github.com/justindujardin/gcspath/compare/v0.0.7...v0.0.8) (2020-04-08) +## [0.0.8](https://github.com/justindujardin/pathy/compare/v0.0.7...v0.0.8) (2020-04-08) ### Features -* allow passing GCSPath to spacy.Model.to_disk ([1d628cb](https://github.com/justindujardin/gcspath/commit/1d628cb8c5056683590d9f2403f1482e2a310971)) -* **use_fs:** allow passing root folder as Path ([3635152](https://github.com/justindujardin/gcspath/commit/36351525bf84001ed4f9b0b7abf842f8e27ef1f0)) +* allow passing Pathy to spacy.Model.to_disk ([1d628cb](https://github.com/justindujardin/pathy/commit/1d628cb8c5056683590d9f2403f1482e2a310971)) +* **use_fs:** allow passing root folder as Path ([3635152](https://github.com/justindujardin/pathy/commit/36351525bf84001ed4f9b0b7abf842f8e27ef1f0)) -## [0.0.7](https://github.com/justindujardin/gcspath/compare/v0.0.6...v0.0.7) (2020-03-30) +## [0.0.7](https://github.com/justindujardin/pathy/compare/v0.0.6...v0.0.7) (2020-03-30) ### Bug Fixes -* **gcs:** gracefully handle invalid gcs client case ([529f630](https://github.com/justindujardin/gcspath/commit/529f63026abe1b11c3336febb152a030e28a85ef)) +* **gcs:** gracefully handle invalid gcs client case ([529f630](https://github.com/justindujardin/pathy/commit/529f63026abe1b11c3336febb152a030e28a85ef)) -## [0.0.6](https://github.com/justindujardin/gcspath/compare/v0.0.5...v0.0.6) (2020-03-30) +## [0.0.6](https://github.com/justindujardin/pathy/compare/v0.0.5...v0.0.6) (2020-03-30) ### Features -* add github releases for each pypi version ([66dbed8](https://github.com/justindujardin/gcspath/commit/66dbed851346372ab84080f027113aba054452af)) +* add github releases for each pypi version ([66dbed8](https://github.com/justindujardin/pathy/commit/66dbed851346372ab84080f027113aba054452af)) -## [0.0.5](https://github.com/justindujardin/gcspath/compare/v0.0.4...v0.0.5) (2020-03-30) +## [0.0.5](https://github.com/justindujardin/pathy/compare/v0.0.4...v0.0.5) (2020-03-30) ### Bug Fixes -- generating changelog ([ef43ed1](https://github.com/justindujardin/gcspath/commit/ef43ed11a140ed3cfaba2e7d72b7c01c7275c8d6)) +- generating changelog ([ef43ed1](https://github.com/justindujardin/pathy/commit/ef43ed11a140ed3cfaba2e7d72b7c01c7275c8d6)) -## [0.0.4](https://github.com/justindujardin/gcspath/compare/v0.0.3...v0.0.4) (2020-03-30) +## [0.0.4](https://github.com/justindujardin/pathy/compare/v0.0.3...v0.0.4) (2020-03-30) ### Features - support unlink path operation -## [0.0.3](https://github.com/justindujardin/gcspath/compare/v0.0.2...v0.0.3) (2020-03-30) +## [0.0.3](https://github.com/justindujardin/pathy/compare/v0.0.2...v0.0.3) (2020-03-30) ### Features -- **gcs:** use smart_open for streaming files ([e557ab9](https://github.com/justindujardin/gcspath/pull/3/commits/e557ab9e3bc7c0edcb02333fe8ea6be760c152dc)) -- add file-system bucket adapter ([1c72f47](https://github.com/justindujardin/gcspath/pull/3/commits/1c72f475fde8de1c6cb3af23d63b793722fe82e2)) -- use_fs stores buckets on the file-system ([f717280](https://github.com/justindujardin/gcspath/pull/3/commits/f7172806d0ae3e408aafc12fe7526b9852ce8b36)) +- **gcs:** use smart_open for streaming files ([e557ab9](https://github.com/justindujardin/pathy/pull/3/commits/e557ab9e3bc7c0edcb02333fe8ea6be760c152dc)) +- add file-system bucket adapter ([1c72f47](https://github.com/justindujardin/pathy/pull/3/commits/1c72f475fde8de1c6cb3af23d63b793722fe82e2)) +- use_fs stores buckets on the file-system ([f717280](https://github.com/justindujardin/pathy/pull/3/commits/f7172806d0ae3e408aafc12fe7526b9852ce8b36)) ## [0.0.2](v0.0.1...v0.0.2) (2020-03-18) diff --git a/MANIFEST.in b/MANIFEST.in index ff60972..db51e4f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ include setup.py include README.md include LICENSE -include gcspath/py.typed +include pathy/py.typed diff --git a/Makefile b/Makefile index c860346..297fb9e 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,9 @@ test: venv publish: venv env3.7/bin/python setup.py sdist bdist_wheel env3.7/bin/twine upload dist/* - rm -rf build dist .egg gcspath.egg-info + rm -rf build dist .egg pathy.egg-info clean: - rm -rf build dist .egg gcspath.egg-info env3.7 + rm -rf build dist .egg pathy.egg-info env3.7 check: test diff --git a/README.md b/README.md index 282ab49..a17b45d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# GCSPath +# Pathy -[![Build status](https://travis-ci.org/justindujardin/gcspath.svg?branch=master)](https://travis-ci.org/justindujardin/gcspath) -[![Pypi version](https://badgen.net/pypi/v/gcspath)](https://pypi.org/project/gcspath/) +[![Build status](https://travis-ci.org/justindujardin/pathy.svg?branch=master)](https://travis-ci.org/justindujardin/pathy) +[![Pypi version](https://badgen.net/pypi/v/pathy)](https://pypi.org/project/pathy/) > IMPORTANT: this library is not ready for use -GCSPath provides a convenient Pythonic File-System/Path like interface to Google Cloud Storage using [google-cloud-storage](https://pypi.org/project/google-cloud-storage/) package as a driver. +Pathy provides a convenient Pythonic File-System/Path like interface to Google Cloud Storage using [google-cloud-storage](https://pypi.org/project/google-cloud-storage/) package as a driver. It is based on the [S3Path](https://github.com/liormizr/s3path) project, which provides a similar interface for S3 buckets. @@ -14,7 +14,7 @@ It is based on the [S3Path](https://github.com/liormizr/s3path) project, which p From PyPI: ```bash -$ pip install gcspath +$ pip install pathy ``` # Requirements diff --git a/package-lock.json b/package-lock.json index 4eacc53..ccf66ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "gcspath", + "name": "pathy", "version": "0.0.0-development", "lockfileVersion": 1, "requires": true, diff --git a/package.json b/package.json index 0660789..de661f2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "gcspath", + "name": "pathy", "version": "0.0.0-development", "description": "", "main": "index.js", @@ -11,14 +11,14 @@ }, "repository": { "type": "git", - "url": "https://github.com/justindujardin/gcspath" + "url": "https://github.com/justindujardin/pathy" }, "author": "", "license": "Apache-2.0", "bugs": { - "url": "https://github.com/justindujardin/gcspath/issues" + "url": "https://github.com/justindujardin/pathy/issues" }, - "homepage": "https://github.com/justindujardin/gcspath#readme", + "homepage": "https://github.com/justindujardin/pathy#readme", "devDependencies": { "@semantic-release/changelog": "^5.0.0", "@semantic-release/git": "^9.0.0", @@ -77,7 +77,7 @@ "@semantic-release/git", { "assets": [ - "gcspath/about.py", + "pathy/about.py", "package.json", "CHANGELOG.md" ], diff --git a/gcspath/__init__.py b/pathy/__init__.py similarity index 97% rename from gcspath/__init__.py rename to pathy/__init__.py index 46f444c..4d76302 100644 --- a/gcspath/__init__.py +++ b/pathy/__init__.py @@ -4,7 +4,7 @@ ClientBlob, ClientBucket, ClientError, - GCSPath, + Pathy, PureGCSPath, FluidPath, clear_fs_cache, diff --git a/gcspath/about.py b/pathy/about.py similarity index 70% rename from gcspath/about.py rename to pathy/about.py index 6924678..d9aac20 100644 --- a/gcspath/about.py +++ b/pathy/about.py @@ -1,7 +1,7 @@ -__title__ = "gcspath" +__title__ = "pathy" __version__ = "0.0.17" __summary__ = "pathlib.Path subclasses for Google Cloud Storage" -__uri__ = "https://github.com/justindujardin/gcspath" +__uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" __email__ = "justin@explosion.ai" __license__ = "Apache 2.0" diff --git a/gcspath/api.py b/pathy/api.py similarity index 95% rename from gcspath/api.py rename to pathy/api.py index e9e82a8..2c93908 100644 --- a/gcspath/api.py +++ b/pathy/api.py @@ -24,7 +24,7 @@ ) from .file import BucketClientFS -__all__ = ("GCSPath", "use_fs", "get_fs_client", "BucketsAccessor") +__all__ = ("Pathy", "use_fs", "get_fs_client", "BucketsAccessor") _SUPPORTED_OPEN_MODES = {"r", "rb", "tr", "rt", "w", "wb", "bw", "wt", "tw"} @@ -46,7 +46,7 @@ def use_fs(root: Optional[Union[str, Path, bool]] = None) -> Optional[BucketClie # None or True - enable FS adapter with default root if root is None or root is True: - # Look up "data" folder of gcspath package similar to spaCy + # Look up "data" folder of pathy package similar to spaCy client_root = Path(__file__).parent / "data" else: assert isinstance(root, (str, Path)), f"root is not a known type: {type(root)}" @@ -109,10 +109,10 @@ def clear_fs_cache(force: bool = False) -> None: shutil.rmtree(str(resolved)) -FluidPath = Union["GCSPath", Path] +FluidPath = Union["Pathy", Path] -class GCSPath(Path, PureGCSPath): +class Pathy(Path, PureGCSPath): """Path subclass for GCS service. Write files to and read files from the GCS service using pathlib.Path @@ -125,7 +125,7 @@ def __truediv__(self: PathType, key: Union[str, PathType]) -> PathType: return super().__truediv__(key) def __rtruediv__(self: PathType, key: Union[str, PathType]) -> PathType: - return cast(GCSPath, super().__rtruediv__(key)) + return cast(Pathy, super().__rtruediv__(key)) def _init(self: PathType, template=None): super()._init(template) # type:ignore @@ -136,7 +136,7 @@ def _init(self: PathType, template=None): @classmethod def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: - """Helper to infer a pathlib.Path or GCSPath from an input path or string. + """Helper to infer a pathlib.Path or Pathy from an input path or string. The returned type is a union of the potential `FluidPath` types and will type-check correctly against the minimum overlapping APIs of all the input @@ -146,25 +146,25 @@ def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: will need to cast the return of this function to the desired type, e.g. # Narrow the type a specific class - assert isinstance(path, GCSPath), "must be GCSPath" + assert isinstance(path, Pathy), "must be Pathy" # Use a member specific to that class print(path.prefix) """ - from_path: FluidPath = GCSPath(path_candidate) + from_path: FluidPath = Pathy(path_candidate) if from_path.root in ["/", ""]: from_path = Path(path_candidate) return from_path @classmethod - def from_bucket(cls: PathType, bucket_name: str) -> "GCSPath": - """Helper to convert a bucket name into a GCSPath without needing + def from_bucket(cls: PathType, bucket_name: str) -> "Pathy": + """Helper to convert a bucket name into a Pathy without needing to add the leading and trailing slashes""" - return GCSPath(f"gs://{bucket_name}/") + return Pathy(f"gs://{bucket_name}/") @classmethod def to_local( - cls: PathType, blob_path: Union["GCSPath", str], recurse: bool = True + cls: PathType, blob_path: Union["Pathy", str], recurse: bool = True ) -> Path: """Get a bucket blob and return a local file cached version of it. The cache is sensitive to the file updated time, and downloads new blobs as they become @@ -177,7 +177,7 @@ def to_local( cache_folder.mkdir(exist_ok=True, parents=True) if isinstance(blob_path, str): - blob_path = GCSPath(blob_path) + blob_path = Pathy(blob_path) cache_blob: Path = cache_folder.absolute() / blob_path.root / blob_path.key cache_time: Path = ( @@ -209,7 +209,7 @@ def to_local( # If not a specific blob, enumerate all the blobs under # the path and cache them, then return the cache folder for blob in blob_path.rglob("*"): - GCSPath.to_local(blob, recurse=False) + Pathy.to_local(blob, recurse=False) return cache_blob def stat(self: PathType) -> BucketStat: @@ -273,7 +273,7 @@ def glob(self: PathType, pattern) -> Generator[PathType, None, None]: def rglob(self: PathType, pattern) -> Generator[PathType, None, None]: """ - This is like calling GCSPath.glob with "**/" added in front of the given + This is like calling Pathy.glob with "**/" added in front of the given relative pattern. """ yield from super().rglob(pattern) @@ -337,7 +337,7 @@ def rename(self: PathType, target: Union[str, PathType]) -> None: If target exists and is a file, it will be replaced silently if the user has permission. If path is a key prefix, it will replace all the keys with the same prefix to the new target prefix. Target can be either a string or - another GCSPath object. + another Pathy object. """ self._absolute_path_validation() self_type = type(self) @@ -628,7 +628,7 @@ def owner(self, path: PathType) -> Optional[str]: def resolve(self, path: PathType, strict: bool = False) -> PathType: path_parts = str(path).replace(path.drive, "") - return GCSPath(f"{path.drive}{os.path.abspath(path_parts)}") + return Pathy(f"{path.drive}{os.path.abspath(path_parts)}") def rename(self, path: PathType, target: PathType) -> None: bucket: ClientBucket = self.client.get_bucket(path) diff --git a/gcspath/base.py b/pathy/base.py similarity index 100% rename from gcspath/base.py rename to pathy/base.py diff --git a/gcspath/cli.py b/pathy/cli.py similarity index 83% rename from gcspath/cli.py rename to pathy/cli.py index 3232f0c..81af927 100644 --- a/gcspath/cli.py +++ b/pathy/cli.py @@ -2,7 +2,7 @@ from pathlib import Path import typer -from gcspath import GCSPath, FluidPath +from pathy import Pathy, FluidPath app = typer.Typer() @@ -12,10 +12,10 @@ def cp(from_location: str, to_location: str): """ Copy a blob or folder of blobs from one bucket to another. """ - from_path: FluidPath = GCSPath.fluid(from_location) + from_path: FluidPath = Pathy.fluid(from_location) if not from_path.exists(): - raise ValueError(f"from_path is not an existing Path or GCSPath: {from_path}") - to_path: FluidPath = GCSPath.fluid(to_location) + raise ValueError(f"from_path is not an existing Path or Pathy: {from_path}") + to_path: FluidPath = Pathy.fluid(to_location) if from_path.is_dir(): to_path.mkdir(parents=True, exist_ok=True) for blob in from_path.rglob("*"): @@ -27,7 +27,7 @@ def cp(from_location: str, to_location: str): # Copy prefix from the source if the to_path has none. # # e.g. "cp ./file.txt gs://bucket-name/" writes "gs://bucket-name/file.txt" - if isinstance(to_path, GCSPath) and to_path.prefix == "": + if isinstance(to_path, Pathy) and to_path.prefix == "": to_path = to_path / from_path to_path.parent.mkdir(parents=True, exist_ok=True) @@ -39,10 +39,10 @@ def mv(from_location: str, to_location: str): """ Move a blob or folder of blobs from one path to another. """ - from_path: FluidPath = GCSPath.fluid(from_location) + from_path: FluidPath = Pathy.fluid(from_location) if not from_path.exists(): - raise ValueError(f"from_path is not an existing Path or GCSPath: {from_path}") - to_path: FluidPath = GCSPath.fluid(to_location) + raise ValueError(f"from_path is not an existing Path or Pathy: {from_path}") + to_path: FluidPath = Pathy.fluid(to_location) if from_path.is_dir(): to_path.mkdir(parents=True, exist_ok=True) to_unlink = [] @@ -60,7 +60,7 @@ def mv(from_location: str, to_location: str): # Copy prefix from the source if the to_path has none. # # e.g. "cp ./file.txt gs://bucket-name/" writes "gs://bucket-name/file.txt" - if isinstance(to_path, GCSPath) and to_path.prefix == "": + if isinstance(to_path, Pathy) and to_path.prefix == "": to_path = to_path / from_path to_path.parent.mkdir(parents=True, exist_ok=True) to_path.write_bytes(from_path.read_bytes()) @@ -72,9 +72,9 @@ def rm(location: str, strict: bool = False): """ Remove a blob or folder of blobs from a given location. """ - path: FluidPath = GCSPath.fluid(location) + path: FluidPath = Pathy.fluid(location) if not path.exists() and strict: - raise ValueError(f"from_path is not an existing Path or GCSPath: {path}") + raise ValueError(f"from_path is not an existing Path or Pathy: {path}") if path.is_dir(): to_unlink = [b for b in path.rglob("*") if b.is_file()] for blob in to_unlink: @@ -90,7 +90,7 @@ def ls(location: str): """ List the blobs that exist at a given location. """ - path: FluidPath = GCSPath.fluid(location) + path: FluidPath = Pathy.fluid(location) if not path.exists() or path.is_file(): typer.echo(f"ls: {path}: No such file or directory") return diff --git a/gcspath/client.py b/pathy/client.py similarity index 100% rename from gcspath/client.py rename to pathy/client.py diff --git a/gcspath/data/.gitkeep b/pathy/data/.gitkeep similarity index 100% rename from gcspath/data/.gitkeep rename to pathy/data/.gitkeep diff --git a/gcspath/file.py b/pathy/file.py similarity index 100% rename from gcspath/file.py rename to pathy/file.py diff --git a/gcspath/gcs.py b/pathy/gcs.py similarity index 100% rename from gcspath/gcs.py rename to pathy/gcs.py diff --git a/gcspath/py.typed b/pathy/py.typed similarity index 100% rename from gcspath/py.typed rename to pathy/py.typed diff --git a/requirements-dev.txt b/requirements-dev.txt index c82d50e..3d4b755 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,6 +6,6 @@ pytest-coverage tox mock -# test serializing spacy models using GCSPath in place of Path +# test serializing spacy models using Pathy in place of Path https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.2.5/en_core_web_sm-2.2.5.tar.gz#egg=en_core_web_sm spacy \ No newline at end of file diff --git a/setup.py b/setup.py index 2ed5da0..a090276 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ def setup_package(): - package_name = "gcspath" + package_name = "pathy" root = pathlib.Path(__file__).parent.resolve() about_path = root / package_name / "about.py" @@ -35,7 +35,7 @@ def setup_package(): install_requires=requirements, entry_points=""" [console_scripts] - pathy=gcspath.cli:app + pathy=pathy.cli:app """, classifiers=[ "Development Status :: 4 - Beta", diff --git a/tests/conftest.py b/tests/conftest.py index 8658804..f483886 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest -from gcspath import GCSPath, use_fs, use_fs_cache +from pathy import Pathy, use_fs, use_fs_cache # TODO: set this up with a service account for the CI @@ -16,12 +16,12 @@ @pytest.fixture() def bucket() -> str: - return "gcspath-tests-1" + return "pathy-tests-1" @pytest.fixture() def other_bucket() -> str: - return "gcspath-tests-2" + return "pathy-tests-2" @pytest.fixture() @@ -48,10 +48,10 @@ def with_adapter(adapter: str, bucket: str, other_bucket: str): # Use local file-system in a temp folder tmp_dir = tempfile.mkdtemp() use_fs(tmp_dir) - bucket_one = GCSPath.from_bucket(bucket) + bucket_one = Pathy.from_bucket(bucket) if not bucket_one.exists(): bucket_one.mkdir() - bucket_two = GCSPath.from_bucket(other_bucket) + bucket_two = Pathy.from_bucket(other_bucket) if not bucket_two.exists(): bucket_two.mkdir() else: diff --git a/tests/test_api.py b/tests/test_api.py index 1b74505..fe38aea 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -9,12 +9,12 @@ from .conftest import TEST_ADAPTERS -from gcspath import ( +from pathy import ( BucketClientFS, BucketsAccessor, BucketStat, FluidPath, - GCSPath, + Pathy, PureGCSPath, clear_fs_cache, get_fs_client, @@ -29,37 +29,37 @@ def test_api_path_support(): - assert PureGCSPath in GCSPath.mro() # type: ignore - assert Path in GCSPath.mro() # type: ignore + assert PureGCSPath in Pathy.mro() # type: ignore + assert Path in Pathy.mro() # type: ignore @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_is_path_instance(with_adapter): - blob = GCSPath("gs://fake/blob") + blob = Pathy("gs://fake/blob") assert isinstance(blob, Path) @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_fluid(with_adapter, bucket: str): - path: FluidPath = GCSPath.fluid(f"gs://{bucket}/fake-key") - assert isinstance(path, GCSPath) - path: FluidPath = GCSPath.fluid(f"foo/bar.txt") + path: FluidPath = Pathy.fluid(f"gs://{bucket}/fake-key") + assert isinstance(path, Pathy) + path: FluidPath = Pathy.fluid(f"foo/bar.txt") assert isinstance(path, Path) - path: FluidPath = GCSPath.fluid(f"/dev/null") + path: FluidPath = Pathy.fluid(f"/dev/null") assert isinstance(path, Path) @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_path_to_local(with_adapter, bucket: str): - root: GCSPath = GCSPath.from_bucket(bucket) / "to_local" - foo_blob: GCSPath = root / "foo" + root: Pathy = Pathy.from_bucket(bucket) / "to_local" + foo_blob: Pathy = root / "foo" foo_blob.write_text("---") - assert isinstance(foo_blob, GCSPath) + assert isinstance(foo_blob, Pathy) use_fs_cache() # Cache a blob - cached: Path = GCSPath.to_local(foo_blob) - second_cached: Path = GCSPath.to_local(foo_blob) + cached: Path = Pathy.to_local(foo_blob) + second_cached: Path = Pathy.to_local(foo_blob) assert isinstance(cached, Path) assert cached.exists() and cached.is_file(), "local file should exist" assert second_cached == cached, "must be the same path" @@ -70,10 +70,10 @@ def test_api_path_to_local(with_adapter, bucket: str): for i in range(3): folder = f"folder_{i}" for j in range(2): - gcs_blob: GCSPath = complex_folder / folder / f"file_{j}.txt" + gcs_blob: Pathy = complex_folder / folder / f"file_{j}.txt" gcs_blob.write_text("---") - cached_folder: Path = GCSPath.to_local(complex_folder) + cached_folder: Path = Pathy.to_local(complex_folder) assert isinstance(cached_folder, Path) assert cached_folder.exists() and cached_folder.is_dir() @@ -91,20 +91,20 @@ def test_api_path_to_local(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): - path = GCSPath(f"gs://{bucket}/directory/foo.txt") + path = Pathy(f"gs://{bucket}/directory/foo.txt") path.write_text("---") - assert isinstance(path, GCSPath) + assert isinstance(path, Pathy) with pytest.raises(ValueError): - GCSPath.to_local(path) + Pathy.to_local(path) use_fs_cache(with_fs) - source_file: Path = GCSPath.to_local(path) + source_file: Path = Pathy.to_local(path) foo_timestamp = Path(f"{source_file}.time") assert foo_timestamp.exists() orig_cache_time = foo_timestamp.read_text() # fetch from the local cache - cached_file: Path = GCSPath.to_local(path) + cached_file: Path = Pathy.to_local(path) assert cached_file == source_file cached_cache_time = foo_timestamp.read_text() assert orig_cache_time == cached_cache_time, "cached blob timestamps should match" @@ -114,51 +114,51 @@ def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): path.write_text('{ "cool" : true }') # Fetch the updated blob - res: Path = GCSPath.to_local(path) + res: Path = Pathy.to_local(path) updated_cache_time = foo_timestamp.read_text() assert updated_cache_time != orig_cache_time, "cached timestamp did not change" @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_stat(with_adapter, bucket: str): - path = GCSPath("fake-bucket-1234-0987/fake-key") + path = Pathy("fake-bucket-1234-0987/fake-key") with pytest.raises(ValueError): path.stat() - path = GCSPath(f"gs://{bucket}/foo.txt") + path = Pathy(f"gs://{bucket}/foo.txt") path.write_text("a-a-a-a-a-a-a") stat = path.stat() assert isinstance(stat, BucketStat) assert stat.size > 0 assert stat.last_modified > 0 with pytest.raises(ValueError): - assert GCSPath(f"gs://{bucket}").stat() + assert Pathy(f"gs://{bucket}").stat() with pytest.raises(FileNotFoundError): - assert GCSPath(f"gs://{bucket}/nonexistant_file.txt").stat() + assert Pathy(f"gs://{bucket}/nonexistant_file.txt").stat() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_resolve(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/fake-key") + path = Pathy(f"gs://{bucket}/fake-key") assert path.resolve() == path - path = GCSPath(f"gs://{bucket}/dir/../fake-key") - assert path.resolve() == GCSPath(f"gs://{bucket}/fake-key") + path = Pathy(f"gs://{bucket}/dir/../fake-key") + assert path.resolve() == Pathy(f"gs://{bucket}/fake-key") @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_exists(with_adapter, bucket: str): - path = GCSPath("./fake-key") + path = Pathy("./fake-key") with pytest.raises(ValueError): path.exists() # GCS buckets are globally unique, "test-bucket" exists so this # raises an access error. - assert GCSPath("gs://test-bucket/fake-key").exists() is False + assert Pathy("gs://test-bucket/fake-key").exists() is False # invalid bucket name - assert GCSPath("gs://unknown-bucket-name-123987519875419").exists() is False + assert Pathy("gs://unknown-bucket-name-123987519875419").exists() is False # valid bucket with invalid object - assert GCSPath(f"gs://{bucket}/not_found_lol_nice.txt").exists() is False + assert Pathy(f"gs://{bucket}/not_found_lol_nice.txt").exists() is False - path = GCSPath(f"gs://{bucket}/directory/foo.txt") + path = Pathy(f"gs://{bucket}/directory/foo.txt") path.write_text("---") assert path.exists() for parent in path.parents: @@ -168,42 +168,42 @@ def test_api_exists(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_glob(with_adapter, bucket: str): for i in range(3): - path = GCSPath(f"gs://{bucket}/glob/{i}.file") + path = Pathy(f"gs://{bucket}/glob/{i}.file") path.write_text("---") for i in range(2): - path = GCSPath(f"gs://{bucket}/glob/{i}/dir/file.txt") + path = Pathy(f"gs://{bucket}/glob/{i}/dir/file.txt") path.write_text("---") - assert list(GCSPath(f"gs://{bucket}/glob/").glob("*.test")) == [] - assert sorted(list(GCSPath(f"gs://{bucket}/glob/").glob("*.file"))) == [ - GCSPath(f"gs://{bucket}/glob/0.file"), - GCSPath(f"gs://{bucket}/glob/1.file"), - GCSPath(f"gs://{bucket}/glob/2.file"), + assert list(Pathy(f"gs://{bucket}/glob/").glob("*.test")) == [] + assert sorted(list(Pathy(f"gs://{bucket}/glob/").glob("*.file"))) == [ + Pathy(f"gs://{bucket}/glob/0.file"), + Pathy(f"gs://{bucket}/glob/1.file"), + Pathy(f"gs://{bucket}/glob/2.file"), ] - assert list(GCSPath(f"gs://{bucket}/glob/0/").glob("*/*.txt")) == [ - GCSPath(f"gs://{bucket}/glob/0/dir/file.txt"), + assert list(Pathy(f"gs://{bucket}/glob/0/").glob("*/*.txt")) == [ + Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), ] - assert sorted(GCSPath(f"gs://{bucket}").glob("*lob/")) == [ - GCSPath(f"gs://{bucket}/glob"), + assert sorted(Pathy(f"gs://{bucket}").glob("*lob/")) == [ + Pathy(f"gs://{bucket}/glob"), ] # Recursive matches - assert sorted(list(GCSPath(f"gs://{bucket}/glob/").glob("**/*.txt"))) == [ - GCSPath(f"gs://{bucket}/glob/0/dir/file.txt"), - GCSPath(f"gs://{bucket}/glob/1/dir/file.txt"), + assert sorted(list(Pathy(f"gs://{bucket}/glob/").glob("**/*.txt"))) == [ + Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), + Pathy(f"gs://{bucket}/glob/1/dir/file.txt"), ] # rglob adds the **/ for you - assert sorted(list(GCSPath(f"gs://{bucket}/glob/").rglob("*.txt"))) == [ - GCSPath(f"gs://{bucket}/glob/0/dir/file.txt"), - GCSPath(f"gs://{bucket}/glob/1/dir/file.txt"), + assert sorted(list(Pathy(f"gs://{bucket}/glob/").rglob("*.txt"))) == [ + Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), + Pathy(f"gs://{bucket}/glob/1/dir/file.txt"), ] @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_unlink_path(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/unlink/404.txt") + path = Pathy(f"gs://{bucket}/unlink/404.txt") with pytest.raises(FileNotFoundError): path.unlink() - path = GCSPath(f"gs://{bucket}/unlink/foo.txt") + path = Pathy(f"gs://{bucket}/unlink/foo.txt") path.write_text("---") assert path.exists() path.unlink() @@ -212,7 +212,7 @@ def test_api_unlink_path(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_is_dir(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/is_dir/subfolder/another/my.file") + path = Pathy(f"gs://{bucket}/is_dir/subfolder/another/my.file") path.write_text("---") assert path.is_dir() is False for parent in path.parents: @@ -221,7 +221,7 @@ def test_api_is_dir(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_is_file(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/is_file/subfolder/another/my.file") + path = Pathy(f"gs://{bucket}/is_file/subfolder/another/my.file") path.write_text("---") # The full file is a file assert path.is_file() is True @@ -234,34 +234,34 @@ def test_api_is_file(with_adapter, bucket: str): def test_api_iterdir(with_adapter, bucket: str): # (n) files in a folder for i in range(2): - path = GCSPath(f"gs://{bucket}/iterdir/{i}.file") + path = Pathy(f"gs://{bucket}/iterdir/{i}.file") path.write_text("---") # 1 file in a subfolder - path = GCSPath(f"gs://{bucket}/iterdir/sub/file.txt") + path = Pathy(f"gs://{bucket}/iterdir/sub/file.txt") path.write_text("---") - path = GCSPath(f"gs://{bucket}/iterdir/") + path = Pathy(f"gs://{bucket}/iterdir/") check = sorted(path.iterdir()) assert check == [ - GCSPath(f"gs://{bucket}/iterdir/0.file"), - GCSPath(f"gs://{bucket}/iterdir/1.file"), - GCSPath(f"gs://{bucket}/iterdir/sub"), + Pathy(f"gs://{bucket}/iterdir/0.file"), + Pathy(f"gs://{bucket}/iterdir/1.file"), + Pathy(f"gs://{bucket}/iterdir/sub"), ] @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_iterdir_pipstore(with_adapter, bucket: str): - path = GCSPath.from_bucket(bucket) / "iterdir_pipstore/prodigy/prodigy.whl" + path = Pathy.from_bucket(bucket) / "iterdir_pipstore/prodigy/prodigy.whl" path.write_bytes(b"---") - path = GCSPath.from_bucket(bucket) / "iterdir_pipstore" + path = Pathy.from_bucket(bucket) / "iterdir_pipstore" res = [e.name for e in sorted(path.iterdir())] assert res == ["prodigy"] @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_open_for_read(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/read/file.txt") + path = Pathy(f"gs://{bucket}/read/file.txt") path.write_text("---") with path.open() as file_obj: assert file_obj.read() == "---" @@ -270,18 +270,18 @@ def test_api_open_for_read(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_open_for_write(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/write/file.txt") + path = Pathy(f"gs://{bucket}/write/file.txt") with path.open(mode="w") as file_obj: file_obj.write("---") file_obj.writelines(["---"]) - path = GCSPath(f"gs://{bucket}/write/file.txt") + path = Pathy(f"gs://{bucket}/write/file.txt") with path.open() as file_obj: assert file_obj.read() == "------" @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_open_binary_read(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/read_binary/file.txt") + path = Pathy(f"gs://{bucket}/read_binary/file.txt") path.write_bytes(b"---") with path.open(mode="rb") as file_obj: assert file_obj.readlines() == [b"---"] @@ -292,7 +292,7 @@ def test_api_open_binary_read(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_readwrite_text(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/write_text/file.txt") + path = Pathy(f"gs://{bucket}/write_text/file.txt") path.write_text("---") with path.open() as file_obj: assert file_obj.read() == "---" @@ -301,14 +301,14 @@ def test_api_readwrite_text(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_readwrite_bytes(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/write_bytes/file.txt") + path = Pathy(f"gs://{bucket}/write_bytes/file.txt") path.write_bytes(b"---") assert path.read_bytes() == b"---" @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_readwrite_lines(with_adapter, bucket: str): - path = GCSPath(f"gs://{bucket}/write_text/file.txt") + path = Pathy(f"gs://{bucket}/write_text/file.txt") with path.open("w") as file_obj: file_obj.writelines(["---"]) with path.open("r") as file_obj: @@ -321,9 +321,9 @@ def test_api_readwrite_lines(with_adapter, bucket: str): def test_api_owner(with_adapter, bucket: str): # Raises for invalid file with pytest.raises(FileNotFoundError): - GCSPath(f"gs://{bucket}/write_text/not_a_valid_blob").owner() + Pathy(f"gs://{bucket}/write_text/not_a_valid_blob").owner() - path = GCSPath(f"gs://{bucket}/write_text/file.txt") + path = Pathy(f"gs://{bucket}/write_text/file.txt") path.write_text("---") # TODO: How to set file owner to non-None in GCS? Then assert here. # @@ -339,35 +339,35 @@ def test_api_owner(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_rename_files_in_bucket(with_adapter, bucket: str): # Rename a single file - GCSPath(f"gs://{bucket}/rename/file.txt").write_text("---") - GCSPath(f"gs://{bucket}/rename/file.txt").rename(f"gs://{bucket}/rename/other.txt") - assert not GCSPath(f"gs://{bucket}/rename/file.txt").exists() - assert GCSPath(f"gs://{bucket}/rename/other.txt").is_file() + Pathy(f"gs://{bucket}/rename/file.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/file.txt").rename(f"gs://{bucket}/rename/other.txt") + assert not Pathy(f"gs://{bucket}/rename/file.txt").exists() + assert Pathy(f"gs://{bucket}/rename/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_rename_files_across_buckets(with_adapter, bucket: str, other_bucket: str): # Rename a single file across buckets - GCSPath(f"gs://{bucket}/rename/file.txt").write_text("---") - GCSPath(f"gs://{bucket}/rename/file.txt").rename( + Pathy(f"gs://{bucket}/rename/file.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/file.txt").rename( f"gs://{other_bucket}/rename/other.txt" ) - assert not GCSPath(f"gs://{bucket}/rename/file.txt").exists() - assert GCSPath(f"gs://{other_bucket}/rename/other.txt").is_file() + assert not Pathy(f"gs://{bucket}/rename/file.txt").exists() + assert Pathy(f"gs://{other_bucket}/rename/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_rename_folders_in_bucket(with_adapter, bucket: str): # Rename a folder in the same bucket - GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") - GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") - path = GCSPath(f"gs://{bucket}/rename/folder/") - new_path = GCSPath(f"gs://{bucket}/rename/other/") + Pathy(f"gs://{bucket}/rename/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/rename/folder/") + new_path = Pathy(f"gs://{bucket}/rename/other/") path.rename(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"gs://{bucket}/rename/other/one.txt").is_file() - assert GCSPath(f"gs://{bucket}/rename/other/two.txt").is_file() + assert Pathy(f"gs://{bucket}/rename/other/one.txt").is_file() + assert Pathy(f"gs://{bucket}/rename/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) @@ -375,51 +375,51 @@ def test_api_rename_folders_across_buckets( with_adapter, bucket: str, other_bucket: str ): # Rename a folder across buckets - GCSPath(f"gs://{bucket}/rename/folder/one.txt").write_text("---") - GCSPath(f"gs://{bucket}/rename/folder/two.txt").write_text("---") - path = GCSPath(f"gs://{bucket}/rename/folder/") - new_path = GCSPath(f"gs://{other_bucket}/rename/other/") + Pathy(f"gs://{bucket}/rename/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/rename/folder/") + new_path = Pathy(f"gs://{other_bucket}/rename/other/") path.rename(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"gs://{other_bucket}/rename/other/one.txt").is_file() - assert GCSPath(f"gs://{other_bucket}/rename/other/two.txt").is_file() + assert Pathy(f"gs://{other_bucket}/rename/other/one.txt").is_file() + assert Pathy(f"gs://{other_bucket}/rename/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_replace_files_in_bucket(with_adapter, bucket: str): # replace a single file - GCSPath(f"gs://{bucket}/replace/file.txt").write_text("---") - GCSPath(f"gs://{bucket}/replace/file.txt").replace( + Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/file.txt").replace( f"gs://{bucket}/replace/other.txt" ) - assert not GCSPath(f"gs://{bucket}/replace/file.txt").exists() - assert GCSPath(f"gs://{bucket}/replace/other.txt").is_file() + assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() + assert Pathy(f"gs://{bucket}/replace/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_replace_files_across_buckets(with_adapter, bucket: str, other_bucket: str): # Rename a single file across buckets - GCSPath(f"gs://{bucket}/replace/file.txt").write_text("---") - GCSPath(f"gs://{bucket}/replace/file.txt").replace( + Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/file.txt").replace( f"gs://{other_bucket}/replace/other.txt" ) - assert not GCSPath(f"gs://{bucket}/replace/file.txt").exists() - assert GCSPath(f"gs://{other_bucket}/replace/other.txt").is_file() + assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() + assert Pathy(f"gs://{other_bucket}/replace/other.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_replace_folders_in_bucket(with_adapter, bucket: str): # Rename a folder in the same bucket - GCSPath(f"gs://{bucket}/replace/folder/one.txt").write_text("---") - GCSPath(f"gs://{bucket}/replace/folder/two.txt").write_text("---") - path = GCSPath(f"gs://{bucket}/replace/folder/") - new_path = GCSPath(f"gs://{bucket}/replace/other/") + Pathy(f"gs://{bucket}/replace/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/replace/folder/") + new_path = Pathy(f"gs://{bucket}/replace/other/") path.replace(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"gs://{bucket}/replace/other/one.txt").is_file() - assert GCSPath(f"gs://{bucket}/replace/other/two.txt").is_file() + assert Pathy(f"gs://{bucket}/replace/other/one.txt").is_file() + assert Pathy(f"gs://{bucket}/replace/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) @@ -427,46 +427,46 @@ def test_api_replace_folders_across_buckets( with_adapter, bucket: str, other_bucket: str ): # Rename a folder across buckets - GCSPath(f"gs://{bucket}/replace/folder/one.txt").write_text("---") - GCSPath(f"gs://{bucket}/replace/folder/two.txt").write_text("---") - path = GCSPath(f"gs://{bucket}/replace/folder/") - new_path = GCSPath(f"gs://{other_bucket}/replace/other/") + Pathy(f"gs://{bucket}/replace/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/replace/folder/") + new_path = Pathy(f"gs://{other_bucket}/replace/other/") path.replace(new_path) assert not path.exists() assert new_path.exists() - assert GCSPath(f"gs://{other_bucket}/replace/other/one.txt").is_file() - assert GCSPath(f"gs://{other_bucket}/replace/other/two.txt").is_file() + assert Pathy(f"gs://{other_bucket}/replace/other/one.txt").is_file() + assert Pathy(f"gs://{other_bucket}/replace/other/two.txt").is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_rmdir(with_adapter, bucket: str): - GCSPath(f"gs://{bucket}/rmdir/one.txt").write_text("---") - GCSPath(f"gs://{bucket}/rmdir/folder/two.txt").write_text("---") - path = GCSPath(f"gs://{bucket}/rmdir/") + Pathy(f"gs://{bucket}/rmdir/one.txt").write_text("---") + Pathy(f"gs://{bucket}/rmdir/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/rmdir/") path.rmdir() - assert not GCSPath(f"gs://{bucket}/rmdir/one.txt").is_file() - assert not GCSPath(f"gs://{bucket}/rmdir/other/two.txt").is_file() + assert not Pathy(f"gs://{bucket}/rmdir/one.txt").is_file() + assert not Pathy(f"gs://{bucket}/rmdir/other/two.txt").is_file() assert not path.exists() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_mkdir(with_adapter, bucket: str): - bucket_name = f"gcspath-e2e-test-{uuid4().hex}" + bucket_name = f"pathy-e2e-test-{uuid4().hex}" # Create a bucket - GCSPath(f"gs://{bucket_name}/").mkdir() + Pathy(f"gs://{bucket_name}/").mkdir() # Does not assert if it already exists - GCSPath(f"gs://{bucket_name}/").mkdir(exist_ok=True) + Pathy(f"gs://{bucket_name}/").mkdir(exist_ok=True) with pytest.raises(FileExistsError): - GCSPath(f"gs://{bucket_name}/").mkdir(exist_ok=False) + Pathy(f"gs://{bucket_name}/").mkdir(exist_ok=False) # with pytest.raises(FileNotFoundError): - # GCSPath("/test-second-bucket/test-directory/file.name").mkdir() - # GCSPath("/test-second-bucket/test-directory/file.name").mkdir(parents=True) - assert GCSPath(f"gs://{bucket_name}/").exists() + # Pathy("/test-second-bucket/test-directory/file.name").mkdir() + # Pathy("/test-second-bucket/test-directory/file.name").mkdir(parents=True) + assert Pathy(f"gs://{bucket_name}/").exists() # remove the bucket # client = storage.Client() # bucket = client.lookup_bucket(bucket_name) # bucket.delete() - GCSPath(f"gs://{bucket_name}/").rmdir() + Pathy(f"gs://{bucket_name}/").rmdir() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) @@ -474,7 +474,7 @@ def test_api_ignore_extension(with_adapter, bucket: str): """The smart_open library does automatic decompression based on the filename. We disable that to avoid errors, e.g. if you have a .tar.gz file that isn't gzipped.""" - not_targz = GCSPath.from_bucket(bucket) / "ignore_ext/one.tar.gz" + not_targz = Pathy.from_bucket(bucket) / "ignore_ext/one.tar.gz" fixture_tar = Path(__file__).parent / "fixtures" / "tar_but_not_gzipped.tar.gz" not_targz.write_bytes(fixture_tar.read_bytes()) again = not_targz.read_bytes() @@ -509,7 +509,7 @@ def test_api_use_fs(with_fs: Path): use_fs(False) -@mock.patch("gcspath.gcs.BucketClientGCS", side_effect=DefaultCredentialsError()) +@mock.patch("pathy.gcs.BucketClientGCS", side_effect=DefaultCredentialsError()) def test_api_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): accessor = BucketsAccessor() # Accessing the client lazily throws with no GCS or FS adapters configured @@ -524,10 +524,10 @@ def test_api_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): def test_api_export_spacy_model(temp_folder): """spaCy model loading is one of the things we need to support""" use_fs(temp_folder) - bucket = GCSPath("gs://my-bucket/") + bucket = Pathy("gs://my-bucket/") bucket.mkdir(exist_ok=True) model = spacy.blank("en") - output_path = GCSPath("gs://my-bucket/models/my_model") + output_path = Pathy("gs://my-bucket/models/my_model") model.to_disk(output_path) sorted_entries = sorted([str(p) for p in output_path.glob("*")]) expected_entries = [ diff --git a/tests/test_base.py b/tests/test_base.py index 541de78..336e9d5 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -4,79 +4,79 @@ import pytest -from gcspath import GCSPath, PureGCSPath +from pathy import Pathy, PureGCSPath def test_base_not_supported(monkeypatch): - monkeypatch.setattr(GCSPath._flavour, "is_supported", False) + monkeypatch.setattr(Pathy._flavour, "is_supported", False) with pytest.raises(NotImplementedError): - GCSPath() + Pathy() def test_base_cwd(): with pytest.raises(NotImplementedError): - GCSPath.cwd() + Pathy.cwd() def test_base_home(): with pytest.raises(NotImplementedError): - GCSPath.home() + Pathy.home() def test_base_chmod(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.chmod(0o666) def test_base_lchmod(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.lchmod(0o666) def test_base_group(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.group() def test_base_is_mount(): - assert not GCSPath("/fake-bucket/fake-key").is_mount() + assert not Pathy("/fake-bucket/fake-key").is_mount() def test_base_is_symlink(): - assert not GCSPath("/fake-bucket/fake-key").is_symlink() + assert not Pathy("/fake-bucket/fake-key").is_symlink() def test_base_is_socket(): - assert not GCSPath("/fake-bucket/fake-key").is_socket() + assert not Pathy("/fake-bucket/fake-key").is_socket() def test_base_is_fifo(): - assert not GCSPath("/fake-bucket/fake-key").is_fifo() + assert not Pathy("/fake-bucket/fake-key").is_fifo() def test_base_is_block_device(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.is_block_device() def test_base_is_char_device(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.is_char_device() def test_base_lstat(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.lstat() def test_base_symlink_to(): - path = GCSPath("/fake-bucket/fake-key") + path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): path.symlink_to("file_name") diff --git a/tests/test_cli.py b/tests/test_cli.py index e3597eb..f09d0bb 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,6 +1,6 @@ import pytest -from gcspath import GCSPath -from gcspath.cli import app +from pathy import Pathy +from pathy.cli import app from typer.testing import CliRunner @@ -13,23 +13,23 @@ def test_cli_cp_file(with_adapter, bucket: str): source = f"gs://{bucket}/cli_cp_file/file.txt" destination = f"gs://{bucket}/cli_cp_file/other.txt" - GCSPath(source).write_text("---") + Pathy(source).write_text("---") assert runner.invoke(app, ["cp", source, destination]).exit_code == 0 - assert GCSPath(source).exists() - assert GCSPath(destination).is_file() + assert Pathy(source).exists() + assert Pathy(destination).is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_cp_folder(with_adapter, bucket: str): - root = GCSPath.from_bucket(bucket) + root = Pathy.from_bucket(bucket) source = root / "cli_cp_folder" destination = root / "cli_cp_folder_other" for i in range(2): for j in range(2): (source / f"{i}" / f"{j}").write_text("---") assert runner.invoke(app, ["cp", str(source), str(destination)]).exit_code == 0 - assert GCSPath(source).exists() - assert GCSPath(destination).is_dir() + assert Pathy(source).exists() + assert Pathy(destination).is_dir() for i in range(2): for j in range(2): assert (destination / f"{i}" / f"{j}").is_file() @@ -37,15 +37,15 @@ def test_cli_cp_folder(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_mv_folder(with_adapter, bucket: str): - root = GCSPath.from_bucket(bucket) + root = Pathy.from_bucket(bucket) source = root / "cli_mv_folder" destination = root / "cli_mv_folder_other" for i in range(2): for j in range(2): (source / f"{i}" / f"{j}").write_text("---") assert runner.invoke(app, ["mv", str(source), str(destination)]).exit_code == 0 - assert not GCSPath(source).exists() - assert GCSPath(destination).is_dir() + assert not Pathy(source).exists() + assert Pathy(destination).is_dir() # Ensure source files are gone for i in range(2): for j in range(2): @@ -60,34 +60,34 @@ def test_cli_mv_folder(with_adapter, bucket: str): def test_cli_mv_file(with_adapter, bucket: str): source = f"gs://{bucket}/cli_mv_file/file.txt" destination = f"gs://{bucket}/cli_mv_file/other.txt" - GCSPath(source).write_text("---") - assert GCSPath(source).exists() + Pathy(source).write_text("---") + assert Pathy(source).exists() assert runner.invoke(app, ["mv", source, destination]).exit_code == 0 - assert not GCSPath(source).exists() - assert GCSPath(destination).is_file() + assert not Pathy(source).exists() + assert Pathy(destination).is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_mv_file_across_buckets(with_adapter, bucket: str, other_bucket: str): source = f"gs://{bucket}/cli_mv_file_across_buckets/file.txt" destination = f"gs://{other_bucket}/cli_mv_file_across_buckets/other.txt" - GCSPath(source).write_text("---") - assert GCSPath(source).exists() + Pathy(source).write_text("---") + assert Pathy(source).exists() assert runner.invoke(app, ["mv", source, destination]).exit_code == 0 - assert not GCSPath(source).exists() - assert GCSPath(destination).is_file() + assert not Pathy(source).exists() + assert Pathy(destination).is_file() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_mv_folder_across_buckets(with_adapter, bucket: str, other_bucket: str): - source = GCSPath.from_bucket(bucket) / "cli_mv_folder_across_buckets" - destination = GCSPath.from_bucket(other_bucket) / "cli_mv_folder_across_buckets" + source = Pathy.from_bucket(bucket) / "cli_mv_folder_across_buckets" + destination = Pathy.from_bucket(other_bucket) / "cli_mv_folder_across_buckets" for i in range(2): for j in range(2): (source / f"{i}" / f"{j}").write_text("---") assert runner.invoke(app, ["mv", str(source), str(destination)]).exit_code == 0 - assert not GCSPath(source).exists() - assert GCSPath(destination).is_dir() + assert not Pathy(source).exists() + assert Pathy(destination).is_dir() # Ensure source files are gone for i in range(2): for j in range(2): @@ -100,13 +100,13 @@ def test_cli_mv_folder_across_buckets(with_adapter, bucket: str, other_bucket: s @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_rm_folder(with_adapter, bucket: str): - root = GCSPath.from_bucket(bucket) + root = Pathy.from_bucket(bucket) source = root / "cli_rm_folder" for i in range(2): for j in range(2): (source / f"{i}" / f"{j}").write_text("---") assert runner.invoke(app, ["rm", str(source)]).exit_code == 0 - assert not GCSPath(source).exists() + assert not Pathy(source).exists() # Ensure source files are gone for i in range(2): for j in range(2): @@ -116,21 +116,21 @@ def test_cli_rm_folder(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_rm_file(with_adapter, bucket: str): source = f"gs://{bucket}/cli_rm_file/file.txt" - GCSPath(source).write_text("---") - assert GCSPath(source).exists() + Pathy(source).write_text("---") + assert Pathy(source).exists() assert runner.invoke(app, ["rm", source]).exit_code == 0 - assert not GCSPath(source).exists() + assert not Pathy(source).exists() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_ls(with_adapter, bucket: str): - root = GCSPath.from_bucket(bucket) / "cli_ls" + root = Pathy.from_bucket(bucket) / "cli_ls" one = str(root / f"file.txt") two = str(root / f"other.txt") three = str(root / f"folder/file.txt") - GCSPath(one).write_text("---") - GCSPath(two).write_text("---") - GCSPath(three).write_text("---") + Pathy(one).write_text("---") + Pathy(two).write_text("---") + Pathy(three).write_text("---") result = runner.invoke(app, ["ls", str(root)]) assert result.exit_code == 0 assert one in result.output diff --git a/tests/test_client.py b/tests/test_client.py index 2e09259..ba526a4 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,8 +1,8 @@ from pathlib import Path -from gcspath import PureGCSPath -from gcspath.file import BucketClientFS +from pathy import PureGCSPath +from pathy.file import BucketClientFS def test_client_create_bucket(temp_folder: Path): diff --git a/tests/test_package.py b/tests/test_package.py index b345b70..9404621 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -1,4 +1,4 @@ -from gcspath.about import __version__ +from pathy.about import __version__ def test_package_defines_version(): diff --git a/tools/ci-set-build-version.ts b/tools/ci-set-build-version.ts index 4335c35..cf1e0e5 100644 --- a/tools/ci-set-build-version.ts +++ b/tools/ci-set-build-version.ts @@ -12,7 +12,7 @@ function getBuildVersion() { // Core options dryRun: true, branch: "master", - repositoryUrl: "https://github.com/justindujardin/gcspath.git" + repositoryUrl: "https://github.com/justindujardin/pathy.git" }, { cwd: "./", @@ -31,9 +31,9 @@ function getBuildVersion() { getBuildVersion() .then((version: any) => { console.log("--- UPDATING build version in python modules to : " + version); - const filePath = path.join(__dirname, "../gcspath/about.py"); + const filePath = path.join(__dirname, "../pathy/about.py"); if (!fs.existsSync(filePath)) { - console.error("gcspath.py is missing!"); + console.error("pathy.py is missing!"); process.exit(1); } const contents = fs.readFileSync(filePath, "utf8"); @@ -42,7 +42,7 @@ getBuildVersion() ); const match = contents.match(regexp); if (!match || match.length !== 3) { - console.error('__version__="x.x.x" string in gcspath.py was not found.'); + console.error('__version__="x.x.x" string in pathy.py was not found.'); } const replaceVersion = `${match[1]}${version}${match[2]}`; const newContents = contents.replace(regexp, replaceVersion); diff --git a/tools/test.sh b/tools/test.sh index 085d0ff..140a7b1 100644 --- a/tools/test.sh +++ b/tools/test.sh @@ -3,5 +3,5 @@ set -e echo "Activating virtualenv... (if this fails you may need to run setup.sh first)" . .env/bin/activate echo "Running tests..." -pytest tests --cov=gcspath +pytest tests --cov=pathy From 5a02f639ee416bda1458a76a29f1d9f6a7c67122 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 24 Apr 2020 15:33:38 +0000 Subject: [PATCH 09/75] chore(release): 0.1.0 # [0.1.0](https://github.com/justindujardin/pathy/compare/v0.0.17...v0.1.0) (2020-04-24) ### Features * add FluidPath and GCSPath.fluid method ([3393226](https://github.com/justindujardin/pathy/commit/3393226bc7f390f696d109bfac5f44e59a8b5151)) * **cli:** add ls [path] command ([17cab1d](https://github.com/justindujardin/pathy/commit/17cab1d8b96d92ca79e18512ac7e8a42aa136066)) * **cli:** add pathy executable with cp and mv commands ([98760fc](https://github.com/justindujardin/pathy/commit/98760fcfc0cb62891b7f2aac81a74fef088fdf78)) * **cli:** add rm [path] command ([31cea91](https://github.com/justindujardin/pathy/commit/31cea9156d99d9d465569c20c566943d4238c5dd)) * **pathy:** rename library to be more generic ([c62b14d](https://github.com/justindujardin/pathy/commit/c62b14da2aba25024af647e29df09ee57a13f6bd)) --- CHANGELOG.md | 11 +++++++++++ pathy/about.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a284323..6da2c61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# [0.1.0](https://github.com/justindujardin/pathy/compare/v0.0.17...v0.1.0) (2020-04-24) + + +### Features + +* add FluidPath and GCSPath.fluid method ([3393226](https://github.com/justindujardin/pathy/commit/3393226bc7f390f696d109bfac5f44e59a8b5151)) +* **cli:** add ls [path] command ([17cab1d](https://github.com/justindujardin/pathy/commit/17cab1d8b96d92ca79e18512ac7e8a42aa136066)) +* **cli:** add pathy executable with cp and mv commands ([98760fc](https://github.com/justindujardin/pathy/commit/98760fcfc0cb62891b7f2aac81a74fef088fdf78)) +* **cli:** add rm [path] command ([31cea91](https://github.com/justindujardin/pathy/commit/31cea9156d99d9d465569c20c566943d4238c5dd)) +* **pathy:** rename library to be more generic ([c62b14d](https://github.com/justindujardin/pathy/commit/c62b14da2aba25024af647e29df09ee57a13f6bd)) + ## [0.0.17](https://github.com/justindujardin/pathy/compare/v0.0.16...v0.0.17) (2020-04-17) diff --git a/pathy/about.py b/pathy/about.py index d9aac20..a01f63b 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.0.17" +__version__ = "0.1.0" __summary__ = "pathlib.Path subclasses for Google Cloud Storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From a87e36fbc13a705c1f7f9ed7909ff6c9fe8e494e Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 24 Apr 2020 10:25:00 -0700 Subject: [PATCH 10/75] feat(cli): add -r and -v flags for safer usage - rm will fail if given a directory without the -r flag (similar to unix rm) - rm will print the removed files/folders when given the -v flag --- pathy/cli.py | 31 +++++++++++++++++++++++-------- tests/test_cli.py | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/pathy/cli.py b/pathy/cli.py index 81af927..b217d56 100644 --- a/pathy/cli.py +++ b/pathy/cli.py @@ -1,8 +1,5 @@ -#!/usr/bin/env python3.7 -from pathlib import Path - import typer -from pathy import Pathy, FluidPath +from .api import Pathy, FluidPath app = typer.Typer() @@ -68,20 +65,38 @@ def mv(from_location: str, to_location: str): @app.command() -def rm(location: str, strict: bool = False): +def rm( + location: str, + recursive: bool = typer.Option( + False, "--recursive", "-r", help="Recursively remove files and folders." + ), + verbose: bool = typer.Option( + False, "--verbose", "-v", help="Print removed files and folders." + ), +): """ Remove a blob or folder of blobs from a given location. """ path: FluidPath = Pathy.fluid(location) - if not path.exists() and strict: - raise ValueError(f"from_path is not an existing Path or Pathy: {path}") + if not path.exists(): + raise typer.Exit(f"rm: {path}: No such file or directory") + if path.is_dir(): - to_unlink = [b for b in path.rglob("*") if b.is_file()] + if not recursive: + raise typer.Exit(f"rm: {path}: is a directory") + selector = path.rglob("*") if recursive else path.glob("*") + to_unlink = [b for b in selector if b.is_file()] for blob in to_unlink: + if verbose: + typer.echo(str(blob)) blob.unlink() if path.exists(): + if verbose: + typer.echo(str(path)) path.rmdir() elif path.is_file(): + if verbose: + typer.echo(str(path)) path.unlink() diff --git a/tests/test_cli.py b/tests/test_cli.py index f09d0bb..35b90d7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -98,6 +98,34 @@ def test_cli_mv_folder_across_buckets(with_adapter, bucket: str, other_bucket: s assert (destination / f"{i}" / f"{j}").is_file() +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_rm_file(with_adapter, bucket: str): + source = f"gs://{bucket}/cli_rm_file/file.txt" + Pathy(source).write_text("---") + assert Pathy(source).exists() + assert runner.invoke(app, ["rm", source]).exit_code == 0 + assert not Pathy(source).exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_cli_rm_verbose(with_adapter, bucket: str): + root = Pathy.from_bucket(bucket) / "cli_rm_folder" + source = str(root / "file.txt") + other = str(root / "folder/other") + Pathy(source).write_text("---") + Pathy(other).write_text("---") + result = runner.invoke(app, ["rm", "-v", source]) + assert result.exit_code == 0 + assert source in result.output + assert other not in result.output + + Pathy(source).write_text("---") + result = runner.invoke(app, ["rm", "-rv", str(root)]) + assert result.exit_code == 0 + assert source in result.output + assert other in result.output + + @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_rm_folder(with_adapter, bucket: str): root = Pathy.from_bucket(bucket) @@ -105,7 +133,10 @@ def test_cli_rm_folder(with_adapter, bucket: str): for i in range(2): for j in range(2): (source / f"{i}" / f"{j}").write_text("---") - assert runner.invoke(app, ["rm", str(source)]).exit_code == 0 + + # Returns exit code 1 without recursive flag when given a folder + assert runner.invoke(app, ["rm", str(source)]).exit_code == 1 + assert runner.invoke(app, ["rm", "-r", str(source)]).exit_code == 0 assert not Pathy(source).exists() # Ensure source files are gone for i in range(2): @@ -113,15 +144,6 @@ def test_cli_rm_folder(with_adapter, bucket: str): assert not (source / f"{i}" / f"{j}").is_file() -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_cli_rm_file(with_adapter, bucket: str): - source = f"gs://{bucket}/cli_rm_file/file.txt" - Pathy(source).write_text("---") - assert Pathy(source).exists() - assert runner.invoke(app, ["rm", source]).exit_code == 0 - assert not Pathy(source).exists() - - @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_ls(with_adapter, bucket: str): root = Pathy.from_bucket(bucket) / "cli_ls" From 263c6867a7b8c878fde431f3d7e2e66f5b96cfbe Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 24 Apr 2020 17:31:32 +0000 Subject: [PATCH 11/75] chore(release): 0.1.1 ## [0.1.1](https://github.com/justindujardin/pathy/compare/v0.1.0...v0.1.1) (2020-04-24) ### Features * **cli:** add -r and -v flags for safer usage ([a87e36f](https://github.com/justindujardin/pathy/commit/a87e36fbc13a705c1f7f9ed7909ff6c9fe8e494e)) --- CHANGELOG.md | 7 +++++++ pathy/about.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6da2c61..586dae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.1](https://github.com/justindujardin/pathy/compare/v0.1.0...v0.1.1) (2020-04-24) + + +### Features + +* **cli:** add -r and -v flags for safer usage ([a87e36f](https://github.com/justindujardin/pathy/commit/a87e36fbc13a705c1f7f9ed7909ff6c9fe8e494e)) + # [0.1.0](https://github.com/justindujardin/pathy/compare/v0.0.17...v0.1.0) (2020-04-24) diff --git a/pathy/about.py b/pathy/about.py index a01f63b..41c687b 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.1.0" +__version__ = "0.1.1" __summary__ = "pathlib.Path subclasses for Google Cloud Storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 2877b06562e4bb1d4767e9c297e2aee2fc1284ad Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sat, 23 May 2020 13:01:23 -0700 Subject: [PATCH 12/75] fix: path.owner() can raise when using filesystem adapter - catch the error and return a None owner --- pathy/cli.py | 23 ++++++++++++----------- pathy/file.py | 9 ++++++++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pathy/cli.py b/pathy/cli.py index b217d56..bf732e3 100644 --- a/pathy/cli.py +++ b/pathy/cli.py @@ -37,9 +37,19 @@ def mv(from_location: str, to_location: str): Move a blob or folder of blobs from one path to another. """ from_path: FluidPath = Pathy.fluid(from_location) - if not from_path.exists(): - raise ValueError(f"from_path is not an existing Path or Pathy: {from_path}") to_path: FluidPath = Pathy.fluid(to_location) + + if from_path.is_file(): + # Copy prefix from the source if the to_path has none. + # + # e.g. "cp ./file.txt gs://bucket-name/" writes "gs://bucket-name/file.txt" + if isinstance(to_path, Pathy) and to_path.prefix == "": + to_path = to_path / from_path + to_path.parent.mkdir(parents=True, exist_ok=True) + to_path.write_bytes(from_path.read_bytes()) + from_path.unlink() + return + if from_path.is_dir(): to_path.mkdir(parents=True, exist_ok=True) to_unlink = [] @@ -53,15 +63,6 @@ def mv(from_location: str, to_location: str): blob.unlink() if from_path.is_dir(): from_path.rmdir() - elif from_path.is_file(): - # Copy prefix from the source if the to_path has none. - # - # e.g. "cp ./file.txt gs://bucket-name/" writes "gs://bucket-name/file.txt" - if isinstance(to_path, Pathy) and to_path.prefix == "": - to_path = to_path / from_path - to_path.parent.mkdir(parents=True, exist_ok=True) - to_path.write_bytes(from_path.read_bytes()) - from_path.unlink() @app.command() diff --git a/pathy/file.py b/pathy/file.py index 6e6891a..59d6acf 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -39,9 +39,16 @@ def get_blob(self, blob_name: str) -> Optional[ClientBlobFS]: if not native_blob.exists() or native_blob.is_dir(): return None stat = native_blob.stat() + # path.owner() raises KeyError if the owner's UID isn't known + # + # https://docs.python.org/3/library/pathlib.html#pathlib.Path.owner + try: + owner = native_blob.owner() + except KeyError: + owner = None return ClientBlobFS( bucket=self, - owner=native_blob.owner(), + owner=owner, name=blob_name, raw=native_blob, size=stat.st_size, From 60c443fabd10386c391a9103816bc60e83c2d389 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 23 May 2020 20:07:00 +0000 Subject: [PATCH 13/75] chore(release): 0.1.2 ## [0.1.2](https://github.com/justindujardin/pathy/compare/v0.1.1...v0.1.2) (2020-05-23) ### Bug Fixes * path.owner() can raise when using filesystem adapter ([2877b06](https://github.com/justindujardin/pathy/commit/2877b06562e4bb1d4767e9c297e2aee2fc1284ad)) --- CHANGELOG.md | 7 +++++++ pathy/about.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 586dae6..75e7f7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.2](https://github.com/justindujardin/pathy/compare/v0.1.1...v0.1.2) (2020-05-23) + + +### Bug Fixes + +* path.owner() can raise when using filesystem adapter ([2877b06](https://github.com/justindujardin/pathy/commit/2877b06562e4bb1d4767e9c297e2aee2fc1284ad)) + ## [0.1.1](https://github.com/justindujardin/pathy/compare/v0.1.0...v0.1.1) (2020-04-24) diff --git a/pathy/about.py b/pathy/about.py index 41c687b..a218fac 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.1.1" +__version__ = "0.1.2" __summary__ = "pathlib.Path subclasses for Google Cloud Storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From e4810004eff21a605626d30cd717983787a6a8c6 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sun, 28 Jun 2020 12:49:06 -0700 Subject: [PATCH 14/75] feat: upgrade typer support - allow anything in range >=0.3.0,<1.0.0 --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0c040f3..e727ffc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ google-cloud-storage>=1.26.0,<2.0.0 smart-open==1.10.0 -typer>=0.1.0,<0.2.0 \ No newline at end of file +typer>=0.3.0,<1.0.0 +dataclasses>=0.6,<1.0; python_version < "3.7" \ No newline at end of file From e6186f8409efbbd2da0e940189f6a8215ffe4713 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 28 Jun 2020 19:53:42 +0000 Subject: [PATCH 15/75] chore(release): 0.1.3 ## [0.1.3](https://github.com/justindujardin/pathy/compare/v0.1.2...v0.1.3) (2020-06-28) ### Features * upgrade typer support ([e481000](https://github.com/justindujardin/pathy/commit/e4810004eff21a605626d30cd717983787a6a8c6)) --- CHANGELOG.md | 7 +++++++ pathy/about.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e7f7e..402f336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.1.3](https://github.com/justindujardin/pathy/compare/v0.1.2...v0.1.3) (2020-06-28) + + +### Features + +* upgrade typer support ([e481000](https://github.com/justindujardin/pathy/commit/e4810004eff21a605626d30cd717983787a6a8c6)) + ## [0.1.2](https://github.com/justindujardin/pathy/compare/v0.1.1...v0.1.2) (2020-05-23) diff --git a/pathy/about.py b/pathy/about.py index a218fac..53bd67e 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.1.2" +__version__ = "0.1.3" __summary__ = "pathlib.Path subclasses for Google Cloud Storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 83b07f208bd7d1146123c1576fab5accd664a02b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2020 21:06:36 +0000 Subject: [PATCH 16/75] chore(deps): bump npm from 6.14.2 to 6.14.6 Bumps [npm](https://github.com/npm/cli) from 6.14.2 to 6.14.6. - [Release notes](https://github.com/npm/cli/releases) - [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md) - [Commits](https://github.com/npm/cli/compare/v6.14.2...v6.14.6) Signed-off-by: dependabot[bot] --- package-lock.json | 62 ++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccf66ea..10fc4e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1670,9 +1670,9 @@ "dev": true }, "npm": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.2.tgz", - "integrity": "sha512-eBVjzvGJ9v2/jRJZFtIkvUVKmJ0sCJNNwc9Z1gI6llwaT7EBYWJe5o61Ipc1QR0FaDCKM3l1GizI09Ro3STJEw==", + "version": "6.14.6", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.6.tgz", + "integrity": "sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==", "dev": true, "requires": { "JSONStream": "^1.3.5", @@ -1704,7 +1704,7 @@ "fs-write-stream-atomic": "~1.0.10", "gentle-fs": "^2.3.0", "glob": "^7.1.6", - "graceful-fs": "^4.2.3", + "graceful-fs": "^4.2.4", "has-unicode": "~2.0.1", "hosted-git-info": "^2.8.8", "iferr": "^1.0.2", @@ -1741,10 +1741,10 @@ "lru-cache": "^5.1.1", "meant": "~1.0.1", "mississippi": "^3.0.0", - "mkdirp": "~0.5.1", + "mkdirp": "^0.5.5", "move-concurrently": "^1.0.1", "node-gyp": "^5.1.0", - "nopt": "~4.0.1", + "nopt": "^4.0.3", "normalize-package-data": "^2.5.0", "npm-audit-report": "^1.3.2", "npm-cache-filename": "~1.0.2", @@ -1754,7 +1754,7 @@ "npm-packlist": "^1.4.8", "npm-pick-manifest": "^3.0.2", "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.3", + "npm-registry-fetch": "^4.0.5", "npm-user-validate": "~1.0.0", "npmlog": "~4.1.2", "once": "~1.4.0", @@ -1775,7 +1775,7 @@ "readdir-scoped-modules": "^1.1.0", "request": "^2.88.0", "retry": "^0.12.0", - "rimraf": "^2.6.3", + "rimraf": "^2.7.1", "safe-buffer": "^5.1.2", "semver": "^5.7.1", "sha": "^3.0.0", @@ -2382,7 +2382,7 @@ "dev": true }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true }, @@ -2927,7 +2927,7 @@ } }, "graceful-fs": { - "version": "4.2.3", + "version": "4.2.4", "bundled": true, "dev": true }, @@ -3105,11 +3105,11 @@ "dev": true }, "is-ci": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "dev": true, "requires": { - "ci-info": "^1.0.0" + "ci-info": "^1.5.0" }, "dependencies": { "ci-info": { @@ -3181,7 +3181,7 @@ } }, "is-retry-allowed": { - "version": "1.1.0", + "version": "1.2.0", "bundled": true, "dev": true }, @@ -3653,11 +3653,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, "minizlib": { "version": "1.3.3", "bundled": true, @@ -3695,11 +3690,18 @@ } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.5", "bundled": true, "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true + } } }, "move-concurrently": { @@ -3766,7 +3768,7 @@ } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "dev": true, "requires": { @@ -3892,7 +3894,7 @@ } }, "npm-registry-fetch": { - "version": "4.0.3", + "version": "4.0.5", "bundled": true, "dev": true, "requires": { @@ -3906,7 +3908,7 @@ }, "dependencies": { "safe-buffer": { - "version": "5.2.0", + "version": "5.2.1", "bundled": true, "dev": true } @@ -4327,18 +4329,18 @@ "dev": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "dev": true, "requires": { - "deep-extend": "^0.5.1", + "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { - "version": "1.2.0", + "version": "1.2.5", "bundled": true, "dev": true } @@ -4418,7 +4420,7 @@ } }, "registry-auth-token": { - "version": "3.3.2", + "version": "3.4.0", "bundled": true, "dev": true, "requires": { @@ -4482,7 +4484,7 @@ "dev": true }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "dev": true, "requires": { @@ -4666,7 +4668,7 @@ } }, "spdx-license-ids": { - "version": "3.0.3", + "version": "3.0.5", "bundled": true, "dev": true }, @@ -5101,7 +5103,7 @@ } }, "widest-line": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { From 18938a216c69b2611b7b7edf82809af66f39f6b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2020 22:27:59 +0000 Subject: [PATCH 17/75] chore(deps): bump lodash from 4.17.15 to 4.17.19 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10fc4e8..efee8ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1357,9 +1357,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.capitalize": { From 5632f264ed5d22b54b1c284ca1d79d2e248c5fd3 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 14:24:34 -0700 Subject: [PATCH 18/75] refactor: rename PureGCSPath to PurePathy Be more consistent with the Pathy naming. BREAKING CHANGE: PureGCSPath is now PurePathy --- pathy/__init__.py | 2 +- pathy/api.py | 4 +- pathy/base.py | 18 +--- pathy/client.py | 24 ++--- pathy/file.py | 28 +++--- pathy/gcs.py | 16 ++-- tests/test_api.py | 4 +- tests/test_base.py | 203 +++++++++++++++++++++--------------------- tests/test_cli.py | 3 + tests/test_client.py | 4 +- tests/test_package.py | 5 -- 11 files changed, 149 insertions(+), 162 deletions(-) delete mode 100644 tests/test_package.py diff --git a/pathy/__init__.py b/pathy/__init__.py index 4d76302..ddaa244 100644 --- a/pathy/__init__.py +++ b/pathy/__init__.py @@ -5,7 +5,7 @@ ClientBucket, ClientError, Pathy, - PureGCSPath, + PurePathy, FluidPath, clear_fs_cache, get_fs_cache, diff --git a/pathy/api.py b/pathy/api.py index 2c93908..7809722 100644 --- a/pathy/api.py +++ b/pathy/api.py @@ -13,7 +13,7 @@ from google.cloud import storage from . import gcs -from .base import PureGCSPath, PathType +from .base import PurePathy, PathType from .client import ( BucketClient, BucketEntry, @@ -112,7 +112,7 @@ def clear_fs_cache(force: bool = False) -> None: FluidPath = Union["Pathy", Path] -class Pathy(Path, PureGCSPath): +class Pathy(Path, PurePathy): """Path subclass for GCS service. Write files to and read files from the GCS service using pathlib.Path diff --git a/pathy/base.py b/pathy/base.py index 97455c9..feef3e2 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -39,31 +39,21 @@ def make_uri(self, path): _gcs_flavour = _GCSFlavour() -class PureGCSPath(PurePath): - """ - PurePath subclass for GCS service. - - GCS is not a file-system but we can look at it like a POSIX system. - """ +class PurePathy(PurePath): + """PurePath subclass for bucket storage.""" _flavour = _gcs_flavour __slots__ = () @property def bucket(self): - """ - bucket property - return a new instance of only the bucket path - """ + """Return a new instance of only the bucket path.""" self._absolute_path_validation() return type(self)(f"{self.drive}//{self.root}") @property def key(self): - """ - key property - return a new instance of only the key path - """ + """Return a new instance of only the key path.""" self._absolute_path_validation() key = self._flavour.sep.join(self.parts[2:]) if not key or len(self.parts) < 2: diff --git a/pathy/client.py b/pathy/client.py index f0b1649..cfd625a 100644 --- a/pathy/client.py +++ b/pathy/client.py @@ -3,7 +3,7 @@ import smart_open -from .base import PureGCSPath +from .base import PurePathy __all__ = ( "BucketStat", @@ -118,16 +118,16 @@ def delete_blobs(self, blobs: List[ClientBlob]) -> None: class BucketClient: """Base class for a client that interacts with a bucket-based storage system.""" - def make_uri(self, path: PureGCSPath) -> str: + def make_uri(self, path: PurePathy) -> str: return path.as_uri() - def is_dir(self, path: PureGCSPath) -> bool: + def is_dir(self, path: PurePathy) -> bool: return any(self.list_blobs(path, prefix=path.prefix)) - def rmdir(self, path: PureGCSPath) -> None: + def rmdir(self, path: PurePathy) -> None: return None - def exists(self, path: PureGCSPath) -> bool: + def exists(self, path: PurePathy) -> bool: # Because we want all the parents of a valid blob (e.g. "directory" in # "directory/foo.file") to return True, we enumerate the blobs with a prefix # and compare the object names to see if they match a substring of the path @@ -141,7 +141,7 @@ def exists(self, path: PureGCSPath) -> bool: def open( self, - path: PureGCSPath, + path: PurePathy, *, mode="r", buffering=-1, @@ -160,10 +160,10 @@ def open( ignore_ext=True, ) - def lookup_bucket(self, path: PureGCSPath) -> Optional[ClientBucket]: + def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucket]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def get_bucket(self, path: PureGCSPath) -> ClientBucket: + def get_bucket(self, path: PurePathy) -> ClientBucket: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) def list_buckets(self) -> Generator[ClientBucket, None, None]: @@ -171,7 +171,7 @@ def list_buckets(self) -> Generator[ClientBucket, None, None]: def list_blobs( self, - path: PureGCSPath, + path: PurePathy, prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, @@ -180,14 +180,14 @@ def list_blobs( def scandir( self, - path: PureGCSPath = None, + path: PurePathy = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def create_bucket(self, path: PureGCSPath) -> ClientBucket: + def create_bucket(self, path: PurePathy) -> ClientBucket: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def delete_bucket(self, path: PureGCSPath) -> None: + def delete_bucket(self, path: PurePathy) -> None: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) diff --git a/pathy/file.py b/pathy/file.py index 59d6acf..48c1091 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from typing import Optional, List, Generator, cast from .client import BucketClient, ClientBucket, ClientBlob, ClientError, BucketEntry -from .base import PureGCSPath +from .base import PurePathy import pathlib import shutil import os @@ -79,11 +79,11 @@ class BucketClientFS(BucketClient): # Root to store file-system buckets as children of root: pathlib.Path - def make_uri(self, path: PureGCSPath): + def make_uri(self, path: PurePathy): uri = super().make_uri(path) return uri.replace("gs://", "file:///") - def full_path(self, path: PureGCSPath) -> pathlib.Path: + def full_path(self, path: PurePathy) -> pathlib.Path: if path.root is None: raise ValueError(f"Invalid bucket name for path: {path}") full_path = self.root.absolute() / path.root @@ -91,20 +91,20 @@ def full_path(self, path: PureGCSPath) -> pathlib.Path: full_path = full_path / path.key return full_path - def exists(self, path: PureGCSPath) -> bool: + def exists(self, path: PurePathy) -> bool: """Return True if the path exists as a file or folder on disk""" return self.full_path(path).exists() - def is_dir(self, path: PureGCSPath) -> bool: + def is_dir(self, path: PurePathy) -> bool: return self.full_path(path).is_dir() - def rmdir(self, path: PureGCSPath) -> None: + def rmdir(self, path: PurePathy) -> None: full_path = self.full_path(path) return shutil.rmtree(str(full_path)) def open( self, - path: PureGCSPath, + path: PurePathy, *, mode="r", buffering=-1, @@ -129,13 +129,13 @@ def open( newline=newline, ) - def make_uri(self, path: PureGCSPath) -> str: + def make_uri(self, path: PurePathy) -> str: if not path.root: raise ValueError(f"cannot make a URI to an invalid bucket: {path.root}") result = f"file://{self.root.absolute() / path.root / path.key}" return result - def create_bucket(self, path: PureGCSPath) -> ClientBucket: + def create_bucket(self, path: PurePathy) -> ClientBucket: if not path.root: raise ValueError(f"Invalid bucket name: {path.root}") bucket_path: pathlib.Path = self.root / path.root @@ -144,19 +144,19 @@ def create_bucket(self, path: PureGCSPath) -> ClientBucket: bucket_path.mkdir(parents=True, exist_ok=True) return ClientBucketFS(str(path.root), bucket=bucket_path) - def delete_bucket(self, path: PureGCSPath) -> None: + def delete_bucket(self, path: PurePathy) -> None: bucket_path: pathlib.Path = self.root / str(path.root) if bucket_path.exists(): shutil.rmtree(bucket_path) - def lookup_bucket(self, path: PureGCSPath) -> Optional[ClientBucketFS]: + def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucketFS]: if path.root: bucket_path: pathlib.Path = self.root / path.root if bucket_path.exists(): return ClientBucketFS(str(path.root), bucket=bucket_path) return None - def get_bucket(self, path: PureGCSPath) -> ClientBucketFS: + def get_bucket(self, path: PurePathy) -> ClientBucketFS: if not path.root: raise ValueError(f"path has an invalid bucket_name: {path.root}") bucket_path: pathlib.Path = self.root / path.root @@ -171,7 +171,7 @@ def list_buckets(self, **kwargs) -> Generator[ClientBucketFS, None, None]: def scandir( self, - path: Optional[PureGCSPath] = None, + path: Optional[PurePathy] = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, ) -> Generator[BucketEntryFS, None, None]: @@ -210,7 +210,7 @@ def scandir( def list_blobs( self, - path: PureGCSPath, + path: PurePathy, prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, diff --git a/pathy/gcs.py b/pathy/gcs.py index 363d49b..3822be9 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from typing import Optional, List, Generator from .client import BucketClient, ClientBucket, ClientBlob, ClientError, BucketEntry -from .base import PureGCSPath +from .base import PurePathy try: from google.cloud import storage @@ -71,17 +71,17 @@ def delete_blobs(self, blobs: List[ClientBlobGCS]) -> None: class BucketClientGCS(BucketClient): client: storage.Client = field(default_factory=lambda: storage.Client()) - def make_uri(self, path: PureGCSPath): + def make_uri(self, path: PurePathy): return str(path) - def create_bucket(self, path: PureGCSPath) -> ClientBucket: + def create_bucket(self, path: PurePathy) -> ClientBucket: return self.client.create_bucket(path.root) - def delete_bucket(self, path: PureGCSPath) -> None: + def delete_bucket(self, path: PurePathy) -> None: bucket = self.client.get_bucket(path.root) bucket.delete() - def lookup_bucket(self, path: PureGCSPath) -> Optional[ClientBucketGCS]: + def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucketGCS]: try: native_bucket = self.client.lookup_bucket(path.root) if native_bucket is not None: @@ -90,7 +90,7 @@ def lookup_bucket(self, path: PureGCSPath) -> Optional[ClientBucketGCS]: pass return None - def get_bucket(self, path: PureGCSPath) -> ClientBucketGCS: + def get_bucket(self, path: PurePathy) -> ClientBucketGCS: try: native_bucket = self.client.lookup_bucket(path.root) if native_bucket is not None: @@ -104,7 +104,7 @@ def list_buckets(self, **kwargs) -> Generator[ClientBucket, None, None]: def scandir( self, - path: Optional[PureGCSPath] = None, + path: Optional[PurePathy] = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, include_raw: bool = False, @@ -152,7 +152,7 @@ def scandir( def list_blobs( self, - path: PureGCSPath, + path: PurePathy, prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, diff --git a/tests/test_api.py b/tests/test_api.py index fe38aea..9263762 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -15,7 +15,7 @@ BucketStat, FluidPath, Pathy, - PureGCSPath, + PurePathy, clear_fs_cache, get_fs_client, use_fs, @@ -29,7 +29,7 @@ def test_api_path_support(): - assert PureGCSPath in Pathy.mro() # type: ignore + assert PurePathy in Pathy.mro() # type: ignore assert Path in Pathy.mro() # type: ignore diff --git a/tests/test_base.py b/tests/test_base.py index 336e9d5..9637284 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -4,7 +4,7 @@ import pytest -from pathy import Pathy, PureGCSPath +from pathy import Pathy, PurePathy def test_base_not_supported(monkeypatch): @@ -83,201 +83,200 @@ def test_base_symlink_to(): def test_base_paths_of_a_different_flavour(): with pytest.raises(TypeError): - PureGCSPath("/bucket/key") < PurePosixPath("/bucket/key") + PurePathy("/bucket/key") < PurePosixPath("/bucket/key") with pytest.raises(TypeError): - PureWindowsPath("/bucket/key") > PureGCSPath("/bucket/key") + PureWindowsPath("/bucket/key") > PurePathy("/bucket/key") def test_base_repr(): - a = PureGCSPath("/var/tests/fake") - f = a.prefix + a = PurePathy("/var/tests/fake") assert a.as_posix() == "/var/tests/fake" - assert repr(PureGCSPath("fake_file.txt")) == "PureGCSPath('fake_file.txt')" - assert str(PureGCSPath("fake_file.txt")) == "fake_file.txt" - assert bytes(PureGCSPath("fake_file.txt")) == b"fake_file.txt" + assert repr(PurePathy("fake_file.txt")) == "PurePathy('fake_file.txt')" + assert str(PurePathy("fake_file.txt")) == "fake_file.txt" + assert bytes(PurePathy("fake_file.txt")) == b"fake_file.txt" @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_base_fspath(): - assert os.fspath(PureGCSPath("/var/tests/fake")) == "/var/tests/fake" + assert os.fspath(PurePathy("/var/tests/fake")) == "/var/tests/fake" def test_base_join_strs(): - assert PureGCSPath("foo", "some/path", "bar") == PureGCSPath("foo/some/path/bar") + assert PurePathy("foo", "some/path", "bar") == PurePathy("foo/some/path/bar") def test_base_join_paths(): - assert PureGCSPath(Path("foo"), Path("bar")) == PureGCSPath("foo/bar") + assert PurePathy(Path("foo"), Path("bar")) == PurePathy("foo/bar") def test_base_empty(): - assert PureGCSPath() == PureGCSPath(".") + assert PurePathy() == PurePathy(".") def test_base_absolute_paths(): - assert PureGCSPath("/etc", "/usr", "lib64") == PureGCSPath("/usr/lib64") + assert PurePathy("/etc", "/usr", "lib64") == PurePathy("/usr/lib64") def test_base_slashes_single_double_dots(): - assert PureGCSPath("foo//bar") == PureGCSPath("foo/bar") - assert PureGCSPath("foo/./bar") == PureGCSPath("foo/bar") - assert PureGCSPath("foo/../bar") == PureGCSPath("bar") - assert PureGCSPath("../bar") == PureGCSPath("../bar") - assert PureGCSPath("foo", "../bar") == PureGCSPath("bar") + assert PurePathy("foo//bar") == PurePathy("foo/bar") + assert PurePathy("foo/./bar") == PurePathy("foo/bar") + assert PurePathy("foo/../bar") == PurePathy("bar") + assert PurePathy("../bar") == PurePathy("../bar") + assert PurePathy("foo", "../bar") == PurePathy("bar") def test_base_operators(): - assert PureGCSPath("/etc") / "init.d" / "apache2" == PureGCSPath( + assert PurePathy("/etc") / "init.d" / "apache2" == PurePathy( "/etc/init.d/apache2" ) - assert "/var" / PureGCSPath("tests") / "fake" == PureGCSPath("/var/tests/fake") + assert "/var" / PurePathy("tests") / "fake" == PurePathy("/var/tests/fake") def test_base_parts(): - assert PureGCSPath("../bar").parts == ("..", "bar") - assert PureGCSPath("foo//bar").parts == ("foo", "bar") - assert PureGCSPath("foo/./bar").parts == ("foo", "bar") - assert PureGCSPath("foo/../bar").parts == ("bar",) - assert PureGCSPath("foo", "../bar").parts == ("bar",) - assert PureGCSPath("/foo/bar").parts == ("/", "foo", "bar") + assert PurePathy("../bar").parts == ("..", "bar") + assert PurePathy("foo//bar").parts == ("foo", "bar") + assert PurePathy("foo/./bar").parts == ("foo", "bar") + assert PurePathy("foo/../bar").parts == ("bar",) + assert PurePathy("foo", "../bar").parts == ("bar",) + assert PurePathy("/foo/bar").parts == ("/", "foo", "bar") def test_base_drive(): - assert PureGCSPath("foo//bar").drive == "" - assert PureGCSPath("foo/./bar").drive == "" - assert PureGCSPath("foo/../bar").drive == "" - assert PureGCSPath("../bar").drive == "" - assert PureGCSPath("foo", "../bar").drive == "" - assert PureGCSPath("/foo/bar").drive == "" + assert PurePathy("foo//bar").drive == "" + assert PurePathy("foo/./bar").drive == "" + assert PurePathy("foo/../bar").drive == "" + assert PurePathy("../bar").drive == "" + assert PurePathy("foo", "../bar").drive == "" + assert PurePathy("/foo/bar").drive == "" def test_base_root(): - assert PureGCSPath("foo//bar").root == "" - assert PureGCSPath("foo/./bar").root == "" - assert PureGCSPath("foo/../bar").root == "" - assert PureGCSPath("../bar").root == "" - assert PureGCSPath("foo", "../bar").root == "" - assert PureGCSPath("/foo/bar").root == "/" + assert PurePathy("foo//bar").root == "" + assert PurePathy("foo/./bar").root == "" + assert PurePathy("foo/../bar").root == "" + assert PurePathy("../bar").root == "" + assert PurePathy("foo", "../bar").root == "" + assert PurePathy("/foo/bar").root == "/" def test_base_anchor(): - assert PureGCSPath("foo//bar").anchor == "" - assert PureGCSPath("foo/./bar").anchor == "" - assert PureGCSPath("foo/../bar").anchor == "" - assert PureGCSPath("../bar").anchor == "" - assert PureGCSPath("foo", "../bar").anchor == "" - assert PureGCSPath("/foo/bar").anchor == "/" + assert PurePathy("foo//bar").anchor == "" + assert PurePathy("foo/./bar").anchor == "" + assert PurePathy("foo/../bar").anchor == "" + assert PurePathy("../bar").anchor == "" + assert PurePathy("foo", "../bar").anchor == "" + assert PurePathy("/foo/bar").anchor == "/" def test_base_parents(): - assert tuple(PureGCSPath("foo//bar").parents) == ( - PureGCSPath("foo"), - PureGCSPath("."), + assert tuple(PurePathy("foo//bar").parents) == ( + PurePathy("foo"), + PurePathy("."), ) - assert tuple(PureGCSPath("foo/./bar").parents) == ( - PureGCSPath("foo"), - PureGCSPath("."), + assert tuple(PurePathy("foo/./bar").parents) == ( + PurePathy("foo"), + PurePathy("."), ) - assert tuple(PureGCSPath("foo/../bar").parents) == (PureGCSPath("."),) - assert tuple(PureGCSPath("../bar").parents) == (PureGCSPath(".."), PureGCSPath(".")) - assert tuple(PureGCSPath("foo", "../bar").parents) == (PureGCSPath("."),) - assert tuple(PureGCSPath("/foo/bar").parents) == ( - PureGCSPath("/foo"), - PureGCSPath("/"), + assert tuple(PurePathy("foo/../bar").parents) == (PurePathy("."),) + assert tuple(PurePathy("../bar").parents) == (PurePathy(".."), PurePathy(".")) + assert tuple(PurePathy("foo", "../bar").parents) == (PurePathy("."),) + assert tuple(PurePathy("/foo/bar").parents) == ( + PurePathy("/foo"), + PurePathy("/"), ) def test_base_parent(): - assert PureGCSPath("foo//bar").parent == PureGCSPath("foo") - assert PureGCSPath("foo/./bar").parent == PureGCSPath("foo") - assert PureGCSPath("foo/../bar").parent == PureGCSPath(".") - assert PureGCSPath("../bar").parent == PureGCSPath("..") - assert PureGCSPath("foo", "../bar").parent == PureGCSPath(".") - assert PureGCSPath("/foo/bar").parent == PureGCSPath("/foo") - assert PureGCSPath(".").parent == PureGCSPath(".") - assert PureGCSPath("/").parent == PureGCSPath("/") + assert PurePathy("foo//bar").parent == PurePathy("foo") + assert PurePathy("foo/./bar").parent == PurePathy("foo") + assert PurePathy("foo/../bar").parent == PurePathy(".") + assert PurePathy("../bar").parent == PurePathy("..") + assert PurePathy("foo", "../bar").parent == PurePathy(".") + assert PurePathy("/foo/bar").parent == PurePathy("/foo") + assert PurePathy(".").parent == PurePathy(".") + assert PurePathy("/").parent == PurePathy("/") def test_base_name(): - assert PureGCSPath("my/library/fake_file.txt").name == "fake_file.txt" + assert PurePathy("my/library/fake_file.txt").name == "fake_file.txt" def test_base_suffix(): - assert PureGCSPath("my/library/fake_file.txt").suffix == ".txt" - assert PureGCSPath("my/library.tar.gz").suffix == ".gz" - assert PureGCSPath("my/library").suffix == "" + assert PurePathy("my/library/fake_file.txt").suffix == ".txt" + assert PurePathy("my/library.tar.gz").suffix == ".gz" + assert PurePathy("my/library").suffix == "" def test_base_suffixes(): - assert PureGCSPath("my/library.tar.gar").suffixes == [".tar", ".gar"] - assert PureGCSPath("my/library.tar.gz").suffixes == [".tar", ".gz"] - assert PureGCSPath("my/library").suffixes == [] + assert PurePathy("my/library.tar.gar").suffixes == [".tar", ".gar"] + assert PurePathy("my/library.tar.gz").suffixes == [".tar", ".gz"] + assert PurePathy("my/library").suffixes == [] def test_base_stem(): - assert PureGCSPath("my/library.tar.gar").stem == "library.tar" - assert PureGCSPath("my/library.tar").stem == "library" - assert PureGCSPath("my/library").stem == "library" + assert PurePathy("my/library.tar.gar").stem == "library.tar" + assert PurePathy("my/library.tar").stem == "library" + assert PurePathy("my/library").stem == "library" def test_base_uri(): - assert PureGCSPath("/etc/passwd").as_uri() == "gs://etc/passwd" - assert PureGCSPath("/etc/init.d/apache2").as_uri() == "gs://etc/init.d/apache2" - assert PureGCSPath("/bucket/key").as_uri() == "gs://bucket/key" + assert PurePathy("/etc/passwd").as_uri() == "gs://etc/passwd" + assert PurePathy("/etc/init.d/apache2").as_uri() == "gs://etc/init.d/apache2" + assert PurePathy("/bucket/key").as_uri() == "gs://bucket/key" def test_base_absolute(): - assert PureGCSPath("/a/b").is_absolute() - assert not PureGCSPath("a/b").is_absolute() + assert PurePathy("/a/b").is_absolute() + assert not PurePathy("a/b").is_absolute() def test_base_reserved(): - assert not PureGCSPath("/a/b").is_reserved() - assert not PureGCSPath("a/b").is_reserved() + assert not PurePathy("/a/b").is_reserved() + assert not PurePathy("a/b").is_reserved() def test_base_joinpath(): - assert PureGCSPath("/etc").joinpath("passwd") == PureGCSPath("/etc/passwd") - assert PureGCSPath("/etc").joinpath(PureGCSPath("passwd")) == PureGCSPath( + assert PurePathy("/etc").joinpath("passwd") == PurePathy("/etc/passwd") + assert PurePathy("/etc").joinpath(PurePathy("passwd")) == PurePathy( "/etc/passwd" ) - assert PureGCSPath("/etc").joinpath("init.d", "apache2") == PureGCSPath( + assert PurePathy("/etc").joinpath("init.d", "apache2") == PurePathy( "/etc/init.d/apache2" ) def test_base_match(): - assert PureGCSPath("a/b.py").match("*.py") - assert PureGCSPath("/a/b/c.py").match("b/*.py") - assert not PureGCSPath("/a/b/c.py").match("a/*.py") - assert PureGCSPath("/a.py").match("/*.py") - assert not PureGCSPath("a/b.py").match("/*.py") - assert not PureGCSPath("a/b.py").match("*.Py") + assert PurePathy("a/b.py").match("*.py") + assert PurePathy("/a/b/c.py").match("b/*.py") + assert not PurePathy("/a/b/c.py").match("a/*.py") + assert PurePathy("/a.py").match("/*.py") + assert not PurePathy("a/b.py").match("/*.py") + assert not PurePathy("a/b.py").match("*.Py") def test_base_relative_to(): - gcs_path = PureGCSPath("/etc/passwd") - assert gcs_path.relative_to("/") == PureGCSPath("etc/passwd") - assert gcs_path.relative_to("/etc") == PureGCSPath("passwd") + gcs_path = PurePathy("/etc/passwd") + assert gcs_path.relative_to("/") == PurePathy("etc/passwd") + assert gcs_path.relative_to("/etc") == PurePathy("passwd") with pytest.raises(ValueError): gcs_path.relative_to("/usr") def test_base_with_name(): - gcs_path = PureGCSPath("/Downloads/pathlib.tar.gz") - assert gcs_path.with_name("fake_file.txt") == PureGCSPath( + gcs_path = PurePathy("/Downloads/pathlib.tar.gz") + assert gcs_path.with_name("fake_file.txt") == PurePathy( "/Downloads/fake_file.txt" ) - gcs_path = PureGCSPath("/") + gcs_path = PurePathy("/") with pytest.raises(ValueError): gcs_path.with_name("fake_file.txt") def test_base_with_suffix(): - gcs_path = PureGCSPath("/Downloads/pathlib.tar.gz") - assert gcs_path.with_suffix(".bz2") == PureGCSPath("/Downloads/pathlib.tar.bz2") - gcs_path = PureGCSPath("README") - assert gcs_path.with_suffix(".txt") == PureGCSPath("README.txt") - gcs_path = PureGCSPath("README.txt") - assert gcs_path.with_suffix("") == PureGCSPath("README") + gcs_path = PurePathy("/Downloads/pathlib.tar.gz") + assert gcs_path.with_suffix(".bz2") == PurePathy("/Downloads/pathlib.tar.bz2") + gcs_path = PurePathy("README") + assert gcs_path.with_suffix(".txt") == PurePathy("README.txt") + gcs_path = PurePathy("README.txt") + assert gcs_path.with_suffix("") == PurePathy("README") diff --git a/tests/test_cli.py b/tests/test_cli.py index 35b90d7..b9b3ab6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -8,6 +8,9 @@ runner = CliRunner() +# TODO: add a test for wildcard cp/mv/rm/ls paths (e.g. "pathy cp gs://my-bucket/*.file ./") +# TODO: add a test for streaming in/out sources (e.g. "pathy cp - gs://my-bucket/my.file") + @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_cp_file(with_adapter, bucket: str): diff --git a/tests/test_client.py b/tests/test_client.py index ba526a4..cb8bbe5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,7 +1,7 @@ from pathlib import Path -from pathy import PureGCSPath +from pathy import PurePathy from pathy.file import BucketClientFS @@ -9,5 +9,5 @@ def test_client_create_bucket(temp_folder: Path): bucket_target = temp_folder / "foo" assert bucket_target.exists() is False cl = BucketClientFS(temp_folder) - cl.create_bucket(PureGCSPath("gs://foo/")) + cl.create_bucket(PurePathy("gs://foo/")) assert bucket_target.exists() is True diff --git a/tests/test_package.py b/tests/test_package.py deleted file mode 100644 index 9404621..0000000 --- a/tests/test_package.py +++ /dev/null @@ -1,5 +0,0 @@ -from pathy.about import __version__ - - -def test_package_defines_version(): - assert isinstance(__version__, str) From 0213d2f7028c08d40d863d1cc123e7d55ff1c89f Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 17:02:17 -0700 Subject: [PATCH 19/75] feat(README): generate API and CLI docs --- README.md | 391 +++++++++++++++++++++++++++++++++++++++++-- pathy/api.py | 83 ++------- pathy/cli.py | 2 +- requirements-dev.txt | 2 + tools/docs.py | 34 ++++ tools/docs.sh | 8 + 6 files changed, 441 insertions(+), 79 deletions(-) create mode 100644 tools/docs.py create mode 100644 tools/docs.sh diff --git a/README.md b/README.md index a17b45d..2175e7f 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,392 @@ -# Pathy +# Pathy: a python Path interface for bucket storage [![Build status](https://travis-ci.org/justindujardin/pathy.svg?branch=master)](https://travis-ci.org/justindujardin/pathy) [![Pypi version](https://badgen.net/pypi/v/pathy)](https://pypi.org/project/pathy/) -> IMPORTANT: this library is not ready for use +Pathy is a python package (_with type annotations_) for working with Bucket storage providers. It provides a CLI app for basic file operations between local files and remote buckets. It enables a smooth developer experience by supporting local file-system backed buckets during development and testing. It makes converting bucket blobs into local files a snap with optional local file caching of blobs. -Pathy provides a convenient Pythonic File-System/Path like interface to Google Cloud Storage using [google-cloud-storage](https://pypi.org/project/google-cloud-storage/) package as a driver. +## 🚀 Quickstart -It is based on the [S3Path](https://github.com/liormizr/s3path) project, which provides a similar interface for S3 buckets. +You can install `pathy` from pip: -# Install +```bash +pip install pathy +``` -From PyPI: +The package exports the `Pathy` class and utilities for configuring the bucket storage provider to use. By default Pathy prefers GoogleCloudStorage paths of the form `gs://bucket_name/folder/blob_name.txt`. Internally Pathy can convert GCS paths to local files, allowing for a nice developer experience. -```bash -$ pip install pathy +```python +from pathy import Pathy + +# Any bucket you have access to will work +greeting = Pathy(f"gs://my_bucket/greeting.txt") +# The blob doesn't exist yet +assert not greeting.exists() +# Create it by writing some text +greeting.write_text("Hello World!") +# Now it exists +assert greeting.exists() +# Delete it +greeting.unlink() +# Now it doesn't +assert not greeting.exists() +``` + +## 🎛 API + + + + +# Pathy + +```python +Pathy(self, args, kwargs) +``` + +Subclass of pathlib.Path that works with bucket storage providers. + +## exists + +```python +Pathy.exists(self: ~PathType) -> bool +``` + +Whether the path points to an existing Bucket, key or key prefix. + +## fluid + +```python +Pathy.fluid( + path_candidate: Union[str, Pathy, pathlib.Path], +) -> Union[Pathy, pathlib.Path] +``` + +Helper to infer a pathlib.Path or Pathy from an input path or string. + +The returned type is a union of the potential `FluidPath` types and will +type-check correctly against the minimum overlapping APIs of all the input +types. + +If you need to use specific implementation details of a type, you +will need to narrow the return of this function to the desired type, e.g. + +```python +# Narrow the type to a specific class +assert isinstance(path, Pathy), "must be Pathy" +# Use a member specific to that class +print(path.prefix) +``` + +## from_bucket + +```python +Pathy.from_bucket(bucket_name: str) -> 'Pathy' +``` + +Helper to convert a bucket name into a Pathy without needing +to add the leading and trailing slashes + +## glob + +```python +Pathy.glob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] +``` + +Glob the given relative pattern in the Bucket / key prefix represented +by this path, yielding all matching files (of any kind) + +## is_dir + +```python +Pathy.is_dir(self: ~PathType) -> bool +``` + +Returns True if the path points to a Bucket or a key prefix, False if it +points to a full key path. False is returned if the path doesn’t exist. +Other errors (such as permission errors) are propagated. + +## is_file + +```python +Pathy.is_file(self: ~PathType) -> bool +``` + +Returns True if the path points to a Bucket key, False if it points to +Bucket or a key prefix. False is returned if the path doesn’t exist. +Other errors (such as permission errors) are propagated. + +## iterdir + +```python +Pathy.iterdir(self: ~PathType) -> Generator[~PathType, NoneType, NoneType] +``` + +When the path points to a Bucket or a key prefix, yield path objects of +the directory contents + +## mkdir + +```python +Pathy.mkdir( + self: ~PathType, + mode: int = 511, + parents: bool = False, + exist_ok: bool = False, +) -> None +``` + +Create a path bucket. +Bucket storage doesn't support folders explicitly, so mkdir will only create a bucket. + +If exist_ok is false (the default), FileExistsError is raised if the +target Bucket already exists. + +If exist_ok is true, OSError exceptions will be ignored. + +if parents is false (the default), mkdir will create the bucket only +if this is a Bucket path. + +if parents is true, mkdir will create the bucket even if the path +have a Key path. + +mode argument is ignored. + +## open + +```python +Pathy.open( + self: ~PathType, + mode = 'r', + buffering = 8192, + encoding = None, + errors = None, + newline = None, +) +``` + +Opens the Bucket key pointed to by the path, returns a Key file object +that you can read/write with. + +## owner + +```python +Pathy.owner(self: ~PathType) -> Optional[str] +``` + +Returns the name of the user owning the Bucket or key. +Similarly to boto3's ObjectSummary owner attribute + +## rename + +```python +Pathy.rename(self: ~PathType, target: Union[str, ~PathType]) -> None +``` + +Rename this file or Bucket / key prefix / key to the given target. +If target exists and is a file, it will be replaced silently if the user +has permission. If path is a key prefix, it will replace all the keys with +the same prefix to the new target prefix. Target can be either a string or +another Pathy object. + +## replace + +```python +Pathy.replace(self: ~PathType, target: Union[str, ~PathType]) -> None +``` + +Renames this Bucket / key prefix / key to the given target. +If target points to an existing Bucket / key prefix / key, it will be +unconditionally replaced. + +## resolve + +```python +Pathy.resolve(self: ~PathType) -> ~PathType +``` + +Resolve the given path to remove any relative path specifiers. + +## rglob + +```python +Pathy.rglob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] ``` -# Requirements +This is like calling Pathy.glob with "\*\*/" added in front of the given +relative pattern. + +## rmdir + +```python +Pathy.rmdir(self: ~PathType) -> None +``` + +Removes this Bucket / key prefix. The Bucket / key prefix must be empty + +## samefile + +```python +Pathy.samefile(self: ~PathType, other_path: ~PathType) -> bool +``` + +Returns whether this path points to the same Bucket key as other_path, +Which can be either a Path object, or a string + +## stat + +```python +Pathy.stat(self: ~PathType) -> pathy.client.BucketStat +``` + +Returns information about this path. +The result is looked up at each call to this method + +## to_local + +```python +Pathy.to_local( + blob_path: Union[Pathy, str], + recurse: bool = True, +) -> pathlib.Path +``` + +Get a bucket blob and return a local file cached version of it. The cache +is sensitive to the file updated time, and downloads new blobs as they become +available. + +## touch + +```python +Pathy.touch(self: ~PathType, mode: int = 438, exist_ok: bool = True) +``` + +Creates a key at this given path. + +If the key already exists, the function succeeds if exist_ok is true +(and its modification time is updated to the current time), otherwise +FileExistsError is raised. + +# use_fs + +```python +use_fs( + root: Optional[str, pathlib.Path, bool] = None, +) -> Optional[pathy.file.BucketClientFS] +``` + +Use a path in the local file-system to store blobs and buckets. + +This is useful for development and testing situations, and for embedded +applications. + +# get_fs_client + +```python +get_fs_client() -> Optional[pathy.file.BucketClientFS] +``` + +Get the file-system client (or None) + +# use_fs_cache + +```python +use_fs_cache( + root: Optional[str, pathlib.Path, bool] = None, +) -> Optional[pathlib.Path] +``` + +Use a path in the local file-system to cache blobs and buckets. + +This is useful for when you want to avoid fetching large blobs multiple +times, or need to pass a local file path to a third-party library. + +# get_fs_cache + +```python +get_fs_cache() -> Optional[pathlib.Path] +``` + +Get the file-system client (or None) + +# CLI + +Pathy command line interface. + +**Usage**: + +```console +$ [OPTIONS] COMMAND [ARGS]... +``` + +**Options**: + +- `--install-completion`: Install completion for the current shell. +- `--show-completion`: Show completion for the current shell, to copy it or customize the installation. +- `--help`: Show this message and exit. + +**Commands**: + +- `cp`: Copy a blob or folder of blobs from one... +- `ls`: List the blobs that exist at a given... +- `mv`: Move a blob or folder of blobs from one path... +- `rm`: Remove a blob or folder of blobs from a given... + +## `cp` + +Copy a blob or folder of blobs from one bucket to another. + +**Usage**: + +```console +$ cp [OPTIONS] FROM_LOCATION TO_LOCATION +``` + +**Options**: + +- `--help`: Show this message and exit. + +## `ls` + +List the blobs that exist at a given location. + +**Usage**: + +```console +$ ls [OPTIONS] LOCATION +``` + +**Options**: + +- `--help`: Show this message and exit. + +## `mv` + +Move a blob or folder of blobs from one path to another. + +**Usage**: + +```console +$ mv [OPTIONS] FROM_LOCATION TO_LOCATION +``` + +**Options**: + +- `--help`: Show this message and exit. + +## `rm` + +Remove a blob or folder of blobs from a given location. + +**Usage**: + +```console +$ rm [OPTIONS] LOCATION +``` + +**Options**: + +- `-r, --recursive`: Recursively remove files and folders. +- `-v, --verbose`: Print removed files and folders. +- `--help`: Show this message and exit. -- Python >= 3.6 -- google-cloud-storage + diff --git a/pathy/api.py b/pathy/api.py index 7809722..e48e430 100644 --- a/pathy/api.py +++ b/pathy/api.py @@ -2,15 +2,12 @@ import os import shutil import tempfile -from collections import namedtuple -from contextlib import suppress from io import DEFAULT_BUFFER_SIZE -from pathlib import Path, PurePath, _Accessor, _PosixFlavour # type:ignore -from typing import Generator, Iterable, List, Optional, Union +from pathlib import Path, _Accessor # type:ignore +from typing import Generator, Optional, Union from google.api_core import exceptions as gcs_errors from google.auth.exceptions import DefaultCredentialsError -from google.cloud import storage from . import gcs from .base import PurePathy, PathType @@ -99,7 +96,7 @@ def get_fs_cache() -> Optional[Path]: def clear_fs_cache(force: bool = False) -> None: """Remove the existing file-system blob cache folder. - + Raises AssertionError if the cache path is unset or points to the root of the file-system.""" cache_path = get_fs_cache() @@ -113,10 +110,7 @@ def clear_fs_cache(force: bool = False) -> None: class Pathy(Path, PurePathy): - """Path subclass for GCS service. - - Write files to and read files from the GCS service using pathlib.Path - methods.""" + """Subclass of pathlib.Path that works with bucket storage providers.""" __slots__ = () _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" @@ -137,19 +131,20 @@ def _init(self: PathType, template=None): @classmethod def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: """Helper to infer a pathlib.Path or Pathy from an input path or string. - + The returned type is a union of the potential `FluidPath` types and will type-check correctly against the minimum overlapping APIs of all the input types. - - If you need to use specific implementation details of a type, you - will need to cast the return of this function to the desired type, e.g. - # Narrow the type a specific class - assert isinstance(path, Pathy), "must be Pathy" - # Use a member specific to that class - print(path.prefix) + If you need to use specific implementation details of a type, you + will need to narrow the return of this function to the desired type, e.g. + ```python + # Narrow the type to a specific class + assert isinstance(path, Pathy), "must be Pathy" + # Use a member specific to that class + print(path.prefix) + ``` """ from_path: FluidPath = Pathy(path_candidate) if from_path.root in ["/", ""]: @@ -324,16 +319,12 @@ def owner(self: PathType) -> Optional[str]: return self._accessor.owner(self) def resolve(self: PathType) -> PathType: - """ - Return a copy of this path. All paths are absolute in buckets, so no - transformation is applied. - """ + """Resolve the given path to remove any relative path specifiers.""" self._absolute_path_validation() return self._accessor.resolve(self) def rename(self: PathType, target: Union[str, PathType]) -> None: - """ - Renames this file or Bucket / key prefix / key to the given target. + """Rename this file or Bucket / key prefix / key to the given target. If target exists and is a file, it will be replaced silently if the user has permission. If path is a key prefix, it will replace all the keys with the same prefix to the new target prefix. Target can be either a string or @@ -394,9 +385,7 @@ def mkdir( ) -> None: """ Create a path bucket. - GCS Service doesn't support folders, therefore the mkdir method will - only create the current bucket. If the bucket path already exists, - FileExistsError is raised. + Bucket storage doesn't support folders explicitly, so mkdir will only create a bucket. If exist_ok is false (the default), FileExistsError is raised if the target Bucket already exists. @@ -438,89 +427,49 @@ def is_fifo(self: PathType) -> bool: @classmethod def cwd(cls: PathType): - """ - cwd class method is unsupported on GCS service - GCS don't have this file system action concept - """ message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.cwd.__qualname__) raise NotImplementedError(message) @classmethod def home(cls: PathType): - """ - home class method is unsupported on GCS service - GCS don't have this file system action concept - """ message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.home.__qualname__) raise NotImplementedError(message) def chmod(self: PathType, mode): - """ - chmod method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format(method=self.chmod.__qualname__) raise NotImplementedError(message) def expanduser(self: PathType): - """ - expanduser method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format( method=self.expanduser.__qualname__ ) raise NotImplementedError(message) def lchmod(self: PathType, mode): - """ - lchmod method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lchmod.__qualname__) raise NotImplementedError(message) def group(self: PathType): - """ - group method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format(method=self.group.__qualname__) raise NotImplementedError(message) def is_block_device(self: PathType): - """ - is_block_device method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format( method=self.is_block_device.__qualname__ ) raise NotImplementedError(message) def is_char_device(self: PathType): - """ - is_char_device method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format( method=self.is_char_device.__qualname__ ) raise NotImplementedError(message) def lstat(self: PathType): - """ - lstat method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lstat.__qualname__) raise NotImplementedError(message) def symlink_to(self: PathType, *args, **kwargs): - """ - symlink_to method is unsupported on GCS service - GCS don't have this file system action concept - """ message = self._NOT_SUPPORTED_MESSAGE.format( method=self.symlink_to.__qualname__ ) diff --git a/pathy/cli.py b/pathy/cli.py index bf732e3..c52bfbe 100644 --- a/pathy/cli.py +++ b/pathy/cli.py @@ -1,7 +1,7 @@ import typer from .api import Pathy, FluidPath -app = typer.Typer() +app = typer.Typer(help="Pathy command line interface.") @app.command() diff --git a/requirements-dev.txt b/requirements-dev.txt index 3d4b755..62012de 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,6 +5,8 @@ black pytest-coverage tox mock +mathy_pydoc +typer-cli # test serializing spacy models using Pathy in place of Path https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.2.5/en_core_web_sm-2.2.5.tar.gz#egg=en_core_web_sm diff --git a/tools/docs.py b/tools/docs.py new file mode 100644 index 0000000..16af328 --- /dev/null +++ b/tools/docs.py @@ -0,0 +1,34 @@ +from typing import List +import typer +from pathlib import Path + + +def main(api_doc_file: Path, cli_doc_file: Path, readme_file: Path): + open_line = "" + close_line = "" + found_open = False + found_close = False + before_lines: List[str] = [] + after_lines: List[str] = [] + line: str + for line in readme_file.read_text().split("\n"): + if not found_open: + before_lines.append(line) + if line.strip() == open_line: + found_open = True + elif not found_close: + if line.strip() == close_line: + found_close = True + after_lines.append(line) + else: + after_lines.append(line) + + api_docs = api_doc_file.read_text().split("\n") + cli_docs = cli_doc_file.read_text().split("\n") + output = "\n".join(before_lines + api_docs + cli_docs + after_lines) + readme_file.write_text(output) + typer.echo("Updated API/CLI docs in readme!") + + +if __name__ == "__main__": + typer.run(main) diff --git a/tools/docs.sh b/tools/docs.sh new file mode 100644 index 0000000..e1a9a52 --- /dev/null +++ b/tools/docs.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +. .env/bin/activate +mathy_pydoc pathy.Pathy+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache > /tmp/pathy_api.md +typer pathy.cli utils docs > /tmp/pathy_cli.md + +python tools/docs.py /tmp/pathy_api.md /tmp/pathy_cli.md README.md +npx prettier README.md --write \ No newline at end of file From 5a32357db47f003fb3ebc6345d7fa4cee829fd99 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 17:15:46 -0700 Subject: [PATCH 20/75] feat(build): use husky to auto update docs when code changes --- README.md | 2 +- package-lock.json | 1052 +++++++++++++++++++++++++++++++++++++-------- package.json | 189 ++++---- pathy/api.py | 2 +- 4 files changed, 971 insertions(+), 274 deletions(-) diff --git a/README.md b/README.md index 2175e7f..1f04ba9 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ assert not greeting.exists() Pathy(self, args, kwargs) ``` -Subclass of pathlib.Path that works with bucket storage providers. +Subclass of `pathlib.Path` that works with bucket APIs. ## exists diff --git a/package-lock.json b/package-lock.json index efee8ba..bcbfeb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -368,6 +368,12 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, "@types/node": { "version": "13.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", @@ -421,6 +427,12 @@ "indent-string": "^4.0.0" } }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -471,12 +483,6 @@ "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=", "dev": true }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -495,6 +501,12 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -535,20 +547,20 @@ "dev": true }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" } }, "cardinal": { @@ -572,12 +584,27 @@ "supports-color": "^5.3.0" } }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, "cli-table": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", @@ -587,6 +614,16 @@ "colors": "1.0.3" } }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -619,51 +656,87 @@ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true - }, "compare-func": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", - "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "requires": { "array-ify": "^1.0.0", - "dot-prop": "^3.0.0" + "dot-prop": "^5.1.0" } }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, "conventional-changelog-angular": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz", - "integrity": "sha512-QDEmLa+7qdhVIv8sFZfVxU1VSyVvnXPsxq8Vam49mKUcO1Z8VTLEJk9uI21uiJUsnmm0I4Hrsdc9TgkOQo9WSA==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz", + "integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==", "dev": true, "requires": { - "compare-func": "^1.3.1", + "compare-func": "^2.0.0", "q": "^1.5.1" + }, + "dependencies": { + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } } }, "conventional-changelog-writer": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.11.tgz", - "integrity": "sha512-g81GQOR392I+57Cw3IyP1f+f42ME6aEkbR+L7v1FBBWolB0xkjKTeCWVguzRrp6UiT1O6gBpJbEy2eq7AnV1rw==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz", + "integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==", "dev": true, "requires": { - "compare-func": "^1.3.1", - "conventional-commits-filter": "^2.0.2", + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.6", "dateformat": "^3.0.0", - "handlebars": "^4.4.0", + "handlebars": "^4.7.6", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.15", - "meow": "^5.0.0", + "meow": "^7.0.0", "semver": "^6.0.0", "split": "^1.0.0", "through2": "^3.0.0" }, "dependencies": { + "conventional-commits-filter": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz", + "integrity": "sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -683,18 +756,120 @@ } }, "conventional-commits-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.8.tgz", - "integrity": "sha512-YcBSGkZbYp7d+Cr3NWUeXbPDFUN6g3SaSIzOybi8bjHL5IJ5225OSCxJJ4LgziyEJ7AaJtE9L2/EU6H7Nt/DDQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz", + "integrity": "sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA==", "dev": true, "requires": { "JSONStream": "^1.0.4", "is-text-path": "^1.0.1", "lodash": "^4.17.15", - "meow": "^5.0.0", + "meow": "^7.0.0", "split2": "^2.0.0", "through2": "^3.0.0", "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "dev": true + }, + "meow": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", + "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + } + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, "core-util-is": { @@ -747,15 +922,6 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -795,6 +961,12 @@ } } }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -823,12 +995,20 @@ } }, "dot-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", - "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } } }, "duplexer2": { @@ -855,6 +1035,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "env-ci": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.0.2.tgz", @@ -995,6 +1184,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "get-stream": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", @@ -1069,17 +1264,24 @@ "dev": true }, "handlebars": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz", - "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==", + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", "dev": true, "requires": { + "minimist": "^1.2.5", "neo-async": "^2.6.0", - "optimist": "^0.6.1", "source-map": "^0.6.1", - "uglify-js": "^3.1.4" + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" } }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1128,6 +1330,76 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, + "husky": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", + "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^6.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "ignore": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", @@ -1243,6 +1515,12 @@ "isobject": "^4.0.0" } }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -1328,12 +1606,193 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, + "lint-staged": { + "version": "10.2.11", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.11.tgz", + "integrity": "sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "cli-truncate": "2.1.0", + "commander": "^5.1.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "enquirer": "^2.3.5", + "execa": "^4.0.1", + "listr2": "^2.1.0", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "listr2": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.0.tgz", + "integrity": "sha512-nwmqTJYQQ+AsKb4fCXH/6/UmLCEDL1jkRAdSn9M6cEUzoRGrs33YD/3N86gAZQnGZ6hxV18XSdlBcJ1GTmetJA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.2", + "through": "^2.3.8" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -1404,14 +1863,115 @@ "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", "dev": true }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } } }, "lru-cache": { @@ -1436,9 +1996,9 @@ "dev": true }, "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", "dev": true }, "marked": { @@ -1514,31 +2074,29 @@ } }, "meow": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0", - "yargs-parser": "^10.0.0" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", + "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" }, "dependencies": { - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true } } }, @@ -1576,6 +2134,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", @@ -1583,13 +2147,14 @@ "dev": true }, "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, "requires": { "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" } }, "modify-values": { @@ -1605,9 +2170,9 @@ "dev": true }, "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "nerf-dart": { @@ -1663,6 +2228,12 @@ } } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "normalize-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.0.0.tgz", @@ -5237,23 +5808,11 @@ "mimic-fn": "^2.1.0" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } - } + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true }, "os-name": { "version": "3.1.0", @@ -5403,6 +5962,75 @@ "load-json-file": "^4.0.0" } }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -5426,9 +6054,9 @@ "dev": true }, "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, "rc": { @@ -5443,28 +6071,6 @@ "strip-json-comments": "~2.0.1" } }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - } - } - }, "read-pkg-up": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", @@ -5581,21 +6187,13 @@ } }, "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - }, - "dependencies": { - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - } + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" } }, "redeyed": { @@ -5649,6 +6247,16 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -5667,6 +6275,15 @@ "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", "dev": true }, + "rxjs": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -5715,6 +6332,12 @@ "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, "semver-diff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", @@ -5793,6 +6416,44 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5893,6 +6554,12 @@ "readable-stream": "^2.0.2" } }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -5913,6 +6580,17 @@ "safe-buffer": "~5.1.0" } }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -5941,10 +6619,13 @@ "dev": true }, "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } }, "strip-json-comments": { "version": "2.0.1", @@ -6051,9 +6732,9 @@ "dev": true }, "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", "dev": true }, "trim-off-newlines": { @@ -6075,6 +6756,12 @@ "yn": "3.1.1" } }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", @@ -6088,15 +6775,11 @@ "dev": true }, "uglify-js": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz", - "integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", + "integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", "dev": true, - "optional": true, - "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" - } + "optional": true }, "unique-string": { "version": "2.0.0", @@ -6159,6 +6842,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, "windows-release": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", @@ -6259,9 +6948,9 @@ } }, "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, "wrap-ansi": { @@ -6354,12 +7043,6 @@ "yargs-parser": "^18.1.1" }, "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -6410,9 +7093,9 @@ "dev": true }, "yargs-parser": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz", - "integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -6422,12 +7105,13 @@ } }, "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } }, "yn": { diff --git a/package.json b/package.json index de661f2..db6cd90 100644 --- a/package.json +++ b/package.json @@ -1,90 +1,103 @@ { - "name": "pathy", - "version": "0.0.0-development", - "description": "", - "main": "index.js", - "directories": { - "test": "tests" - }, - "scripts": { - "semantic-release": "semantic-release" - }, - "repository": { - "type": "git", - "url": "https://github.com/justindujardin/pathy" - }, - "author": "", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/justindujardin/pathy/issues" - }, - "homepage": "https://github.com/justindujardin/pathy#readme", - "devDependencies": { - "@semantic-release/changelog": "^5.0.0", - "@semantic-release/git": "^9.0.0", - "@semantic-release/github": "^7.0.5", - "@types/node": "^13.1.1", - "minimist": "^1.2.5", - "semantic-release": "^17.0.4", - "stream-buffers": "^3.0.2", - "ts-node": "^8.0.2", - "typescript": "^3.0.3" - }, - "release": { - "plugins": [ - [ - "@semantic-release/commit-analyzer", - { - "preset": "angular", - "releaseRules": [ - { - "breaking": true, - "release": "minor" - }, - { - "revert": true, - "release": "patch" - }, - { - "type": "feat", - "release": "patch" - }, - { - "type": "fix", - "release": "patch" - }, - { - "type": "perf", - "release": "patch" - } - ], - "parserOpts": { - "noteKeywords": [ - "BREAKING CHANGE", - "BREAKING CHANGES" - ] - } + "name": "pathy", + "version": "0.0.0-development", + "description": "", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "semantic-release": "semantic-release" + }, + "repository": { + "type": "git", + "url": "https://github.com/justindujardin/pathy" + }, + "author": "", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/justindujardin/pathy/issues" + }, + "homepage": "https://github.com/justindujardin/pathy#readme", + "husky": { + "hooks": { + "pre-commit": "npx lint-staged" } - ], - "@semantic-release/release-notes-generator", - [ - "@semantic-release/changelog", - { - "changelogFile": "CHANGELOG.md" - } - ], - [ - "@semantic-release/git", - { - "assets": [ - "pathy/about.py", - "package.json", - "CHANGELOG.md" - ], - "message": "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}" - } - ], - "@semantic-release/github" - ] - } -} + }, + "lint-staged": { + "pathy/*.py": [ + "sh tools/docs.sh", + "git add README.md" + ] + }, + "devDependencies": { + "@semantic-release/changelog": "^5.0.0", + "@semantic-release/git": "^9.0.0", + "@semantic-release/github": "^7.0.5", + "@types/node": "^13.1.1", + "husky": "^4.2.5", + "lint-staged": "^10.2.11", + "minimist": "^1.2.5", + "semantic-release": "^17.0.4", + "stream-buffers": "^3.0.2", + "ts-node": "^8.0.2", + "typescript": "^3.0.3" + }, + "release": { + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "angular", + "releaseRules": [ + { + "breaking": true, + "release": "minor" + }, + { + "revert": true, + "release": "patch" + }, + { + "type": "feat", + "release": "patch" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "perf", + "release": "patch" + } + ], + "parserOpts": { + "noteKeywords": [ + "BREAKING CHANGE", + "BREAKING CHANGES" + ] + } + } + ], + "@semantic-release/release-notes-generator", + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "pathy/about.py", + "package.json", + "CHANGELOG.md" + ], + "message": "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] + } +} \ No newline at end of file diff --git a/pathy/api.py b/pathy/api.py index e48e430..eb8ac56 100644 --- a/pathy/api.py +++ b/pathy/api.py @@ -110,7 +110,7 @@ def clear_fs_cache(force: bool = False) -> None: class Pathy(Path, PurePathy): - """Subclass of pathlib.Path that works with bucket storage providers.""" + """Subclass of `pathlib.Path` that works with bucket APIs.""" __slots__ = () _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" From e3a2c49939b4fe65dd3ef112ae16f6c2cdd9aedf Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 18:06:07 -0700 Subject: [PATCH 21/75] chore: update docs --- README.md | 125 ++++++++++++++++++++++------------------ pathy/api.py | 160 ++++++++++++++++++++++++--------------------------- 2 files changed, 144 insertions(+), 141 deletions(-) diff --git a/README.md b/README.md index 1f04ba9..c1f73c4 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Subclass of `pathlib.Path` that works with bucket APIs. Pathy.exists(self: ~PathType) -> bool ``` -Whether the path points to an existing Bucket, key or key prefix. +Returns True if the path points to an existing bucket, blob, or prefix. ## fluid @@ -61,20 +61,21 @@ Pathy.fluid( ) -> Union[Pathy, pathlib.Path] ``` -Helper to infer a pathlib.Path or Pathy from an input path or string. +Infer either a Pathy or pathlib.Path from an input path or string. The returned type is a union of the potential `FluidPath` types and will type-check correctly against the minimum overlapping APIs of all the input types. -If you need to use specific implementation details of a type, you -will need to narrow the return of this function to the desired type, e.g. +If you need to use specific implementation details of a type, "narrow" the +return of this function to the desired type, e.g. ```python +fluid_path = FluidPath("gs://my_bucket/foo.txt") # Narrow the type to a specific class -assert isinstance(path, Pathy), "must be Pathy" +assert isinstance(fluid_path, Pathy), "must be Pathy" # Use a member specific to that class -print(path.prefix) +print(fluid_path.prefix) ``` ## from_bucket @@ -83,8 +84,13 @@ print(path.prefix) Pathy.from_bucket(bucket_name: str) -> 'Pathy' ``` -Helper to convert a bucket name into a Pathy without needing -to add the leading and trailing slashes +Initialize a Pathy from a bucket name. This helper adds a trailing slash and +the appropriate prefix. + +```python +assert str(Pathy.from_bucket("one")) == "gs://one/" +assert str(Pathy.from_bucket("two")) == "gs://two/" +``` ## glob @@ -92,8 +98,8 @@ to add the leading and trailing slashes Pathy.glob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] ``` -Glob the given relative pattern in the Bucket / key prefix represented -by this path, yielding all matching files (of any kind) +Perform a glob match relative to this Pathy instance, yielding all matched +blobs. ## is_dir @@ -101,9 +107,11 @@ by this path, yielding all matching files (of any kind) Pathy.is_dir(self: ~PathType) -> bool ``` -Returns True if the path points to a Bucket or a key prefix, False if it -points to a full key path. False is returned if the path doesn’t exist. -Other errors (such as permission errors) are propagated. +Determine if the path points to a bucket or a prefix of a given blob +in the bucket. + +Returns True if the path points to a bucket or a blob prefix. +Returns False if it points to a blob or the path doesn't exist. ## is_file @@ -111,9 +119,11 @@ Other errors (such as permission errors) are propagated. Pathy.is_file(self: ~PathType) -> bool ``` -Returns True if the path points to a Bucket key, False if it points to -Bucket or a key prefix. False is returned if the path doesn’t exist. -Other errors (such as permission errors) are propagated. +Determine if the path points to a blob in the bucket. + +Returns True if the path points to a blob. +Returns False if it points to a bucket or blob prefix, or if the path doesn’t +exist. ## iterdir @@ -121,8 +131,7 @@ Other errors (such as permission errors) are propagated. Pathy.iterdir(self: ~PathType) -> Generator[~PathType, NoneType, NoneType] ``` -When the path points to a Bucket or a key prefix, yield path objects of -the directory contents +Iterate over the blobs found in the given bucket or blob prefix path. ## mkdir @@ -135,21 +144,17 @@ Pathy.mkdir( ) -> None ``` -Create a path bucket. -Bucket storage doesn't support folders explicitly, so mkdir will only create a bucket. - -If exist_ok is false (the default), FileExistsError is raised if the -target Bucket already exists. +Create a bucket from the given path. Since bucket APIs only have implicit +folder structures (determined by the existence of a blob with an overlapping +prefix) this does nothing other than create buckets. -If exist_ok is true, OSError exceptions will be ignored. +If parents is False, the bucket will only be created if the path points to +exactly the bucket and nothing else. If parents is true the bucket will be +created even if the path points to a specific blob. -if parents is false (the default), mkdir will create the bucket only -if this is a Bucket path. +The mode param is ignored. -if parents is true, mkdir will create the bucket even if the path -have a Key path. - -mode argument is ignored. +Raises FileExistsError if exist_ok is false and the bucket already exists. ## open @@ -161,11 +166,12 @@ Pathy.open( encoding = None, errors = None, newline = None, -) +) -> io.IOBase ``` -Opens the Bucket key pointed to by the path, returns a Key file object -that you can read/write with. +Open the given blob for streaming. This delegates to the `smart_open` +library that handles large file streaming for a number of bucket API +providers. ## owner @@ -173,8 +179,9 @@ that you can read/write with. Pathy.owner(self: ~PathType) -> Optional[str] ``` -Returns the name of the user owning the Bucket or key. -Similarly to boto3's ObjectSummary owner attribute +Returns the name of the user that owns the bucket or blob +this path points to. Returns None if the owner is unknown or +not supported by the bucket API provider. ## rename @@ -182,11 +189,13 @@ Similarly to boto3's ObjectSummary owner attribute Pathy.rename(self: ~PathType, target: Union[str, ~PathType]) -> None ``` -Rename this file or Bucket / key prefix / key to the given target. -If target exists and is a file, it will be replaced silently if the user -has permission. If path is a key prefix, it will replace all the keys with -the same prefix to the new target prefix. Target can be either a string or -another Pathy object. +Rename this path to the given target. + +If the target exists and is a file, it will be replaced silently if the user +has permission. + +If path is a blob prefix, it will replace all the blobs with the same prefix +to match the target prefix. ## replace @@ -194,9 +203,9 @@ another Pathy object. Pathy.replace(self: ~PathType, target: Union[str, ~PathType]) -> None ``` -Renames this Bucket / key prefix / key to the given target. -If target points to an existing Bucket / key prefix / key, it will be -unconditionally replaced. +Renames this path to the given target. + +If target points to an existing path, it will be replaced. ## resolve @@ -206,14 +215,19 @@ Pathy.resolve(self: ~PathType) -> ~PathType Resolve the given path to remove any relative path specifiers. +```python +path = Pathy("gs://my_bucket/folder/../blob") +assert path.resolve() == Pathy("gs://my_bucket/blob") +``` + ## rglob ```python Pathy.rglob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] ``` -This is like calling Pathy.glob with "\*\*/" added in front of the given -relative pattern. +Perform a recursive glob match relative to this Pathy instance, yielding +all matched blobs. Imagine adding "\*\*/" before a call to glob. ## rmdir @@ -221,7 +235,7 @@ relative pattern. Pathy.rmdir(self: ~PathType) -> None ``` -Removes this Bucket / key prefix. The Bucket / key prefix must be empty +Removes this bucket or blob prefix. It must be empty. ## samefile @@ -229,8 +243,7 @@ Removes this Bucket / key prefix. The Bucket / key prefix must be empty Pathy.samefile(self: ~PathType, other_path: ~PathType) -> bool ``` -Returns whether this path points to the same Bucket key as other_path, -Which can be either a Path object, or a string +Determine if this path points to the same location as other_path. ## stat @@ -238,8 +251,7 @@ Which can be either a Path object, or a string Pathy.stat(self: ~PathType) -> pathy.client.BucketStat ``` -Returns information about this path. -The result is looked up at each call to this method +Returns information about this bucket path. ## to_local @@ -250,9 +262,10 @@ Pathy.to_local( ) -> pathlib.Path ``` -Get a bucket blob and return a local file cached version of it. The cache -is sensitive to the file updated time, and downloads new blobs as they become -available. +Download and cache either a blob or a set of blobs matching a prefix. + +The cache is sensitive to the file updated time, and downloads new blobs +as their updated timestamps change. ## touch @@ -260,9 +273,9 @@ available. Pathy.touch(self: ~PathType, mode: int = 438, exist_ok: bool = True) ``` -Creates a key at this given path. +Create a blob at this path. -If the key already exists, the function succeeds if exist_ok is true +If the blob already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised. @@ -306,7 +319,7 @@ times, or need to pass a local file path to a third-party library. get_fs_cache() -> Optional[pathlib.Path] ``` -Get the file-system client (or None) +Get the folder that holds file-system cached blobs and timestamps. # CLI diff --git a/pathy/api.py b/pathy/api.py index eb8ac56..6d87a4a 100644 --- a/pathy/api.py +++ b/pathy/api.py @@ -1,16 +1,16 @@ -from typing import cast +import io import os import shutil import tempfile from io import DEFAULT_BUFFER_SIZE from pathlib import Path, _Accessor # type:ignore -from typing import Generator, Optional, Union +from typing import Generator, Optional, Union, cast from google.api_core import exceptions as gcs_errors from google.auth.exceptions import DefaultCredentialsError from . import gcs -from .base import PurePathy, PathType +from .base import PathType, PurePathy from .client import ( BucketClient, BucketEntry, @@ -88,7 +88,7 @@ def use_fs_cache(root: Optional[Union[str, Path, bool]] = None) -> Optional[Path def get_fs_cache() -> Optional[Path]: - """Get the file-system client (or None)""" + """Get the folder that holds file-system cached blobs and timestamps.""" global _fs_cache assert _fs_cache is None or isinstance(_fs_cache, Path), "invalid root type" return _fs_cache @@ -130,20 +130,21 @@ def _init(self: PathType, template=None): @classmethod def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: - """Helper to infer a pathlib.Path or Pathy from an input path or string. + """Infer either a Pathy or pathlib.Path from an input path or string. The returned type is a union of the potential `FluidPath` types and will type-check correctly against the minimum overlapping APIs of all the input types. - If you need to use specific implementation details of a type, you - will need to narrow the return of this function to the desired type, e.g. + If you need to use specific implementation details of a type, "narrow" the + return of this function to the desired type, e.g. ```python + fluid_path = FluidPath("gs://my_bucket/foo.txt") # Narrow the type to a specific class - assert isinstance(path, Pathy), "must be Pathy" + assert isinstance(fluid_path, Pathy), "must be Pathy" # Use a member specific to that class - print(path.prefix) + print(fluid_path.prefix) ``` """ from_path: FluidPath = Pathy(path_candidate) @@ -153,17 +154,24 @@ def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: @classmethod def from_bucket(cls: PathType, bucket_name: str) -> "Pathy": - """Helper to convert a bucket name into a Pathy without needing - to add the leading and trailing slashes""" + """Initialize a Pathy from a bucket name. This helper adds a trailing slash and + the appropriate prefix. + + ```python + assert str(Pathy.from_bucket("one")) == "gs://one/" + assert str(Pathy.from_bucket("two")) == "gs://two/" + ``` + """ return Pathy(f"gs://{bucket_name}/") @classmethod def to_local( cls: PathType, blob_path: Union["Pathy", str], recurse: bool = True ) -> Path: - """Get a bucket blob and return a local file cached version of it. The cache - is sensitive to the file updated time, and downloads new blobs as they become - available.""" + """Download and cache either a blob or a set of blobs matching a prefix. + + The cache is sensitive to the file updated time, and downloads new blobs + as their updated timestamps change.""" cache_folder = get_fs_cache() if cache_folder is None: raise ValueError( @@ -208,29 +216,25 @@ def to_local( return cache_blob def stat(self: PathType) -> BucketStat: - """ - Returns information about this path. - The result is looked up at each call to this method - """ + """Returns information about this bucket path.""" self._absolute_path_validation() if not self.key: raise ValueError("cannot stat a bucket without a key") return cast(BucketStat, super().stat()) def exists(self: PathType) -> bool: - """ - Whether the path points to an existing Bucket, key or key prefix. - """ + """Returns True if the path points to an existing bucket, blob, or prefix.""" self._absolute_path_validation() if not self.bucket: return True return self._accessor.exists(self) def is_dir(self: PathType) -> bool: - """ - Returns True if the path points to a Bucket or a key prefix, False if it - points to a full key path. False is returned if the path doesn’t exist. - Other errors (such as permission errors) are propagated. + """Determine if the path points to a bucket or a prefix of a given blob + in the bucket. + + Returns True if the path points to a bucket or a blob prefix. + Returns False if it points to a blob or the path doesn't exist. """ self._absolute_path_validation() if self.bucket and not self.key: @@ -238,10 +242,11 @@ def is_dir(self: PathType) -> bool: return self._accessor.is_dir(self) def is_file(self: PathType) -> bool: - """ - Returns True if the path points to a Bucket key, False if it points to - Bucket or a key prefix. False is returned if the path doesn’t exist. - Other errors (such as permission errors) are propagated. + """Determine if the path points to a blob in the bucket. + + Returns True if the path points to a blob. + Returns False if it points to a bucket or blob prefix, or if the path doesn’t + exist. """ self._absolute_path_validation() if not self.bucket or not self.key: @@ -252,25 +257,18 @@ def is_file(self: PathType) -> bool: return False def iterdir(self: PathType) -> Generator[PathType, None, None]: - """ - When the path points to a Bucket or a key prefix, yield path objects of - the directory contents - """ + """Iterate over the blobs found in the given bucket or blob prefix path.""" self._absolute_path_validation() yield from super().iterdir() def glob(self: PathType, pattern) -> Generator[PathType, None, None]: - """ - Glob the given relative pattern in the Bucket / key prefix represented - by this path, yielding all matching files (of any kind) - """ + """Perform a glob match relative to this Pathy instance, yielding all matched + blobs.""" yield from super().glob(pattern) def rglob(self: PathType, pattern) -> Generator[PathType, None, None]: - """ - This is like calling Pathy.glob with "**/" added in front of the given - relative pattern. - """ + """Perform a recursive glob match relative to this Pathy instance, yielding + all matched blobs. Imagine adding "**/" before a call to glob.""" yield from super().rglob(pattern) def open( @@ -280,11 +278,10 @@ def open( encoding=None, errors=None, newline=None, - ): - """ - Opens the Bucket key pointed to by the path, returns a Key file object - that you can read/write with. - """ + ) -> io.IOBase: + """Open the given blob for streaming. This delegates to the `smart_open` + library that handles large file streaming for a number of bucket API + providers.""" self._absolute_path_validation() if mode not in _SUPPORTED_OPEN_MODES: raise ValueError( @@ -309,27 +306,33 @@ def open( ) def owner(self: PathType) -> Optional[str]: - """ - Returns the name of the user owning the Bucket or key. - Similarly to boto3's ObjectSummary owner attribute - """ + """Returns the name of the user that owns the bucket or blob + this path points to. Returns None if the owner is unknown or + not supported by the bucket API provider.""" self._absolute_path_validation() if not self.is_file(): raise FileNotFoundError(str(self)) return self._accessor.owner(self) def resolve(self: PathType) -> PathType: - """Resolve the given path to remove any relative path specifiers.""" + """Resolve the given path to remove any relative path specifiers. + + ```python + path = Pathy("gs://my_bucket/folder/../blob") + assert path.resolve() == Pathy("gs://my_bucket/blob") + ``` + """ self._absolute_path_validation() return self._accessor.resolve(self) def rename(self: PathType, target: Union[str, PathType]) -> None: - """Rename this file or Bucket / key prefix / key to the given target. - If target exists and is a file, it will be replaced silently if the user - has permission. If path is a key prefix, it will replace all the keys with - the same prefix to the new target prefix. Target can be either a string or - another Pathy object. - """ + """Rename this path to the given target. + + If the target exists and is a file, it will be replaced silently if the user + has permission. + + If path is a blob prefix, it will replace all the blobs with the same prefix + to match the target prefix.""" self._absolute_path_validation() self_type = type(self) if not isinstance(target, self_type): @@ -338,17 +341,13 @@ def rename(self: PathType, target: Union[str, PathType]) -> None: super().rename(target) def replace(self: PathType, target: Union[str, PathType]) -> None: - """ - Renames this Bucket / key prefix / key to the given target. - If target points to an existing Bucket / key prefix / key, it will be - unconditionally replaced. - """ + """Renames this path to the given target. + + If target points to an existing path, it will be replaced.""" self.rename(target) def rmdir(self: PathType) -> None: - """ - Removes this Bucket / key prefix. The Bucket / key prefix must be empty - """ + """Removes this bucket or blob prefix. It must be empty.""" self._absolute_path_validation() if self.is_file(): raise NotADirectoryError() @@ -357,10 +356,7 @@ def rmdir(self: PathType) -> None: super().rmdir() def samefile(self: PathType, other_path: PathType) -> bool: - """ - Returns whether this path points to the same Bucket key as other_path, - Which can be either a Path object, or a string - """ + """Determine if this path points to the same location as other_path.""" self._absolute_path_validation() if not isinstance(other_path, Path): other_path = type(self)(other_path) @@ -369,10 +365,9 @@ def samefile(self: PathType, other_path: PathType) -> bool: ) def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True): - """ - Creates a key at this given path. + """Create a blob at this path. - If the key already exists, the function succeeds if exist_ok is true + If the blob already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised. """ @@ -383,22 +378,17 @@ def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True): def mkdir( self: PathType, mode: int = 0o777, parents: bool = False, exist_ok: bool = False ) -> None: - """ - Create a path bucket. - Bucket storage doesn't support folders explicitly, so mkdir will only create a bucket. - - If exist_ok is false (the default), FileExistsError is raised if the - target Bucket already exists. - - If exist_ok is true, OSError exceptions will be ignored. + """Create a bucket from the given path. Since bucket APIs only have implicit + folder structures (determined by the existence of a blob with an overlapping + prefix) this does nothing other than create buckets. - if parents is false (the default), mkdir will create the bucket only - if this is a Bucket path. + If parents is False, the bucket will only be created if the path points to + exactly the bucket and nothing else. If parents is true the bucket will be + created even if the path points to a specific blob. - if parents is true, mkdir will create the bucket even if the path - have a Key path. + The mode param is ignored. - mode argument is ignored. + Raises FileExistsError if exist_ok is false and the bucket already exists. """ try: if self.bucket is None: From 9a7f8d819a27e8282abc5e14ed9c62a810cdc038 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 18:10:37 -0700 Subject: [PATCH 22/75] chore: add BucketStat to docs --- README.md | 8 ++++++++ tools/docs.sh | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1f73c4..2819645 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,14 @@ If the blob already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised. +# BucketStat + +```python +BucketStat(self, size: int, last_modified: int) -> None +``` + +Stat for a bucket item + # use_fs ```python diff --git a/tools/docs.sh b/tools/docs.sh index e1a9a52..38d8fdb 100644 --- a/tools/docs.sh +++ b/tools/docs.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e . .env/bin/activate -mathy_pydoc pathy.Pathy+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache > /tmp/pathy_api.md +mathy_pydoc pathy.Pathy+ pathy.BucketStat+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache > /tmp/pathy_api.md typer pathy.cli utils docs > /tmp/pathy_cli.md python tools/docs.py /tmp/pathy_api.md /tmp/pathy_cli.md README.md From aacb9f7d46526b4643fe978333d6700f06db15c9 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 18:15:13 -0700 Subject: [PATCH 23/75] chore: fix ci build badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2819645..9292dad 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pathy: a python Path interface for bucket storage -[![Build status](https://travis-ci.org/justindujardin/pathy.svg?branch=master)](https://travis-ci.org/justindujardin/pathy) +[![Build status](https://travis-ci.com/justindujardin/pathy.svg?branch=master)](https://travis-ci.com/justindujardin/pathy) [![Pypi version](https://badgen.net/pypi/v/pathy)](https://pypi.org/project/pathy/) Pathy is a python package (_with type annotations_) for working with Bucket storage providers. It provides a CLI app for basic file operations between local files and remote buckets. It enables a smooth developer experience by supporting local file-system backed buckets during development and testing. It makes converting bucket blobs into local files a snap with optional local file caching of blobs. From 038a43a57171b2c82a1530b23afb66e8082dd05c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 22 Aug 2020 01:21:20 +0000 Subject: [PATCH 24/75] chore(release): 0.2.0 # [0.2.0](https://github.com/justindujardin/pathy/compare/v0.1.3...v0.2.0) (2020-08-22) ### Code Refactoring * rename PureGCSPath to PurePathy ([5632f26](https://github.com/justindujardin/pathy/commit/5632f264ed5d22b54b1c284ca1d79d2e248c5fd3)) ### Features * **build:** use husky to auto update docs when code changes ([5a32357](https://github.com/justindujardin/pathy/commit/5a32357db47f003fb3ebc6345d7fa4cee829fd99)) * **README:** generate API and CLI docs ([0213d2f](https://github.com/justindujardin/pathy/commit/0213d2f7028c08d40d863d1cc123e7d55ff1c89f)) ### BREAKING CHANGES * PureGCSPath is now PurePathy --- CHANGELOG.md | 18 ++++++++++++++++++ package.json | 2 +- pathy/about.py | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 402f336..8bfd973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# [0.2.0](https://github.com/justindujardin/pathy/compare/v0.1.3...v0.2.0) (2020-08-22) + + +### Code Refactoring + +* rename PureGCSPath to PurePathy ([5632f26](https://github.com/justindujardin/pathy/commit/5632f264ed5d22b54b1c284ca1d79d2e248c5fd3)) + + +### Features + +* **build:** use husky to auto update docs when code changes ([5a32357](https://github.com/justindujardin/pathy/commit/5a32357db47f003fb3ebc6345d7fa4cee829fd99)) +* **README:** generate API and CLI docs ([0213d2f](https://github.com/justindujardin/pathy/commit/0213d2f7028c08d40d863d1cc123e7d55ff1c89f)) + + +### BREAKING CHANGES + +* PureGCSPath is now PurePathy + ## [0.1.3](https://github.com/justindujardin/pathy/compare/v0.1.2...v0.1.3) (2020-06-28) diff --git a/package.json b/package.json index db6cd90..6636a4e 100644 --- a/package.json +++ b/package.json @@ -100,4 +100,4 @@ "@semantic-release/github" ] } -} \ No newline at end of file +} diff --git a/pathy/about.py b/pathy/about.py index 53bd67e..1b6f315 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.1.3" +__version__ = "0.2.0" __summary__ = "pathlib.Path subclasses for Google Cloud Storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From cece85243ec0456debade6874d3be2f3f42d16a7 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 18:24:42 -0700 Subject: [PATCH 25/75] chore: add codecov script --- .travis.yml | 2 +- tools/codecov.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tools/codecov.sh diff --git a/.travis.yml b/.travis.yml index 488a1b9..a3b49b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ matrix: script: - sh tools/test.sh after_success: - - echo "todo: python coverage reporting to coveralls" + - sh tools/codecov.sh - if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then sh tools/deploy.sh; fi cache: directories: diff --git a/tools/codecov.sh b/tools/codecov.sh new file mode 100644 index 0000000..475134a --- /dev/null +++ b/tools/codecov.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e +. .env/bin/activate +pip install codecov +python -m codecov From 70d96b707f362a70d4ab52e1791bf9ba9bca9879 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Fri, 21 Aug 2020 18:30:28 -0700 Subject: [PATCH 26/75] chore: add coverage badge to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9292dad..be53e49 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Pathy: a python Path interface for bucket storage [![Build status](https://travis-ci.com/justindujardin/pathy.svg?branch=master)](https://travis-ci.com/justindujardin/pathy) +[![codecov](https://codecov.io/gh/justindujardin/pathy/branch/master/graph/badge.svg)](https://codecov.io/gh/justindujardin/pathy) [![Pypi version](https://badgen.net/pypi/v/pathy)](https://pypi.org/project/pathy/) Pathy is a python package (_with type annotations_) for working with Bucket storage providers. It provides a CLI app for basic file operations between local files and remote buckets. It enables a smooth developer experience by supporting local file-system backed buckets during development and testing. It makes converting bucket blobs into local files a snap with optional local file caching of blobs. From a537fa98c619021d0a2815ff329c7af8e2ecefee Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Sat, 22 Aug 2020 16:03:05 -0700 Subject: [PATCH 27/75] chore: update readme restore the credits to s3path library --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index be53e49..3769ec7 100644 --- a/README.md +++ b/README.md @@ -412,3 +412,7 @@ $ rm [OPTIONS] LOCATION - `--help`: Show this message and exit. + +# Credits + +Pathy is originally based on the [S3Path](https://github.com/liormizr/s3path) project, which provides a Path interface for S3 buckets. From 8a787125df45f17f9f3d9a617ced20210559fe76 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sat, 22 Aug 2020 18:05:13 -0700 Subject: [PATCH 28/75] chore: update docs --- README.md | 60 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 3769ec7..e696509 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ assert not greeting.exists() -# Pathy +# Pathy class ```python Pathy(self, args, kwargs) @@ -46,7 +46,7 @@ Pathy(self, args, kwargs) Subclass of `pathlib.Path` that works with bucket APIs. -## exists +## exists method ```python Pathy.exists(self: ~PathType) -> bool @@ -54,7 +54,7 @@ Pathy.exists(self: ~PathType) -> bool Returns True if the path points to an existing bucket, blob, or prefix. -## fluid +## fluid classmethod ```python Pathy.fluid( @@ -79,7 +79,7 @@ assert isinstance(fluid_path, Pathy), "must be Pathy" print(fluid_path.prefix) ``` -## from_bucket +## from_bucket classmethod ```python Pathy.from_bucket(bucket_name: str) -> 'Pathy' @@ -93,7 +93,7 @@ assert str(Pathy.from_bucket("one")) == "gs://one/" assert str(Pathy.from_bucket("two")) == "gs://two/" ``` -## glob +## glob method ```python Pathy.glob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] @@ -102,7 +102,7 @@ Pathy.glob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] Perform a glob match relative to this Pathy instance, yielding all matched blobs. -## is_dir +## is_dir method ```python Pathy.is_dir(self: ~PathType) -> bool @@ -114,7 +114,7 @@ in the bucket. Returns True if the path points to a bucket or a blob prefix. Returns False if it points to a blob or the path doesn't exist. -## is_file +## is_file method ```python Pathy.is_file(self: ~PathType) -> bool @@ -126,7 +126,7 @@ Returns True if the path points to a blob. Returns False if it points to a bucket or blob prefix, or if the path doesn’t exist. -## iterdir +## iterdir method ```python Pathy.iterdir(self: ~PathType) -> Generator[~PathType, NoneType, NoneType] @@ -134,7 +134,7 @@ Pathy.iterdir(self: ~PathType) -> Generator[~PathType, NoneType, NoneType] Iterate over the blobs found in the given bucket or blob prefix path. -## mkdir +## mkdir method ```python Pathy.mkdir( @@ -157,7 +157,7 @@ The mode param is ignored. Raises FileExistsError if exist_ok is false and the bucket already exists. -## open +## open method ```python Pathy.open( @@ -174,7 +174,7 @@ Open the given blob for streaming. This delegates to the `smart_open` library that handles large file streaming for a number of bucket API providers. -## owner +## owner method ```python Pathy.owner(self: ~PathType) -> Optional[str] @@ -184,7 +184,7 @@ Returns the name of the user that owns the bucket or blob this path points to. Returns None if the owner is unknown or not supported by the bucket API provider. -## rename +## rename method ```python Pathy.rename(self: ~PathType, target: Union[str, ~PathType]) -> None @@ -198,7 +198,7 @@ has permission. If path is a blob prefix, it will replace all the blobs with the same prefix to match the target prefix. -## replace +## replace method ```python Pathy.replace(self: ~PathType, target: Union[str, ~PathType]) -> None @@ -208,7 +208,7 @@ Renames this path to the given target. If target points to an existing path, it will be replaced. -## resolve +## resolve method ```python Pathy.resolve(self: ~PathType) -> ~PathType @@ -221,7 +221,7 @@ path = Pathy("gs://my_bucket/folder/../blob") assert path.resolve() == Pathy("gs://my_bucket/blob") ``` -## rglob +## rglob method ```python Pathy.rglob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] @@ -230,7 +230,7 @@ Pathy.rglob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType Perform a recursive glob match relative to this Pathy instance, yielding all matched blobs. Imagine adding "\*\*/" before a call to glob. -## rmdir +## rmdir method ```python Pathy.rmdir(self: ~PathType) -> None @@ -238,7 +238,7 @@ Pathy.rmdir(self: ~PathType) -> None Removes this bucket or blob prefix. It must be empty. -## samefile +## samefile method ```python Pathy.samefile(self: ~PathType, other_path: ~PathType) -> bool @@ -246,7 +246,7 @@ Pathy.samefile(self: ~PathType, other_path: ~PathType) -> bool Determine if this path points to the same location as other_path. -## stat +## stat method ```python Pathy.stat(self: ~PathType) -> pathy.client.BucketStat @@ -254,7 +254,7 @@ Pathy.stat(self: ~PathType) -> pathy.client.BucketStat Returns information about this bucket path. -## to_local +## to_local classmethod ```python Pathy.to_local( @@ -268,7 +268,7 @@ Download and cache either a blob or a set of blobs matching a prefix. The cache is sensitive to the file updated time, and downloads new blobs as their updated timestamps change. -## touch +## touch method ```python Pathy.touch(self: ~PathType, mode: int = 438, exist_ok: bool = True) @@ -280,7 +280,7 @@ If the blob already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised. -# BucketStat +# BucketStat dataclass ```python BucketStat(self, size: int, last_modified: int) -> None @@ -288,7 +288,7 @@ BucketStat(self, size: int, last_modified: int) -> None Stat for a bucket item -# use_fs +# use_fs function ```python use_fs( @@ -301,7 +301,7 @@ Use a path in the local file-system to store blobs and buckets. This is useful for development and testing situations, and for embedded applications. -# get_fs_client +# get_fs_client function ```python get_fs_client() -> Optional[pathy.file.BucketClientFS] @@ -309,7 +309,7 @@ get_fs_client() -> Optional[pathy.file.BucketClientFS] Get the file-system client (or None) -# use_fs_cache +# use_fs_cache function ```python use_fs_cache( @@ -322,7 +322,7 @@ Use a path in the local file-system to cache blobs and buckets. This is useful for when you want to avoid fetching large blobs multiple times, or need to pass a local file path to a third-party library. -# get_fs_cache +# get_fs_cache function ```python get_fs_cache() -> Optional[pathlib.Path] @@ -365,6 +365,8 @@ $ cp [OPTIONS] FROM_LOCATION TO_LOCATION **Options**: +- `FROM_LOCATION`: [required] +- `TO_LOCATION`: [required] - `--help`: Show this message and exit. ## `ls` @@ -379,6 +381,7 @@ $ ls [OPTIONS] LOCATION **Options**: +- `LOCATION`: [required] - `--help`: Show this message and exit. ## `mv` @@ -393,6 +396,8 @@ $ mv [OPTIONS] FROM_LOCATION TO_LOCATION **Options**: +- `FROM_LOCATION`: [required] +- `TO_LOCATION`: [required] - `--help`: Show this message and exit. ## `rm` @@ -407,8 +412,9 @@ $ rm [OPTIONS] LOCATION **Options**: -- `-r, --recursive`: Recursively remove files and folders. -- `-v, --verbose`: Print removed files and folders. +- `LOCATION`: [required] +- `-r, --recursive`: Recursively remove files and folders. [default: False] +- `-v, --verbose`: Print removed files and folders. [default: False] - `--help`: Show this message and exit. From c034dd1429e7745d1f8e0a661f2c4499ffd72d73 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Wed, 2 Sep 2020 17:14:57 -0700 Subject: [PATCH 29/75] refactor(pypi): move gcs dependencies into pathy[gcs] extras --- requirements.txt | 3 +-- setup.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index e727ffc..f5391e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -google-cloud-storage>=1.26.0,<2.0.0 -smart-open==1.10.0 +smart-open>=1.10.0,<2.0.0 typer>=0.3.0,<1.0.0 dataclasses>=0.6,<1.0; python_version < "3.7" \ No newline at end of file diff --git a/setup.py b/setup.py index a090276..79f6e66 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,8 @@ def setup_package(): readme_path = root / "README.md" with readme_path.open("r", encoding="utf8") as f: long_description = f.read() - + extras = {"gcs": ["google-cloud-storage>=1.26.0,<2.0.0"]} + extras["all"] = [item for group in extras.values() for item in group] setup( name=about["__title__"], description=about["__summary__"], From b54d141c75f24a2b2fdcd85cc9ba91e9917d9d8f Mon Sep 17 00:00:00 2001 From: justindujardin Date: Wed, 2 Sep 2020 17:18:52 -0700 Subject: [PATCH 30/75] chore: add auto format and lint scripts --- .flake8 | 5 +++++ mypy.ini | 3 +++ requirements-dev.txt | 4 +++- tools/format.sh | 8 ++++++++ tools/lint.sh | 9 +++++++++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 .flake8 create mode 100644 mypy.ini create mode 100644 tools/format.sh create mode 100644 tools/lint.sh diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..af28862 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 88 +select = C,E,F,W,B,B9 +ignore = E203, E501, W503 +exclude = __init__.py \ No newline at end of file diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..e406519 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +disallow_untyped_defs = True +ignore_missing_imports = True \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 62012de..3b0ecbb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,7 @@ pytest mypy +isort +autoflake flake8 black pytest-coverage @@ -10,4 +12,4 @@ typer-cli # test serializing spacy models using Pathy in place of Path https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.2.5/en_core_web_sm-2.2.5.tar.gz#egg=en_core_web_sm -spacy \ No newline at end of file +spacy diff --git a/tools/format.sh b/tools/format.sh new file mode 100644 index 0000000..a1c318e --- /dev/null +++ b/tools/format.sh @@ -0,0 +1,8 @@ +#!/bin/sh -e +set -x + +# Sort imports one per line, so autoflake can remove unused imports +isort pathy tests --force-single-line-imports +autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place pathy tests --exclude=__init__.py +isort pathy tests +black pathy tests \ No newline at end of file diff --git a/tools/lint.sh b/tools/lint.sh new file mode 100644 index 0000000..f497813 --- /dev/null +++ b/tools/lint.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e +set -x + +mypy pathy +flake8 pathy tests +black pathy tests --check +isort pathy tests docs_src scripts --check-only \ No newline at end of file From 747815b46e1e3cd61e6e69d04b52f5f5958ed373 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 09:36:22 -0700 Subject: [PATCH 31/75] feat: add get_client/register_client for supporting multiple services Adds a simple registry of known schemes and their mappings to BucketClient subclasses. There's a hardcoded list of built-in services, and (in theory) you can register more dynamically. I think I prefer to hardcode and include most of the known services, and lazily import them so you only need their packages when you actually use them. The hope is that this lets the strong typings flow through to the clients (because they can be statically inspected). If we can't get specific types flowing through nicely, maybe it's okay to do more of a dynamic import style registration. BREAKING CHANGE use_fs, get_fs_client, use_fs_cache, get_fs_cache, and clear_fs_cache moved from pathy.api to pathy.clients --- README.md | 18 ++-- pathy/__init__.py | 18 ++-- pathy/api.py | 228 +++++++++++++---------------------------- pathy/base.py | 50 +++++---- pathy/cli.py | 3 +- pathy/client.py | 103 +++++++++---------- pathy/clients.py | 122 ++++++++++++++++++++++ pathy/file.py | 37 ++++--- pathy/gcs.py | 70 ++++++++++--- tests/conftest.py | 9 +- tests/test_api.py | 35 ++++--- tests/test_base.py | 12 +-- tests/test_cli.py | 11 +- tests/test_client.py | 45 +++++++- tests/test_registry.py | 15 +++ tools/docs.sh | 2 +- 16 files changed, 458 insertions(+), 320 deletions(-) create mode 100644 pathy/clients.py create mode 100644 tests/test_registry.py diff --git a/README.md b/README.md index e696509..9e5ada2 100644 --- a/README.md +++ b/README.md @@ -162,12 +162,12 @@ Raises FileExistsError if exist_ok is false and the bucket already exists. ```python Pathy.open( self: ~PathType, - mode = 'r', - buffering = 8192, - encoding = None, - errors = None, - newline = None, -) -> io.IOBase + mode: str = 'r', + buffering: int = 8192, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, +) -> Union[_io.TextIOWrapper, _io.FileIO, _io.BytesIO] ``` Open the given blob for streaming. This delegates to the `smart_open` @@ -249,7 +249,7 @@ Determine if this path points to the same location as other_path. ## stat method ```python -Pathy.stat(self: ~PathType) -> pathy.client.BucketStat +Pathy.stat(self: ~PathType) -> pathy.client.BlobStat ``` Returns information about this bucket path. @@ -280,10 +280,10 @@ If the blob already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised. -# BucketStat dataclass +# BlobStat dataclass ```python -BucketStat(self, size: int, last_modified: int) -> None +BlobStat(self, size: int, last_modified: int) -> None ``` Stat for a bucket item diff --git a/pathy/__init__.py b/pathy/__init__.py index ddaa244..611419d 100644 --- a/pathy/__init__.py +++ b/pathy/__init__.py @@ -1,18 +1,22 @@ -from .api import ( # noqa +from .api import ( + Blob, + Bucket, BucketClient, BucketsAccessor, - ClientBlob, - ClientBucket, ClientError, + FluidPath, Pathy, PurePathy, - FluidPath, +) +from .client import BlobStat, BucketEntry +from .clients import ( clear_fs_cache, + get_client, get_fs_cache, get_fs_client, + register_client, use_fs, use_fs_cache, ) -from .client import BucketEntry, BucketStat # noqa -from .file import BucketClientFS, BucketEntryFS, ClientBlobFS, ClientBucketFS # noqa -from .gcs import BucketClientGCS, BucketEntryGCS, ClientBlobGCS, ClientBucketGCS # noqa +from .file import BucketClientFS, BucketEntryFS, ClientBlobFS, ClientBucketFS +from .gcs import BucketClientGCS, BucketEntryGCS, ClientBlobGCS, ClientBucketGCS diff --git a/pathy/api.py b/pathy/api.py index 6d87a4a..dccccaf 100644 --- a/pathy/api.py +++ b/pathy/api.py @@ -1,111 +1,17 @@ -import io import os -import shutil -import tempfile from io import DEFAULT_BUFFER_SIZE -from pathlib import Path, _Accessor # type:ignore +from pathlib import _Accessor # type:ignore +from pathlib import Path from typing import Generator, Optional, Union, cast -from google.api_core import exceptions as gcs_errors -from google.auth.exceptions import DefaultCredentialsError +from .base import PathType, PurePathy, StreamableType +from .client import Blob, BlobStat, Bucket, BucketClient, BucketEntry, ClientError -from . import gcs -from .base import PathType, PurePathy -from .client import ( - BucketClient, - BucketEntry, - BucketStat, - ClientBlob, - ClientBucket, - ClientError, -) -from .file import BucketClientFS - -__all__ = ("Pathy", "use_fs", "get_fs_client", "BucketsAccessor") +__all__ = () _SUPPORTED_OPEN_MODES = {"r", "rb", "tr", "rt", "w", "wb", "bw", "wt", "tw"} -_fs_client: Optional[BucketClientFS] = None -_fs_cache: Optional[Path] = None - - -def use_fs(root: Optional[Union[str, Path, bool]] = None) -> Optional[BucketClientFS]: - """Use a path in the local file-system to store blobs and buckets. - - This is useful for development and testing situations, and for embedded - applications.""" - global _fs_client - # False - disable adapter - if root is False: - _fs_client = None - return None - - # None or True - enable FS adapter with default root - if root is None or root is True: - # Look up "data" folder of pathy package similar to spaCy - client_root = Path(__file__).parent / "data" - else: - assert isinstance(root, (str, Path)), f"root is not a known type: {type(root)}" - client_root = Path(root) - if not client_root.exists(): - client_root.mkdir(parents=True) - _fs_client = BucketClientFS(root=client_root) - return _fs_client - - -def get_fs_client() -> Optional[BucketClientFS]: - """Get the file-system client (or None)""" - global _fs_client - assert _fs_client is None or isinstance( - _fs_client, BucketClientFS - ), "invalid root type" - return _fs_client - - -def use_fs_cache(root: Optional[Union[str, Path, bool]] = None) -> Optional[Path]: - """Use a path in the local file-system to cache blobs and buckets. - - This is useful for when you want to avoid fetching large blobs multiple - times, or need to pass a local file path to a third-party library.""" - global _fs_cache - # False - disable adapter - if root is False: - _fs_cache = None - return None - - # None or True - enable FS cache with default root - if root is None or root is True: - # Use a temporary folder. Cache will be removed according to OS policy - cache_root = Path(tempfile.mkdtemp()) - else: - assert isinstance(root, (str, Path)), f"root is not a known type: {type(root)}" - cache_root = Path(root) - if not cache_root.exists(): - cache_root.mkdir(parents=True) - _fs_cache = cache_root - return cache_root - - -def get_fs_cache() -> Optional[Path]: - """Get the folder that holds file-system cached blobs and timestamps.""" - global _fs_cache - assert _fs_cache is None or isinstance(_fs_cache, Path), "invalid root type" - return _fs_cache - - -def clear_fs_cache(force: bool = False) -> None: - """Remove the existing file-system blob cache folder. - - Raises AssertionError if the cache path is unset or points to the - root of the file-system.""" - cache_path = get_fs_cache() - assert cache_path is not None, "no cache to clear" - resolved = cache_path.resolve() - assert str(resolved) != "/", f"refusing to remove a root path: {resolved}" - shutil.rmtree(str(resolved)) - - FluidPath = Union["Pathy", Path] @@ -172,6 +78,8 @@ def to_local( The cache is sensitive to the file updated time, and downloads new blobs as their updated timestamps change.""" + from .clients import get_fs_cache + cache_folder = get_fs_cache() if cache_folder is None: raise ValueError( @@ -191,7 +99,7 @@ def to_local( # - cached_stat.updated != latest_stat.updated if cache_blob.exists() and cache_time.exists(): fs_time: str = cache_time.read_text() - gcs_stat: BucketStat = blob_path.stat() + gcs_stat: BlobStat = blob_path.stat() # If the times match, return the cached blob if fs_time == str(gcs_stat.last_modified): return cache_blob @@ -206,7 +114,7 @@ def to_local( dest_folder = cache_blob.parent dest_folder.mkdir(exist_ok=True, parents=True) cache_blob.write_bytes(blob_path.read_bytes()) - blob_stat: BucketStat = blob_path.stat() + blob_stat: BlobStat = blob_path.stat() cache_time.write_text(str(blob_stat.last_modified)) elif recurse: # If not a specific blob, enumerate all the blobs under @@ -215,12 +123,12 @@ def to_local( Pathy.to_local(blob, recurse=False) return cache_blob - def stat(self: PathType) -> BucketStat: + def stat(self: PathType) -> BlobStat: """Returns information about this bucket path.""" self._absolute_path_validation() if not self.key: raise ValueError("cannot stat a bucket without a key") - return cast(BucketStat, super().stat()) + return cast(BlobStat, super().stat()) def exists(self: PathType) -> bool: """Returns True if the path points to an existing bucket, blob, or prefix.""" @@ -253,7 +161,7 @@ def is_file(self: PathType) -> bool: return False try: return bool(self.stat()) - except (gcs_errors.ClientError, FileNotFoundError): + except (ClientError, FileNotFoundError): return False def iterdir(self: PathType) -> Generator[PathType, None, None]: @@ -273,12 +181,12 @@ def rglob(self: PathType, pattern) -> Generator[PathType, None, None]: def open( self: PathType, - mode="r", - buffering=DEFAULT_BUFFER_SIZE, - encoding=None, - errors=None, - newline=None, - ) -> io.IOBase: + mode: str = "r", + buffering: int = DEFAULT_BUFFER_SIZE, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: """Open the given blob for streaming. This delegates to the `smart_open` library that handles large file streaming for a number of bucket API providers.""" @@ -360,6 +268,7 @@ def samefile(self: PathType, other_path: PathType) -> bool: self._absolute_path_validation() if not isinstance(other_path, Path): other_path = type(self)(other_path) + assert isinstance(other_path, Pathy) return ( self.bucket == other_path.bucket and self.key == self.key and self.is_file() ) @@ -393,7 +302,7 @@ def mkdir( try: if self.bucket is None: raise FileNotFoundError("No bucket in {} {}".format(type(self), self)) - # If the whole path is just the bucket, respect the + # If the whole path is just the bucket, respect the result of "bucket.exists()" if self.key is None and not exist_ok and self.bucket.exists(): raise FileExistsError("Bucket {} already exists".format(self.bucket)) return super().mkdir(mode, parents=parents, exist_ok=exist_ok) @@ -471,25 +380,17 @@ class BucketsAccessor(_Accessor): _client: Optional[BucketClient] - @property - def client(self) -> BucketClient: - global _fs_client - if _fs_client is not None: - return _fs_client - assert self._client is not None, "neither GCS or FS clients are enabled" - return self._client + def client(self, path: PathType) -> BucketClient: + # lazily avoid circular imports + from .clients import get_client - def __init__(self, **kwargs) -> None: - try: - self._client = gcs.BucketClientGCS() - except DefaultCredentialsError: - self._client = None + return get_client(path.scheme) - def get_blob(self, path: PathType) -> Optional[ClientBlob]: + def get_blob(self, path: PathType) -> Optional[Blob]: """Get the blob associated with a path or return None""" if not path.root: return None - bucket = self.client.lookup_bucket(path) + bucket = self.client(path).lookup_bucket(path) if bucket is None: return None key_name = str(path.key) @@ -497,46 +398,48 @@ def get_blob(self, path: PathType) -> Optional[ClientBlob]: def unlink(self, path: PathType) -> None: """Delete a link to a blob in a bucket.""" - bucket = self.client.get_bucket(path) - blob: Optional[ClientBlob] = bucket.get_blob(str(path.key)) + bucket = self.client(path).get_bucket(path) + blob: Optional[Blob] = bucket.get_blob(str(path.key)) if blob is None: raise FileNotFoundError(path) blob.delete() - def stat(self, path: PathType) -> BucketStat: - bucket = self.client.get_bucket(path) - blob: Optional[ClientBlob] = bucket.get_blob(str(path.key)) + def stat(self, path: PathType) -> BlobStat: + bucket = self.client(path).get_bucket(path) + blob: Optional[Blob] = bucket.get_blob(str(path.key)) if blob is None: raise FileNotFoundError(path) - return BucketStat(size=blob.size, last_modified=blob.updated) + return BlobStat(size=blob.size, last_modified=blob.updated) def is_dir(self, path: PathType) -> bool: if str(path) == path.root: return True if self.get_blob(path) is not None: return False - return self.client.is_dir(path) + return self.client(path).is_dir(path) def exists(self, path: PathType) -> bool: + client = self.client(path) if not path.root: - return any(self.client.list_buckets()) + return any(client.list_buckets()) try: - bucket = self.client.lookup_bucket(path) - except gcs_errors.ClientError: + bucket = client.lookup_bucket(path) + except ClientError: return False - if not path.key: - return bucket is not None - if bucket is None: + if bucket is None or not bucket.exists(): return False + if not path.key: + return True + key_name = str(path.key) blob = bucket.get_blob(key_name) if blob is not None: return blob.exists() # Determine if the path exists according to the current adapter - return self.client.exists(path) + return client.exists(path) def scandir(self, path: PathType) -> Generator[BucketEntry, None, None]: - return self.client.scandir(path, prefix=path.prefix) + return self.client(path).scandir(path, prefix=path.prefix) def listdir(self, path: PathType) -> Generator[str, None, None]: for entry in self.scandir(path): @@ -546,13 +449,13 @@ def open( self: PathType, path: PathType, *, - mode="r", - buffering=-1, - encoding=None, - errors=None, - newline=None, - ): - return self.client.open( + mode: str = "r", + buffering: int = -1, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: + return self.client(path).open( path, mode=mode, buffering=buffering, @@ -562,7 +465,7 @@ def open( ) def owner(self, path: PathType) -> Optional[str]: - blob: Optional[ClientBlob] = self.get_blob(path) + blob: Optional[Blob] = self.get_blob(path) return blob.owner if blob is not None else None def resolve(self, path: PathType, strict: bool = False) -> PathType: @@ -570,12 +473,13 @@ def resolve(self, path: PathType, strict: bool = False) -> PathType: return Pathy(f"{path.drive}{os.path.abspath(path_parts)}") def rename(self, path: PathType, target: PathType) -> None: - bucket: ClientBucket = self.client.get_bucket(path) - target_bucket: ClientBucket = self.client.get_bucket(target) + client: BucketClient = self.client(path) + bucket: Bucket = client.get_bucket(path) + target_bucket: Bucket = client.get_bucket(target) # Single file if not self.is_dir(path): - from_blob: Optional[ClientBlob] = bucket.get_blob(str(path.key)) + from_blob: Optional[Blob] = bucket.get_blob(str(path.key)) if from_blob is None: raise FileNotFoundError(f'source file "{path}" does not exist') target_bucket.copy_blob(from_blob, target_bucket, str(target.key)) @@ -584,7 +488,7 @@ def rename(self, path: PathType, target: PathType) -> None: # Folder with objects sep = path._flavour.sep - blobs = list(self.client.list_blobs(path, prefix=path.prefix, delimiter=sep)) + blobs = list(client.list_blobs(path, prefix=path.prefix, delimiter=sep)) # First rename for blob in blobs: target_key_name = blob.name.replace(str(path.key), str(target.key)) @@ -597,16 +501,24 @@ def replace(self, path: PathType, target: PathType) -> None: return self.rename(path, target) def rmdir(self, path: PathType) -> None: + client: BucketClient = self.client(path) key_name = str(path.key) if path.key is not None else None - bucket = self.client.get_bucket(path) - blobs = list(self.client.list_blobs(path, prefix=key_name)) + bucket = client.get_bucket(path) + blobs = list(client.list_blobs(path, prefix=key_name)) bucket.delete_blobs(blobs) - if self.client.is_dir(path): - self.client.rmdir(path) + if client.is_dir(path): + client.rmdir(path) def mkdir(self, path: PathType, mode) -> None: - if not self.client.lookup_bucket(path): - self.client.create_bucket(path) + client: BucketClient = self.client(path) + bucket: Optional[Bucket] = client.lookup_bucket(path) + if bucket is None or not bucket.exists(): + client.create_bucket(path) + elif path.key is not None: + assert isinstance(path, Pathy) + blob: Optional[Blob] = bucket.get_blob(str(path.key)) + if blob is not None and blob.exists(): + raise OSError(f"Blob already exists: {path}") _gcs_accessor = BucketsAccessor() diff --git a/pathy/base.py b/pathy/base.py index feef3e2..745ef87 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -1,28 +1,23 @@ -from pathlib import PurePath, _PosixFlavour # noqa -from typing import TypeVar, List, Tuple -import os - -try: - import google.cloud.storage # noqa - - has_gcs = True -except ImportError: - has_gcs = False - +import io +from pathlib import _PosixFlavour # type:ignore +from pathlib import PurePath +from typing import List, TypeVar, Union PathType = TypeVar("PathType") +StreamableType = Union[io.TextIOWrapper, io.FileIO, io.BytesIO] + class _GCSFlavour(_PosixFlavour): - is_supported = bool(has_gcs) + is_supported = True - def parse_parts(self, parts): + def parse_parts(self, parts: List[str]): drv, root, parsed = super().parse_parts(parts) - if len(parsed) and parsed[0] == "gs:": + if len(parsed) and parsed[0].endswith(":"): if len(parsed) < 2: raise ValueError("need atleast two parts") # Restore the - drv = parsed[0] # gs: + drv = parsed[0] # scheme: root = parsed[1] # bucket_name for part in parsed[1:]: if part == "..": @@ -46,13 +41,30 @@ class PurePathy(PurePath): __slots__ = () @property - def bucket(self): + def scheme(self) -> str: + """Return the scheme portion of this path. A path's scheme is the leading + few characters. In a website you would see a scheme of "http" or "https". + + Consider a few examples: + + ```python + assert Pathy("gs://foo/bar").scheme == "gs" + assert Pathy("file:///tmp/foo/bar").scheme == "file" + + """ + # This is an assumption of mine. I think it's fine, but let's + # cause an error if it's not the case. + assert self.drive[-1] == ":", "drive should end with :" + return self.drive[:-1] + + @property + def bucket(self: PathType) -> PathType: """Return a new instance of only the bucket path.""" self._absolute_path_validation() return type(self)(f"{self.drive}//{self.root}") @property - def key(self): + def key(self: PathType) -> "PathType": """Return a new instance of only the key path.""" self._absolute_path_validation() key = self._flavour.sep.join(self.parts[2:]) @@ -61,9 +73,9 @@ def key(self): return type(self)(key) @property - def prefix(self) -> str: + def prefix(self: PathType) -> str: sep = self._flavour.sep - a = str(self) + str(self) key = self.key if not key: return "" diff --git a/pathy/cli.py b/pathy/cli.py index c52bfbe..f946562 100644 --- a/pathy/cli.py +++ b/pathy/cli.py @@ -1,5 +1,6 @@ import typer -from .api import Pathy, FluidPath + +from .api import FluidPath, Pathy app = typer.Typer(help="Pathy command line interface.") diff --git a/pathy/client.py b/pathy/client.py index cfd625a..78c8fd5 100644 --- a/pathy/client.py +++ b/pathy/client.py @@ -1,17 +1,18 @@ from dataclasses import dataclass +from io import DEFAULT_BUFFER_SIZE from typing import Generator, Generic, List, Optional, TypeVar import smart_open -from .base import PurePathy +from .base import PurePathy, StreamableType __all__ = ( - "BucketStat", + "BlobStat", "BucketEntry", "BucketClient", "ClientError", - "ClientBucket", - "ClientBlob", + "Bucket", + "Blob", ) BucketType = TypeVar("BucketType") @@ -23,7 +24,7 @@ @dataclass class ClientError(BaseException): message: str - code: int + code: Optional[int] def __str__(self) -> str: return self.__repr__() @@ -33,7 +34,7 @@ def __repr__(self) -> str: @dataclass -class BucketStat: +class BlobStat: """Stat for a bucket item""" size: int @@ -41,8 +42,8 @@ class BucketStat: @dataclass -class ClientBlob(Generic[BucketType, BucketBlobType]): - bucket: "ClientBucket" +class Blob(Generic[BucketType, BucketBlobType]): + bucket: "Bucket" name: str size: int updated: int @@ -61,21 +62,21 @@ class BucketEntry(Generic[BucketType, BucketBlobType]): name: str _is_dir: bool - _stat: BucketStat - raw: Optional[ClientBlob[BucketType, BucketBlobType]] + _stat: BlobStat + raw: Optional[Blob[BucketType, BucketBlobType]] def __init__( self, name: str, - is_dir: bool, - size: int = None, - last_modified: int = None, - raw: Optional[ClientBlob[BucketType, BucketBlobType]] = None, + is_dir: bool = False, + size: int = -1, + last_modified: int = -1, + raw: Optional[Blob[BucketType, BucketBlobType]] = None, ): self.name = name self.raw = raw self._is_dir = is_dir - self._stat = BucketStat(size=size, last_modified=last_modified) + self._stat = BlobStat(size=size, last_modified=last_modified) def __repr__(self): return "{}(name={}, is_dir={}, stat={})".format( @@ -99,56 +100,36 @@ def stat(self): @dataclass -class ClientBucket: - def get_blob(self, blob_name: str) -> Optional[ClientBlob]: +class Bucket: + def get_blob(self, blob_name: str) -> Optional[Blob]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def copy_blob( - self, blob: ClientBlob, target: "ClientBucket", name: str - ) -> Optional[ClientBlob]: + def copy_blob(self, blob: Blob, target: "Bucket", name: str) -> Optional[Blob]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def delete_blob(self, blob: ClientBlob) -> None: + def delete_blob(self, blob: Blob) -> None: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def delete_blobs(self, blobs: List[ClientBlob]) -> None: + def delete_blobs(self, blobs: List[Blob]) -> None: + raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + + def exists(self) -> bool: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) class BucketClient: """Base class for a client that interacts with a bucket-based storage system.""" - def make_uri(self, path: PurePathy) -> str: - return path.as_uri() - - def is_dir(self, path: PurePathy) -> bool: - return any(self.list_blobs(path, prefix=path.prefix)) - - def rmdir(self, path: PurePathy) -> None: - return None - - def exists(self, path: PurePathy) -> bool: - # Because we want all the parents of a valid blob (e.g. "directory" in - # "directory/foo.file") to return True, we enumerate the blobs with a prefix - # and compare the object names to see if they match a substring of the path - key_name = str(path.key) - for obj in self.list_blobs(path): - if obj.name == key_name: - return True - if obj.name.startswith(key_name + path._flavour.sep): - return True - return False - def open( self, path: PurePathy, *, - mode="r", - buffering=-1, - encoding=None, - errors=None, - newline=None, - ): + mode: str = "r", + buffering: int = DEFAULT_BUFFER_SIZE, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: return smart_open.open( self.make_uri(path), mode=mode, @@ -158,15 +139,27 @@ def open( newline=newline, # Disable de/compression based on the file extension ignore_ext=True, - ) + ) # type:ignore + + def make_uri(self, path: PurePathy) -> str: + return path.as_uri() + + def is_dir(self, path: PurePathy) -> bool: + return any(self.list_blobs(path, prefix=path.prefix)) + + def rmdir(self, path: PurePathy) -> None: + return None + + def exists(self, path: PurePathy) -> bool: + raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucket]: + def lookup_bucket(self, path: PurePathy) -> Optional[Bucket]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def get_bucket(self, path: PurePathy) -> ClientBucket: + def get_bucket(self, path: PurePathy) -> Bucket: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def list_buckets(self) -> Generator[ClientBucket, None, None]: + def list_buckets(self) -> Generator[Bucket, None, None]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) def list_blobs( @@ -175,7 +168,7 @@ def list_blobs( prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, - ) -> Generator[ClientBlob, None, None]: + ) -> Generator[Blob, None, None]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) def scandir( @@ -186,7 +179,7 @@ def scandir( ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) - def create_bucket(self, path: PurePathy) -> ClientBucket: + def create_bucket(self, path: PurePathy) -> Bucket: raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) def delete_bucket(self, path: PurePathy) -> None: diff --git a/pathy/clients.py b/pathy/clients.py new file mode 100644 index 0000000..a28e8e6 --- /dev/null +++ b/pathy/clients.py @@ -0,0 +1,122 @@ +import pathlib +import shutil +import tempfile +from typing import Dict, Optional, Type, Union + +from .client import BucketClient +from .file import BucketClientFS +from .gcs import BucketClientGCS + +# TODO: Maybe this should be dynamic, but I'm trying to see if we can +# hardcode it (atleast the base schemes) to get nice types flowing +# in cases where they would otherwise be lost. +_client_registry: Dict[str, Type[BucketClient]] = { + "file": BucketClientFS, + "gs": BucketClientGCS, +} + +_instance_cache: Dict[str, BucketClient] = {} +_fs_client: Optional[BucketClientFS] = None +_fs_cache: Optional[pathlib.Path] = None + + +def register_client() -> None: + """Register a bucket client for use with certain scheme Pathy objects""" + global _client_registry + + +def get_client(scheme: str) -> "BucketClient": + """Retrieve the bucket client for use with a given scheme""" + global _client_registry, _instance_cache, _fs_client + if _fs_client is not None: + return _fs_client + if scheme in _instance_cache: + return _instance_cache[scheme] + elif scheme in _client_registry: + _instance_cache[scheme] = _client_registry[scheme]() + return _instance_cache[scheme] + raise ValueError(f'There is no client registered to handle "{scheme}" paths') + + +def use_fs( + root: Optional[Union[str, pathlib.Path, bool]] = None +) -> Optional[BucketClientFS]: + """Use a path in the local file-system to store blobs and buckets. + + This is useful for development and testing situations, and for embedded + applications.""" + global _fs_client + # False - disable adapter + if root is False: + _fs_client = None + return None + + # None or True - enable FS adapter with default root + if root is None or root is True: + # Look up "data" folder of pathy package similar to spaCy + client_root = pathlib.Path(__file__).parent / "data" + else: + assert isinstance( + root, (str, pathlib.Path) + ), f"root is not a known type: {type(root)}" + client_root = pathlib.Path(root) + if not client_root.exists(): + client_root.mkdir(parents=True) + _fs_client = BucketClientFS(root=client_root) + return _fs_client + + +def get_fs_client() -> Optional[BucketClientFS]: + """Get the file-system client (or None)""" + global _fs_client + assert _fs_client is None or isinstance( + _fs_client, BucketClientFS + ), "invalid root type" + return _fs_client + + +def use_fs_cache( + root: Optional[Union[str, pathlib.Path, bool]] = None +) -> Optional[pathlib.Path]: + """Use a path in the local file-system to cache blobs and buckets. + + This is useful for when you want to avoid fetching large blobs multiple + times, or need to pass a local file path to a third-party library.""" + global _fs_cache + # False - disable adapter + if root is False: + _fs_cache = None + return None + + # None or True - enable FS cache with default root + if root is None or root is True: + # Use a temporary folder. Cache will be removed according to OS policy + cache_root = pathlib.Path(tempfile.mkdtemp()) + else: + assert isinstance( + root, (str, pathlib.Path) + ), f"root is not a known type: {type(root)}" + cache_root = pathlib.Path(root) + if not cache_root.exists(): + cache_root.mkdir(parents=True) + _fs_cache = cache_root + return cache_root + + +def get_fs_cache() -> Optional[pathlib.Path]: + """Get the folder that holds file-system cached blobs and timestamps.""" + global _fs_cache + assert _fs_cache is None or isinstance(_fs_cache, pathlib.Path), "invalid root type" + return _fs_cache + + +def clear_fs_cache(force: bool = False) -> None: + """Remove the existing file-system blob cache folder. + + Raises AssertionError if the cache path is unset or points to the + root of the file-system.""" + cache_path = get_fs_cache() + assert cache_path is not None, "no cache to clear" + resolved = cache_path.resolve() + assert str(resolved) != "/", f"refusing to remove a root path: {resolved}" + shutil.rmtree(str(resolved)) diff --git a/pathy/file.py b/pathy/file.py index 48c1091..777e1b5 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -1,10 +1,12 @@ -from dataclasses import dataclass, field -from typing import Optional, List, Generator, cast -from .client import BucketClient, ClientBucket, ClientBlob, ClientError, BucketEntry -from .base import PurePathy +import os import pathlib import shutil -import os +from dataclasses import dataclass, field +from io import DEFAULT_BUFFER_SIZE +from typing import Generator, List, Optional + +from .base import PurePathy, StreamableType +from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError class BucketEntryFS(BucketEntry["ClientBucketFS", pathlib.Path]): @@ -12,7 +14,7 @@ class BucketEntryFS(BucketEntry["ClientBucketFS", pathlib.Path]): @dataclass -class ClientBlobFS(ClientBlob["ClientBucketFS", pathlib.Path]): +class ClientBlobFS(Blob["ClientBucketFS", pathlib.Path]): raw: pathlib.Path bucket: "ClientBucketFS" @@ -30,7 +32,7 @@ def exists(self) -> bool: @dataclass -class ClientBucketFS(ClientBucket): +class ClientBucketFS(Bucket): name: str bucket: pathlib.Path @@ -73,11 +75,14 @@ def delete_blobs(self, blobs: List[ClientBlobFS]) -> None: for blob in blobs: blob.delete() + def exists(self) -> bool: + return self.bucket.exists() + @dataclass class BucketClientFS(BucketClient): # Root to store file-system buckets as children of - root: pathlib.Path + root: pathlib.Path = field(default_factory=lambda: pathlib.Path("/tmp/")) def make_uri(self, path: PurePathy): uri = super().make_uri(path) @@ -106,12 +111,12 @@ def open( self, path: PurePathy, *, - mode="r", - buffering=-1, - encoding=None, - errors=None, - newline=None, - ): + mode: str = "r", + buffering: int = DEFAULT_BUFFER_SIZE, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: if self.lookup_bucket(path) is None: raise ClientError(message=f'bucket "{path.root}" does not exist', code=404) @@ -135,7 +140,7 @@ def make_uri(self, path: PurePathy) -> str: result = f"file://{self.root.absolute() / path.root / path.key}" return result - def create_bucket(self, path: PurePathy) -> ClientBucket: + def create_bucket(self, path: PurePathy) -> Bucket: if not path.root: raise ValueError(f"Invalid bucket name: {path.root}") bucket_path: pathlib.Path = self.root / path.root @@ -192,7 +197,7 @@ def scandir( stat = file_path.stat() file_size = stat.st_size updated = int(round(stat.st_mtime_ns * 1000)) - blob: ClientBlob = ClientBlobFS( + blob: Blob = ClientBlobFS( self.get_bucket(path), name=dir_entry.name, size=file_size, diff --git a/pathy/gcs.py b/pathy/gcs.py index 3822be9..9723868 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -1,11 +1,13 @@ -from dataclasses import dataclass, field -from typing import Optional, List, Generator -from .client import BucketClient, ClientBucket, ClientBlob, ClientError, BucketEntry +from dataclasses import dataclass +from typing import Generator, List, Optional + from .base import PurePathy +from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError try: - from google.cloud import storage from google.api_core import exceptions as gcs_errors + from google.auth.exceptions import DefaultCredentialsError + from google.cloud import storage has_gcs = True except ImportError: @@ -13,12 +15,12 @@ has_gcs = False -class BucketEntryGCS(BucketEntry["ClientBucketGCS", storage.Blob]): +class BucketEntryGCS(BucketEntry["ClientBucketGCS", "storage.Blob"]): ... @dataclass -class ClientBlobGCS(ClientBlob["ClientBucketGCS", storage.Blob]): +class ClientBlobGCS(Blob["ClientBucketGCS", "storage.Blob"]): def delete(self) -> None: self.raw.delete() @@ -27,12 +29,19 @@ def exists(self) -> bool: @dataclass -class ClientBucketGCS(ClientBucket): +class ClientBucketGCS(Bucket): name: str - bucket: storage.Bucket + bucket: "storage.Bucket" def get_blob(self, blob_name: str) -> Optional[ClientBlobGCS]: - native_blob = self.bucket.get_blob(blob_name) + assert isinstance( + blob_name, str + ), f"expected str blob name, but found: {type(blob_name)}" + native_blob = None + try: + native_blob = self.bucket.get_blob(blob_name) + except gcs_errors.ClientError: + pass if native_blob is None: return None return ClientBlobGCS( @@ -66,40 +75,67 @@ def delete_blob(self, blob: ClientBlobGCS) -> None: def delete_blobs(self, blobs: List[ClientBlobGCS]) -> None: return self.bucket.delete_blobs(blobs) + def exists(self) -> bool: + try: + return self.bucket.exists() + except gcs_errors.ClientError: + return False + -@dataclass class BucketClientGCS(BucketClient): - client: storage.Client = field(default_factory=lambda: storage.Client()) + client: Optional["storage.Client"] + + def __init__(self, client: Optional["storage.Client"] = None): + try: + self.client = storage.Client() if storage else None + except (BaseException, DefaultCredentialsError): + self.client = None def make_uri(self, path: PurePathy): return str(path) - def create_bucket(self, path: PurePathy) -> ClientBucket: + def create_bucket(self, path: PurePathy) -> Bucket: return self.client.create_bucket(path.root) def delete_bucket(self, path: PurePathy) -> None: bucket = self.client.get_bucket(path.root) bucket.delete() + def exists(self, path: PurePathy) -> bool: + # Because we want all the parents of a valid blob (e.g. "directory" in + # "directory/foo.file") to return True, we enumerate the blobs with a prefix + # and compare the object names to see if they match a substring of the path + key_name = str(path.key) + try: + for obj in self.list_blobs(path): + if obj.name == key_name: + return True + if obj.name.startswith(key_name + path._flavour.sep): + return True + except gcs_errors.ClientError: + return False + return False + def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucketGCS]: try: - native_bucket = self.client.lookup_bucket(path.root) + native_bucket = self.client.bucket(path.root) if native_bucket is not None: return ClientBucketGCS(str(path.root), bucket=native_bucket) - except gcs_errors.ClientError: - pass + except gcs_errors.ClientError as err: + print(err) + return None def get_bucket(self, path: PurePathy) -> ClientBucketGCS: try: - native_bucket = self.client.lookup_bucket(path.root) + native_bucket = self.client.bucket(path.root) if native_bucket is not None: return ClientBucketGCS(str(path.root), bucket=native_bucket) raise FileNotFoundError(f"Bucket {path.root} does not exist!") except gcs_errors.ClientError as e: raise ClientError(message=e.message, code=e.code) - def list_buckets(self, **kwargs) -> Generator[ClientBucket, None, None]: + def list_buckets(self, **kwargs) -> Generator[Bucket, None, None]: return self.client.list_buckets(**kwargs) def scandir( diff --git a/tests/conftest.py b/tests/conftest.py index f483886..0b6de65 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,24 +4,25 @@ from pathlib import Path import pytest -from pathy import Pathy, use_fs, use_fs_cache +from pathy import Pathy, use_fs, use_fs_cache +from pathy.gcs import has_gcs # TODO: set this up with a service account for the CI has_credentials = "CI" not in os.environ # Which adapters to use -TEST_ADAPTERS = ["gcs", "fs"] if has_credentials else ["fs"] +TEST_ADAPTERS = ["gcs", "fs"] if has_credentials and has_gcs else ["fs"] @pytest.fixture() def bucket() -> str: - return "pathy-tests-1" + return "pathy-tests-bucket" @pytest.fixture() def other_bucket() -> str: - return "pathy-tests-2" + return "pathy-tests-bucket-other" @pytest.fixture() diff --git a/tests/test_api.py b/tests/test_api.py index 9263762..1a18c1a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -7,12 +7,10 @@ import spacy from google.auth.exceptions import DefaultCredentialsError -from .conftest import TEST_ADAPTERS - from pathy import ( + BlobStat, BucketClientFS, BucketsAccessor, - BucketStat, FluidPath, Pathy, PurePathy, @@ -22,6 +20,8 @@ use_fs_cache, ) +from .conftest import TEST_ADAPTERS + # todo: test samefile/touch/write_text/write_bytes method # todo: test security and boto config changes # todo: test open method check R/W bytes/unicode @@ -127,7 +127,7 @@ def test_api_stat(with_adapter, bucket: str): path = Pathy(f"gs://{bucket}/foo.txt") path.write_text("a-a-a-a-a-a-a") stat = path.stat() - assert isinstance(stat, BucketStat) + assert isinstance(stat, BlobStat) assert stat.size > 0 assert stat.last_modified > 0 with pytest.raises(ValueError): @@ -390,9 +390,7 @@ def test_api_rename_folders_across_buckets( def test_api_replace_files_in_bucket(with_adapter, bucket: str): # replace a single file Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") - Pathy(f"gs://{bucket}/replace/file.txt").replace( - f"gs://{bucket}/replace/other.txt" - ) + Pathy(f"gs://{bucket}/replace/file.txt").replace(f"gs://{bucket}/replace/other.txt") assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() assert Pathy(f"gs://{bucket}/replace/other.txt").is_file() @@ -453,26 +451,28 @@ def test_api_rmdir(with_adapter, bucket: str): def test_api_mkdir(with_adapter, bucket: str): bucket_name = f"pathy-e2e-test-{uuid4().hex}" # Create a bucket - Pathy(f"gs://{bucket_name}/").mkdir() + path = Pathy(f"gs://{bucket_name}/") + path.mkdir() + assert path.exists() # Does not assert if it already exists - Pathy(f"gs://{bucket_name}/").mkdir(exist_ok=True) + path.mkdir(exist_ok=True) with pytest.raises(FileExistsError): - Pathy(f"gs://{bucket_name}/").mkdir(exist_ok=False) + path.mkdir(exist_ok=False) # with pytest.raises(FileNotFoundError): # Pathy("/test-second-bucket/test-directory/file.name").mkdir() # Pathy("/test-second-bucket/test-directory/file.name").mkdir(parents=True) - assert Pathy(f"gs://{bucket_name}/").exists() + assert path.exists() # remove the bucket # client = storage.Client() # bucket = client.lookup_bucket(bucket_name) # bucket.delete() - Pathy(f"gs://{bucket_name}/").rmdir() + path.rmdir() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_ignore_extension(with_adapter, bucket: str): """The smart_open library does automatic decompression based - on the filename. We disable that to avoid errors, e.g. if you + on the filename. We disable that to avoid errors, e.g. if you have a .tar.gz file that isn't gzipped.""" not_targz = Pathy.from_bucket(bucket) / "ignore_ext/one.tar.gz" fixture_tar = Path(__file__).parent / "fixtures" / "tar_but_not_gzipped.tar.gz" @@ -512,13 +512,14 @@ def test_api_use_fs(with_fs: Path): @mock.patch("pathy.gcs.BucketClientGCS", side_effect=DefaultCredentialsError()) def test_api_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): accessor = BucketsAccessor() - # Accessing the client lazily throws with no GCS or FS adapters configured - with pytest.raises(AssertionError): - accessor.client + path = Pathy("foo://foo") + # Accessing the client throws with no GCS or FS adapters configured + with pytest.raises(ValueError): + accessor.client(path) # Setting a fallback FS adapter fixes the problem use_fs(str(temp_folder)) - assert isinstance(accessor.client, BucketClientFS) + assert isinstance(accessor.client(path), BucketClientFS) def test_api_export_spacy_model(temp_folder): diff --git a/tests/test_base.py b/tests/test_base.py index 9637284..13cff2b 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -127,9 +127,7 @@ def test_base_slashes_single_double_dots(): def test_base_operators(): - assert PurePathy("/etc") / "init.d" / "apache2" == PurePathy( - "/etc/init.d/apache2" - ) + assert PurePathy("/etc") / "init.d" / "apache2" == PurePathy("/etc/init.d/apache2") assert "/var" / PurePathy("tests") / "fake" == PurePathy("/var/tests/fake") @@ -238,9 +236,7 @@ def test_base_reserved(): def test_base_joinpath(): assert PurePathy("/etc").joinpath("passwd") == PurePathy("/etc/passwd") - assert PurePathy("/etc").joinpath(PurePathy("passwd")) == PurePathy( - "/etc/passwd" - ) + assert PurePathy("/etc").joinpath(PurePathy("passwd")) == PurePathy("/etc/passwd") assert PurePathy("/etc").joinpath("init.d", "apache2") == PurePathy( "/etc/init.d/apache2" ) @@ -265,9 +261,7 @@ def test_base_relative_to(): def test_base_with_name(): gcs_path = PurePathy("/Downloads/pathlib.tar.gz") - assert gcs_path.with_name("fake_file.txt") == PurePathy( - "/Downloads/fake_file.txt" - ) + assert gcs_path.with_name("fake_file.txt") == PurePathy("/Downloads/fake_file.txt") gcs_path = PurePathy("/") with pytest.raises(ValueError): gcs_path.with_name("fake_file.txt") diff --git a/tests/test_cli.py b/tests/test_cli.py index b9b3ab6..2eb704e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,8 +1,8 @@ import pytest -from pathy import Pathy -from pathy.cli import app from typer.testing import CliRunner +from pathy import Pathy +from pathy.cli import app from .conftest import TEST_ADAPTERS @@ -104,10 +104,11 @@ def test_cli_mv_folder_across_buckets(with_adapter, bucket: str, other_bucket: s @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_rm_file(with_adapter, bucket: str): source = f"gs://{bucket}/cli_rm_file/file.txt" - Pathy(source).write_text("---") - assert Pathy(source).exists() + path = Pathy(source) + path.write_text("---") + assert path.exists() assert runner.invoke(app, ["rm", source]).exit_code == 0 - assert not Pathy(source).exists() + assert not path.exists() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) diff --git a/tests/test_client.py b/tests/test_client.py index cb8bbe5..b54fc15 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,8 +1,8 @@ from pathlib import Path +import pytest -from pathy import PurePathy -from pathy.file import BucketClientFS +from pathy import Blob, Bucket, BucketClient, BucketClientFS, Pathy, PurePathy def test_client_create_bucket(temp_folder: Path): @@ -11,3 +11,44 @@ def test_client_create_bucket(temp_folder: Path): cl = BucketClientFS(temp_folder) cl.create_bucket(PurePathy("gs://foo/")) assert bucket_target.exists() is True + + +def test_client_base_bucket_raises_not_implemented(): + bucket = Bucket() + blob = Blob(bucket, "foo", -1, -1, None, None) + with pytest.raises(NotImplementedError): + bucket.copy_blob(blob, bucket, "baz") + with pytest.raises(NotImplementedError): + bucket.get_blob("baz") + with pytest.raises(NotImplementedError): + bucket.delete_blobs([blob]) + with pytest.raises(NotImplementedError): + bucket.delete_blob(blob) + with pytest.raises(NotImplementedError): + bucket.exists() + + +def test_client_base_blob_raises_not_implemented(): + blob = Blob(Bucket(), "foo", -1, -1, None, None) + with pytest.raises(NotImplementedError): + blob.delete() + with pytest.raises(NotImplementedError): + blob.exists() + + +def test_client_base_bucket_client_raises_not_implemented(): + client = BucketClient() + with pytest.raises(NotImplementedError): + client.lookup_bucket(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.get_bucket(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.list_buckets() + with pytest.raises(NotImplementedError): + client.list_blobs(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.scandir(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.create_bucket(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.delete_bucket(Pathy("gs://foo")) diff --git a/tests/test_registry.py b/tests/test_registry.py new file mode 100644 index 0000000..0e676f5 --- /dev/null +++ b/tests/test_registry.py @@ -0,0 +1,15 @@ +import pytest + +from pathy import get_client +from pathy.file import BucketClientFS +from pathy.gcs import BucketClientGCS + + +def test_registry_get_client_works_with_builtin_schems(): + assert isinstance(get_client("gs"), BucketClientGCS) + assert isinstance(get_client("file"), BucketClientFS) + + +def test_registry_get_client_errors_with_unknown_scheme(): + with pytest.raises(ValueError): + get_client("foo") diff --git a/tools/docs.sh b/tools/docs.sh index 38d8fdb..501dd80 100644 --- a/tools/docs.sh +++ b/tools/docs.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e . .env/bin/activate -mathy_pydoc pathy.Pathy+ pathy.BucketStat+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache > /tmp/pathy_api.md +mathy_pydoc pathy.Pathy+ pathy.BlobStat+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache > /tmp/pathy_api.md typer pathy.cli utils docs > /tmp/pathy_cli.md python tools/docs.py /tmp/pathy_api.md /tmp/pathy_cli.md README.md From ac3d0ab0327422c00df1c59b2010a711643f43bf Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 09:38:33 -0700 Subject: [PATCH 32/75] chore: add semantic PR title linting github action --- .github/workflows/semantic_lint.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/semantic_lint.yml diff --git a/.github/workflows/semantic_lint.yml b/.github/workflows/semantic_lint.yml new file mode 100644 index 0000000..dd13a04 --- /dev/null +++ b/.github/workflows/semantic_lint.yml @@ -0,0 +1,15 @@ +name: "Valid Semantic Title" +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v1.2.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 78f5361f12e7cabb0a77e79bf08226816802fc1a Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 09:51:02 -0700 Subject: [PATCH 33/75] chore: fix tests without google-auth installed --- tests/test_api.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 1a18c1a..48e0089 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2,10 +2,8 @@ from time import sleep from uuid import uuid4 -import mock import pytest import spacy -from google.auth.exceptions import DefaultCredentialsError from pathy import ( BlobStat, @@ -509,8 +507,7 @@ def test_api_use_fs(with_fs: Path): use_fs(False) -@mock.patch("pathy.gcs.BucketClientGCS", side_effect=DefaultCredentialsError()) -def test_api_bucket_accessor_without_gcs(bucket_client_gcs_mock, temp_folder): +def test_api_bucket_accessor_without_gcs(temp_folder): accessor = BucketsAccessor() path = Pathy("foo://foo") # Accessing the client throws with no GCS or FS adapters configured From 71defad27550bdfc0753bbd36c4d5305956fc5a0 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 10:02:06 -0700 Subject: [PATCH 34/75] chore: drop github action for semantic pr titles - really the title is secondary to the commits in the PR. We'll continue to use the Semantic PR github app as long as it works --- .github/workflows/semantic_lint.yml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .github/workflows/semantic_lint.yml diff --git a/.github/workflows/semantic_lint.yml b/.github/workflows/semantic_lint.yml deleted file mode 100644 index dd13a04..0000000 --- a/.github/workflows/semantic_lint.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: "Valid Semantic Title" -on: - pull_request_target: - types: - - opened - - edited - - synchronize - -jobs: - main: - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v1.2.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 5d7cb8afa8f0ae76fe49718a907f9fef604831c8 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 10:03:01 -0700 Subject: [PATCH 35/75] chore: cleanup from review --- tests/test_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 48e0089..56ac417 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -507,13 +507,11 @@ def test_api_use_fs(with_fs: Path): use_fs(False) -def test_api_bucket_accessor_without_gcs(temp_folder): +def test_api_raises_with_no_known_bucket_clients_for_a_scheme(temp_folder): accessor = BucketsAccessor() path = Pathy("foo://foo") - # Accessing the client throws with no GCS or FS adapters configured with pytest.raises(ValueError): accessor.client(path) - # Setting a fallback FS adapter fixes the problem use_fs(str(temp_folder)) assert isinstance(accessor.client(path), BucketClientFS) From 35569a356f54ffe15299d13c2bce95ec2839cb5a Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 14:08:08 -0700 Subject: [PATCH 36/75] chore: fix extras in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 79f6e66..0cc504e 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ def setup_package(): packages=find_packages(), python_requires=">= 3.6", install_requires=requirements, + extras_require=extras, entry_points=""" [console_scripts] pathy=pathy.cli:app From 796dd407fca72c5297914e597f3221fdbcd9e95e Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 15:07:07 -0700 Subject: [PATCH 37/75] refactor: add BasePathy class to bind PathType var to BREAKING CHANGE: This renames the internal GCS/File adapter classes by removing the prefix Client. ClientBucketFS -> BucketFS ClientBlobFS -> BlobFS ClientBucketGCS -> BucketGCS ClientBlobGCS -> BlobGCS --- README.md | 43 ++++++----- pathy/__init__.py | 8 +- pathy/api.py | 149 ++++++++++--------------------------- pathy/base.py | 179 +++++++++++++++++++++++++++++++++++++++++---- pathy/cli.py | 14 ++-- pathy/client.py | 96 +++++++++++------------- pathy/clients.py | 4 +- pathy/file.py | 42 +++++------ pathy/gcs.py | 41 ++++++----- tests/test_api.py | 15 ++++ tests/test_base.py | 30 ++++++++ tests/test_file.py | 25 +++++++ 12 files changed, 401 insertions(+), 245 deletions(-) create mode 100644 tests/test_file.py diff --git a/README.md b/README.md index 9e5ada2..f80a893 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Subclass of `pathlib.Path` that works with bucket APIs. ## exists method ```python -Pathy.exists(self: ~PathType) -> bool +Pathy.exists(self) -> bool ``` Returns True if the path points to an existing bucket, blob, or prefix. @@ -58,8 +58,8 @@ Returns True if the path points to an existing bucket, blob, or prefix. ```python Pathy.fluid( - path_candidate: Union[str, Pathy, pathlib.Path], -) -> Union[Pathy, pathlib.Path] + path_candidate: Union[str, BasePathy, pathlib.Path], +) -> Union[BasePathy, pathlib.Path] ``` Infer either a Pathy or pathlib.Path from an input path or string. @@ -82,7 +82,7 @@ print(fluid_path.prefix) ## from_bucket classmethod ```python -Pathy.from_bucket(bucket_name: str) -> 'Pathy' +Pathy.from_bucket(bucket_name: str) -> ~PathType ``` Initialize a Pathy from a bucket name. This helper adds a trailing slash and @@ -96,7 +96,10 @@ assert str(Pathy.from_bucket("two")) == "gs://two/" ## glob method ```python -Pathy.glob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] +Pathy.glob( + self: ~PathType, + pattern: str, +) -> Generator[~PathType, NoneType, NoneType] ``` Perform a glob match relative to this Pathy instance, yielding all matched @@ -138,7 +141,7 @@ Iterate over the blobs found in the given bucket or blob prefix path. ```python Pathy.mkdir( - self: ~PathType, + self, mode: int = 511, parents: bool = False, exist_ok: bool = False, @@ -167,7 +170,7 @@ Pathy.open( encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None, -) -> Union[_io.TextIOWrapper, _io.FileIO, _io.BytesIO] +) -> IO[Any] ``` Open the given blob for streaming. This delegates to the `smart_open` @@ -177,7 +180,7 @@ providers. ## owner method ```python -Pathy.owner(self: ~PathType) -> Optional[str] +Pathy.owner(self: 'Pathy') -> Optional[str] ``` Returns the name of the user that owns the bucket or blob @@ -187,7 +190,7 @@ not supported by the bucket API provider. ## rename method ```python -Pathy.rename(self: ~PathType, target: Union[str, ~PathType]) -> None +Pathy.rename(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> None ``` Rename this path to the given target. @@ -201,7 +204,7 @@ to match the target prefix. ## replace method ```python -Pathy.replace(self: ~PathType, target: Union[str, ~PathType]) -> None +Pathy.replace(self: ~PathType, target: Union[str, pathlib.PurePath]) -> None ``` Renames this path to the given target. @@ -211,7 +214,7 @@ If target points to an existing path, it will be replaced. ## resolve method ```python -Pathy.resolve(self: ~PathType) -> ~PathType +Pathy.resolve(self, strict: bool = False) -> ~PathType ``` Resolve the given path to remove any relative path specifiers. @@ -224,7 +227,10 @@ assert path.resolve() == Pathy("gs://my_bucket/blob") ## rglob method ```python -Pathy.rglob(self: ~PathType, pattern) -> Generator[~PathType, NoneType, NoneType] +Pathy.rglob( + self: ~PathType, + pattern: str, +) -> Generator[~PathType, NoneType, NoneType] ``` Perform a recursive glob match relative to this Pathy instance, yielding @@ -233,7 +239,7 @@ all matched blobs. Imagine adding "\*\*/" before a call to glob. ## rmdir method ```python -Pathy.rmdir(self: ~PathType) -> None +Pathy.rmdir(self: 'Pathy') -> None ``` Removes this bucket or blob prefix. It must be empty. @@ -241,7 +247,10 @@ Removes this bucket or blob prefix. It must be empty. ## samefile method ```python -Pathy.samefile(self: ~PathType, other_path: ~PathType) -> bool +Pathy.samefile( + self: 'Pathy', + other_path: Union[str, bytes, int, pathlib.Path], +) -> bool ``` Determine if this path points to the same location as other_path. @@ -249,7 +258,7 @@ Determine if this path points to the same location as other_path. ## stat method ```python -Pathy.stat(self: ~PathType) -> pathy.client.BlobStat +Pathy.stat(self: ~PathType) -> pathy.base.BlobStat ``` Returns information about this bucket path. @@ -258,7 +267,7 @@ Returns information about this bucket path. ```python Pathy.to_local( - blob_path: Union[Pathy, str], + blob_path: Union[~PathType, str], recurse: bool = True, ) -> pathlib.Path ``` @@ -271,7 +280,7 @@ as their updated timestamps change. ## touch method ```python -Pathy.touch(self: ~PathType, mode: int = 438, exist_ok: bool = True) +Pathy.touch(self: ~PathType, mode: int = 438, exist_ok: bool = True) -> None ``` Create a blob at this path. diff --git a/pathy/__init__.py b/pathy/__init__.py index 611419d..280e525 100644 --- a/pathy/__init__.py +++ b/pathy/__init__.py @@ -6,9 +6,9 @@ ClientError, FluidPath, Pathy, - PurePathy, ) -from .client import BlobStat, BucketEntry +from .base import BlobStat, PurePathy +from .client import BucketEntry from .clients import ( clear_fs_cache, get_client, @@ -18,5 +18,5 @@ use_fs, use_fs_cache, ) -from .file import BucketClientFS, BucketEntryFS, ClientBlobFS, ClientBucketFS -from .gcs import BucketClientGCS, BucketEntryGCS, ClientBlobGCS, ClientBucketGCS +from .file import BlobFS, BucketClientFS, BucketEntryFS, BucketFS +from .gcs import BlobGCS, BucketClientGCS, BucketEntryGCS, BucketGCS diff --git a/pathy/api.py b/pathy/api.py index dccccaf..a06de5d 100644 --- a/pathy/api.py +++ b/pathy/api.py @@ -1,33 +1,24 @@ import os from io import DEFAULT_BUFFER_SIZE -from pathlib import _Accessor # type:ignore -from pathlib import Path -from typing import Generator, Optional, Union, cast +from pathlib import Path # type:ignore +from pathlib import PurePath, _Accessor +from typing import Any, Generator, Optional, Union, cast -from .base import PathType, PurePathy, StreamableType -from .client import Blob, BlobStat, Bucket, BucketClient, BucketEntry, ClientError +from .base import BasePathy, BlobStat, FluidPath, PathType, StreamableType +from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError __all__ = () _SUPPORTED_OPEN_MODES = {"r", "rb", "tr", "rt", "w", "wb", "bw", "wt", "tw"} -FluidPath = Union["Pathy", Path] - - -class Pathy(Path, PurePathy): +class Pathy(BasePathy): """Subclass of `pathlib.Path` that works with bucket APIs.""" __slots__ = () - _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" - - def __truediv__(self: PathType, key: Union[str, PathType]) -> PathType: - return super().__truediv__(key) - - def __rtruediv__(self: PathType, key: Union[str, PathType]) -> PathType: - return cast(Pathy, super().__rtruediv__(key)) + _accessor: "BucketsAccessor" - def _init(self: PathType, template=None): + def _init(self: "Pathy", template: Optional[Any] = None) -> None: super()._init(template) # type:ignore if template is None: self._accessor = _gcs_accessor @@ -35,7 +26,7 @@ def _init(self: PathType, template=None): self._accessor = template._accessor @classmethod - def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: + def fluid(cls, path_candidate: Union[str, FluidPath]) -> FluidPath: """Infer either a Pathy or pathlib.Path from an input path or string. The returned type is a union of the potential `FluidPath` types and will @@ -59,7 +50,7 @@ def fluid(cls: PathType, path_candidate: Union[str, FluidPath]) -> FluidPath: return from_path @classmethod - def from_bucket(cls: PathType, bucket_name: str) -> "Pathy": + def from_bucket(cls, bucket_name: str) -> PathType: """Initialize a Pathy from a bucket name. This helper adds a trailing slash and the appropriate prefix. @@ -71,9 +62,7 @@ def from_bucket(cls: PathType, bucket_name: str) -> "Pathy": return Pathy(f"gs://{bucket_name}/") @classmethod - def to_local( - cls: PathType, blob_path: Union["Pathy", str], recurse: bool = True - ) -> Path: + def to_local(cls, blob_path: Union[PathType, str], recurse: bool = True) -> Path: """Download and cache either a blob or a set of blobs matching a prefix. The cache is sensitive to the file updated time, and downloads new blobs @@ -87,7 +76,7 @@ def to_local( ) cache_folder.mkdir(exist_ok=True, parents=True) - if isinstance(blob_path, str): + if not isinstance(blob_path, Pathy): blob_path = Pathy(blob_path) cache_blob: Path = cache_folder.absolute() / blob_path.root / blob_path.key @@ -130,11 +119,9 @@ def stat(self: PathType) -> BlobStat: raise ValueError("cannot stat a bucket without a key") return cast(BlobStat, super().stat()) - def exists(self: PathType) -> bool: + def exists(self) -> bool: """Returns True if the path points to an existing bucket, blob, or prefix.""" self._absolute_path_validation() - if not self.bucket: - return True return self._accessor.exists(self) def is_dir(self: PathType) -> bool: @@ -169,12 +156,12 @@ def iterdir(self: PathType) -> Generator[PathType, None, None]: self._absolute_path_validation() yield from super().iterdir() - def glob(self: PathType, pattern) -> Generator[PathType, None, None]: + def glob(self: PathType, pattern: str) -> Generator[PathType, None, None]: """Perform a glob match relative to this Pathy instance, yielding all matched blobs.""" yield from super().glob(pattern) - def rglob(self: PathType, pattern) -> Generator[PathType, None, None]: + def rglob(self: PathType, pattern: str) -> Generator[PathType, None, None]: """Perform a recursive glob match relative to this Pathy instance, yielding all matched blobs. Imagine adding "**/" before a call to glob.""" yield from super().rglob(pattern) @@ -202,8 +189,9 @@ def open( if "b" in mode and encoding: raise ValueError("binary mode doesn't take an encoding argument") - if self._closed: - self._raise_closed() + # Leftover pathlib internals stuff + if self._closed: # type:ignore + self._raise_closed() # type:ignore return self._accessor.open( self, mode=mode, @@ -213,7 +201,7 @@ def open( newline=newline, ) - def owner(self: PathType) -> Optional[str]: + def owner(self: "Pathy") -> Optional[str]: """Returns the name of the user that owns the bucket or blob this path points to. Returns None if the owner is unknown or not supported by the bucket API provider.""" @@ -222,7 +210,7 @@ def owner(self: PathType) -> Optional[str]: raise FileNotFoundError(str(self)) return self._accessor.owner(self) - def resolve(self: PathType) -> PathType: + def resolve(self, strict: bool = False) -> PathType: """Resolve the given path to remove any relative path specifiers. ```python @@ -231,9 +219,9 @@ def resolve(self: PathType) -> PathType: ``` """ self._absolute_path_validation() - return self._accessor.resolve(self) + return self._accessor.resolve(self, strict=strict) - def rename(self: PathType, target: Union[str, PathType]) -> None: + def rename(self: "Pathy", target: Union[str, PurePath]) -> None: """Rename this path to the given target. If the target exists and is a file, it will be replaced silently if the user @@ -248,13 +236,13 @@ def rename(self: PathType, target: Union[str, PathType]) -> None: target._absolute_path_validation() # type:ignore super().rename(target) - def replace(self: PathType, target: Union[str, PathType]) -> None: + def replace(self: PathType, target: Union[str, PurePath]) -> None: """Renames this path to the given target. If target points to an existing path, it will be replaced.""" self.rename(target) - def rmdir(self: PathType) -> None: + def rmdir(self: "Pathy") -> None: """Removes this bucket or blob prefix. It must be empty.""" self._absolute_path_validation() if self.is_file(): @@ -263,17 +251,17 @@ def rmdir(self: PathType) -> None: raise FileNotFoundError() super().rmdir() - def samefile(self: PathType, other_path: PathType) -> bool: + def samefile(self: "Pathy", other_path: Union[str, bytes, int, Path]) -> bool: """Determine if this path points to the same location as other_path.""" self._absolute_path_validation() if not isinstance(other_path, Path): - other_path = type(self)(other_path) + other_path = type(self)(other_path) # type:ignore assert isinstance(other_path, Pathy) return ( self.bucket == other_path.bucket and self.key == self.key and self.is_file() ) - def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True): + def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True) -> None: """Create a blob at this path. If the blob already exists, the function succeeds if exist_ok is true @@ -285,7 +273,7 @@ def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True): self.write_text("") def mkdir( - self: PathType, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False ) -> None: """Create a bucket from the given path. Since bucket APIs only have implicit folder structures (determined by the existence of a blob with an overlapping @@ -310,70 +298,6 @@ def mkdir( if not exist_ok: raise - def is_mount(self: PathType) -> bool: - return False - - def is_symlink(self: PathType) -> bool: - return False - - def is_socket(self: PathType) -> bool: - return False - - def is_fifo(self: PathType) -> bool: - return False - - # Unsupported operations below here - - @classmethod - def cwd(cls: PathType): - message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.cwd.__qualname__) - raise NotImplementedError(message) - - @classmethod - def home(cls: PathType): - message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.home.__qualname__) - raise NotImplementedError(message) - - def chmod(self: PathType, mode): - message = self._NOT_SUPPORTED_MESSAGE.format(method=self.chmod.__qualname__) - raise NotImplementedError(message) - - def expanduser(self: PathType): - message = self._NOT_SUPPORTED_MESSAGE.format( - method=self.expanduser.__qualname__ - ) - raise NotImplementedError(message) - - def lchmod(self: PathType, mode): - message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lchmod.__qualname__) - raise NotImplementedError(message) - - def group(self: PathType): - message = self._NOT_SUPPORTED_MESSAGE.format(method=self.group.__qualname__) - raise NotImplementedError(message) - - def is_block_device(self: PathType): - message = self._NOT_SUPPORTED_MESSAGE.format( - method=self.is_block_device.__qualname__ - ) - raise NotImplementedError(message) - - def is_char_device(self: PathType): - message = self._NOT_SUPPORTED_MESSAGE.format( - method=self.is_char_device.__qualname__ - ) - raise NotImplementedError(message) - - def lstat(self: PathType): - message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lstat.__qualname__) - raise NotImplementedError(message) - - def symlink_to(self: PathType, *args, **kwargs): - message = self._NOT_SUPPORTED_MESSAGE.format( - method=self.symlink_to.__qualname__ - ) - raise NotImplementedError(message) - class BucketsAccessor(_Accessor): """Access data from blob buckets""" @@ -432,7 +356,7 @@ def exists(self, path: PathType) -> bool: return True key_name = str(path.key) - blob = bucket.get_blob(key_name) + blob: Optional[Blob] = bucket.get_blob(key_name) if blob is not None: return blob.exists() # Determine if the path exists according to the current adapter @@ -446,7 +370,7 @@ def listdir(self, path: PathType) -> Generator[str, None, None]: yield entry.name def open( - self: PathType, + self, path: PathType, *, mode: str = "r", @@ -468,7 +392,7 @@ def owner(self, path: PathType) -> Optional[str]: blob: Optional[Blob] = self.get_blob(path) return blob.owner if blob is not None else None - def resolve(self, path: PathType, strict: bool = False) -> PathType: + def resolve(self, path: PathType, strict: bool = False) -> "Pathy": path_parts = str(path).replace(path.drive, "") return Pathy(f"{path.drive}{os.path.abspath(path_parts)}") @@ -503,18 +427,21 @@ def replace(self, path: PathType, target: PathType) -> None: def rmdir(self, path: PathType) -> None: client: BucketClient = self.client(path) key_name = str(path.key) if path.key is not None else None - bucket = client.get_bucket(path) + bucket: Bucket = client.get_bucket(path) blobs = list(client.list_blobs(path, prefix=key_name)) bucket.delete_blobs(blobs) - if client.is_dir(path): + # The path is just the bucket + if key_name is None: + client.delete_bucket(path) + elif client.is_dir(path): client.rmdir(path) - def mkdir(self, path: PathType, mode) -> None: + def mkdir(self, path: PathType, mode: int) -> None: client: BucketClient = self.client(path) bucket: Optional[Bucket] = client.lookup_bucket(path) if bucket is None or not bucket.exists(): client.create_bucket(path) - elif path.key is not None: + elif isinstance(path, Pathy) and path.key is not None: assert isinstance(path, Pathy) blob: Optional[Blob] = bucket.get_blob(str(path.key)) if blob is not None and blob.exists(): diff --git a/pathy/base.py b/pathy/base.py index 745ef87..ae92337 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -1,17 +1,19 @@ -import io +from dataclasses import dataclass +from io import DEFAULT_BUFFER_SIZE from pathlib import _PosixFlavour # type:ignore -from pathlib import PurePath -from typing import List, TypeVar, Union +from pathlib import Path, PurePath +from typing import IO, Any, Generator, List, Optional, Tuple, TypeVar, Union, cast -PathType = TypeVar("PathType") - -StreamableType = Union[io.TextIOWrapper, io.FileIO, io.BytesIO] +SUBCLASS_ERROR = "must be implemented in a subclass" +PathType = TypeVar("PathType", bound="BasePathy") +StreamableType = IO[Any] +FluidPath = Union["BasePathy", Path] class _GCSFlavour(_PosixFlavour): is_supported = True - def parse_parts(self, parts: List[str]): + def parse_parts(self, parts: List[str]) -> Tuple[str, str, List[str]]: drv, root, parsed = super().parse_parts(parts) if len(parsed) and parsed[0].endswith(":"): if len(parsed) < 2: @@ -26,7 +28,7 @@ def parse_parts(self, parts: List[str]): parsed.remove(part) return drv, root, parsed - def make_uri(self, path): + def make_uri(self, path: PathType) -> str: uri = super().make_uri(path) return uri.replace("file:///", "gs://") @@ -34,6 +36,14 @@ def make_uri(self, path): _gcs_flavour = _GCSFlavour() +@dataclass +class BlobStat: + """Stat for a bucket item""" + + size: int + last_modified: int + + class PurePathy(PurePath): """PurePath subclass for bucket storage.""" @@ -58,22 +68,22 @@ def scheme(self) -> str: return self.drive[:-1] @property - def bucket(self: PathType) -> PathType: + def bucket(self) -> "BasePathy": """Return a new instance of only the bucket path.""" self._absolute_path_validation() - return type(self)(f"{self.drive}//{self.root}") + return cast(BasePathy, type(self)(f"{self.drive}//{self.root}")) @property - def key(self: PathType) -> "PathType": + def key(self) -> Optional["BasePathy"]: """Return a new instance of only the key path.""" self._absolute_path_validation() key = self._flavour.sep.join(self.parts[2:]) if not key or len(self.parts) < 2: return None - return type(self)(key) + return cast(BasePathy, type(self)(key)) @property - def prefix(self: PathType) -> str: + def prefix(self) -> str: sep = self._flavour.sep str(self) key = self.key @@ -84,12 +94,12 @@ def prefix(self: PathType) -> str: return key_name + sep return key_name - def _absolute_path_validation(self): + def _absolute_path_validation(self) -> None: if not self.is_absolute(): raise ValueError("relative paths has no bucket/key specification") @classmethod - def _format_parsed_parts(cls, drv, root, parts): + def _format_parsed_parts(cls, drv: str, root: str, parts: List[str]) -> str: # Bucket path "gs://foo/bar" if drv and root: return f"{drv}//{root}/" + cls._flavour.join(parts[2:]) @@ -99,3 +109,142 @@ def _format_parsed_parts(cls, drv, root, parts): else: # Relative path return cls._flavour.join(parts) + + +class BasePathy(Path, PurePathy): + """Abstract base class for Pathy, which exists to help + keep strong types flowing through the various clients.""" + + __slots__ = () + _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" + + def __truediv__(self, key: Union[str, Path, "BasePathy", PurePathy]) -> PathType: + return super().__truediv__(key) # type:ignore + + def __rtruediv__(self, key: Union[str, Path, "BasePathy", PurePathy]) -> PathType: + return super().__rtruediv__(key) # type:ignore + + def stat(self: PathType) -> BlobStat: + return super().stat() # type:ignore + + def exists(self: PathType) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + def is_dir(self: PathType) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + def is_file(self: PathType) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + def iterdir(self: PathType) -> Generator[PathType, None, None]: + yield from super().iterdir() # type:ignore + + def glob(self: PathType, pattern: str) -> Generator[PathType, None, None]: + yield from super().glob(pattern=pattern) # type:ignore + + def rglob(self: PathType, pattern: str) -> Generator[PathType, None, None]: + yield from super().rglob(pattern=pattern) # type:ignore + + def open( + self: PathType, + mode: str = "r", + buffering: int = DEFAULT_BUFFER_SIZE, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: + raise NotImplementedError(SUBCLASS_ERROR) + + def owner(self: PathType) -> str: + raise NotImplementedError(SUBCLASS_ERROR) + + def resolve(self: PathType, strict: bool = False) -> PathType: + raise NotImplementedError(SUBCLASS_ERROR) + + def rename(self: PathType, target: Union[str, PurePath]) -> None: + return super().rename(target) + + def replace(self: PathType, target: Union[str, PurePath]) -> None: + raise NotImplementedError(SUBCLASS_ERROR) + + def rmdir(self: PathType) -> None: + return super().rmdir() + + def samefile( + self: PathType, other_path: Union[str, bytes, int, Path, PathType] + ) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True) -> None: + raise NotImplementedError(SUBCLASS_ERROR) + + def mkdir( + self: PathType, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + ) -> None: + return super().mkdir(mode=mode, parents=parents, exist_ok=exist_ok) + + def is_mount(self: PathType) -> bool: + return False + + def is_symlink(self: PathType) -> bool: + return False + + def is_socket(self: PathType) -> bool: + return False + + def is_fifo(self: PathType) -> bool: + return False + + # Unsupported operations below here + + @classmethod + def cwd(cls) -> PathType: + message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.cwd.__qualname__) + raise NotImplementedError(message) + + @classmethod + def home(cls) -> PathType: + message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.home.__qualname__) + raise NotImplementedError(message) + + def chmod(self: PathType, mode: int) -> None: + message = self._NOT_SUPPORTED_MESSAGE.format(method=self.chmod.__qualname__) + raise NotImplementedError(message) + + def expanduser(self: PathType) -> PathType: + message = self._NOT_SUPPORTED_MESSAGE.format( + method=self.expanduser.__qualname__ + ) + raise NotImplementedError(message) + + def lchmod(self: PathType, mode: int) -> None: + message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lchmod.__qualname__) + raise NotImplementedError(message) + + def group(self: PathType) -> str: + message = self._NOT_SUPPORTED_MESSAGE.format(method=self.group.__qualname__) + raise NotImplementedError(message) + + def is_block_device(self: PathType) -> bool: + message = self._NOT_SUPPORTED_MESSAGE.format( + method=self.is_block_device.__qualname__ + ) + raise NotImplementedError(message) + + def is_char_device(self: PathType) -> bool: + message = self._NOT_SUPPORTED_MESSAGE.format( + method=self.is_char_device.__qualname__ + ) + raise NotImplementedError(message) + + def lstat(self: PathType) -> BlobStat: + message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lstat.__qualname__) + raise NotImplementedError(message) + + def symlink_to( + self, target: Union[str, Path], target_is_directory: bool = False + ) -> None: + message = self._NOT_SUPPORTED_MESSAGE.format( + method=self.symlink_to.__qualname__ + ) + raise NotImplementedError(message) diff --git a/pathy/cli.py b/pathy/cli.py index f946562..d4bcc94 100644 --- a/pathy/cli.py +++ b/pathy/cli.py @@ -6,7 +6,7 @@ @app.command() -def cp(from_location: str, to_location: str): +def cp(from_location: str, to_location: str) -> None: """ Copy a blob or folder of blobs from one bucket to another. """ @@ -33,7 +33,7 @@ def cp(from_location: str, to_location: str): @app.command() -def mv(from_location: str, to_location: str): +def mv(from_location: str, to_location: str) -> None: """ Move a blob or folder of blobs from one path to another. """ @@ -75,17 +75,19 @@ def rm( verbose: bool = typer.Option( False, "--verbose", "-v", help="Print removed files and folders." ), -): +) -> None: """ Remove a blob or folder of blobs from a given location. """ path: FluidPath = Pathy.fluid(location) if not path.exists(): - raise typer.Exit(f"rm: {path}: No such file or directory") + typer.echo(f"rm: {path}: No such file or directory") + raise typer.Exit(1) if path.is_dir(): if not recursive: - raise typer.Exit(f"rm: {path}: is a directory") + typer.echo(f"rm: {path}: is a directory") + raise typer.Exit(1) selector = path.rglob("*") if recursive else path.glob("*") to_unlink = [b for b in selector if b.is_file()] for blob in to_unlink: @@ -103,7 +105,7 @@ def rm( @app.command() -def ls(location: str): +def ls(location: str) -> None: """ List the blobs that exist at a given location. """ diff --git a/pathy/client.py b/pathy/client.py index 78c8fd5..59c01d8 100644 --- a/pathy/client.py +++ b/pathy/client.py @@ -1,13 +1,12 @@ from dataclasses import dataclass from io import DEFAULT_BUFFER_SIZE -from typing import Generator, Generic, List, Optional, TypeVar +from typing import Any, Dict, Generator, Generic, List, Optional, TypeVar import smart_open -from .base import PurePathy, StreamableType +from .base import SUBCLASS_ERROR, BlobStat, PathType, StreamableType __all__ = ( - "BlobStat", "BucketEntry", "BucketClient", "ClientError", @@ -15,10 +14,9 @@ "Blob", ) -BucketType = TypeVar("BucketType") -BucketBlobType = TypeVar("BucketBlobType") - -_SUBCLASS_MUST_IMPLEMENT = "must be implemented in a subclass" +BucketClientType = TypeVar("BucketClientType", bound="BucketClient") +BucketType = TypeVar("BucketType", bound="Bucket") +BucketBlobType = TypeVar("BucketBlobType", bound="Blob") @dataclass @@ -33,14 +31,6 @@ def __repr__(self) -> str: return f"({self.code}) {self.message}" -@dataclass -class BlobStat: - """Stat for a bucket item""" - - size: int - last_modified: int - - @dataclass class Blob(Generic[BucketType, BucketBlobType]): bucket: "Bucket" @@ -50,11 +40,11 @@ class Blob(Generic[BucketType, BucketBlobType]): owner: Optional[str] raw: BucketBlobType - def delete(self): - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def delete(self) -> None: + raise NotImplementedError(SUBCLASS_ERROR) def exists(self) -> bool: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + raise NotImplementedError(SUBCLASS_ERROR) class BucketEntry(Generic[BucketType, BucketBlobType]): @@ -78,43 +68,45 @@ def __init__( self._is_dir = is_dir self._stat = BlobStat(size=size, last_modified=last_modified) - def __repr__(self): + def __repr__(self) -> str: return "{}(name={}, is_dir={}, stat={})".format( type(self).__name__, self.name, self._is_dir, self._stat ) - def inode(self, *args, **kwargs): + def inode(self, *args: Any, **kwargs: Dict[str, Any]) -> None: return None - def is_dir(self): + def is_dir(self) -> bool: return self._is_dir - def is_file(self): + def is_file(self) -> bool: return not self._is_dir - def is_symlink(self, *args, **kwargs): + def is_symlink(self) -> bool: return False - def stat(self): + def stat(self) -> BlobStat: return self._stat @dataclass class Bucket: - def get_blob(self, blob_name: str) -> Optional[Blob]: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def get_blob(self, blob_name: str) -> Optional[Blob[BucketType, BucketBlobType]]: + raise NotImplementedError(SUBCLASS_ERROR) - def copy_blob(self, blob: Blob, target: "Bucket", name: str) -> Optional[Blob]: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def copy_blob( + self, blob: Blob[BucketType, BucketBlobType], target: "Bucket", name: str + ) -> Optional[Blob[BucketType, BucketBlobType]]: + raise NotImplementedError(SUBCLASS_ERROR) - def delete_blob(self, blob: Blob) -> None: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def delete_blob(self, blob: Blob[BucketType, BucketBlobType]) -> None: + raise NotImplementedError(SUBCLASS_ERROR) - def delete_blobs(self, blobs: List[Blob]) -> None: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def delete_blobs(self, blobs: List[Blob[BucketType, BucketBlobType]]) -> None: + raise NotImplementedError(SUBCLASS_ERROR) def exists(self) -> bool: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + raise NotImplementedError(SUBCLASS_ERROR) class BucketClient: @@ -122,7 +114,7 @@ class BucketClient: def open( self, - path: PurePathy, + path: PathType, *, mode: str = "r", buffering: int = DEFAULT_BUFFER_SIZE, @@ -141,46 +133,46 @@ def open( ignore_ext=True, ) # type:ignore - def make_uri(self, path: PurePathy) -> str: + def make_uri(self, path: PathType) -> str: return path.as_uri() - def is_dir(self, path: PurePathy) -> bool: + def is_dir(self, path: PathType) -> bool: return any(self.list_blobs(path, prefix=path.prefix)) - def rmdir(self, path: PurePathy) -> None: + def rmdir(self, path: PathType) -> None: return None - def exists(self, path: PurePathy) -> bool: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def exists(self, path: PathType) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) - def lookup_bucket(self, path: PurePathy) -> Optional[Bucket]: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def lookup_bucket(self, path: PathType) -> Optional[Bucket]: + raise NotImplementedError(SUBCLASS_ERROR) - def get_bucket(self, path: PurePathy) -> Bucket: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def get_bucket(self, path: PathType) -> Bucket: + raise NotImplementedError(SUBCLASS_ERROR) def list_buckets(self) -> Generator[Bucket, None, None]: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + raise NotImplementedError(SUBCLASS_ERROR) def list_blobs( self, - path: PurePathy, + path: PathType, prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, ) -> Generator[Blob, None, None]: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + raise NotImplementedError(SUBCLASS_ERROR) def scandir( self, - path: PurePathy = None, + path: PathType = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + raise NotImplementedError(SUBCLASS_ERROR) - def create_bucket(self, path: PurePathy) -> Bucket: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def create_bucket(self, path: PathType) -> Bucket: + raise NotImplementedError(SUBCLASS_ERROR) - def delete_bucket(self, path: PurePathy) -> None: - raise NotImplementedError(_SUBCLASS_MUST_IMPLEMENT) + def delete_bucket(self, path: PathType) -> None: + raise NotImplementedError(SUBCLASS_ERROR) diff --git a/pathy/clients.py b/pathy/clients.py index a28e8e6..c767ed2 100644 --- a/pathy/clients.py +++ b/pathy/clients.py @@ -3,7 +3,7 @@ import tempfile from typing import Dict, Optional, Type, Union -from .client import BucketClient +from .client import BucketClient, BucketClientType from .file import BucketClientFS from .gcs import BucketClientGCS @@ -25,7 +25,7 @@ def register_client() -> None: global _client_registry -def get_client(scheme: str) -> "BucketClient": +def get_client(scheme: str) -> "BucketClientType": """Retrieve the bucket client for use with a given scheme""" global _client_registry, _instance_cache, _fs_client if _fs_client is not None: diff --git a/pathy/file.py b/pathy/file.py index 777e1b5..4030b38 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -9,14 +9,14 @@ from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError -class BucketEntryFS(BucketEntry["ClientBucketFS", pathlib.Path]): +class BucketEntryFS(BucketEntry["BucketFS", pathlib.Path]): ... @dataclass -class ClientBlobFS(Blob["ClientBucketFS", pathlib.Path]): +class BlobFS(Blob["BucketFS", pathlib.Path]): raw: pathlib.Path - bucket: "ClientBucketFS" + bucket: "BucketFS" def delete(self) -> None: """Delete a file-based blob.""" @@ -32,11 +32,11 @@ def exists(self) -> bool: @dataclass -class ClientBucketFS(Bucket): +class BucketFS(Bucket): name: str bucket: pathlib.Path - def get_blob(self, blob_name: str) -> Optional[ClientBlobFS]: + def get_blob(self, blob_name: str) -> Optional[BlobFS]: native_blob = self.bucket / blob_name if not native_blob.exists() or native_blob.is_dir(): return None @@ -48,7 +48,7 @@ def get_blob(self, blob_name: str) -> Optional[ClientBlobFS]: owner = native_blob.owner() except KeyError: owner = None - return ClientBlobFS( + return BlobFS( bucket=self, owner=owner, name=blob_name, @@ -58,8 +58,8 @@ def get_blob(self, blob_name: str) -> Optional[ClientBlobFS]: ) def copy_blob( - self, blob: ClientBlobFS, target: "ClientBucketFS", name: str - ) -> Optional[ClientBlobFS]: + self, blob: BlobFS, target: "BucketFS", name: str + ) -> Optional[BlobFS]: in_file = str(blob.bucket.bucket / blob.name) out_file = str(target.bucket / name) out_path = pathlib.Path(os.path.dirname(out_file)) @@ -68,10 +68,10 @@ def copy_blob( shutil.copy(in_file, out_file) return None - def delete_blob(self, blob: ClientBlobFS) -> None: + def delete_blob(self, blob: BlobFS) -> None: blob.delete() - def delete_blobs(self, blobs: List[ClientBlobFS]) -> None: + def delete_blobs(self, blobs: List[BlobFS]) -> None: for blob in blobs: blob.delete() @@ -147,32 +147,32 @@ def create_bucket(self, path: PurePathy) -> Bucket: if bucket_path.exists(): raise FileExistsError(f"Bucket already exists at: {bucket_path}") bucket_path.mkdir(parents=True, exist_ok=True) - return ClientBucketFS(str(path.root), bucket=bucket_path) + return BucketFS(str(path.root), bucket=bucket_path) def delete_bucket(self, path: PurePathy) -> None: bucket_path: pathlib.Path = self.root / str(path.root) if bucket_path.exists(): shutil.rmtree(bucket_path) - def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucketFS]: + def lookup_bucket(self, path: PurePathy) -> Optional[BucketFS]: if path.root: bucket_path: pathlib.Path = self.root / path.root if bucket_path.exists(): - return ClientBucketFS(str(path.root), bucket=bucket_path) + return BucketFS(str(path.root), bucket=bucket_path) return None - def get_bucket(self, path: PurePathy) -> ClientBucketFS: + def get_bucket(self, path: PurePathy) -> BucketFS: if not path.root: raise ValueError(f"path has an invalid bucket_name: {path.root}") bucket_path: pathlib.Path = self.root / path.root if bucket_path.is_dir(): - return ClientBucketFS(str(path.root), bucket=bucket_path) + return BucketFS(str(path.root), bucket=bucket_path) raise FileNotFoundError(f"Bucket {path.root} does not exist!") - def list_buckets(self, **kwargs) -> Generator[ClientBucketFS, None, None]: + def list_buckets(self, **kwargs) -> Generator[BucketFS, None, None]: for f in self.root.glob("*"): if f.is_dir(): - yield ClientBucketFS(f.name, f) + yield BucketFS(f.name, f) def scandir( self, @@ -197,7 +197,7 @@ def scandir( stat = file_path.stat() file_size = stat.st_size updated = int(round(stat.st_mtime_ns * 1000)) - blob: Blob = ClientBlobFS( + blob: Blob = BlobFS( self.get_bucket(path), name=dir_entry.name, size=file_size, @@ -219,7 +219,7 @@ def list_blobs( prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, - ) -> Generator[ClientBlobFS, None, None]: + ) -> Generator[BlobFS, None, None]: assert path.root is not None bucket = self.get_bucket(path) scan_path = self.root / path.root @@ -233,7 +233,7 @@ def list_blobs( stat = scan_path.stat() file_size = stat.st_size updated = int(round(stat.st_mtime_ns * 1000)) - yield ClientBlobFS( + yield BlobFS( bucket, name=scan_path.name, size=file_size, @@ -249,7 +249,7 @@ def list_blobs( stat = file_path.stat() file_size = stat.st_size updated = int(round(stat.st_mtime_ns * 1000)) - yield ClientBlobFS( + yield BlobFS( bucket, name=f"{prefix if prefix is not None else ''}{file_path.name}", size=file_size, diff --git a/pathy/gcs.py b/pathy/gcs.py index 9723868..422a176 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -15,12 +15,12 @@ has_gcs = False -class BucketEntryGCS(BucketEntry["ClientBucketGCS", "storage.Blob"]): +class BucketEntryGCS(BucketEntry["BucketGCS", "storage.Blob"]): ... @dataclass -class ClientBlobGCS(Blob["ClientBucketGCS", "storage.Blob"]): +class BlobGCS(Blob["BucketGCS", "storage.Blob"]): def delete(self) -> None: self.raw.delete() @@ -29,11 +29,11 @@ def exists(self) -> bool: @dataclass -class ClientBucketGCS(Bucket): +class BucketGCS(Bucket): name: str bucket: "storage.Bucket" - def get_blob(self, blob_name: str) -> Optional[ClientBlobGCS]: + def get_blob(self, blob_name: str) -> Optional[BlobGCS]: assert isinstance( blob_name, str ), f"expected str blob name, but found: {type(blob_name)}" @@ -44,7 +44,7 @@ def get_blob(self, blob_name: str) -> Optional[ClientBlobGCS]: pass if native_blob is None: return None - return ClientBlobGCS( + return BlobGCS( bucket=self.bucket, owner=native_blob.owner, name=native_blob.name, @@ -54,13 +54,13 @@ def get_blob(self, blob_name: str) -> Optional[ClientBlobGCS]: ) def copy_blob( - self, blob: ClientBlobGCS, target: "ClientBucketGCS", name: str - ) -> Optional[ClientBlobGCS]: + self, blob: BlobGCS, target: "BucketGCS", name: str + ) -> Optional[BlobGCS]: assert blob.raw is not None, "raw storage.Blob instance required" native_blob = self.bucket.copy_blob(blob.raw, target.bucket, name) if native_blob is None: return None - return ClientBlobGCS( + return BlobGCS( bucket=self.bucket, owner=native_blob.owner, name=native_blob.name, @@ -69,10 +69,10 @@ def copy_blob( updated=native_blob.updated.timestamp(), ) - def delete_blob(self, blob: ClientBlobGCS) -> None: + def delete_blob(self, blob: BlobGCS) -> None: return self.bucket.delete_blob(blob.name) - def delete_blobs(self, blobs: List[ClientBlobGCS]) -> None: + def delete_blobs(self, blobs: List[BlobGCS]) -> None: return self.bucket.delete_blobs(blobs) def exists(self) -> bool: @@ -91,13 +91,15 @@ def __init__(self, client: Optional["storage.Client"] = None): except (BaseException, DefaultCredentialsError): self.client = None - def make_uri(self, path: PurePathy): + def make_uri(self, path: PurePathy) -> str: return str(path) def create_bucket(self, path: PurePathy) -> Bucket: + assert self.client is not None return self.client.create_bucket(path.root) def delete_bucket(self, path: PurePathy) -> None: + assert self.client is not None bucket = self.client.get_bucket(path.root) bucket.delete() @@ -116,26 +118,29 @@ def exists(self, path: PurePathy) -> bool: return False return False - def lookup_bucket(self, path: PurePathy) -> Optional[ClientBucketGCS]: + def lookup_bucket(self, path: PurePathy) -> Optional[BucketGCS]: + assert self.client is not None try: native_bucket = self.client.bucket(path.root) if native_bucket is not None: - return ClientBucketGCS(str(path.root), bucket=native_bucket) + return BucketGCS(str(path.root), bucket=native_bucket) except gcs_errors.ClientError as err: print(err) return None - def get_bucket(self, path: PurePathy) -> ClientBucketGCS: + def get_bucket(self, path: PurePathy) -> BucketGCS: + assert self.client is not None try: native_bucket = self.client.bucket(path.root) if native_bucket is not None: - return ClientBucketGCS(str(path.root), bucket=native_bucket) + return BucketGCS(str(path.root), bucket=native_bucket) raise FileNotFoundError(f"Bucket {path.root} does not exist!") except gcs_errors.ClientError as e: raise ClientError(message=e.message, code=e.code) def list_buckets(self, **kwargs) -> Generator[Bucket, None, None]: + assert self.client is not None return self.client.list_buckets(**kwargs) def scandir( @@ -145,6 +150,7 @@ def scandir( delimiter: Optional[str] = None, include_raw: bool = False, ) -> Generator[BucketEntryGCS, None, None]: + assert self.client is not None continuation_token = None if path is None or not path.root: for bucket in self.list_buckets(): @@ -192,7 +198,8 @@ def list_blobs( prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, - ) -> Generator[ClientBlobGCS, None, None]: + ) -> Generator[BlobGCS, None, None]: + assert self.client is not None continuation_token = None bucket = self.lookup_bucket(path) if bucket is None: @@ -211,7 +218,7 @@ def list_blobs( ) for page in response.pages: for item in page: - yield ClientBlobGCS( + yield BlobGCS( bucket=bucket, owner=item.owner, name=item.name, diff --git a/tests/test_api.py b/tests/test_api.py index 56ac417..475fd27 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -465,6 +465,21 @@ def test_api_mkdir(with_adapter, bucket: str): # bucket = client.lookup_bucket(bucket_name) # bucket.delete() path.rmdir() + assert not path.exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_list_buckets(with_adapter): + bucket_name = f"pathy-e2e-test-{uuid4().hex}" + bucket = Pathy(f"gs://{bucket_name}/") + bucket.mkdir() + assert bucket.exists() + + Pathy("").glob("*") + # Can enumerate the buckets (including the one we just created) + + bucket.rmdir() + assert not bucket.exists() @pytest.mark.parametrize("adapter", TEST_ADAPTERS) diff --git a/tests/test_base.py b/tests/test_base.py index 13cff2b..6ddf0bb 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -5,6 +5,7 @@ import pytest from pathy import Pathy, PurePathy +from pathy.base import BasePathy def test_base_not_supported(monkeypatch): @@ -23,6 +24,12 @@ def test_base_home(): Pathy.home() +def test_base_expanduser(): + path = Pathy("/fake-bucket/fake-key") + with pytest.raises(NotImplementedError): + path.expanduser() + + def test_base_chmod(): path = Pathy("/fake-bucket/fake-key") with pytest.raises(NotImplementedError): @@ -274,3 +281,26 @@ def test_base_with_suffix(): assert gcs_path.with_suffix(".txt") == PurePathy("README.txt") gcs_path = PurePathy("README.txt") assert gcs_path.with_suffix("") == PurePathy("README") + + +def test_base_class_abstract_methods(): + path = BasePathy("gs://foo/bar") + other = Pathy("gs://other") + with pytest.raises(NotImplementedError): + path.exists() + with pytest.raises(NotImplementedError): + path.is_dir() + with pytest.raises(NotImplementedError): + path.is_file() + with pytest.raises(NotImplementedError): + path.touch() + with pytest.raises(NotImplementedError): + path.open() + with pytest.raises(NotImplementedError): + path.owner() + with pytest.raises(NotImplementedError): + path.resolve() + with pytest.raises(NotImplementedError): + path.replace(other) + with pytest.raises(NotImplementedError): + path.samefile(other) diff --git a/tests/test_file.py b/tests/test_file.py new file mode 100644 index 0000000..5d88eaa --- /dev/null +++ b/tests/test_file.py @@ -0,0 +1,25 @@ +import pathlib +from typing import Optional + +import mock +import pytest + +from pathy import Pathy, get_client +from pathy.file import BlobFS, BucketClientFS, BucketFS + + +def raise_owner(self): + raise KeyError("duh") + + +@pytest.mark.parametrize("adapter", ["fs"]) +@mock.patch.object(pathlib.Path, "owner", raise_owner) +def test_file_get_blob_owner_key_error_protection(with_adapter): + gs_bucket = Pathy("gs://my_bucket") + gs_bucket.mkdir() + path = gs_bucket / "blob.txt" + path.write_text("hello world!") + gcs_client: BucketClientFS = get_client("gs") + bucket: BucketFS = gcs_client.get_bucket(gs_bucket) + blob: Optional[BlobFS] = bucket.get_blob("blob.txt") + assert blob.owner is None From a52246141b52440b9f9b3ace46d0a451af268be6 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 15:42:23 -0700 Subject: [PATCH 38/75] refactor: combine api/client with base.py - this makes the Pathy type accessible where it otherwise would not be for TypeVars. --- README.md | 6 +- pathy/__init__.py | 7 +- pathy/api.py | 451 ------------------------------- pathy/base.py | 660 +++++++++++++++++++++++++++++++++++++++++---- pathy/cli.py | 2 +- pathy/client.py | 178 ------------ pathy/clients.py | 10 +- pathy/file.py | 15 +- pathy/gcs.py | 3 +- tests/test_base.py | 24 -- 10 files changed, 631 insertions(+), 725 deletions(-) delete mode 100644 pathy/api.py delete mode 100644 pathy/client.py diff --git a/README.md b/README.md index f80a893..7832d88 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ Returns True if the path points to an existing bucket, blob, or prefix. ```python Pathy.fluid( - path_candidate: Union[str, BasePathy, pathlib.Path], -) -> Union[BasePathy, pathlib.Path] + path_candidate: Union[str, Pathy, pathlib.Path], +) -> Union[Pathy, pathlib.Path] ``` Infer either a Pathy or pathlib.Path from an input path or string. @@ -214,7 +214,7 @@ If target points to an existing path, it will be replaced. ## resolve method ```python -Pathy.resolve(self, strict: bool = False) -> ~PathType +Pathy.resolve(self, strict: bool = False) -> 'Pathy' ``` Resolve the given path to remove any relative path specifiers. diff --git a/pathy/__init__.py b/pathy/__init__.py index 280e525..6d2be60 100644 --- a/pathy/__init__.py +++ b/pathy/__init__.py @@ -1,14 +1,15 @@ -from .api import ( +from .base import ( Blob, + BlobStat, Bucket, BucketClient, + BucketEntry, BucketsAccessor, ClientError, FluidPath, Pathy, + PurePathy, ) -from .base import BlobStat, PurePathy -from .client import BucketEntry from .clients import ( clear_fs_cache, get_client, diff --git a/pathy/api.py b/pathy/api.py deleted file mode 100644 index a06de5d..0000000 --- a/pathy/api.py +++ /dev/null @@ -1,451 +0,0 @@ -import os -from io import DEFAULT_BUFFER_SIZE -from pathlib import Path # type:ignore -from pathlib import PurePath, _Accessor -from typing import Any, Generator, Optional, Union, cast - -from .base import BasePathy, BlobStat, FluidPath, PathType, StreamableType -from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError - -__all__ = () - -_SUPPORTED_OPEN_MODES = {"r", "rb", "tr", "rt", "w", "wb", "bw", "wt", "tw"} - - -class Pathy(BasePathy): - """Subclass of `pathlib.Path` that works with bucket APIs.""" - - __slots__ = () - _accessor: "BucketsAccessor" - - def _init(self: "Pathy", template: Optional[Any] = None) -> None: - super()._init(template) # type:ignore - if template is None: - self._accessor = _gcs_accessor - else: - self._accessor = template._accessor - - @classmethod - def fluid(cls, path_candidate: Union[str, FluidPath]) -> FluidPath: - """Infer either a Pathy or pathlib.Path from an input path or string. - - The returned type is a union of the potential `FluidPath` types and will - type-check correctly against the minimum overlapping APIs of all the input - types. - - If you need to use specific implementation details of a type, "narrow" the - return of this function to the desired type, e.g. - - ```python - fluid_path = FluidPath("gs://my_bucket/foo.txt") - # Narrow the type to a specific class - assert isinstance(fluid_path, Pathy), "must be Pathy" - # Use a member specific to that class - print(fluid_path.prefix) - ``` - """ - from_path: FluidPath = Pathy(path_candidate) - if from_path.root in ["/", ""]: - from_path = Path(path_candidate) - return from_path - - @classmethod - def from_bucket(cls, bucket_name: str) -> PathType: - """Initialize a Pathy from a bucket name. This helper adds a trailing slash and - the appropriate prefix. - - ```python - assert str(Pathy.from_bucket("one")) == "gs://one/" - assert str(Pathy.from_bucket("two")) == "gs://two/" - ``` - """ - return Pathy(f"gs://{bucket_name}/") - - @classmethod - def to_local(cls, blob_path: Union[PathType, str], recurse: bool = True) -> Path: - """Download and cache either a blob or a set of blobs matching a prefix. - - The cache is sensitive to the file updated time, and downloads new blobs - as their updated timestamps change.""" - from .clients import get_fs_cache - - cache_folder = get_fs_cache() - if cache_folder is None: - raise ValueError( - 'cannot get and cache a blob without first calling "use_fs_cache"' - ) - - cache_folder.mkdir(exist_ok=True, parents=True) - if not isinstance(blob_path, Pathy): - blob_path = Pathy(blob_path) - - cache_blob: Path = cache_folder.absolute() / blob_path.root / blob_path.key - cache_time: Path = ( - cache_folder.absolute() / blob_path.root / f"{blob_path.key}.time" - ) - # Keep a cache of downloaded files. Fetch new ones when: - # - the file isn't in the cache - # - cached_stat.updated != latest_stat.updated - if cache_blob.exists() and cache_time.exists(): - fs_time: str = cache_time.read_text() - gcs_stat: BlobStat = blob_path.stat() - # If the times match, return the cached blob - if fs_time == str(gcs_stat.last_modified): - return cache_blob - # remove the cache files because they're out of date - cache_blob.unlink() - cache_time.unlink() - - # If the file isn't in the cache, download it - if not cache_blob.exists(): - # Is a blob - if blob_path.is_file(): - dest_folder = cache_blob.parent - dest_folder.mkdir(exist_ok=True, parents=True) - cache_blob.write_bytes(blob_path.read_bytes()) - blob_stat: BlobStat = blob_path.stat() - cache_time.write_text(str(blob_stat.last_modified)) - elif recurse: - # If not a specific blob, enumerate all the blobs under - # the path and cache them, then return the cache folder - for blob in blob_path.rglob("*"): - Pathy.to_local(blob, recurse=False) - return cache_blob - - def stat(self: PathType) -> BlobStat: - """Returns information about this bucket path.""" - self._absolute_path_validation() - if not self.key: - raise ValueError("cannot stat a bucket without a key") - return cast(BlobStat, super().stat()) - - def exists(self) -> bool: - """Returns True if the path points to an existing bucket, blob, or prefix.""" - self._absolute_path_validation() - return self._accessor.exists(self) - - def is_dir(self: PathType) -> bool: - """Determine if the path points to a bucket or a prefix of a given blob - in the bucket. - - Returns True if the path points to a bucket or a blob prefix. - Returns False if it points to a blob or the path doesn't exist. - """ - self._absolute_path_validation() - if self.bucket and not self.key: - return True - return self._accessor.is_dir(self) - - def is_file(self: PathType) -> bool: - """Determine if the path points to a blob in the bucket. - - Returns True if the path points to a blob. - Returns False if it points to a bucket or blob prefix, or if the path doesn’t - exist. - """ - self._absolute_path_validation() - if not self.bucket or not self.key: - return False - try: - return bool(self.stat()) - except (ClientError, FileNotFoundError): - return False - - def iterdir(self: PathType) -> Generator[PathType, None, None]: - """Iterate over the blobs found in the given bucket or blob prefix path.""" - self._absolute_path_validation() - yield from super().iterdir() - - def glob(self: PathType, pattern: str) -> Generator[PathType, None, None]: - """Perform a glob match relative to this Pathy instance, yielding all matched - blobs.""" - yield from super().glob(pattern) - - def rglob(self: PathType, pattern: str) -> Generator[PathType, None, None]: - """Perform a recursive glob match relative to this Pathy instance, yielding - all matched blobs. Imagine adding "**/" before a call to glob.""" - yield from super().rglob(pattern) - - def open( - self: PathType, - mode: str = "r", - buffering: int = DEFAULT_BUFFER_SIZE, - encoding: Optional[str] = None, - errors: Optional[str] = None, - newline: Optional[str] = None, - ) -> StreamableType: - """Open the given blob for streaming. This delegates to the `smart_open` - library that handles large file streaming for a number of bucket API - providers.""" - self._absolute_path_validation() - if mode not in _SUPPORTED_OPEN_MODES: - raise ValueError( - "supported modes are {} got {}".format(_SUPPORTED_OPEN_MODES, mode) - ) - if buffering == 0 or buffering == 1: - raise ValueError( - "supported buffering values are only block sizes, no 0 or 1" - ) - if "b" in mode and encoding: - raise ValueError("binary mode doesn't take an encoding argument") - - # Leftover pathlib internals stuff - if self._closed: # type:ignore - self._raise_closed() # type:ignore - return self._accessor.open( - self, - mode=mode, - buffering=buffering, - encoding=encoding, - errors=errors, - newline=newline, - ) - - def owner(self: "Pathy") -> Optional[str]: - """Returns the name of the user that owns the bucket or blob - this path points to. Returns None if the owner is unknown or - not supported by the bucket API provider.""" - self._absolute_path_validation() - if not self.is_file(): - raise FileNotFoundError(str(self)) - return self._accessor.owner(self) - - def resolve(self, strict: bool = False) -> PathType: - """Resolve the given path to remove any relative path specifiers. - - ```python - path = Pathy("gs://my_bucket/folder/../blob") - assert path.resolve() == Pathy("gs://my_bucket/blob") - ``` - """ - self._absolute_path_validation() - return self._accessor.resolve(self, strict=strict) - - def rename(self: "Pathy", target: Union[str, PurePath]) -> None: - """Rename this path to the given target. - - If the target exists and is a file, it will be replaced silently if the user - has permission. - - If path is a blob prefix, it will replace all the blobs with the same prefix - to match the target prefix.""" - self._absolute_path_validation() - self_type = type(self) - if not isinstance(target, self_type): - target = self_type(target) - target._absolute_path_validation() # type:ignore - super().rename(target) - - def replace(self: PathType, target: Union[str, PurePath]) -> None: - """Renames this path to the given target. - - If target points to an existing path, it will be replaced.""" - self.rename(target) - - def rmdir(self: "Pathy") -> None: - """Removes this bucket or blob prefix. It must be empty.""" - self._absolute_path_validation() - if self.is_file(): - raise NotADirectoryError() - if not self.is_dir(): - raise FileNotFoundError() - super().rmdir() - - def samefile(self: "Pathy", other_path: Union[str, bytes, int, Path]) -> bool: - """Determine if this path points to the same location as other_path.""" - self._absolute_path_validation() - if not isinstance(other_path, Path): - other_path = type(self)(other_path) # type:ignore - assert isinstance(other_path, Pathy) - return ( - self.bucket == other_path.bucket and self.key == self.key and self.is_file() - ) - - def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True) -> None: - """Create a blob at this path. - - If the blob already exists, the function succeeds if exist_ok is true - (and its modification time is updated to the current time), otherwise - FileExistsError is raised. - """ - if self.exists() and not exist_ok: - raise FileExistsError() - self.write_text("") - - def mkdir( - self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False - ) -> None: - """Create a bucket from the given path. Since bucket APIs only have implicit - folder structures (determined by the existence of a blob with an overlapping - prefix) this does nothing other than create buckets. - - If parents is False, the bucket will only be created if the path points to - exactly the bucket and nothing else. If parents is true the bucket will be - created even if the path points to a specific blob. - - The mode param is ignored. - - Raises FileExistsError if exist_ok is false and the bucket already exists. - """ - try: - if self.bucket is None: - raise FileNotFoundError("No bucket in {} {}".format(type(self), self)) - # If the whole path is just the bucket, respect the result of "bucket.exists()" - if self.key is None and not exist_ok and self.bucket.exists(): - raise FileExistsError("Bucket {} already exists".format(self.bucket)) - return super().mkdir(mode, parents=parents, exist_ok=exist_ok) - except OSError: - if not exist_ok: - raise - - -class BucketsAccessor(_Accessor): - """Access data from blob buckets""" - - _client: Optional[BucketClient] - - def client(self, path: PathType) -> BucketClient: - # lazily avoid circular imports - from .clients import get_client - - return get_client(path.scheme) - - def get_blob(self, path: PathType) -> Optional[Blob]: - """Get the blob associated with a path or return None""" - if not path.root: - return None - bucket = self.client(path).lookup_bucket(path) - if bucket is None: - return None - key_name = str(path.key) - return bucket.get_blob(key_name) - - def unlink(self, path: PathType) -> None: - """Delete a link to a blob in a bucket.""" - bucket = self.client(path).get_bucket(path) - blob: Optional[Blob] = bucket.get_blob(str(path.key)) - if blob is None: - raise FileNotFoundError(path) - blob.delete() - - def stat(self, path: PathType) -> BlobStat: - bucket = self.client(path).get_bucket(path) - blob: Optional[Blob] = bucket.get_blob(str(path.key)) - if blob is None: - raise FileNotFoundError(path) - return BlobStat(size=blob.size, last_modified=blob.updated) - - def is_dir(self, path: PathType) -> bool: - if str(path) == path.root: - return True - if self.get_blob(path) is not None: - return False - return self.client(path).is_dir(path) - - def exists(self, path: PathType) -> bool: - client = self.client(path) - if not path.root: - return any(client.list_buckets()) - try: - bucket = client.lookup_bucket(path) - except ClientError: - return False - if bucket is None or not bucket.exists(): - return False - if not path.key: - return True - - key_name = str(path.key) - blob: Optional[Blob] = bucket.get_blob(key_name) - if blob is not None: - return blob.exists() - # Determine if the path exists according to the current adapter - return client.exists(path) - - def scandir(self, path: PathType) -> Generator[BucketEntry, None, None]: - return self.client(path).scandir(path, prefix=path.prefix) - - def listdir(self, path: PathType) -> Generator[str, None, None]: - for entry in self.scandir(path): - yield entry.name - - def open( - self, - path: PathType, - *, - mode: str = "r", - buffering: int = -1, - encoding: Optional[str] = None, - errors: Optional[str] = None, - newline: Optional[str] = None, - ) -> StreamableType: - return self.client(path).open( - path, - mode=mode, - buffering=buffering, - encoding=encoding, - errors=errors, - newline=newline, - ) - - def owner(self, path: PathType) -> Optional[str]: - blob: Optional[Blob] = self.get_blob(path) - return blob.owner if blob is not None else None - - def resolve(self, path: PathType, strict: bool = False) -> "Pathy": - path_parts = str(path).replace(path.drive, "") - return Pathy(f"{path.drive}{os.path.abspath(path_parts)}") - - def rename(self, path: PathType, target: PathType) -> None: - client: BucketClient = self.client(path) - bucket: Bucket = client.get_bucket(path) - target_bucket: Bucket = client.get_bucket(target) - - # Single file - if not self.is_dir(path): - from_blob: Optional[Blob] = bucket.get_blob(str(path.key)) - if from_blob is None: - raise FileNotFoundError(f'source file "{path}" does not exist') - target_bucket.copy_blob(from_blob, target_bucket, str(target.key)) - bucket.delete_blob(from_blob) - return - - # Folder with objects - sep = path._flavour.sep - blobs = list(client.list_blobs(path, prefix=path.prefix, delimiter=sep)) - # First rename - for blob in blobs: - target_key_name = blob.name.replace(str(path.key), str(target.key)) - target_bucket.copy_blob(blob, target_bucket, target_key_name) - # Then delete the sources - for blob in blobs: - bucket.delete_blob(blob) - - def replace(self, path: PathType, target: PathType) -> None: - return self.rename(path, target) - - def rmdir(self, path: PathType) -> None: - client: BucketClient = self.client(path) - key_name = str(path.key) if path.key is not None else None - bucket: Bucket = client.get_bucket(path) - blobs = list(client.list_blobs(path, prefix=key_name)) - bucket.delete_blobs(blobs) - # The path is just the bucket - if key_name is None: - client.delete_bucket(path) - elif client.is_dir(path): - client.rmdir(path) - - def mkdir(self, path: PathType, mode: int) -> None: - client: BucketClient = self.client(path) - bucket: Optional[Bucket] = client.lookup_bucket(path) - if bucket is None or not bucket.exists(): - client.create_bucket(path) - elif isinstance(path, Pathy) and path.key is not None: - assert isinstance(path, Pathy) - blob: Optional[Blob] = bucket.get_blob(str(path.key)) - if blob is not None and blob.exists(): - raise OSError(f"Blob already exists: {path}") - - -_gcs_accessor = BucketsAccessor() diff --git a/pathy/base.py b/pathy/base.py index ae92337..5d4d7d9 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -1,16 +1,203 @@ +import os from dataclasses import dataclass from io import DEFAULT_BUFFER_SIZE +from pathlib import Path # type:ignore +from pathlib import _Accessor # type:ignore from pathlib import _PosixFlavour # type:ignore -from pathlib import Path, PurePath -from typing import IO, Any, Generator, List, Optional, Tuple, TypeVar, Union, cast +from pathlib import PurePath +from typing import ( + IO, + Any, + Dict, + Generator, + Generic, + List, + Optional, + Tuple, + TypeVar, + Union, + cast, +) + +import smart_open SUBCLASS_ERROR = "must be implemented in a subclass" -PathType = TypeVar("PathType", bound="BasePathy") +PathType = TypeVar("PathType", bound="Pathy") StreamableType = IO[Any] -FluidPath = Union["BasePathy", Path] +FluidPath = Union["Pathy", Path] +BucketClientType = TypeVar("BucketClientType", bound="BucketClient") +BucketType = TypeVar("BucketType", bound="Bucket") +BucketBlobType = TypeVar("BucketBlobType") -class _GCSFlavour(_PosixFlavour): +@dataclass +class ClientError(BaseException): + message: str + code: Optional[int] + + def __str__(self) -> str: + return self.__repr__() + + def __repr__(self) -> str: + return f"({self.code}) {self.message}" + + +@dataclass +class BlobStat: + """Stat for a bucket item""" + + size: int + last_modified: int + + +@dataclass +class Blob(Generic[BucketType, BucketBlobType]): + bucket: "Bucket" + name: str + size: int + updated: int + owner: Optional[str] + raw: BucketBlobType + + def delete(self) -> None: + raise NotImplementedError(SUBCLASS_ERROR) + + def exists(self) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + +class BucketEntry(Generic[BucketType, BucketBlobType]): + """A single item returned from scanning a path""" + + name: str + _is_dir: bool + _stat: BlobStat + raw: Optional[Blob[BucketType, BucketBlobType]] + + def __init__( + self, + name: str, + is_dir: bool = False, + size: int = -1, + last_modified: int = -1, + raw: Optional[Blob[BucketType, BucketBlobType]] = None, + ): + self.name = name + self.raw = raw + self._is_dir = is_dir + self._stat = BlobStat(size=size, last_modified=last_modified) + + def __repr__(self) -> str: + return "{}(name={}, is_dir={}, stat={})".format( + type(self).__name__, self.name, self._is_dir, self._stat + ) + + def inode(self, *args: Any, **kwargs: Dict[str, Any]) -> None: + return None + + def is_dir(self) -> bool: + return self._is_dir + + def is_file(self) -> bool: + return not self._is_dir + + def is_symlink(self) -> bool: + return False + + def stat(self) -> BlobStat: + return self._stat + + +@dataclass +class Bucket: + def get_blob(self, blob_name: str) -> Optional[Blob[BucketType, BucketBlobType]]: + raise NotImplementedError(SUBCLASS_ERROR) + + def copy_blob( + self, blob: Blob[BucketType, BucketBlobType], target: "Bucket", name: str + ) -> Optional[Blob[BucketType, BucketBlobType]]: + raise NotImplementedError(SUBCLASS_ERROR) + + def delete_blob(self, blob: Blob[BucketType, BucketBlobType]) -> None: + raise NotImplementedError(SUBCLASS_ERROR) + + def delete_blobs(self, blobs: List[Blob[BucketType, BucketBlobType]]) -> None: + raise NotImplementedError(SUBCLASS_ERROR) + + def exists(self) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + +class BucketClient: + """Base class for a client that interacts with a bucket-based storage system.""" + + def open( + self, + path: PathType, + *, + mode: str = "r", + buffering: int = DEFAULT_BUFFER_SIZE, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: + return smart_open.open( + self.make_uri(path), + mode=mode, + buffering=buffering, + encoding=encoding, + errors=errors, + newline=newline, + # Disable de/compression based on the file extension + ignore_ext=True, + ) # type:ignore + + def make_uri(self, path: PathType) -> str: + return path.as_uri() + + def is_dir(self, path: PathType) -> bool: + return any(self.list_blobs(path, prefix=path.prefix)) + + def rmdir(self, path: PathType) -> None: + return None + + def exists(self, path: PathType) -> bool: + raise NotImplementedError(SUBCLASS_ERROR) + + def lookup_bucket(self, path: PathType) -> Optional[Bucket]: + raise NotImplementedError(SUBCLASS_ERROR) + + def get_bucket(self, path: PathType) -> Bucket: + raise NotImplementedError(SUBCLASS_ERROR) + + def list_buckets(self) -> Generator[Bucket, None, None]: + raise NotImplementedError(SUBCLASS_ERROR) + + def list_blobs( + self, + path: PathType, + prefix: Optional[str] = None, + delimiter: Optional[str] = None, + include_dirs: bool = False, + ) -> Generator[Blob, None, None]: + raise NotImplementedError(SUBCLASS_ERROR) + + def scandir( + self, + path: PathType = None, + prefix: Optional[str] = None, + delimiter: Optional[str] = None, + ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: + raise NotImplementedError(SUBCLASS_ERROR) + + def create_bucket(self, path: PathType) -> Bucket: + raise NotImplementedError(SUBCLASS_ERROR) + + def delete_bucket(self, path: PathType) -> None: + raise NotImplementedError(SUBCLASS_ERROR) + + +class _PathyFlavour(_PosixFlavour): is_supported = True def parse_parts(self, parts: List[str]) -> Tuple[str, str, List[str]]: @@ -33,21 +220,10 @@ def make_uri(self, path: PathType) -> str: return uri.replace("file:///", "gs://") -_gcs_flavour = _GCSFlavour() - - -@dataclass -class BlobStat: - """Stat for a bucket item""" - - size: int - last_modified: int - - class PurePathy(PurePath): """PurePath subclass for bucket storage.""" - _flavour = _gcs_flavour + _flavour = _PathyFlavour() __slots__ = () @property @@ -68,19 +244,19 @@ def scheme(self) -> str: return self.drive[:-1] @property - def bucket(self) -> "BasePathy": + def bucket(self) -> "Pathy": """Return a new instance of only the bucket path.""" self._absolute_path_validation() - return cast(BasePathy, type(self)(f"{self.drive}//{self.root}")) + return cast(Pathy, type(self)(f"{self.drive}//{self.root}")) @property - def key(self) -> Optional["BasePathy"]: + def key(self) -> Optional["Pathy"]: """Return a new instance of only the key path.""" self._absolute_path_validation() key = self._flavour.sep.join(self.parts[2:]) if not key or len(self.parts) < 2: return None - return cast(BasePathy, type(self)(key)) + return cast(Pathy, type(self)(key)) @property def prefix(self) -> str: @@ -111,39 +287,325 @@ def _format_parsed_parts(cls, drv: str, root: str, parts: List[str]) -> str: return cls._flavour.join(parts) -class BasePathy(Path, PurePathy): - """Abstract base class for Pathy, which exists to help - keep strong types flowing through the various clients.""" +_SUPPORTED_OPEN_MODES = {"r", "rb", "tr", "rt", "w", "wb", "bw", "wt", "tw"} + + +class BucketsAccessor(_Accessor): + """Access data from blob buckets""" + + _client: Optional[BucketClient] + + def client(self, path: PathType) -> BucketClient: + # lazily avoid circular imports + from .clients import get_client + + return get_client(path.scheme) + + def get_blob(self, path: PathType) -> Optional[Blob]: + """Get the blob associated with a path or return None""" + if not path.root: + return None + bucket = self.client(path).lookup_bucket(path) + if bucket is None: + return None + key_name = str(path.key) + return bucket.get_blob(key_name) + + def unlink(self, path: PathType) -> None: + """Delete a link to a blob in a bucket.""" + bucket = self.client(path).get_bucket(path) + blob: Optional[Blob] = bucket.get_blob(str(path.key)) + if blob is None: + raise FileNotFoundError(path) + blob.delete() + + def stat(self, path: PathType) -> BlobStat: + bucket = self.client(path).get_bucket(path) + blob: Optional[Blob] = bucket.get_blob(str(path.key)) + if blob is None: + raise FileNotFoundError(path) + return BlobStat(size=blob.size, last_modified=blob.updated) + + def is_dir(self, path: PathType) -> bool: + if str(path) == path.root: + return True + if self.get_blob(path) is not None: + return False + return self.client(path).is_dir(path) + + def exists(self, path: PathType) -> bool: + client = self.client(path) + if not path.root: + return any(client.list_buckets()) + try: + bucket = client.lookup_bucket(path) + except ClientError: + return False + if bucket is None or not bucket.exists(): + return False + if not path.key: + return True + + key_name = str(path.key) + blob: Optional[Blob] = bucket.get_blob(key_name) + if blob is not None: + return blob.exists() + # Determine if the path exists according to the current adapter + return client.exists(path) + + def scandir(self, path: PathType) -> Generator[BucketEntry, None, None]: + return self.client(path).scandir(path, prefix=path.prefix) + + def listdir(self, path: PathType) -> Generator[str, None, None]: + for entry in self.scandir(path): + yield entry.name + + def open( + self, + path: PathType, + *, + mode: str = "r", + buffering: int = -1, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + ) -> StreamableType: + return self.client(path).open( + path, + mode=mode, + buffering=buffering, + encoding=encoding, + errors=errors, + newline=newline, + ) + + def owner(self, path: PathType) -> Optional[str]: + blob: Optional[Blob] = self.get_blob(path) + return blob.owner if blob is not None else None + + def resolve(self, path: PathType, strict: bool = False) -> "Pathy": + path_parts = str(path).replace(path.drive, "") + return Pathy(f"{path.drive}{os.path.abspath(path_parts)}") + + def rename(self, path: PathType, target: PathType) -> None: + client: BucketClient = self.client(path) + bucket: Bucket = client.get_bucket(path) + target_bucket: Bucket = client.get_bucket(target) + + # Single file + if not self.is_dir(path): + from_blob: Optional[Blob] = bucket.get_blob(str(path.key)) + if from_blob is None: + raise FileNotFoundError(f'source file "{path}" does not exist') + target_bucket.copy_blob(from_blob, target_bucket, str(target.key)) + bucket.delete_blob(from_blob) + return + + # Folder with objects + sep = path._flavour.sep + blobs = list(client.list_blobs(path, prefix=path.prefix, delimiter=sep)) + # First rename + for blob in blobs: + target_key_name = blob.name.replace(str(path.key), str(target.key)) + target_bucket.copy_blob(blob, target_bucket, target_key_name) + # Then delete the sources + for blob in blobs: + bucket.delete_blob(blob) + + def replace(self, path: PathType, target: PathType) -> None: + return self.rename(path, target) + + def rmdir(self, path: PathType) -> None: + client: BucketClient = self.client(path) + key_name = str(path.key) if path.key is not None else None + bucket: Bucket = client.get_bucket(path) + blobs = list(client.list_blobs(path, prefix=key_name)) + bucket.delete_blobs(blobs) + # The path is just the bucket + if key_name is None: + client.delete_bucket(path) + elif client.is_dir(path): + client.rmdir(path) + + def mkdir(self, path: PathType, mode: int) -> None: + client: BucketClient = self.client(path) + bucket: Optional[Bucket] = client.lookup_bucket(path) + if bucket is None or not bucket.exists(): + client.create_bucket(path) + elif isinstance(path, Pathy) and path.key is not None: + assert isinstance(path, Pathy) + blob: Optional[Blob] = bucket.get_blob(str(path.key)) + if blob is not None and blob.exists(): + raise OSError(f"Blob already exists: {path}") + + +class Pathy(Path, PurePathy): + """Subclass of `pathlib.Path` that works with bucket APIs.""" __slots__ = () + _accessor: "BucketsAccessor" + _default_accessor = BucketsAccessor() _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" - def __truediv__(self, key: Union[str, Path, "BasePathy", PurePathy]) -> PathType: + def __truediv__(self, key: Union[str, Path, "Pathy", PurePathy]) -> PathType: # type: ignore[override] return super().__truediv__(key) # type:ignore - def __rtruediv__(self, key: Union[str, Path, "BasePathy", PurePathy]) -> PathType: + def __rtruediv__(self, key: Union[str, Path, "Pathy", PurePathy]) -> PathType: # type: ignore[override] return super().__rtruediv__(key) # type:ignore - def stat(self: PathType) -> BlobStat: - return super().stat() # type:ignore + def _init(self: "Pathy", template: Optional[Any] = None) -> None: + super()._init(template) # type:ignore + if template is None: + self._accessor = Pathy._default_accessor + else: + self._accessor = template._accessor - def exists(self: PathType) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) + @classmethod + def fluid(cls, path_candidate: Union[str, FluidPath]) -> FluidPath: + """Infer either a Pathy or pathlib.Path from an input path or string. + + The returned type is a union of the potential `FluidPath` types and will + type-check correctly against the minimum overlapping APIs of all the input + types. + + If you need to use specific implementation details of a type, "narrow" the + return of this function to the desired type, e.g. + + ```python + fluid_path = FluidPath("gs://my_bucket/foo.txt") + # Narrow the type to a specific class + assert isinstance(fluid_path, Pathy), "must be Pathy" + # Use a member specific to that class + print(fluid_path.prefix) + ``` + """ + from_path: FluidPath = Pathy(path_candidate) + if from_path.root in ["/", ""]: + from_path = Path(path_candidate) + return from_path + + @classmethod + def from_bucket(cls, bucket_name: str) -> PathType: + """Initialize a Pathy from a bucket name. This helper adds a trailing slash and + the appropriate prefix. + + ```python + assert str(Pathy.from_bucket("one")) == "gs://one/" + assert str(Pathy.from_bucket("two")) == "gs://two/" + ``` + """ + return Pathy(f"gs://{bucket_name}/") # type:ignore + + @classmethod + def to_local(cls, blob_path: Union[PathType, str], recurse: bool = True) -> Path: + """Download and cache either a blob or a set of blobs matching a prefix. + + The cache is sensitive to the file updated time, and downloads new blobs + as their updated timestamps change.""" + from .clients import get_fs_cache + + cache_folder = get_fs_cache() + if cache_folder is None: + raise ValueError( + 'cannot get and cache a blob without first calling "use_fs_cache"' + ) + + cache_folder.mkdir(exist_ok=True, parents=True) + + in_path: Pathy + if not isinstance(blob_path, Pathy): + in_path = Pathy(blob_path) + else: + in_path = blob_path + + cache_blob: Path = cache_folder.absolute() / in_path.root + if in_path.key is not None: + cache_blob /= in_path.key + cache_time: Path = ( + cache_folder.absolute() / in_path.root / f"{in_path.key}.time" + ) + # Keep a cache of downloaded files. Fetch new ones when: + # - the file isn't in the cache + # - cached_stat.updated != latest_stat.updated + if cache_blob.exists() and cache_time.exists(): + fs_time: str = cache_time.read_text() + gcs_stat: BlobStat = in_path.stat() + # If the times match, return the cached blob + if fs_time == str(gcs_stat.last_modified): + return cache_blob + # remove the cache files because they're out of date + cache_blob.unlink() + cache_time.unlink() + + # If the file isn't in the cache, download it + if not cache_blob.exists(): + # Is a blob + if in_path.is_file(): + dest_folder = cache_blob.parent + dest_folder.mkdir(exist_ok=True, parents=True) + cache_blob.write_bytes(in_path.read_bytes()) + blob_stat: BlobStat = in_path.stat() + cache_time.write_text(str(blob_stat.last_modified)) + elif recurse: + # If not a specific blob, enumerate all the blobs under + # the path and cache them, then return the cache folder + for blob in in_path.rglob("*"): + Pathy.to_local(blob, recurse=False) + return cache_blob + + def stat(self: PathType) -> BlobStat: # type: ignore[override] + """Returns information about this bucket path.""" + self._absolute_path_validation() + if not self.key: + raise ValueError("cannot stat a bucket without a key") + return cast(BlobStat, super().stat()) + + def exists(self) -> bool: + """Returns True if the path points to an existing bucket, blob, or prefix.""" + self._absolute_path_validation() + return self._accessor.exists(self) def is_dir(self: PathType) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) + """Determine if the path points to a bucket or a prefix of a given blob + in the bucket. + + Returns True if the path points to a bucket or a blob prefix. + Returns False if it points to a blob or the path doesn't exist. + """ + self._absolute_path_validation() + if self.bucket and not self.key: + return True + return self._accessor.is_dir(self) def is_file(self: PathType) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) + """Determine if the path points to a blob in the bucket. + + Returns True if the path points to a blob. + Returns False if it points to a bucket or blob prefix, or if the path doesn’t + exist. + """ + self._absolute_path_validation() + if not self.bucket or not self.key: + return False + try: + return bool(self.stat()) + except (ClientError, FileNotFoundError): + return False def iterdir(self: PathType) -> Generator[PathType, None, None]: + """Iterate over the blobs found in the given bucket or blob prefix path.""" + self._absolute_path_validation() yield from super().iterdir() # type:ignore def glob(self: PathType, pattern: str) -> Generator[PathType, None, None]: - yield from super().glob(pattern=pattern) # type:ignore + """Perform a glob match relative to this Pathy instance, yielding all matched + blobs.""" + yield from super().glob(pattern) # type:ignore def rglob(self: PathType, pattern: str) -> Generator[PathType, None, None]: - yield from super().rglob(pattern=pattern) # type:ignore + """Perform a recursive glob match relative to this Pathy instance, yielding + all matched blobs. Imagine adding "**/" before a call to glob.""" + yield from super().rglob(pattern) # type:ignore def open( self: PathType, @@ -153,35 +615,129 @@ def open( errors: Optional[str] = None, newline: Optional[str] = None, ) -> StreamableType: - raise NotImplementedError(SUBCLASS_ERROR) + """Open the given blob for streaming. This delegates to the `smart_open` + library that handles large file streaming for a number of bucket API + providers.""" + self._absolute_path_validation() + if mode not in _SUPPORTED_OPEN_MODES: + raise ValueError( + "supported modes are {} got {}".format(_SUPPORTED_OPEN_MODES, mode) + ) + if buffering == 0 or buffering == 1: + raise ValueError( + "supported buffering values are only block sizes, no 0 or 1" + ) + if "b" in mode and encoding: + raise ValueError("binary mode doesn't take an encoding argument") + + # Leftover pathlib internals stuff + if self._closed: # type:ignore + self._raise_closed() # type:ignore + return self._accessor.open( + self, + mode=mode, + buffering=buffering, + encoding=encoding, + errors=errors, + newline=newline, + ) - def owner(self: PathType) -> str: - raise NotImplementedError(SUBCLASS_ERROR) + def owner(self: "Pathy") -> Optional[str]: # type:ignore[override] + """Returns the name of the user that owns the bucket or blob + this path points to. Returns None if the owner is unknown or + not supported by the bucket API provider.""" + self._absolute_path_validation() + if not self.is_file(): + raise FileNotFoundError(str(self)) + return self._accessor.owner(self) - def resolve(self: PathType, strict: bool = False) -> PathType: - raise NotImplementedError(SUBCLASS_ERROR) + def resolve(self, strict: bool = False) -> "Pathy": + """Resolve the given path to remove any relative path specifiers. - def rename(self: PathType, target: Union[str, PurePath]) -> None: - return super().rename(target) + ```python + path = Pathy("gs://my_bucket/folder/../blob") + assert path.resolve() == Pathy("gs://my_bucket/blob") + ``` + """ + self._absolute_path_validation() + return self._accessor.resolve(self, strict=strict) + + def rename(self: "Pathy", target: Union[str, PurePath]) -> None: + """Rename this path to the given target. + + If the target exists and is a file, it will be replaced silently if the user + has permission. + + If path is a blob prefix, it will replace all the blobs with the same prefix + to match the target prefix.""" + self._absolute_path_validation() + self_type = type(self) + if not isinstance(target, self_type): + target = self_type(target) + target._absolute_path_validation() # type:ignore + super().rename(target) def replace(self: PathType, target: Union[str, PurePath]) -> None: - raise NotImplementedError(SUBCLASS_ERROR) + """Renames this path to the given target. - def rmdir(self: PathType) -> None: - return super().rmdir() + If target points to an existing path, it will be replaced.""" + self.rename(target) - def samefile( - self: PathType, other_path: Union[str, bytes, int, Path, PathType] - ) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) + def rmdir(self: "Pathy") -> None: + """Removes this bucket or blob prefix. It must be empty.""" + self._absolute_path_validation() + if self.is_file(): + raise NotADirectoryError() + if not self.is_dir(): + raise FileNotFoundError() + super().rmdir() + + def samefile(self: "Pathy", other_path: Union[str, bytes, int, Path]) -> bool: + """Determine if this path points to the same location as other_path.""" + self._absolute_path_validation() + if not isinstance(other_path, Path): + other_path = type(self)(other_path) # type:ignore + assert isinstance(other_path, Pathy) + return ( + self.bucket == other_path.bucket and self.key == self.key and self.is_file() + ) def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True) -> None: - raise NotImplementedError(SUBCLASS_ERROR) + """Create a blob at this path. + + If the blob already exists, the function succeeds if exist_ok is true + (and its modification time is updated to the current time), otherwise + FileExistsError is raised. + """ + if self.exists() and not exist_ok: + raise FileExistsError() + self.write_text("") def mkdir( - self: PathType, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False ) -> None: - return super().mkdir(mode=mode, parents=parents, exist_ok=exist_ok) + """Create a bucket from the given path. Since bucket APIs only have implicit + folder structures (determined by the existence of a blob with an overlapping + prefix) this does nothing other than create buckets. + + If parents is False, the bucket will only be created if the path points to + exactly the bucket and nothing else. If parents is true the bucket will be + created even if the path points to a specific blob. + + The mode param is ignored. + + Raises FileExistsError if exist_ok is false and the bucket already exists. + """ + try: + if self.bucket is None: + raise FileNotFoundError("No bucket in {} {}".format(type(self), self)) + # If the whole path is just the bucket, respect the result of "bucket.exists()" + if self.key is None and not exist_ok and self.bucket.exists(): + raise FileExistsError("Bucket {} already exists".format(self.bucket)) + return super().mkdir(mode, parents=parents, exist_ok=exist_ok) + except OSError: + if not exist_ok: + raise def is_mount(self: PathType) -> bool: return False @@ -237,7 +793,7 @@ def is_char_device(self: PathType) -> bool: ) raise NotImplementedError(message) - def lstat(self: PathType) -> BlobStat: + def lstat(self: PathType) -> os.stat_result: message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lstat.__qualname__) raise NotImplementedError(message) diff --git a/pathy/cli.py b/pathy/cli.py index d4bcc94..0744abe 100644 --- a/pathy/cli.py +++ b/pathy/cli.py @@ -1,6 +1,6 @@ import typer -from .api import FluidPath, Pathy +from .base import FluidPath, Pathy app = typer.Typer(help="Pathy command line interface.") diff --git a/pathy/client.py b/pathy/client.py deleted file mode 100644 index 59c01d8..0000000 --- a/pathy/client.py +++ /dev/null @@ -1,178 +0,0 @@ -from dataclasses import dataclass -from io import DEFAULT_BUFFER_SIZE -from typing import Any, Dict, Generator, Generic, List, Optional, TypeVar - -import smart_open - -from .base import SUBCLASS_ERROR, BlobStat, PathType, StreamableType - -__all__ = ( - "BucketEntry", - "BucketClient", - "ClientError", - "Bucket", - "Blob", -) - -BucketClientType = TypeVar("BucketClientType", bound="BucketClient") -BucketType = TypeVar("BucketType", bound="Bucket") -BucketBlobType = TypeVar("BucketBlobType", bound="Blob") - - -@dataclass -class ClientError(BaseException): - message: str - code: Optional[int] - - def __str__(self) -> str: - return self.__repr__() - - def __repr__(self) -> str: - return f"({self.code}) {self.message}" - - -@dataclass -class Blob(Generic[BucketType, BucketBlobType]): - bucket: "Bucket" - name: str - size: int - updated: int - owner: Optional[str] - raw: BucketBlobType - - def delete(self) -> None: - raise NotImplementedError(SUBCLASS_ERROR) - - def exists(self) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) - - -class BucketEntry(Generic[BucketType, BucketBlobType]): - """A single item returned from scanning a path""" - - name: str - _is_dir: bool - _stat: BlobStat - raw: Optional[Blob[BucketType, BucketBlobType]] - - def __init__( - self, - name: str, - is_dir: bool = False, - size: int = -1, - last_modified: int = -1, - raw: Optional[Blob[BucketType, BucketBlobType]] = None, - ): - self.name = name - self.raw = raw - self._is_dir = is_dir - self._stat = BlobStat(size=size, last_modified=last_modified) - - def __repr__(self) -> str: - return "{}(name={}, is_dir={}, stat={})".format( - type(self).__name__, self.name, self._is_dir, self._stat - ) - - def inode(self, *args: Any, **kwargs: Dict[str, Any]) -> None: - return None - - def is_dir(self) -> bool: - return self._is_dir - - def is_file(self) -> bool: - return not self._is_dir - - def is_symlink(self) -> bool: - return False - - def stat(self) -> BlobStat: - return self._stat - - -@dataclass -class Bucket: - def get_blob(self, blob_name: str) -> Optional[Blob[BucketType, BucketBlobType]]: - raise NotImplementedError(SUBCLASS_ERROR) - - def copy_blob( - self, blob: Blob[BucketType, BucketBlobType], target: "Bucket", name: str - ) -> Optional[Blob[BucketType, BucketBlobType]]: - raise NotImplementedError(SUBCLASS_ERROR) - - def delete_blob(self, blob: Blob[BucketType, BucketBlobType]) -> None: - raise NotImplementedError(SUBCLASS_ERROR) - - def delete_blobs(self, blobs: List[Blob[BucketType, BucketBlobType]]) -> None: - raise NotImplementedError(SUBCLASS_ERROR) - - def exists(self) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) - - -class BucketClient: - """Base class for a client that interacts with a bucket-based storage system.""" - - def open( - self, - path: PathType, - *, - mode: str = "r", - buffering: int = DEFAULT_BUFFER_SIZE, - encoding: Optional[str] = None, - errors: Optional[str] = None, - newline: Optional[str] = None, - ) -> StreamableType: - return smart_open.open( - self.make_uri(path), - mode=mode, - buffering=buffering, - encoding=encoding, - errors=errors, - newline=newline, - # Disable de/compression based on the file extension - ignore_ext=True, - ) # type:ignore - - def make_uri(self, path: PathType) -> str: - return path.as_uri() - - def is_dir(self, path: PathType) -> bool: - return any(self.list_blobs(path, prefix=path.prefix)) - - def rmdir(self, path: PathType) -> None: - return None - - def exists(self, path: PathType) -> bool: - raise NotImplementedError(SUBCLASS_ERROR) - - def lookup_bucket(self, path: PathType) -> Optional[Bucket]: - raise NotImplementedError(SUBCLASS_ERROR) - - def get_bucket(self, path: PathType) -> Bucket: - raise NotImplementedError(SUBCLASS_ERROR) - - def list_buckets(self) -> Generator[Bucket, None, None]: - raise NotImplementedError(SUBCLASS_ERROR) - - def list_blobs( - self, - path: PathType, - prefix: Optional[str] = None, - delimiter: Optional[str] = None, - include_dirs: bool = False, - ) -> Generator[Blob, None, None]: - raise NotImplementedError(SUBCLASS_ERROR) - - def scandir( - self, - path: PathType = None, - prefix: Optional[str] = None, - delimiter: Optional[str] = None, - ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: - raise NotImplementedError(SUBCLASS_ERROR) - - def create_bucket(self, path: PathType) -> Bucket: - raise NotImplementedError(SUBCLASS_ERROR) - - def delete_bucket(self, path: PathType) -> None: - raise NotImplementedError(SUBCLASS_ERROR) diff --git a/pathy/clients.py b/pathy/clients.py index c767ed2..bd61141 100644 --- a/pathy/clients.py +++ b/pathy/clients.py @@ -1,9 +1,9 @@ import pathlib import shutil import tempfile -from typing import Dict, Optional, Type, Union +from typing import Any, Dict, Optional, Type, Union -from .client import BucketClient, BucketClientType +from .base import BucketClient, BucketClientType from .file import BucketClientFS from .gcs import BucketClientGCS @@ -15,7 +15,7 @@ "gs": BucketClientGCS, } -_instance_cache: Dict[str, BucketClient] = {} +_instance_cache: Dict[str, Any] = {} _fs_client: Optional[BucketClientFS] = None _fs_cache: Optional[pathlib.Path] = None @@ -25,11 +25,11 @@ def register_client() -> None: global _client_registry -def get_client(scheme: str) -> "BucketClientType": +def get_client(scheme: str) -> BucketClientType: """Retrieve the bucket client for use with a given scheme""" global _client_registry, _instance_cache, _fs_client if _fs_client is not None: - return _fs_client + return _fs_client # type: ignore if scheme in _instance_cache: return _instance_cache[scheme] elif scheme in _client_registry: diff --git a/pathy/file.py b/pathy/file.py index 4030b38..e65bce2 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -5,8 +5,15 @@ from io import DEFAULT_BUFFER_SIZE from typing import Generator, List, Optional -from .base import PurePathy, StreamableType -from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError +from .base import ( + Blob, + Bucket, + BucketClient, + BucketEntry, + ClientError, + PurePathy, + StreamableType, +) class BucketEntryFS(BucketEntry["BucketFS", pathlib.Path]): @@ -84,10 +91,6 @@ class BucketClientFS(BucketClient): # Root to store file-system buckets as children of root: pathlib.Path = field(default_factory=lambda: pathlib.Path("/tmp/")) - def make_uri(self, path: PurePathy): - uri = super().make_uri(path) - return uri.replace("gs://", "file:///") - def full_path(self, path: PurePathy) -> pathlib.Path: if path.root is None: raise ValueError(f"Invalid bucket name for path: {path}") diff --git a/pathy/gcs.py b/pathy/gcs.py index 422a176..f118f4d 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -1,8 +1,7 @@ from dataclasses import dataclass from typing import Generator, List, Optional -from .base import PurePathy -from .client import Blob, Bucket, BucketClient, BucketEntry, ClientError +from .base import Blob, Bucket, BucketClient, BucketEntry, ClientError, PurePathy try: from google.api_core import exceptions as gcs_errors diff --git a/tests/test_base.py b/tests/test_base.py index 6ddf0bb..0460a11 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -5,7 +5,6 @@ import pytest from pathy import Pathy, PurePathy -from pathy.base import BasePathy def test_base_not_supported(monkeypatch): @@ -281,26 +280,3 @@ def test_base_with_suffix(): assert gcs_path.with_suffix(".txt") == PurePathy("README.txt") gcs_path = PurePathy("README.txt") assert gcs_path.with_suffix("") == PurePathy("README") - - -def test_base_class_abstract_methods(): - path = BasePathy("gs://foo/bar") - other = Pathy("gs://other") - with pytest.raises(NotImplementedError): - path.exists() - with pytest.raises(NotImplementedError): - path.is_dir() - with pytest.raises(NotImplementedError): - path.is_file() - with pytest.raises(NotImplementedError): - path.touch() - with pytest.raises(NotImplementedError): - path.open() - with pytest.raises(NotImplementedError): - path.owner() - with pytest.raises(NotImplementedError): - path.resolve() - with pytest.raises(NotImplementedError): - path.replace(other) - with pytest.raises(NotImplementedError): - path.samefile(other) From d8dbcd41d1e813090cad906c81df95880ae7289c Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:01:54 -0700 Subject: [PATCH 39/75] feat(GCS): print install command when using GCS without deps installed - make the assertion prettier :sunglasses: --- pathy/gcs.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pathy/gcs.py b/pathy/gcs.py index f118f4d..2d4f1b2 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -13,6 +13,15 @@ storage = None has_gcs = False +_MISSING_DEPS = """You are using the GCS functionality of Pathy without +having the required dependencies installed. + +Please try installing them: + + pip install pathy[gcs] + +""" + class BucketEntryGCS(BucketEntry["BucketGCS", "storage.Blob"]): ... @@ -94,11 +103,11 @@ def make_uri(self, path: PurePathy) -> str: return str(path) def create_bucket(self, path: PurePathy) -> Bucket: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS return self.client.create_bucket(path.root) def delete_bucket(self, path: PurePathy) -> None: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS bucket = self.client.get_bucket(path.root) bucket.delete() @@ -118,7 +127,7 @@ def exists(self, path: PurePathy) -> bool: return False def lookup_bucket(self, path: PurePathy) -> Optional[BucketGCS]: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS try: native_bucket = self.client.bucket(path.root) if native_bucket is not None: @@ -129,7 +138,7 @@ def lookup_bucket(self, path: PurePathy) -> Optional[BucketGCS]: return None def get_bucket(self, path: PurePathy) -> BucketGCS: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS try: native_bucket = self.client.bucket(path.root) if native_bucket is not None: @@ -139,7 +148,7 @@ def get_bucket(self, path: PurePathy) -> BucketGCS: raise ClientError(message=e.message, code=e.code) def list_buckets(self, **kwargs) -> Generator[Bucket, None, None]: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS return self.client.list_buckets(**kwargs) def scandir( @@ -149,7 +158,7 @@ def scandir( delimiter: Optional[str] = None, include_raw: bool = False, ) -> Generator[BucketEntryGCS, None, None]: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS continuation_token = None if path is None or not path.root: for bucket in self.list_buckets(): @@ -198,7 +207,7 @@ def list_blobs( delimiter: Optional[str] = None, include_dirs: bool = False, ) -> Generator[BlobGCS, None, None]: - assert self.client is not None + assert self.client is not None, _MISSING_DEPS continuation_token = None bucket = self.lookup_bucket(path) if bucket is None: From 968a226507498bd319dd64f61c25a6dd4df627f6 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:26:28 -0700 Subject: [PATCH 40/75] chore: fix the remaining mypy errors - in some cases the mypy errors are too uptight about subclasses and their types. When that happens we silence the error and provide the expected subclass type. --- README.md | 6 +++++- pathy/base.py | 10 +++++----- pathy/file.py | 35 ++++++++++++++++++++--------------- pathy/gcs.py | 32 +++++++++++++++++--------------- tests/test_api.py | 6 +++--- tests/test_cli.py | 6 +++--- 6 files changed, 53 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 7832d88..4ba33ad 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,11 @@ FileExistsError is raised. # BlobStat dataclass ```python -BlobStat(self, size: int, last_modified: int) -> None +BlobStat( + self, + size: Optional[int], + last_modified: Optional[int], +) -> None ``` Stat for a bucket item diff --git a/pathy/base.py b/pathy/base.py index 5d4d7d9..65e9ea6 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -26,7 +26,7 @@ StreamableType = IO[Any] FluidPath = Union["Pathy", Path] BucketClientType = TypeVar("BucketClientType", bound="BucketClient") -BucketType = TypeVar("BucketType", bound="Bucket") +BucketType = TypeVar("BucketType") BucketBlobType = TypeVar("BucketBlobType") @@ -46,16 +46,16 @@ def __repr__(self) -> str: class BlobStat: """Stat for a bucket item""" - size: int - last_modified: int + size: Optional[int] + last_modified: Optional[int] @dataclass class Blob(Generic[BucketType, BucketBlobType]): bucket: "Bucket" name: str - size: int - updated: int + size: Optional[int] + updated: Optional[int] owner: Optional[str] raw: BucketBlobType diff --git a/pathy/file.py b/pathy/file.py index e65bce2..8591c8c 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -3,7 +3,7 @@ import shutil from dataclasses import dataclass, field from io import DEFAULT_BUFFER_SIZE -from typing import Generator, List, Optional +from typing import Any, Dict, Generator, List, Optional from .base import ( Blob, @@ -11,6 +11,7 @@ BucketClient, BucketEntry, ClientError, + Pathy, PurePathy, StreamableType, ) @@ -43,7 +44,7 @@ class BucketFS(Bucket): name: str bucket: pathlib.Path - def get_blob(self, blob_name: str) -> Optional[BlobFS]: + def get_blob(self, blob_name: str) -> Optional[BlobFS]: # type:ignore[override] native_blob = self.bucket / blob_name if not native_blob.exists() or native_blob.is_dir(): return None @@ -51,6 +52,7 @@ def get_blob(self, blob_name: str) -> Optional[BlobFS]: # path.owner() raises KeyError if the owner's UID isn't known # # https://docs.python.org/3/library/pathlib.html#pathlib.Path.owner + owner: Optional[str] try: owner = native_blob.owner() except KeyError: @@ -64,7 +66,7 @@ def get_blob(self, blob_name: str) -> Optional[BlobFS]: updated=int(round(stat.st_mtime_ns * 1000)), ) - def copy_blob( + def copy_blob( # type:ignore[override] self, blob: BlobFS, target: "BucketFS", name: str ) -> Optional[BlobFS]: in_file = str(blob.bucket.bucket / blob.name) @@ -75,10 +77,10 @@ def copy_blob( shutil.copy(in_file, out_file) return None - def delete_blob(self, blob: BlobFS) -> None: + def delete_blob(self, blob: BlobFS) -> None: # type:ignore[override] blob.delete() - def delete_blobs(self, blobs: List[BlobFS]) -> None: + def delete_blobs(self, blobs: List[BlobFS]) -> None: # type:ignore[override] for blob in blobs: blob.delete() @@ -91,7 +93,7 @@ class BucketClientFS(BucketClient): # Root to store file-system buckets as children of root: pathlib.Path = field(default_factory=lambda: pathlib.Path("/tmp/")) - def full_path(self, path: PurePathy) -> pathlib.Path: + def full_path(self, path: Pathy) -> pathlib.Path: if path.root is None: raise ValueError(f"Invalid bucket name for path: {path}") full_path = self.root.absolute() / path.root @@ -99,20 +101,20 @@ def full_path(self, path: PurePathy) -> pathlib.Path: full_path = full_path / path.key return full_path - def exists(self, path: PurePathy) -> bool: + def exists(self, path: Pathy) -> bool: """Return True if the path exists as a file or folder on disk""" return self.full_path(path).exists() - def is_dir(self, path: PurePathy) -> bool: + def is_dir(self, path: Pathy) -> bool: return self.full_path(path).is_dir() - def rmdir(self, path: PurePathy) -> None: + def rmdir(self, path: Pathy) -> None: full_path = self.full_path(path) return shutil.rmtree(str(full_path)) def open( self, - path: PurePathy, + path: Pathy, *, mode: str = "r", buffering: int = DEFAULT_BUFFER_SIZE, @@ -140,7 +142,10 @@ def open( def make_uri(self, path: PurePathy) -> str: if not path.root: raise ValueError(f"cannot make a URI to an invalid bucket: {path.root}") - result = f"file://{self.root.absolute() / path.root / path.key}" + full_path = self.root.absolute() / path.root + if path.key is not None: + full_path /= path.key + result = f"file://{full_path}" return result def create_bucket(self, path: PurePathy) -> Bucket: @@ -172,17 +177,17 @@ def get_bucket(self, path: PurePathy) -> BucketFS: return BucketFS(str(path.root), bucket=bucket_path) raise FileNotFoundError(f"Bucket {path.root} does not exist!") - def list_buckets(self, **kwargs) -> Generator[BucketFS, None, None]: + def list_buckets(self, **kwargs: Dict[str, Any]) -> Generator[BucketFS, None, None]: for f in self.root.glob("*"): if f.is_dir(): yield BucketFS(f.name, f) - def scandir( + def scandir( # type:ignore[override] self, - path: Optional[PurePathy] = None, + path: Pathy = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, - ) -> Generator[BucketEntryFS, None, None]: + ) -> Generator[BucketEntry[BucketFS, pathlib.Path], None, None]: if path is None or not path.root: for bucket in self.list_buckets(): yield BucketEntryFS(bucket.name, is_dir=True, raw=None) diff --git a/pathy/gcs.py b/pathy/gcs.py index 2d4f1b2..05eb8be 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Generator, List, Optional +from typing import Any, Dict, Generator, List, Optional from .base import Blob, Bucket, BucketClient, BucketEntry, ClientError, PurePathy @@ -14,7 +14,7 @@ has_gcs = False _MISSING_DEPS = """You are using the GCS functionality of Pathy without -having the required dependencies installed. +having the required dependencies installed. Please try installing them: @@ -28,7 +28,7 @@ class BucketEntryGCS(BucketEntry["BucketGCS", "storage.Blob"]): @dataclass -class BlobGCS(Blob["BucketGCS", "storage.Blob"]): +class BlobGCS(Blob["storage.Bucket", "storage.Blob"]): def delete(self) -> None: self.raw.delete() @@ -58,10 +58,10 @@ def get_blob(self, blob_name: str) -> Optional[BlobGCS]: name=native_blob.name, raw=native_blob, size=native_blob.size, - updated=native_blob.updated.timestamp(), + updated=int(native_blob.updated.timestamp()), ) - def copy_blob( + def copy_blob( # type:ignore[override] self, blob: BlobGCS, target: "BucketGCS", name: str ) -> Optional[BlobGCS]: assert blob.raw is not None, "raw storage.Blob instance required" @@ -74,13 +74,13 @@ def copy_blob( name=native_blob.name, raw=native_blob, size=native_blob.size, - updated=native_blob.updated.timestamp(), + updated=int(native_blob.updated.timestamp()), ) - def delete_blob(self, blob: BlobGCS) -> None: + def delete_blob(self, blob: BlobGCS) -> None: # type:ignore[override] return self.bucket.delete_blob(blob.name) - def delete_blobs(self, blobs: List[BlobGCS]) -> None: + def delete_blobs(self, blobs: List[BlobGCS]) -> None: # type:ignore[override] return self.bucket.delete_blobs(blobs) def exists(self) -> bool: @@ -147,22 +147,24 @@ def get_bucket(self, path: PurePathy) -> BucketGCS: except gcs_errors.ClientError as e: raise ClientError(message=e.message, code=e.code) - def list_buckets(self, **kwargs) -> Generator[Bucket, None, None]: + def list_buckets( + self, **kwargs: Dict[str, Any] + ) -> Generator["storage.Bucket", None, None]: assert self.client is not None, _MISSING_DEPS - return self.client.list_buckets(**kwargs) + return self.client.list_buckets(**kwargs) # type:ignore - def scandir( + def scandir( # type:ignore[override] self, path: Optional[PurePathy] = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, - include_raw: bool = False, - ) -> Generator[BucketEntryGCS, None, None]: + ) -> Generator[BucketEntryGCS, None, None]: # type:ignore[override] assert self.client is not None, _MISSING_DEPS continuation_token = None if path is None or not path.root: - for bucket in self.list_buckets(): - yield BucketEntryGCS(bucket.name, is_dir=True, raw=None) + gcs_bucket: "storage.Bucket" + for gcs_bucket in self.list_buckets(): + yield BucketEntryGCS(gcs_bucket.name, is_dir=True, raw=None) return sep = path._flavour.sep bucket = self.lookup_bucket(path) diff --git a/tests/test_api.py b/tests/test_api.py index 475fd27..8327315 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -41,9 +41,9 @@ def test_api_is_path_instance(with_adapter): def test_api_fluid(with_adapter, bucket: str): path: FluidPath = Pathy.fluid(f"gs://{bucket}/fake-key") assert isinstance(path, Pathy) - path: FluidPath = Pathy.fluid(f"foo/bar.txt") + path: FluidPath = Pathy.fluid("foo/bar.txt") assert isinstance(path, Path) - path: FluidPath = Pathy.fluid(f"/dev/null") + path: FluidPath = Pathy.fluid("/dev/null") assert isinstance(path, Path) @@ -112,7 +112,7 @@ def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): path.write_text('{ "cool" : true }') # Fetch the updated blob - res: Path = Pathy.to_local(path) + Pathy.to_local(path) updated_cache_time = foo_timestamp.read_text() assert updated_cache_time != orig_cache_time, "cached timestamp did not change" diff --git a/tests/test_cli.py b/tests/test_cli.py index 2eb704e..91c2643 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -151,9 +151,9 @@ def test_cli_rm_folder(with_adapter, bucket: str): @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_cli_ls(with_adapter, bucket: str): root = Pathy.from_bucket(bucket) / "cli_ls" - one = str(root / f"file.txt") - two = str(root / f"other.txt") - three = str(root / f"folder/file.txt") + one = str(root / "file.txt") + two = str(root / "other.txt") + three = str(root / "folder/file.txt") Pathy(one).write_text("---") Pathy(two).write_text("---") Pathy(three).write_text("---") From c06f6abbf0bdae38f270fa3781847dc906779779 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:26:55 -0700 Subject: [PATCH 41/75] chore: remove isort test from lint script - we black format last, so the order/indent could be changed. --- tools/lint.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/lint.sh b/tools/lint.sh index f497813..0cc5370 100644 --- a/tools/lint.sh +++ b/tools/lint.sh @@ -6,4 +6,3 @@ set -x mypy pathy flake8 pathy tests black pathy tests --check -isort pathy tests docs_src scripts --check-only \ No newline at end of file From d6e7e92798b736e1a8ae5b9c02157c06982f2c33 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:39:35 -0700 Subject: [PATCH 42/75] chore: misc cleanup --- pathy/about.py | 2 +- tests/test_api.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pathy/about.py b/pathy/about.py index 1b6f315..4bf796e 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,6 +1,6 @@ __title__ = "pathy" __version__ = "0.2.0" -__summary__ = "pathlib.Path subclasses for Google Cloud Storage" +__summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" __email__ = "justin@explosion.ai" diff --git a/tests/test_api.py b/tests/test_api.py index 8327315..f2ef344 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -20,8 +20,7 @@ from .conftest import TEST_ADAPTERS -# todo: test samefile/touch/write_text/write_bytes method -# todo: test security and boto config changes +# todo: test samefile/touch method # todo: test open method check R/W bytes/unicode # todo(jd): replace global test-bucket with mock or generate buckets and call these e2e tests From 3168af4566ef79d55f1b821c34d57b312fd34395 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:47:28 -0700 Subject: [PATCH 43/75] chore: drop PathType variable - since consolidating the Pathy class in the base.py file, there's no need to TypeVars, we can just use a forward ref to Pathy itself :tada: --- README.md | 28 +++++++------ pathy/base.py | 108 +++++++++++++++++++++++++------------------------- 2 files changed, 69 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 4ba33ad..1c921a9 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ print(fluid_path.prefix) ## from_bucket classmethod ```python -Pathy.from_bucket(bucket_name: str) -> ~PathType +Pathy.from_bucket(bucket_name: str) -> 'Pathy' ``` Initialize a Pathy from a bucket name. This helper adds a trailing slash and @@ -97,9 +97,9 @@ assert str(Pathy.from_bucket("two")) == "gs://two/" ```python Pathy.glob( - self: ~PathType, + self: 'Pathy', pattern: str, -) -> Generator[~PathType, NoneType, NoneType] +) -> Generator[Pathy, NoneType, NoneType] ``` Perform a glob match relative to this Pathy instance, yielding all matched @@ -108,7 +108,7 @@ blobs. ## is_dir method ```python -Pathy.is_dir(self: ~PathType) -> bool +Pathy.is_dir(self: 'Pathy') -> bool ``` Determine if the path points to a bucket or a prefix of a given blob @@ -120,7 +120,7 @@ Returns False if it points to a blob or the path doesn't exist. ## is_file method ```python -Pathy.is_file(self: ~PathType) -> bool +Pathy.is_file(self: 'Pathy') -> bool ``` Determine if the path points to a blob in the bucket. @@ -132,7 +132,9 @@ exist. ## iterdir method ```python -Pathy.iterdir(self: ~PathType) -> Generator[~PathType, NoneType, NoneType] +Pathy.iterdir( + self: 'Pathy', +) -> Generator[Pathy, NoneType, NoneType] ``` Iterate over the blobs found in the given bucket or blob prefix path. @@ -164,7 +166,7 @@ Raises FileExistsError if exist_ok is false and the bucket already exists. ```python Pathy.open( - self: ~PathType, + self: 'Pathy', mode: str = 'r', buffering: int = 8192, encoding: Optional[str] = None, @@ -204,7 +206,7 @@ to match the target prefix. ## replace method ```python -Pathy.replace(self: ~PathType, target: Union[str, pathlib.PurePath]) -> None +Pathy.replace(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> None ``` Renames this path to the given target. @@ -228,9 +230,9 @@ assert path.resolve() == Pathy("gs://my_bucket/blob") ```python Pathy.rglob( - self: ~PathType, + self: 'Pathy', pattern: str, -) -> Generator[~PathType, NoneType, NoneType] +) -> Generator[Pathy, NoneType, NoneType] ``` Perform a recursive glob match relative to this Pathy instance, yielding @@ -258,7 +260,7 @@ Determine if this path points to the same location as other_path. ## stat method ```python -Pathy.stat(self: ~PathType) -> pathy.base.BlobStat +Pathy.stat(self: 'Pathy') -> pathy.base.BlobStat ``` Returns information about this bucket path. @@ -267,7 +269,7 @@ Returns information about this bucket path. ```python Pathy.to_local( - blob_path: Union[~PathType, str], + blob_path: Union[Pathy, str], recurse: bool = True, ) -> pathlib.Path ``` @@ -280,7 +282,7 @@ as their updated timestamps change. ## touch method ```python -Pathy.touch(self: ~PathType, mode: int = 438, exist_ok: bool = True) -> None +Pathy.touch(self: 'Pathy', mode: int = 438, exist_ok: bool = True) -> None ``` Create a blob at this path. diff --git a/pathy/base.py b/pathy/base.py index 65e9ea6..73d5450 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -22,7 +22,7 @@ import smart_open SUBCLASS_ERROR = "must be implemented in a subclass" -PathType = TypeVar("PathType", bound="Pathy") + StreamableType = IO[Any] FluidPath = Union["Pathy", Path] BucketClientType = TypeVar("BucketClientType", bound="BucketClient") @@ -133,7 +133,7 @@ class BucketClient: def open( self, - path: PathType, + path: "Pathy", *, mode: str = "r", buffering: int = DEFAULT_BUFFER_SIZE, @@ -152,22 +152,22 @@ def open( ignore_ext=True, ) # type:ignore - def make_uri(self, path: PathType) -> str: + def make_uri(self, path: "Pathy") -> str: return path.as_uri() - def is_dir(self, path: PathType) -> bool: + def is_dir(self, path: "Pathy") -> bool: return any(self.list_blobs(path, prefix=path.prefix)) - def rmdir(self, path: PathType) -> None: + def rmdir(self, path: "Pathy") -> None: return None - def exists(self, path: PathType) -> bool: + def exists(self, path: "Pathy") -> bool: raise NotImplementedError(SUBCLASS_ERROR) - def lookup_bucket(self, path: PathType) -> Optional[Bucket]: + def lookup_bucket(self, path: "Pathy") -> Optional[Bucket]: raise NotImplementedError(SUBCLASS_ERROR) - def get_bucket(self, path: PathType) -> Bucket: + def get_bucket(self, path: "Pathy") -> Bucket: raise NotImplementedError(SUBCLASS_ERROR) def list_buckets(self) -> Generator[Bucket, None, None]: @@ -175,7 +175,7 @@ def list_buckets(self) -> Generator[Bucket, None, None]: def list_blobs( self, - path: PathType, + path: "Pathy", prefix: Optional[str] = None, delimiter: Optional[str] = None, include_dirs: bool = False, @@ -184,16 +184,16 @@ def list_blobs( def scandir( self, - path: PathType = None, + path: "Pathy" = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: raise NotImplementedError(SUBCLASS_ERROR) - def create_bucket(self, path: PathType) -> Bucket: + def create_bucket(self, path: "Pathy") -> Bucket: raise NotImplementedError(SUBCLASS_ERROR) - def delete_bucket(self, path: PathType) -> None: + def delete_bucket(self, path: "Pathy") -> None: raise NotImplementedError(SUBCLASS_ERROR) @@ -215,7 +215,7 @@ def parse_parts(self, parts: List[str]) -> Tuple[str, str, List[str]]: parsed.remove(part) return drv, root, parsed - def make_uri(self, path: PathType) -> str: + def make_uri(self, path: "Pathy") -> str: uri = super().make_uri(path) return uri.replace("file:///", "gs://") @@ -295,13 +295,13 @@ class BucketsAccessor(_Accessor): _client: Optional[BucketClient] - def client(self, path: PathType) -> BucketClient: + def client(self, path: "Pathy") -> BucketClient: # lazily avoid circular imports from .clients import get_client return get_client(path.scheme) - def get_blob(self, path: PathType) -> Optional[Blob]: + def get_blob(self, path: "Pathy") -> Optional[Blob]: """Get the blob associated with a path or return None""" if not path.root: return None @@ -311,7 +311,7 @@ def get_blob(self, path: PathType) -> Optional[Blob]: key_name = str(path.key) return bucket.get_blob(key_name) - def unlink(self, path: PathType) -> None: + def unlink(self, path: "Pathy") -> None: """Delete a link to a blob in a bucket.""" bucket = self.client(path).get_bucket(path) blob: Optional[Blob] = bucket.get_blob(str(path.key)) @@ -319,21 +319,21 @@ def unlink(self, path: PathType) -> None: raise FileNotFoundError(path) blob.delete() - def stat(self, path: PathType) -> BlobStat: + def stat(self, path: "Pathy") -> BlobStat: bucket = self.client(path).get_bucket(path) blob: Optional[Blob] = bucket.get_blob(str(path.key)) if blob is None: raise FileNotFoundError(path) return BlobStat(size=blob.size, last_modified=blob.updated) - def is_dir(self, path: PathType) -> bool: + def is_dir(self, path: "Pathy") -> bool: if str(path) == path.root: return True if self.get_blob(path) is not None: return False return self.client(path).is_dir(path) - def exists(self, path: PathType) -> bool: + def exists(self, path: "Pathy") -> bool: client = self.client(path) if not path.root: return any(client.list_buckets()) @@ -353,16 +353,16 @@ def exists(self, path: PathType) -> bool: # Determine if the path exists according to the current adapter return client.exists(path) - def scandir(self, path: PathType) -> Generator[BucketEntry, None, None]: + def scandir(self, path: "Pathy") -> Generator[BucketEntry, None, None]: return self.client(path).scandir(path, prefix=path.prefix) - def listdir(self, path: PathType) -> Generator[str, None, None]: + def listdir(self, path: "Pathy") -> Generator[str, None, None]: for entry in self.scandir(path): yield entry.name def open( self, - path: PathType, + path: "Pathy", *, mode: str = "r", buffering: int = -1, @@ -379,15 +379,15 @@ def open( newline=newline, ) - def owner(self, path: PathType) -> Optional[str]: + def owner(self, path: "Pathy") -> Optional[str]: blob: Optional[Blob] = self.get_blob(path) return blob.owner if blob is not None else None - def resolve(self, path: PathType, strict: bool = False) -> "Pathy": + def resolve(self, path: "Pathy", strict: bool = False) -> "Pathy": path_parts = str(path).replace(path.drive, "") return Pathy(f"{path.drive}{os.path.abspath(path_parts)}") - def rename(self, path: PathType, target: PathType) -> None: + def rename(self, path: "Pathy", target: "Pathy") -> None: client: BucketClient = self.client(path) bucket: Bucket = client.get_bucket(path) target_bucket: Bucket = client.get_bucket(target) @@ -412,10 +412,10 @@ def rename(self, path: PathType, target: PathType) -> None: for blob in blobs: bucket.delete_blob(blob) - def replace(self, path: PathType, target: PathType) -> None: + def replace(self, path: "Pathy", target: "Pathy") -> None: return self.rename(path, target) - def rmdir(self, path: PathType) -> None: + def rmdir(self, path: "Pathy") -> None: client: BucketClient = self.client(path) key_name = str(path.key) if path.key is not None else None bucket: Bucket = client.get_bucket(path) @@ -427,7 +427,7 @@ def rmdir(self, path: PathType) -> None: elif client.is_dir(path): client.rmdir(path) - def mkdir(self, path: PathType, mode: int) -> None: + def mkdir(self, path: "Pathy", mode: int) -> None: client: BucketClient = self.client(path) bucket: Optional[Bucket] = client.lookup_bucket(path) if bucket is None or not bucket.exists(): @@ -447,10 +447,10 @@ class Pathy(Path, PurePathy): _default_accessor = BucketsAccessor() _NOT_SUPPORTED_MESSAGE = "{method} is an unsupported bucket operation" - def __truediv__(self, key: Union[str, Path, "Pathy", PurePathy]) -> PathType: # type: ignore[override] + def __truediv__(self, key: Union[str, Path, "Pathy", PurePathy]) -> "Pathy": # type: ignore[override] return super().__truediv__(key) # type:ignore - def __rtruediv__(self, key: Union[str, Path, "Pathy", PurePathy]) -> PathType: # type: ignore[override] + def __rtruediv__(self, key: Union[str, Path, "Pathy", PurePathy]) -> "Pathy": # type: ignore[override] return super().__rtruediv__(key) # type:ignore def _init(self: "Pathy", template: Optional[Any] = None) -> None: @@ -485,7 +485,7 @@ def fluid(cls, path_candidate: Union[str, FluidPath]) -> FluidPath: return from_path @classmethod - def from_bucket(cls, bucket_name: str) -> PathType: + def from_bucket(cls, bucket_name: str) -> "Pathy": """Initialize a Pathy from a bucket name. This helper adds a trailing slash and the appropriate prefix. @@ -497,7 +497,7 @@ def from_bucket(cls, bucket_name: str) -> PathType: return Pathy(f"gs://{bucket_name}/") # type:ignore @classmethod - def to_local(cls, blob_path: Union[PathType, str], recurse: bool = True) -> Path: + def to_local(cls, blob_path: Union["Pathy", str], recurse: bool = True) -> Path: """Download and cache either a blob or a set of blobs matching a prefix. The cache is sensitive to the file updated time, and downloads new blobs @@ -553,7 +553,7 @@ def to_local(cls, blob_path: Union[PathType, str], recurse: bool = True) -> Path Pathy.to_local(blob, recurse=False) return cache_blob - def stat(self: PathType) -> BlobStat: # type: ignore[override] + def stat(self: "Pathy") -> BlobStat: # type: ignore[override] """Returns information about this bucket path.""" self._absolute_path_validation() if not self.key: @@ -565,7 +565,7 @@ def exists(self) -> bool: self._absolute_path_validation() return self._accessor.exists(self) - def is_dir(self: PathType) -> bool: + def is_dir(self: "Pathy") -> bool: """Determine if the path points to a bucket or a prefix of a given blob in the bucket. @@ -577,7 +577,7 @@ def is_dir(self: PathType) -> bool: return True return self._accessor.is_dir(self) - def is_file(self: PathType) -> bool: + def is_file(self: "Pathy") -> bool: """Determine if the path points to a blob in the bucket. Returns True if the path points to a blob. @@ -592,23 +592,23 @@ def is_file(self: PathType) -> bool: except (ClientError, FileNotFoundError): return False - def iterdir(self: PathType) -> Generator[PathType, None, None]: + def iterdir(self: "Pathy") -> Generator["Pathy", None, None]: """Iterate over the blobs found in the given bucket or blob prefix path.""" self._absolute_path_validation() yield from super().iterdir() # type:ignore - def glob(self: PathType, pattern: str) -> Generator[PathType, None, None]: + def glob(self: "Pathy", pattern: str) -> Generator["Pathy", None, None]: """Perform a glob match relative to this Pathy instance, yielding all matched blobs.""" yield from super().glob(pattern) # type:ignore - def rglob(self: PathType, pattern: str) -> Generator[PathType, None, None]: + def rglob(self: "Pathy", pattern: str) -> Generator["Pathy", None, None]: """Perform a recursive glob match relative to this Pathy instance, yielding all matched blobs. Imagine adding "**/" before a call to glob.""" yield from super().rglob(pattern) # type:ignore def open( - self: PathType, + self: "Pathy", mode: str = "r", buffering: int = DEFAULT_BUFFER_SIZE, encoding: Optional[str] = None, @@ -677,7 +677,7 @@ def rename(self: "Pathy", target: Union[str, PurePath]) -> None: target._absolute_path_validation() # type:ignore super().rename(target) - def replace(self: PathType, target: Union[str, PurePath]) -> None: + def replace(self: "Pathy", target: Union[str, PurePath]) -> None: """Renames this path to the given target. If target points to an existing path, it will be replaced.""" @@ -702,7 +702,7 @@ def samefile(self: "Pathy", other_path: Union[str, bytes, int, Path]) -> bool: self.bucket == other_path.bucket and self.key == self.key and self.is_file() ) - def touch(self: PathType, mode: int = 0o666, exist_ok: bool = True) -> None: + def touch(self: "Pathy", mode: int = 0o666, exist_ok: bool = True) -> None: """Create a blob at this path. If the blob already exists, the function succeeds if exist_ok is true @@ -739,61 +739,61 @@ def mkdir( if not exist_ok: raise - def is_mount(self: PathType) -> bool: + def is_mount(self: "Pathy") -> bool: return False - def is_symlink(self: PathType) -> bool: + def is_symlink(self: "Pathy") -> bool: return False - def is_socket(self: PathType) -> bool: + def is_socket(self: "Pathy") -> bool: return False - def is_fifo(self: PathType) -> bool: + def is_fifo(self: "Pathy") -> bool: return False # Unsupported operations below here @classmethod - def cwd(cls) -> PathType: + def cwd(cls) -> "Pathy": message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.cwd.__qualname__) raise NotImplementedError(message) @classmethod - def home(cls) -> PathType: + def home(cls) -> "Pathy": message = cls._NOT_SUPPORTED_MESSAGE.format(method=cls.home.__qualname__) raise NotImplementedError(message) - def chmod(self: PathType, mode: int) -> None: + def chmod(self: "Pathy", mode: int) -> None: message = self._NOT_SUPPORTED_MESSAGE.format(method=self.chmod.__qualname__) raise NotImplementedError(message) - def expanduser(self: PathType) -> PathType: + def expanduser(self: "Pathy") -> "Pathy": message = self._NOT_SUPPORTED_MESSAGE.format( method=self.expanduser.__qualname__ ) raise NotImplementedError(message) - def lchmod(self: PathType, mode: int) -> None: + def lchmod(self: "Pathy", mode: int) -> None: message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lchmod.__qualname__) raise NotImplementedError(message) - def group(self: PathType) -> str: + def group(self: "Pathy") -> str: message = self._NOT_SUPPORTED_MESSAGE.format(method=self.group.__qualname__) raise NotImplementedError(message) - def is_block_device(self: PathType) -> bool: + def is_block_device(self: "Pathy") -> bool: message = self._NOT_SUPPORTED_MESSAGE.format( method=self.is_block_device.__qualname__ ) raise NotImplementedError(message) - def is_char_device(self: PathType) -> bool: + def is_char_device(self: "Pathy") -> bool: message = self._NOT_SUPPORTED_MESSAGE.format( method=self.is_char_device.__qualname__ ) raise NotImplementedError(message) - def lstat(self: PathType) -> os.stat_result: + def lstat(self: "Pathy") -> os.stat_result: message = self._NOT_SUPPORTED_MESSAGE.format(method=self.lstat.__qualname__) raise NotImplementedError(message) From 263348028fe5c217163632d9fd002cd7f5b5c77c Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:50:32 -0700 Subject: [PATCH 44/75] feat(ci): add lint check before testing --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a3b49b2..ce48010 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ matrix: before_script: - npm install && npx ts-node tools/ci-set-build-version.ts - sh tools/setup.sh + script: + - sh tools/lint.sh script: - sh tools/test.sh after_success: From 96fa4ea1db3d6d2f4986569fb73cfffaa59f8f74 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 16:58:26 -0700 Subject: [PATCH 45/75] chore: fix travis lint invocation --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ce48010..c817c8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ matrix: - sh tools/setup.sh script: - sh tools/lint.sh - script: - sh tools/test.sh after_success: - sh tools/codecov.sh From 3b5aef3b96a05e742ed297984e4a2617f73ad0e0 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 17:03:18 -0700 Subject: [PATCH 46/75] chore: use venv when linting --- tools/lint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lint.sh b/tools/lint.sh index 0cc5370..9b43570 100644 --- a/tools/lint.sh +++ b/tools/lint.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -set -x +. .env/bin/activate mypy pathy flake8 pathy tests From c7f6d19b1b3e264014ab8c51800dd1c6e8c6aa81 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 17:28:32 -0700 Subject: [PATCH 47/75] docs: add section about semantic version to readme --- README.md | 24 +++++++++++++++++++++++- pathy/base.py | 3 +-- tools/format.sh | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1c921a9..64b63d7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# Pathy: a python Path interface for bucket storage +# Pathy: a Path interface for local and cloud bucket storage [![Build status](https://travis-ci.com/justindujardin/pathy.svg?branch=master)](https://travis-ci.com/justindujardin/pathy) [![codecov](https://codecov.io/gh/justindujardin/pathy/branch/master/graph/badge.svg)](https://codecov.io/gh/justindujardin/pathy) [![Pypi version](https://badgen.net/pypi/v/pathy)](https://pypi.org/project/pathy/) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) Pathy is a python package (_with type annotations_) for working with Bucket storage providers. It provides a CLI app for basic file operations between local files and remote buckets. It enables a smooth developer experience by supporting local file-system backed buckets during development and testing. It makes converting bucket blobs into local files a snap with optional local file caching of blobs. @@ -33,6 +34,27 @@ greeting.unlink() assert not greeting.exists() ``` +## Semantic Versioning + +Before Pathy reaches v1.0 the project is not guaranteed to have a consistent API, which means that types and classes may move around or be removed. That said, we try to be predictable when it comes to breaking changes, so the project uses semantic versioning to help users avoid breakage. + +Specifically, new releases increase the `patch` semver component for new features and fixes, and the `minor` component when there are breaking changes. If you don't know much about semver strings, they're usually formatted `{major}.{minor}.{patch}` so increasing the `patch` component means incrementing the last number. + +Consider a few examples: + +| From Version | To Version | Changes are Breaking | +| :----------: | :--------: | :------------------: | +| 0.2.0 | 0.2.1 | No | +| 0.3.2 | 0.3.6 | No | +| 0.3.1 | 0.3.17 | No | +| 0.2.2 | 0.3.0 | Yes | + +If you are concerned about breaking changes, you can pin the version in your requirements so that it does not go beyond the current semver `minor` component, for example if the current version was `0.1.37`: + +``` +pathy>=0.1.37,<0.2.0 +``` + ## 🎛 API diff --git a/pathy/base.py b/pathy/base.py index 73d5450..7051df7 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -1,10 +1,9 @@ import os from dataclasses import dataclass from io import DEFAULT_BUFFER_SIZE -from pathlib import Path # type:ignore from pathlib import _Accessor # type:ignore from pathlib import _PosixFlavour # type:ignore -from pathlib import PurePath +from pathlib import Path, PurePath from typing import ( IO, Any, diff --git a/tools/format.sh b/tools/format.sh index a1c318e..b9e08cb 100644 --- a/tools/format.sh +++ b/tools/format.sh @@ -1,5 +1,5 @@ #!/bin/sh -e -set -x +. .env/bin/activate # Sort imports one per line, so autoflake can remove unused imports isort pathy tests --force-single-line-imports From 4828045b1ccaa41cb54cec89938bacb00ca10548 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 4 Sep 2020 00:42:59 +0000 Subject: [PATCH 48/75] chore(release): 0.3.0 # [0.3.0](https://github.com/justindujardin/pathy/compare/v0.2.0...v0.3.0) (2020-09-04) ### Code Refactoring * add BasePathy class to bind PathType var to ([796dd40](https://github.com/justindujardin/pathy/commit/796dd407fca72c5297914e597f3221fdbcd9e95e)) ### Features * add get_client/register_client for supporting multiple services ([747815b](https://github.com/justindujardin/pathy/commit/747815b46e1e3cd61e6e69d04b52f5f5958ed373)) * **ci:** add lint check before testing ([2633480](https://github.com/justindujardin/pathy/commit/263348028fe5c217163632d9fd002cd7f5b5c77c)) * **GCS:** print install command when using GCS without deps installed ([d8dbcd4](https://github.com/justindujardin/pathy/commit/d8dbcd41d1e813090cad906c81df95880ae7289c)) ### BREAKING CHANGES * This renames the internal GCS/File adapter classes by removing the prefix Client. ClientBucketFS -> BucketFS ClientBlobFS -> BlobFS ClientBucketGCS -> BucketGCS ClientBlobGCS -> BlobGCS * use_fs, get_fs_client, use_fs_cache, get_fs_cache, and clear_fs_cache moved from pathy.api to pathy.clients --- CHANGELOG.md | 25 +++++++++++++++++++++++++ pathy/about.py | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bfd973..3ae2177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# [0.3.0](https://github.com/justindujardin/pathy/compare/v0.2.0...v0.3.0) (2020-09-04) + + +### Code Refactoring + +* add BasePathy class to bind PathType var to ([796dd40](https://github.com/justindujardin/pathy/commit/796dd407fca72c5297914e597f3221fdbcd9e95e)) + + +### Features + +* add get_client/register_client for supporting multiple services ([747815b](https://github.com/justindujardin/pathy/commit/747815b46e1e3cd61e6e69d04b52f5f5958ed373)) +* **ci:** add lint check before testing ([2633480](https://github.com/justindujardin/pathy/commit/263348028fe5c217163632d9fd002cd7f5b5c77c)) +* **GCS:** print install command when using GCS without deps installed ([d8dbcd4](https://github.com/justindujardin/pathy/commit/d8dbcd41d1e813090cad906c81df95880ae7289c)) + + +### BREAKING CHANGES + +* This renames the internal GCS/File adapter classes by removing the prefix Client. + +ClientBucketFS -> BucketFS +ClientBlobFS -> BlobFS +ClientBucketGCS -> BucketGCS +ClientBlobGCS -> BlobGCS +* use_fs, get_fs_client, use_fs_cache, get_fs_cache, and clear_fs_cache moved from pathy.api to pathy.clients + # [0.2.0](https://github.com/justindujardin/pathy/compare/v0.1.3...v0.2.0) (2020-08-22) diff --git a/pathy/about.py b/pathy/about.py index 4bf796e..1c6599a 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.2.0" +__version__ = "0.3.0" __summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 976374f9ab25422ea702f64117fbcf8d84e60f55 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 18:05:06 -0700 Subject: [PATCH 49/75] chore: add test for about.py to avoid failed codecov checks when releasing - if about.py changes and we don't test it, codecov is all like "wah, you didn't hit your diff targets because you went from 0% to 0% on about.py" :sweat_smile: --- tests/test_api.py | 548 ----------------------------------------- tests/test_base.py | 529 ++++++++++++++++++++++++++++++++++++++- tests/test_client.py | 54 ---- tests/test_clients.py | 79 ++++++ tests/test_registry.py | 15 -- 5 files changed, 607 insertions(+), 618 deletions(-) delete mode 100644 tests/test_api.py delete mode 100644 tests/test_client.py create mode 100644 tests/test_clients.py delete mode 100644 tests/test_registry.py diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index f2ef344..0000000 --- a/tests/test_api.py +++ /dev/null @@ -1,548 +0,0 @@ -from pathlib import Path -from time import sleep -from uuid import uuid4 - -import pytest -import spacy - -from pathy import ( - BlobStat, - BucketClientFS, - BucketsAccessor, - FluidPath, - Pathy, - PurePathy, - clear_fs_cache, - get_fs_client, - use_fs, - use_fs_cache, -) - -from .conftest import TEST_ADAPTERS - -# todo: test samefile/touch method -# todo: test open method check R/W bytes/unicode -# todo(jd): replace global test-bucket with mock or generate buckets and call these e2e tests - - -def test_api_path_support(): - assert PurePathy in Pathy.mro() # type: ignore - assert Path in Pathy.mro() # type: ignore - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_is_path_instance(with_adapter): - blob = Pathy("gs://fake/blob") - assert isinstance(blob, Path) - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_fluid(with_adapter, bucket: str): - path: FluidPath = Pathy.fluid(f"gs://{bucket}/fake-key") - assert isinstance(path, Pathy) - path: FluidPath = Pathy.fluid("foo/bar.txt") - assert isinstance(path, Path) - path: FluidPath = Pathy.fluid("/dev/null") - assert isinstance(path, Path) - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_path_to_local(with_adapter, bucket: str): - root: Pathy = Pathy.from_bucket(bucket) / "to_local" - foo_blob: Pathy = root / "foo" - foo_blob.write_text("---") - assert isinstance(foo_blob, Pathy) - use_fs_cache() - - # Cache a blob - cached: Path = Pathy.to_local(foo_blob) - second_cached: Path = Pathy.to_local(foo_blob) - assert isinstance(cached, Path) - assert cached.exists() and cached.is_file(), "local file should exist" - assert second_cached == cached, "must be the same path" - assert second_cached.stat() == cached.stat(), "must have the same stat" - - # Cache a folder hierarchy with blobs - complex_folder = root / "complex" - for i in range(3): - folder = f"folder_{i}" - for j in range(2): - gcs_blob: Pathy = complex_folder / folder / f"file_{j}.txt" - gcs_blob.write_text("---") - - cached_folder: Path = Pathy.to_local(complex_folder) - assert isinstance(cached_folder, Path) - assert cached_folder.exists() and cached_folder.is_dir() - - # Verify all the files exist in the file-system cache folder - for i in range(3): - folder = f"folder_{i}" - for j in range(2): - iter_blob: Path = cached_folder / folder / f"file_{j}.txt" - assert iter_blob.exists() - assert iter_blob.read_text() == "---" - - clear_fs_cache() - assert not cached.exists(), "cache clear should delete file" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): - path = Pathy(f"gs://{bucket}/directory/foo.txt") - path.write_text("---") - assert isinstance(path, Pathy) - with pytest.raises(ValueError): - Pathy.to_local(path) - - use_fs_cache(with_fs) - source_file: Path = Pathy.to_local(path) - foo_timestamp = Path(f"{source_file}.time") - assert foo_timestamp.exists() - orig_cache_time = foo_timestamp.read_text() - - # fetch from the local cache - cached_file: Path = Pathy.to_local(path) - assert cached_file == source_file - cached_cache_time = foo_timestamp.read_text() - assert orig_cache_time == cached_cache_time, "cached blob timestamps should match" - - # Update the blob - sleep(0.1) - path.write_text('{ "cool" : true }') - - # Fetch the updated blob - Pathy.to_local(path) - updated_cache_time = foo_timestamp.read_text() - assert updated_cache_time != orig_cache_time, "cached timestamp did not change" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_stat(with_adapter, bucket: str): - path = Pathy("fake-bucket-1234-0987/fake-key") - with pytest.raises(ValueError): - path.stat() - path = Pathy(f"gs://{bucket}/foo.txt") - path.write_text("a-a-a-a-a-a-a") - stat = path.stat() - assert isinstance(stat, BlobStat) - assert stat.size > 0 - assert stat.last_modified > 0 - with pytest.raises(ValueError): - assert Pathy(f"gs://{bucket}").stat() - with pytest.raises(FileNotFoundError): - assert Pathy(f"gs://{bucket}/nonexistant_file.txt").stat() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_resolve(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/fake-key") - assert path.resolve() == path - path = Pathy(f"gs://{bucket}/dir/../fake-key") - assert path.resolve() == Pathy(f"gs://{bucket}/fake-key") - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_exists(with_adapter, bucket: str): - path = Pathy("./fake-key") - with pytest.raises(ValueError): - path.exists() - - # GCS buckets are globally unique, "test-bucket" exists so this - # raises an access error. - assert Pathy("gs://test-bucket/fake-key").exists() is False - # invalid bucket name - assert Pathy("gs://unknown-bucket-name-123987519875419").exists() is False - # valid bucket with invalid object - assert Pathy(f"gs://{bucket}/not_found_lol_nice.txt").exists() is False - - path = Pathy(f"gs://{bucket}/directory/foo.txt") - path.write_text("---") - assert path.exists() - for parent in path.parents: - assert parent.exists() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_glob(with_adapter, bucket: str): - for i in range(3): - path = Pathy(f"gs://{bucket}/glob/{i}.file") - path.write_text("---") - for i in range(2): - path = Pathy(f"gs://{bucket}/glob/{i}/dir/file.txt") - path.write_text("---") - - assert list(Pathy(f"gs://{bucket}/glob/").glob("*.test")) == [] - assert sorted(list(Pathy(f"gs://{bucket}/glob/").glob("*.file"))) == [ - Pathy(f"gs://{bucket}/glob/0.file"), - Pathy(f"gs://{bucket}/glob/1.file"), - Pathy(f"gs://{bucket}/glob/2.file"), - ] - assert list(Pathy(f"gs://{bucket}/glob/0/").glob("*/*.txt")) == [ - Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), - ] - assert sorted(Pathy(f"gs://{bucket}").glob("*lob/")) == [ - Pathy(f"gs://{bucket}/glob"), - ] - # Recursive matches - assert sorted(list(Pathy(f"gs://{bucket}/glob/").glob("**/*.txt"))) == [ - Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), - Pathy(f"gs://{bucket}/glob/1/dir/file.txt"), - ] - # rglob adds the **/ for you - assert sorted(list(Pathy(f"gs://{bucket}/glob/").rglob("*.txt"))) == [ - Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), - Pathy(f"gs://{bucket}/glob/1/dir/file.txt"), - ] - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_unlink_path(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/unlink/404.txt") - with pytest.raises(FileNotFoundError): - path.unlink() - path = Pathy(f"gs://{bucket}/unlink/foo.txt") - path.write_text("---") - assert path.exists() - path.unlink() - assert not path.exists() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_is_dir(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/is_dir/subfolder/another/my.file") - path.write_text("---") - assert path.is_dir() is False - for parent in path.parents: - assert parent.is_dir() is True - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_is_file(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/is_file/subfolder/another/my.file") - path.write_text("---") - # The full file is a file - assert path.is_file() is True - # Each parent node in the path is only a directory - for parent in path.parents: - assert parent.is_file() is False - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_iterdir(with_adapter, bucket: str): - # (n) files in a folder - for i in range(2): - path = Pathy(f"gs://{bucket}/iterdir/{i}.file") - path.write_text("---") - - # 1 file in a subfolder - path = Pathy(f"gs://{bucket}/iterdir/sub/file.txt") - path.write_text("---") - - path = Pathy(f"gs://{bucket}/iterdir/") - check = sorted(path.iterdir()) - assert check == [ - Pathy(f"gs://{bucket}/iterdir/0.file"), - Pathy(f"gs://{bucket}/iterdir/1.file"), - Pathy(f"gs://{bucket}/iterdir/sub"), - ] - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_iterdir_pipstore(with_adapter, bucket: str): - path = Pathy.from_bucket(bucket) / "iterdir_pipstore/prodigy/prodigy.whl" - path.write_bytes(b"---") - path = Pathy.from_bucket(bucket) / "iterdir_pipstore" - res = [e.name for e in sorted(path.iterdir())] - assert res == ["prodigy"] - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_open_for_read(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/read/file.txt") - path.write_text("---") - with path.open() as file_obj: - assert file_obj.read() == "---" - assert path.read_text() == "---" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_open_for_write(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/write/file.txt") - with path.open(mode="w") as file_obj: - file_obj.write("---") - file_obj.writelines(["---"]) - path = Pathy(f"gs://{bucket}/write/file.txt") - with path.open() as file_obj: - assert file_obj.read() == "------" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_open_binary_read(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/read_binary/file.txt") - path.write_bytes(b"---") - with path.open(mode="rb") as file_obj: - assert file_obj.readlines() == [b"---"] - with path.open(mode="rb") as file_obj: - assert file_obj.readline() == b"---" - assert file_obj.readline() == b"" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_readwrite_text(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/write_text/file.txt") - path.write_text("---") - with path.open() as file_obj: - assert file_obj.read() == "---" - assert path.read_text() == "---" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_readwrite_bytes(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/write_bytes/file.txt") - path.write_bytes(b"---") - assert path.read_bytes() == b"---" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_readwrite_lines(with_adapter, bucket: str): - path = Pathy(f"gs://{bucket}/write_text/file.txt") - with path.open("w") as file_obj: - file_obj.writelines(["---"]) - with path.open("r") as file_obj: - assert file_obj.readlines() == ["---"] - with path.open("rt") as file_obj: - assert file_obj.readline() == "---" - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_owner(with_adapter, bucket: str): - # Raises for invalid file - with pytest.raises(FileNotFoundError): - Pathy(f"gs://{bucket}/write_text/not_a_valid_blob").owner() - - path = Pathy(f"gs://{bucket}/write_text/file.txt") - path.write_text("---") - # TODO: How to set file owner to non-None in GCS? Then assert here. - # - # NOTE: The owner is always set when using the filesystem adapter, so - # we can't assert the same behavior here until we fix the above - # todo comment. - try: - path.owner() - except BaseException: - pytest.fail("Should not raise") - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_rename_files_in_bucket(with_adapter, bucket: str): - # Rename a single file - Pathy(f"gs://{bucket}/rename/file.txt").write_text("---") - Pathy(f"gs://{bucket}/rename/file.txt").rename(f"gs://{bucket}/rename/other.txt") - assert not Pathy(f"gs://{bucket}/rename/file.txt").exists() - assert Pathy(f"gs://{bucket}/rename/other.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_rename_files_across_buckets(with_adapter, bucket: str, other_bucket: str): - # Rename a single file across buckets - Pathy(f"gs://{bucket}/rename/file.txt").write_text("---") - Pathy(f"gs://{bucket}/rename/file.txt").rename( - f"gs://{other_bucket}/rename/other.txt" - ) - assert not Pathy(f"gs://{bucket}/rename/file.txt").exists() - assert Pathy(f"gs://{other_bucket}/rename/other.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_rename_folders_in_bucket(with_adapter, bucket: str): - # Rename a folder in the same bucket - Pathy(f"gs://{bucket}/rename/folder/one.txt").write_text("---") - Pathy(f"gs://{bucket}/rename/folder/two.txt").write_text("---") - path = Pathy(f"gs://{bucket}/rename/folder/") - new_path = Pathy(f"gs://{bucket}/rename/other/") - path.rename(new_path) - assert not path.exists() - assert new_path.exists() - assert Pathy(f"gs://{bucket}/rename/other/one.txt").is_file() - assert Pathy(f"gs://{bucket}/rename/other/two.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_rename_folders_across_buckets( - with_adapter, bucket: str, other_bucket: str -): - # Rename a folder across buckets - Pathy(f"gs://{bucket}/rename/folder/one.txt").write_text("---") - Pathy(f"gs://{bucket}/rename/folder/two.txt").write_text("---") - path = Pathy(f"gs://{bucket}/rename/folder/") - new_path = Pathy(f"gs://{other_bucket}/rename/other/") - path.rename(new_path) - assert not path.exists() - assert new_path.exists() - assert Pathy(f"gs://{other_bucket}/rename/other/one.txt").is_file() - assert Pathy(f"gs://{other_bucket}/rename/other/two.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_replace_files_in_bucket(with_adapter, bucket: str): - # replace a single file - Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") - Pathy(f"gs://{bucket}/replace/file.txt").replace(f"gs://{bucket}/replace/other.txt") - assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() - assert Pathy(f"gs://{bucket}/replace/other.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_replace_files_across_buckets(with_adapter, bucket: str, other_bucket: str): - # Rename a single file across buckets - Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") - Pathy(f"gs://{bucket}/replace/file.txt").replace( - f"gs://{other_bucket}/replace/other.txt" - ) - assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() - assert Pathy(f"gs://{other_bucket}/replace/other.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_replace_folders_in_bucket(with_adapter, bucket: str): - # Rename a folder in the same bucket - Pathy(f"gs://{bucket}/replace/folder/one.txt").write_text("---") - Pathy(f"gs://{bucket}/replace/folder/two.txt").write_text("---") - path = Pathy(f"gs://{bucket}/replace/folder/") - new_path = Pathy(f"gs://{bucket}/replace/other/") - path.replace(new_path) - assert not path.exists() - assert new_path.exists() - assert Pathy(f"gs://{bucket}/replace/other/one.txt").is_file() - assert Pathy(f"gs://{bucket}/replace/other/two.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_replace_folders_across_buckets( - with_adapter, bucket: str, other_bucket: str -): - # Rename a folder across buckets - Pathy(f"gs://{bucket}/replace/folder/one.txt").write_text("---") - Pathy(f"gs://{bucket}/replace/folder/two.txt").write_text("---") - path = Pathy(f"gs://{bucket}/replace/folder/") - new_path = Pathy(f"gs://{other_bucket}/replace/other/") - path.replace(new_path) - assert not path.exists() - assert new_path.exists() - assert Pathy(f"gs://{other_bucket}/replace/other/one.txt").is_file() - assert Pathy(f"gs://{other_bucket}/replace/other/two.txt").is_file() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_rmdir(with_adapter, bucket: str): - Pathy(f"gs://{bucket}/rmdir/one.txt").write_text("---") - Pathy(f"gs://{bucket}/rmdir/folder/two.txt").write_text("---") - path = Pathy(f"gs://{bucket}/rmdir/") - path.rmdir() - assert not Pathy(f"gs://{bucket}/rmdir/one.txt").is_file() - assert not Pathy(f"gs://{bucket}/rmdir/other/two.txt").is_file() - assert not path.exists() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_mkdir(with_adapter, bucket: str): - bucket_name = f"pathy-e2e-test-{uuid4().hex}" - # Create a bucket - path = Pathy(f"gs://{bucket_name}/") - path.mkdir() - assert path.exists() - # Does not assert if it already exists - path.mkdir(exist_ok=True) - with pytest.raises(FileExistsError): - path.mkdir(exist_ok=False) - # with pytest.raises(FileNotFoundError): - # Pathy("/test-second-bucket/test-directory/file.name").mkdir() - # Pathy("/test-second-bucket/test-directory/file.name").mkdir(parents=True) - assert path.exists() - # remove the bucket - # client = storage.Client() - # bucket = client.lookup_bucket(bucket_name) - # bucket.delete() - path.rmdir() - assert not path.exists() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_list_buckets(with_adapter): - bucket_name = f"pathy-e2e-test-{uuid4().hex}" - bucket = Pathy(f"gs://{bucket_name}/") - bucket.mkdir() - assert bucket.exists() - - Pathy("").glob("*") - # Can enumerate the buckets (including the one we just created) - - bucket.rmdir() - assert not bucket.exists() - - -@pytest.mark.parametrize("adapter", TEST_ADAPTERS) -def test_api_ignore_extension(with_adapter, bucket: str): - """The smart_open library does automatic decompression based - on the filename. We disable that to avoid errors, e.g. if you - have a .tar.gz file that isn't gzipped.""" - not_targz = Pathy.from_bucket(bucket) / "ignore_ext/one.tar.gz" - fixture_tar = Path(__file__).parent / "fixtures" / "tar_but_not_gzipped.tar.gz" - not_targz.write_bytes(fixture_tar.read_bytes()) - again = not_targz.read_bytes() - assert again is not None - - -def test_api_use_fs(with_fs: Path): - assert get_fs_client() is None - # Use the default path - use_fs() - client = get_fs_client() - assert isinstance(client, BucketClientFS) - assert client.root.exists() - - # Use with False disables the client - use_fs(False) - client = get_fs_client() - assert client is None - - # Can use a given path - use_fs(str(with_fs)) - client = get_fs_client() - assert isinstance(client, BucketClientFS) - assert client.root == with_fs - - # Can use a pathlib.Path - use_fs(with_fs) - client = get_fs_client() - assert isinstance(client, BucketClientFS) - assert client.root == with_fs - - use_fs(False) - - -def test_api_raises_with_no_known_bucket_clients_for_a_scheme(temp_folder): - accessor = BucketsAccessor() - path = Pathy("foo://foo") - with pytest.raises(ValueError): - accessor.client(path) - # Setting a fallback FS adapter fixes the problem - use_fs(str(temp_folder)) - assert isinstance(accessor.client(path), BucketClientFS) - - -def test_api_export_spacy_model(temp_folder): - """spaCy model loading is one of the things we need to support""" - use_fs(temp_folder) - bucket = Pathy("gs://my-bucket/") - bucket.mkdir(exist_ok=True) - model = spacy.blank("en") - output_path = Pathy("gs://my-bucket/models/my_model") - model.to_disk(output_path) - sorted_entries = sorted([str(p) for p in output_path.glob("*")]) - expected_entries = [ - "gs://my-bucket/models/my_model/meta.json", - "gs://my-bucket/models/my_model/tokenizer", - "gs://my-bucket/models/my_model/vocab", - ] - assert sorted_entries == expected_entries diff --git a/tests/test_base.py b/tests/test_base.py index 0460a11..23b9981 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,10 +1,38 @@ +from pathlib import Path + +import pytest + +from pathy import Blob, Bucket, BucketClient, BucketClientFS, Pathy, PurePathy + import os import sys from pathlib import Path, PurePosixPath, PureWindowsPath +from uuid import uuid4 import pytest +import spacy + +from pathy import ( + BlobStat, + BucketClientFS, + BucketsAccessor, + FluidPath, + Pathy, + PurePathy, + clear_fs_cache, + use_fs, + use_fs_cache, +) +from pathy.about import __version__ +from .conftest import TEST_ADAPTERS -from pathy import Pathy, PurePathy +# todo: test samefile/touch method +# todo: test open method check R/W bytes/unicode +# todo(jd): replace global test-bucket with mock or generate buckets and call these e2e tests + + +def test_base_package_declares_version(): + assert __version__ is not None def test_base_not_supported(monkeypatch): @@ -280,3 +308,502 @@ def test_base_with_suffix(): assert gcs_path.with_suffix(".txt") == PurePathy("README.txt") gcs_path = PurePathy("README.txt") assert gcs_path.with_suffix("") == PurePathy("README") + + +def test_api_path_support(): + assert PurePathy in Pathy.mro() # type: ignore + assert Path in Pathy.mro() # type: ignore + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_is_path_instance(with_adapter): + blob = Pathy("gs://fake/blob") + assert isinstance(blob, Path) + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_fluid(with_adapter, bucket: str): + path: FluidPath = Pathy.fluid(f"gs://{bucket}/fake-key") + assert isinstance(path, Pathy) + path: FluidPath = Pathy.fluid("foo/bar.txt") + assert isinstance(path, Path) + path: FluidPath = Pathy.fluid("/dev/null") + assert isinstance(path, Path) + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_path_to_local(with_adapter, bucket: str): + root: Pathy = Pathy.from_bucket(bucket) / "to_local" + foo_blob: Pathy = root / "foo" + foo_blob.write_text("---") + assert isinstance(foo_blob, Pathy) + use_fs_cache() + + # Cache a blob + cached: Path = Pathy.to_local(foo_blob) + second_cached: Path = Pathy.to_local(foo_blob) + assert isinstance(cached, Path) + assert cached.exists() and cached.is_file(), "local file should exist" + assert second_cached == cached, "must be the same path" + assert second_cached.stat() == cached.stat(), "must have the same stat" + + # Cache a folder hierarchy with blobs + complex_folder = root / "complex" + for i in range(3): + folder = f"folder_{i}" + for j in range(2): + gcs_blob: Pathy = complex_folder / folder / f"file_{j}.txt" + gcs_blob.write_text("---") + + cached_folder: Path = Pathy.to_local(complex_folder) + assert isinstance(cached_folder, Path) + assert cached_folder.exists() and cached_folder.is_dir() + + # Verify all the files exist in the file-system cache folder + for i in range(3): + folder = f"folder_{i}" + for j in range(2): + iter_blob: Path = cached_folder / folder / f"file_{j}.txt" + assert iter_blob.exists() + assert iter_blob.read_text() == "---" + + clear_fs_cache() + assert not cached.exists(), "cache clear should delete file" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_stat(with_adapter, bucket: str): + path = Pathy("fake-bucket-1234-0987/fake-key") + with pytest.raises(ValueError): + path.stat() + path = Pathy(f"gs://{bucket}/foo.txt") + path.write_text("a-a-a-a-a-a-a") + stat = path.stat() + assert isinstance(stat, BlobStat) + assert stat.size > 0 + assert stat.last_modified > 0 + with pytest.raises(ValueError): + assert Pathy(f"gs://{bucket}").stat() + with pytest.raises(FileNotFoundError): + assert Pathy(f"gs://{bucket}/nonexistant_file.txt").stat() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_resolve(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/fake-key") + assert path.resolve() == path + path = Pathy(f"gs://{bucket}/dir/../fake-key") + assert path.resolve() == Pathy(f"gs://{bucket}/fake-key") + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_exists(with_adapter, bucket: str): + path = Pathy("./fake-key") + with pytest.raises(ValueError): + path.exists() + + # GCS buckets are globally unique, "test-bucket" exists so this + # raises an access error. + assert Pathy("gs://test-bucket/fake-key").exists() is False + # invalid bucket name + assert Pathy("gs://unknown-bucket-name-123987519875419").exists() is False + # valid bucket with invalid object + assert Pathy(f"gs://{bucket}/not_found_lol_nice.txt").exists() is False + + path = Pathy(f"gs://{bucket}/directory/foo.txt") + path.write_text("---") + assert path.exists() + for parent in path.parents: + assert parent.exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_glob(with_adapter, bucket: str): + for i in range(3): + path = Pathy(f"gs://{bucket}/glob/{i}.file") + path.write_text("---") + for i in range(2): + path = Pathy(f"gs://{bucket}/glob/{i}/dir/file.txt") + path.write_text("---") + + assert list(Pathy(f"gs://{bucket}/glob/").glob("*.test")) == [] + assert sorted(list(Pathy(f"gs://{bucket}/glob/").glob("*.file"))) == [ + Pathy(f"gs://{bucket}/glob/0.file"), + Pathy(f"gs://{bucket}/glob/1.file"), + Pathy(f"gs://{bucket}/glob/2.file"), + ] + assert list(Pathy(f"gs://{bucket}/glob/0/").glob("*/*.txt")) == [ + Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), + ] + assert sorted(Pathy(f"gs://{bucket}").glob("*lob/")) == [ + Pathy(f"gs://{bucket}/glob"), + ] + # Recursive matches + assert sorted(list(Pathy(f"gs://{bucket}/glob/").glob("**/*.txt"))) == [ + Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), + Pathy(f"gs://{bucket}/glob/1/dir/file.txt"), + ] + # rglob adds the **/ for you + assert sorted(list(Pathy(f"gs://{bucket}/glob/").rglob("*.txt"))) == [ + Pathy(f"gs://{bucket}/glob/0/dir/file.txt"), + Pathy(f"gs://{bucket}/glob/1/dir/file.txt"), + ] + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_unlink_path(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/unlink/404.txt") + with pytest.raises(FileNotFoundError): + path.unlink() + path = Pathy(f"gs://{bucket}/unlink/foo.txt") + path.write_text("---") + assert path.exists() + path.unlink() + assert not path.exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_is_dir(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/is_dir/subfolder/another/my.file") + path.write_text("---") + assert path.is_dir() is False + for parent in path.parents: + assert parent.is_dir() is True + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_is_file(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/is_file/subfolder/another/my.file") + path.write_text("---") + # The full file is a file + assert path.is_file() is True + # Each parent node in the path is only a directory + for parent in path.parents: + assert parent.is_file() is False + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_iterdir(with_adapter, bucket: str): + # (n) files in a folder + for i in range(2): + path = Pathy(f"gs://{bucket}/iterdir/{i}.file") + path.write_text("---") + + # 1 file in a subfolder + path = Pathy(f"gs://{bucket}/iterdir/sub/file.txt") + path.write_text("---") + + path = Pathy(f"gs://{bucket}/iterdir/") + check = sorted(path.iterdir()) + assert check == [ + Pathy(f"gs://{bucket}/iterdir/0.file"), + Pathy(f"gs://{bucket}/iterdir/1.file"), + Pathy(f"gs://{bucket}/iterdir/sub"), + ] + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_iterdir_pipstore(with_adapter, bucket: str): + path = Pathy.from_bucket(bucket) / "iterdir_pipstore/prodigy/prodigy.whl" + path.write_bytes(b"---") + path = Pathy.from_bucket(bucket) / "iterdir_pipstore" + res = [e.name for e in sorted(path.iterdir())] + assert res == ["prodigy"] + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_open_for_read(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/read/file.txt") + path.write_text("---") + with path.open() as file_obj: + assert file_obj.read() == "---" + assert path.read_text() == "---" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_open_for_write(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/write/file.txt") + with path.open(mode="w") as file_obj: + file_obj.write("---") + file_obj.writelines(["---"]) + path = Pathy(f"gs://{bucket}/write/file.txt") + with path.open() as file_obj: + assert file_obj.read() == "------" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_open_binary_read(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/read_binary/file.txt") + path.write_bytes(b"---") + with path.open(mode="rb") as file_obj: + assert file_obj.readlines() == [b"---"] + with path.open(mode="rb") as file_obj: + assert file_obj.readline() == b"---" + assert file_obj.readline() == b"" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_readwrite_text(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/write_text/file.txt") + path.write_text("---") + with path.open() as file_obj: + assert file_obj.read() == "---" + assert path.read_text() == "---" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_readwrite_bytes(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/write_bytes/file.txt") + path.write_bytes(b"---") + assert path.read_bytes() == b"---" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_readwrite_lines(with_adapter, bucket: str): + path = Pathy(f"gs://{bucket}/write_text/file.txt") + with path.open("w") as file_obj: + file_obj.writelines(["---"]) + with path.open("r") as file_obj: + assert file_obj.readlines() == ["---"] + with path.open("rt") as file_obj: + assert file_obj.readline() == "---" + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_owner(with_adapter, bucket: str): + # Raises for invalid file + with pytest.raises(FileNotFoundError): + Pathy(f"gs://{bucket}/write_text/not_a_valid_blob").owner() + + path = Pathy(f"gs://{bucket}/write_text/file.txt") + path.write_text("---") + # TODO: How to set file owner to non-None in GCS? Then assert here. + # + # NOTE: The owner is always set when using the filesystem adapter, so + # we can't assert the same behavior here until we fix the above + # todo comment. + path.owner() + # dumb assert means we didn't raise + assert True + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_rename_files_in_bucket(with_adapter, bucket: str): + # Rename a single file + Pathy(f"gs://{bucket}/rename/file.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/file.txt").rename(f"gs://{bucket}/rename/other.txt") + assert not Pathy(f"gs://{bucket}/rename/file.txt").exists() + assert Pathy(f"gs://{bucket}/rename/other.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_rename_files_across_buckets(with_adapter, bucket: str, other_bucket: str): + # Rename a single file across buckets + Pathy(f"gs://{bucket}/rename/file.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/file.txt").rename( + f"gs://{other_bucket}/rename/other.txt" + ) + assert not Pathy(f"gs://{bucket}/rename/file.txt").exists() + assert Pathy(f"gs://{other_bucket}/rename/other.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_rename_folders_in_bucket(with_adapter, bucket: str): + # Rename a folder in the same bucket + Pathy(f"gs://{bucket}/rename/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/rename/folder/") + new_path = Pathy(f"gs://{bucket}/rename/other/") + path.rename(new_path) + assert not path.exists() + assert new_path.exists() + assert Pathy(f"gs://{bucket}/rename/other/one.txt").is_file() + assert Pathy(f"gs://{bucket}/rename/other/two.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_rename_folders_across_buckets( + with_adapter, bucket: str, other_bucket: str +): + # Rename a folder across buckets + Pathy(f"gs://{bucket}/rename/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/rename/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/rename/folder/") + new_path = Pathy(f"gs://{other_bucket}/rename/other/") + path.rename(new_path) + assert not path.exists() + assert new_path.exists() + assert Pathy(f"gs://{other_bucket}/rename/other/one.txt").is_file() + assert Pathy(f"gs://{other_bucket}/rename/other/two.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_replace_files_in_bucket(with_adapter, bucket: str): + # replace a single file + Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/file.txt").replace(f"gs://{bucket}/replace/other.txt") + assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() + assert Pathy(f"gs://{bucket}/replace/other.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_replace_files_across_buckets(with_adapter, bucket: str, other_bucket: str): + # Rename a single file across buckets + Pathy(f"gs://{bucket}/replace/file.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/file.txt").replace( + f"gs://{other_bucket}/replace/other.txt" + ) + assert not Pathy(f"gs://{bucket}/replace/file.txt").exists() + assert Pathy(f"gs://{other_bucket}/replace/other.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_replace_folders_in_bucket(with_adapter, bucket: str): + # Rename a folder in the same bucket + Pathy(f"gs://{bucket}/replace/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/replace/folder/") + new_path = Pathy(f"gs://{bucket}/replace/other/") + path.replace(new_path) + assert not path.exists() + assert new_path.exists() + assert Pathy(f"gs://{bucket}/replace/other/one.txt").is_file() + assert Pathy(f"gs://{bucket}/replace/other/two.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_replace_folders_across_buckets( + with_adapter, bucket: str, other_bucket: str +): + # Rename a folder across buckets + Pathy(f"gs://{bucket}/replace/folder/one.txt").write_text("---") + Pathy(f"gs://{bucket}/replace/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/replace/folder/") + new_path = Pathy(f"gs://{other_bucket}/replace/other/") + path.replace(new_path) + assert not path.exists() + assert new_path.exists() + assert Pathy(f"gs://{other_bucket}/replace/other/one.txt").is_file() + assert Pathy(f"gs://{other_bucket}/replace/other/two.txt").is_file() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_rmdir(with_adapter, bucket: str): + Pathy(f"gs://{bucket}/rmdir/one.txt").write_text("---") + Pathy(f"gs://{bucket}/rmdir/folder/two.txt").write_text("---") + path = Pathy(f"gs://{bucket}/rmdir/") + path.rmdir() + assert not Pathy(f"gs://{bucket}/rmdir/one.txt").is_file() + assert not Pathy(f"gs://{bucket}/rmdir/other/two.txt").is_file() + assert not path.exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_mkdir(with_adapter, bucket: str): + bucket_name = f"pathy-e2e-test-{uuid4().hex}" + # Create a bucket + path = Pathy(f"gs://{bucket_name}/") + path.mkdir() + assert path.exists() + # Does not assert if it already exists + path.mkdir(exist_ok=True) + with pytest.raises(FileExistsError): + path.mkdir(exist_ok=False) + # with pytest.raises(FileNotFoundError): + # Pathy("/test-second-bucket/test-directory/file.name").mkdir() + # Pathy("/test-second-bucket/test-directory/file.name").mkdir(parents=True) + assert path.exists() + # remove the bucket + # client = storage.Client() + # bucket = client.lookup_bucket(bucket_name) + # bucket.delete() + path.rmdir() + assert not path.exists() + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_ignore_extension(with_adapter, bucket: str): + """The smart_open library does automatic decompression based + on the filename. We disable that to avoid errors, e.g. if you + have a .tar.gz file that isn't gzipped.""" + not_targz = Pathy.from_bucket(bucket) / "ignore_ext/one.tar.gz" + fixture_tar = Path(__file__).parent / "fixtures" / "tar_but_not_gzipped.tar.gz" + not_targz.write_bytes(fixture_tar.read_bytes()) + again = not_targz.read_bytes() + assert again is not None + + +def test_api_raises_with_no_known_bucket_clients_for_a_scheme(temp_folder): + accessor = BucketsAccessor() + path = Pathy("foo://foo") + with pytest.raises(ValueError): + accessor.client(path) + # Setting a fallback FS adapter fixes the problem + use_fs(str(temp_folder)) + assert isinstance(accessor.client(path), BucketClientFS) + + +def test_api_export_spacy_model(temp_folder): + """spaCy model loading is one of the things we need to support""" + use_fs(temp_folder) + bucket = Pathy("gs://my-bucket/") + bucket.mkdir(exist_ok=True) + model = spacy.blank("en") + output_path = Pathy("gs://my-bucket/models/my_model") + model.to_disk(output_path) + sorted_entries = sorted([str(p) for p in output_path.glob("*")]) + expected_entries = [ + "gs://my-bucket/models/my_model/meta.json", + "gs://my-bucket/models/my_model/tokenizer", + "gs://my-bucket/models/my_model/vocab", + ] + assert sorted_entries == expected_entries + + +def test_client_create_bucket(temp_folder: Path): + bucket_target = temp_folder / "foo" + assert bucket_target.exists() is False + cl = BucketClientFS(temp_folder) + cl.create_bucket(PurePathy("gs://foo/")) + assert bucket_target.exists() is True + + +def test_client_base_bucket_raises_not_implemented(): + bucket = Bucket() + blob = Blob(bucket, "foo", -1, -1, None, None) + with pytest.raises(NotImplementedError): + bucket.copy_blob(blob, bucket, "baz") + with pytest.raises(NotImplementedError): + bucket.get_blob("baz") + with pytest.raises(NotImplementedError): + bucket.delete_blobs([blob]) + with pytest.raises(NotImplementedError): + bucket.delete_blob(blob) + with pytest.raises(NotImplementedError): + bucket.exists() + + +def test_client_base_blob_raises_not_implemented(): + blob = Blob(Bucket(), "foo", -1, -1, None, None) + with pytest.raises(NotImplementedError): + blob.delete() + with pytest.raises(NotImplementedError): + blob.exists() + + +def test_client_base_bucket_client_raises_not_implemented(): + client = BucketClient() + with pytest.raises(NotImplementedError): + client.lookup_bucket(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.get_bucket(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.list_buckets() + with pytest.raises(NotImplementedError): + client.list_blobs(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.scandir(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.create_bucket(Pathy("gs://foo")) + with pytest.raises(NotImplementedError): + client.delete_bucket(Pathy("gs://foo")) diff --git a/tests/test_client.py b/tests/test_client.py deleted file mode 100644 index b54fc15..0000000 --- a/tests/test_client.py +++ /dev/null @@ -1,54 +0,0 @@ -from pathlib import Path - -import pytest - -from pathy import Blob, Bucket, BucketClient, BucketClientFS, Pathy, PurePathy - - -def test_client_create_bucket(temp_folder: Path): - bucket_target = temp_folder / "foo" - assert bucket_target.exists() is False - cl = BucketClientFS(temp_folder) - cl.create_bucket(PurePathy("gs://foo/")) - assert bucket_target.exists() is True - - -def test_client_base_bucket_raises_not_implemented(): - bucket = Bucket() - blob = Blob(bucket, "foo", -1, -1, None, None) - with pytest.raises(NotImplementedError): - bucket.copy_blob(blob, bucket, "baz") - with pytest.raises(NotImplementedError): - bucket.get_blob("baz") - with pytest.raises(NotImplementedError): - bucket.delete_blobs([blob]) - with pytest.raises(NotImplementedError): - bucket.delete_blob(blob) - with pytest.raises(NotImplementedError): - bucket.exists() - - -def test_client_base_blob_raises_not_implemented(): - blob = Blob(Bucket(), "foo", -1, -1, None, None) - with pytest.raises(NotImplementedError): - blob.delete() - with pytest.raises(NotImplementedError): - blob.exists() - - -def test_client_base_bucket_client_raises_not_implemented(): - client = BucketClient() - with pytest.raises(NotImplementedError): - client.lookup_bucket(Pathy("gs://foo")) - with pytest.raises(NotImplementedError): - client.get_bucket(Pathy("gs://foo")) - with pytest.raises(NotImplementedError): - client.list_buckets() - with pytest.raises(NotImplementedError): - client.list_blobs(Pathy("gs://foo")) - with pytest.raises(NotImplementedError): - client.scandir(Pathy("gs://foo")) - with pytest.raises(NotImplementedError): - client.create_bucket(Pathy("gs://foo")) - with pytest.raises(NotImplementedError): - client.delete_bucket(Pathy("gs://foo")) diff --git a/tests/test_clients.py b/tests/test_clients.py new file mode 100644 index 0000000..972b97b --- /dev/null +++ b/tests/test_clients.py @@ -0,0 +1,79 @@ +import time +from pathlib import Path + +import pytest + +from pathy import Pathy, get_client +from pathy.clients import get_fs_client, use_fs, use_fs_cache +from pathy.file import BucketClientFS +from pathy.gcs import BucketClientGCS + +from .conftest import TEST_ADAPTERS + + +def test_clients_get_client_works_with_builtin_schems(): + assert isinstance(get_client("gs"), BucketClientGCS) + assert isinstance(get_client("file"), BucketClientFS) + + +def test_clients_get_client_errors_with_unknown_scheme(): + with pytest.raises(ValueError): + get_client("foo") + + +def test_clients_use_fs(with_fs: Path): + assert get_fs_client() is None + # Use the default path + use_fs() + client = get_fs_client() + assert isinstance(client, BucketClientFS) + assert client.root.exists() + + # Use with False disables the client + use_fs(False) + client = get_fs_client() + assert client is None + + # Can use a given path + use_fs(str(with_fs)) + client = get_fs_client() + assert isinstance(client, BucketClientFS) + assert client.root == with_fs + + # Can use a pathlib.Path + use_fs(with_fs) + client = get_fs_client() + assert isinstance(client, BucketClientFS) + assert client.root == with_fs + + use_fs(False) + + +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): + path = Pathy(f"gs://{bucket}/directory/foo.txt") + path.write_text("---") + assert isinstance(path, Pathy) + with pytest.raises(ValueError): + Pathy.to_local(path) + + use_fs_cache(with_fs) + source_file: Path = Pathy.to_local(path) + foo_timestamp = Path(f"{source_file}.time") + assert foo_timestamp.exists() + orig_cache_time = foo_timestamp.read_text() + + # fetch from the local cache + cached_file: Path = Pathy.to_local(path) + assert cached_file == source_file + cached_cache_time = foo_timestamp.read_text() + assert orig_cache_time == cached_cache_time, "cached blob timestamps should match" + + # Update the blob + time.sleep(0.1) + path.write_text('{ "cool" : true }') + + # Fetch the updated blob + Pathy.to_local(path) + updated_cache_time = foo_timestamp.read_text() + assert updated_cache_time != orig_cache_time, "cached timestamp did not change" diff --git a/tests/test_registry.py b/tests/test_registry.py deleted file mode 100644 index 0e676f5..0000000 --- a/tests/test_registry.py +++ /dev/null @@ -1,15 +0,0 @@ -import pytest - -from pathy import get_client -from pathy.file import BucketClientFS -from pathy.gcs import BucketClientGCS - - -def test_registry_get_client_works_with_builtin_schems(): - assert isinstance(get_client("gs"), BucketClientGCS) - assert isinstance(get_client("file"), BucketClientFS) - - -def test_registry_get_client_errors_with_unknown_scheme(): - with pytest.raises(ValueError): - get_client("foo") From 2453f067484a77a35d79e11657482aa2213a7b8a Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 18:09:25 -0700 Subject: [PATCH 50/75] chore: lint --- tests/test_base.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_base.py b/tests/test_base.py index 23b9981..7ddfa4f 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,9 +1,3 @@ -from pathlib import Path - -import pytest - -from pathy import Blob, Bucket, BucketClient, BucketClientFS, Pathy, PurePathy - import os import sys from pathlib import Path, PurePosixPath, PureWindowsPath @@ -13,7 +7,10 @@ import spacy from pathy import ( + Blob, BlobStat, + Bucket, + BucketClient, BucketClientFS, BucketsAccessor, FluidPath, @@ -24,6 +21,7 @@ use_fs_cache, ) from pathy.about import __version__ + from .conftest import TEST_ADAPTERS # todo: test samefile/touch method From 0d46e72571df3925c601f92579c343e4db4f437e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 17:01:45 +0000 Subject: [PATCH 51/75] chore(deps): bump node-fetch from 2.6.0 to 2.6.1 Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1. - [Release notes](https://github.com/bitinn/node-fetch/releases) - [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bcbfeb0..757a146 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2197,9 +2197,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "normalize-package-data": { From 10ce34d13ddc99232b3ce7681db27f561d51c87b Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 18:35:40 -0700 Subject: [PATCH 52/75] feat(ci): add pyright check to lint step --- .travis.yml | 4 +- package-lock.json | 14313 +++++++++++++++++++++++--------------------- package.json | 6 +- tools/lint.sh | 1 + 4 files changed, 7337 insertions(+), 6987 deletions(-) diff --git a/.travis.yml b/.travis.yml index c817c8a..b35ed8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ matrix: - language: python python: 3.7 before_install: - - nvm install 10.18 - - nvm use 10.18 + - nvm install 12 + - nvm use 12 before_script: - npm install && npx ts-node tools/ci-set-build-version.ts - sh tools/setup.sh diff --git a/package-lock.json b/package-lock.json index 757a146..ea56e11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7124 +1,7471 @@ { - "name": "pathy", - "version": "0.0.0-development", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.8.3" + "name": "pathy", + "version": "0.0.0-development", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/runtime": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", + "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0" + } + }, + "@octokit/core": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-2.4.3.tgz", + "integrity": "sha512-9T91nYeBB7+PNK3oxOuA+6DXCPRvhJ80ke+NqhXirBjVtNepTKFJXoWPqguRSBQ+dkEVA8dZJMxfFzjz9yhiuA==", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.0", + "@octokit/graphql": "^4.3.1", + "@octokit/request": "^5.3.1", + "@octokit/types": "^2.0.0", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^5.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.0.tgz", + "integrity": "sha512-3nx+MEYoZeD0uJ+7F/gvELLvQJzLXhep2Az0bBSXagbApDvDW0LWwpnAIY/hb0Jwe17A0fJdz0O12dPh05cj7A==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0", + "is-plain-object": "^3.0.0", + "universal-user-agent": "^5.0.0" + } + }, + "@octokit/graphql": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz", + "integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==", + "dev": true, + "requires": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^2.0.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", + "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==", + "dev": true, + "requires": { + "os-name": "^3.1.0" + } + } + } + }, + "@octokit/plugin-paginate-rest": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.0.2.tgz", + "integrity": "sha512-HzODcSUt9mjErly26TlTOGZrhf9bmF/FEDQ2zln1izhgmIV6ulsjsHmgmR4VZ0wzVr/m52Eb6U2XuyS8fkcR1A==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.1" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", + "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==", + "dev": true + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-3.4.0.tgz", + "integrity": "sha512-Tvctk0u5SVrSLAzi8SLo0KrLSBl5biAHBgWy3L65vsbO/2fjzr62HVkoDPyr+WRT+eHhhqpKAERF3dQWOIUOvQ==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.1", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.4.tgz", + "integrity": "sha512-qyj8G8BxQyXjt9Xu6NvfvOr1E0l35lsXtwm3SopsYg/JWXjlsnwqLc8rsD2OLguEL/JjLfBvrXr4az7z8Lch2A==", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.0", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^5.0.0" + } + }, + "@octokit/request-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz", + "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "17.1.4", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-17.1.4.tgz", + "integrity": "sha512-LGghhepxoXyvi7ew0OdedrlwXQog8gvTbcdXoQ6RDKnzoYW2rBpcqeWC4fTuPUp9K0UEykcMix8kFnQ5b+64JQ==", + "dev": true, + "requires": { + "@octokit/core": "^2.4.3", + "@octokit/plugin-paginate-rest": "^2.0.0", + "@octokit/plugin-request-log": "^1.0.0", + "@octokit/plugin-rest-endpoint-methods": "3.4.0" + } + }, + "@octokit/types": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.5.1.tgz", + "integrity": "sha512-q4Wr7RexkPRrkQpXzUYF5Fj/14Mr65RyOHj6B9d/sQACpqGcStkHZj4qMEtlMY5SnD/69jlL9ItGPbDM0dR/dA==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + }, + "@semantic-release/changelog": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-5.0.0.tgz", + "integrity": "sha512-A1uKqWtQG4WX9Vh4QI5b2ddhqx1qAJFlbow8szSNiXn+TaJg15LSUA9NVqyu0VxQFy3hKUJYwbBHGRXCxCy2fg==", + "dev": true, + "requires": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^8.0.0", + "lodash": "^4.17.4" + } + }, + "@semantic-release/commit-analyzer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-8.0.1.tgz", + "integrity": "sha512-5bJma/oB7B4MtwUkZC2Bf7O1MHfi4gWe4mA+MIQ3lsEV0b422Bvl1z5HRpplDnMLHH3EXMoRdEng6Ds5wUqA3A==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.7", + "debug": "^4.0.0", + "import-from": "^3.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + } + }, + "@semantic-release/error": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", + "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==", + "dev": true + }, + "@semantic-release/git": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-9.0.0.tgz", + "integrity": "sha512-AZ4Zha5NAPAciIJH3ipzw/WU9qLAn8ENaoVAhD6srRPxTpTzuV3NhNh14rcAo8Paj9dO+5u4rTKcpetOBluYVw==", + "dev": true, + "requires": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + } + }, + "@semantic-release/github": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-7.0.5.tgz", + "integrity": "sha512-1nJCMeomspRIXKiFO3VXtkUMbIBEreYLFNBdWoLjvlUNcEK0/pEbupEZJA3XHfJuSzv43u3OLpPhF/JBrMuv+A==", + "dev": true, + "requires": { + "@octokit/rest": "^17.0.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "bottleneck": "^2.18.1", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^9.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^2.4.3", + "p-filter": "^2.0.0", + "p-retry": "^4.0.0", + "url-join": "^4.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "@semantic-release/npm": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.5.tgz", + "integrity": "sha512-D+oEmsx9aHE1q806NFQwSC9KdBO8ri/VO99eEz0wWbX2jyLqVyWr7t0IjKC8aSnkkQswg/4KN/ZjfF6iz1XOpw==", + "dev": true, + "requires": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "execa": "^4.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^5.0.0", + "npm": "^6.10.3", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^4.0.0", + "semver": "^7.1.2", + "tempy": "^0.5.0" + }, + "dependencies": { + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "@semantic-release/release-notes-generator": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-9.0.1.tgz", + "integrity": "sha512-bOoTiH6SiiR0x2uywSNR7uZcRDl22IpZhj+Q5Bn0v+98MFtOMhCxFhbrKQjhbYoZw7vps1mvMRmFkp/g6R9cvQ==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^4.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "debug": "^4.0.0", + "get-stream": "^5.0.0", + "import-from": "^3.0.0", + "into-stream": "^5.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + } + }, + "@tootallnate/once": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", + "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==", + "dev": true + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, + "@types/node": { + "version": "13.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", + "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "agent-base": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", + "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", + "dev": true + }, + "bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", + "dev": true, + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "dev": true, + "requires": { + "colors": "1.0.3" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "meow": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", + "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + } } }, - "@babel/runtime": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", - "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true }, - "@octokit/auth-token": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", - "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.0" - } + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true }, - "@octokit/core": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-2.4.3.tgz", - "integrity": "sha512-9T91nYeBB7+PNK3oxOuA+6DXCPRvhJ80ke+NqhXirBjVtNepTKFJXoWPqguRSBQ+dkEVA8dZJMxfFzjz9yhiuA==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/graphql": "^4.3.1", - "@octokit/request": "^5.3.1", - "@octokit/types": "^2.0.0", - "before-after-hook": "^2.1.0", - "universal-user-agent": "^5.0.0" - } + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true }, - "@octokit/endpoint": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.0.tgz", - "integrity": "sha512-3nx+MEYoZeD0uJ+7F/gvELLvQJzLXhep2Az0bBSXagbApDvDW0LWwpnAIY/hb0Jwe17A0fJdz0O12dPh05cj7A==", + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, "requires": { - "@octokit/types": "^2.0.0", - "is-plain-object": "^3.0.0", - "universal-user-agent": "^5.0.0" + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" } }, - "@octokit/graphql": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz", - "integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==", - "dev": true, - "requires": { - "@octokit/request": "^5.3.0", - "@octokit/types": "^2.0.0", - "universal-user-agent": "^4.0.0" - }, - "dependencies": { - "universal-user-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", - "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==", - "dev": true, - "requires": { - "os-name": "^3.1.0" - } - } - } + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true }, - "@octokit/plugin-paginate-rest": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.0.2.tgz", - "integrity": "sha512-HzODcSUt9mjErly26TlTOGZrhf9bmF/FEDQ2zln1izhgmIV6ulsjsHmgmR4VZ0wzVr/m52Eb6U2XuyS8fkcR1A==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.1" - } + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, - "@octokit/plugin-request-log": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", - "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==", + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-3.4.0.tgz", - "integrity": "sha512-Tvctk0u5SVrSLAzi8SLo0KrLSBl5biAHBgWy3L65vsbO/2fjzr62HVkoDPyr+WRT+eHhhqpKAERF3dQWOIUOvQ==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.1", - "deprecation": "^2.3.1" - } + "nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", + "dev": true }, - "@octokit/request": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.4.tgz", - "integrity": "sha512-qyj8G8BxQyXjt9Xu6NvfvOr1E0l35lsXtwm3SopsYg/JWXjlsnwqLc8rsD2OLguEL/JjLfBvrXr4az7z8Lch2A==", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.0", - "@octokit/request-error": "^2.0.0", - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^5.0.0" - } + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, - "@octokit/request-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz", - "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==", + "node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", "dev": true, "requires": { - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "lodash.toarray": "^4.4.0" } }, - "@octokit/rest": { - "version": "17.1.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-17.1.4.tgz", - "integrity": "sha512-LGghhepxoXyvi7ew0OdedrlwXQog8gvTbcdXoQ6RDKnzoYW2rBpcqeWC4fTuPUp9K0UEykcMix8kFnQ5b+64JQ==", - "dev": true, - "requires": { - "@octokit/core": "^2.4.3", - "@octokit/plugin-paginate-rest": "^2.0.0", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "3.4.0" - } + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true }, - "@octokit/types": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.5.1.tgz", - "integrity": "sha512-q4Wr7RexkPRrkQpXzUYF5Fj/14Mr65RyOHj6B9d/sQACpqGcStkHZj4qMEtlMY5SnD/69jlL9ItGPbDM0dR/dA==", + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { - "@types/node": ">= 8" - } - }, - "@semantic-release/changelog": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-5.0.0.tgz", - "integrity": "sha512-A1uKqWtQG4WX9Vh4QI5b2ddhqx1qAJFlbow8szSNiXn+TaJg15LSUA9NVqyu0VxQFy3hKUJYwbBHGRXCxCy2fg==", - "dev": true, - "requires": { - "@semantic-release/error": "^2.1.0", - "aggregate-error": "^3.0.0", - "fs-extra": "^8.0.0", - "lodash": "^4.17.4" - } - }, - "@semantic-release/commit-analyzer": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-8.0.1.tgz", - "integrity": "sha512-5bJma/oB7B4MtwUkZC2Bf7O1MHfi4gWe4mA+MIQ3lsEV0b422Bvl1z5HRpplDnMLHH3EXMoRdEng6Ds5wUqA3A==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.7", - "debug": "^4.0.0", - "import-from": "^3.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.2" - } - }, - "@semantic-release/error": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", - "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==", - "dev": true - }, - "@semantic-release/git": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-9.0.0.tgz", - "integrity": "sha512-AZ4Zha5NAPAciIJH3ipzw/WU9qLAn8ENaoVAhD6srRPxTpTzuV3NhNh14rcAo8Paj9dO+5u4rTKcpetOBluYVw==", - "dev": true, - "requires": { - "@semantic-release/error": "^2.1.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "execa": "^4.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.0", - "p-reduce": "^2.0.0" - } - }, - "@semantic-release/github": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-7.0.5.tgz", - "integrity": "sha512-1nJCMeomspRIXKiFO3VXtkUMbIBEreYLFNBdWoLjvlUNcEK0/pEbupEZJA3XHfJuSzv43u3OLpPhF/JBrMuv+A==", - "dev": true, - "requires": { - "@octokit/rest": "^17.0.0", - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^9.0.0", - "globby": "^11.0.0", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^2.4.3", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" }, "dependencies": { - "fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true - } - } - }, - "@semantic-release/npm": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.4.tgz", - "integrity": "sha512-+Loi8ZpGn1OrnHMa96cGJviCcsSIfXTb18J9OlwQTzCnw62ofQTph9Ty3DY3QlyxArJTz/IAz2d68fUEVdE7YA==", - "dev": true, - "requires": { - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "execa": "^4.0.0", - "fs-extra": "^8.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^5.0.0", - "npm": "^6.10.3", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^4.0.0", - "semver": "^7.1.2", - "tempy": "^0.5.0" - }, - "dependencies": { - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - } + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, - "@semantic-release/release-notes-generator": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-9.0.1.tgz", - "integrity": "sha512-bOoTiH6SiiR0x2uywSNR7uZcRDl22IpZhj+Q5Bn0v+98MFtOMhCxFhbrKQjhbYoZw7vps1mvMRmFkp/g6R9cvQ==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^4.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.0", - "debug": "^4.0.0", - "get-stream": "^5.0.0", - "import-from": "^3.0.0", - "into-stream": "^5.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" - } - }, - "@tootallnate/once": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", - "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==", - "dev": true - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", - "dev": true - }, - "@types/node": { - "version": "13.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", - "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "normalize-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.0.0.tgz", + "integrity": "sha512-bAEm2fx8Dq/a35Z6PIRkkBBJvR56BbEJvhpNtvCZ4W9FyORSna77fn+xtYFjqk5JpBS+fMnAOG/wFgkQBmB7hw==", "dev": true }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "npm": { + "version": "6.14.6", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.6.tgz", + "integrity": "sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==", "dev": true, "requires": { - "type-fest": "^0.11.0" + "JSONStream": "^1.3.5", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "^2.0.0", + "archy": "~1.0.0", + "bin-links": "^1.1.7", + "bluebird": "^3.5.5", + "byte-size": "^5.0.1", + "cacache": "^12.0.3", + "call-limit": "^1.1.1", + "chownr": "^1.1.4", + "ci-info": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.1", + "cmd-shim": "^3.0.3", + "columnify": "~1.5.4", + "config-chain": "^1.1.12", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.3.0", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.8.8", + "iferr": "^1.0.2", + "imurmurhash": "*", + "infer-owner": "^1.0.4", + "inflight": "~1.0.6", + "inherits": "^2.0.4", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^3.0.0", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^4.0.7", + "libnpm": "^3.0.1", + "libnpmaccess": "^3.0.2", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "libnpx": "^10.2.2", + "lock-verify": "^2.1.0", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^5.1.1", + "meant": "~1.0.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.5", + "move-concurrently": "^1.0.1", + "node-gyp": "^5.1.0", + "nopt": "^4.0.3", + "normalize-package-data": "^2.5.0", + "npm-audit-report": "^1.3.2", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "^3.0.2", + "npm-lifecycle": "^3.1.4", + "npm-package-arg": "^6.1.1", + "npm-packlist": "^1.4.8", + "npm-pick-manifest": "^3.0.2", + "npm-profile": "^4.0.4", + "npm-registry-fetch": "^4.0.5", + "npm-user-validate": "~1.0.0", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "^1.5.1", + "osenv": "^0.1.5", + "pacote": "^9.5.12", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.8.2", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "^1.0.5", + "read-installed": "~4.0.3", + "read-package-json": "^2.1.1", + "read-package-tree": "^5.3.1", + "readable-stream": "^3.6.0", + "readdir-scoped-modules": "^1.1.0", + "request": "^2.88.0", + "retry": "^0.12.0", + "rimraf": "^2.7.1", + "safe-buffer": "^5.1.2", + "semver": "^5.7.1", + "sha": "^3.0.0", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.1", + "stringify-package": "^1.0.1", + "tar": "^4.4.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "^1.1.1", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.3", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.7.0", + "write-file-atomic": "^2.4.3" }, "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "JSONStream": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "before-after-hook": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", - "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", - "dev": true - }, - "bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", - "dev": true, - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "requires": { - "colors": "1.0.3" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - }, - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "conventional-changelog-angular": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz", - "integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "dependencies": { + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + }, "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "conventional-changelog-angular": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz", + "integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "dependencies": { + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } + } + }, + "conventional-changelog-writer": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz", + "integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.6", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^7.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^3.0.0" + }, + "dependencies": { + "conventional-commits-filter": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz", + "integrity": "sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - } - } - }, - "conventional-changelog-writer": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz", - "integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.6", - "dateformat": "^3.0.0", - "handlebars": "^4.7.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^7.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^3.0.0" - }, - "dependencies": { "conventional-commits-filter": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz", - "integrity": "sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw==", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", + "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "conventional-commits-filter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", - "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz", - "integrity": "sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA==", - "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.1", - "lodash": "^4.17.15", - "meow": "^7.0.0", - "split2": "^2.0.0", - "through2": "^3.0.0", - "trim-off-newlines": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "conventional-commits-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz", + "integrity": "sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^7.0.0", + "split2": "^2.0.0", + "through2": "^3.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "dev": true + }, + "meow": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", + "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + } + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "dependencies": { + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + } + } }, - "map-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", - "dev": true + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } }, - "meow": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", - "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^2.5.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - } + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true }, - "trim-newlines": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", - "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", - "dev": true + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } }, - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } + } }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "dependencies": { - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - } - } - }, - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - } - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - }, - "dependencies": { - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - } - } - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "env-ci": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.0.2.tgz", - "integrity": "sha512-Xc41mKvjouTXD3Oy9AqySz1IeyvJvHZ20Twf5ZLYbNpPPIuCnL/qHCmNlD01LoNy0JTunw9HPYVptD19Ac7Mbw==", - "dev": true, - "requires": { - "execa": "^4.0.0", - "java-properties": "^1.0.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz", - "integrity": "sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fast-glob": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", - "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fastq": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.1.tgz", - "integrity": "sha512-mpIH5sKYueh3YyeJwqtVo8sORi0CgtmkVbK6kZStpQlZBYQuTzG2CZ7idSiJuA7bY0SFCWUc5WIs+oYumGCQNw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "find-versions": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", - "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", - "dev": true, - "requires": { - "semver-regex": "^2.0.0" - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "git-log-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=", - "dev": true, - "requires": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - }, - "dependencies": { - "split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=", - "dev": true, - "requires": { - "through2": "~2.0.0" - } + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globby": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz", - "integrity": "sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "hook-std": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", - "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", - "dev": true - }, - "hosted-git-info": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz", - "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "husky": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", - "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "compare-versions": "^3.6.0", - "cosmiconfig": "^6.0.0", - "find-versions": "^3.2.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^4.2.0", - "please-upgrade-node": "^3.2.0", - "slash": "^3.0.0", - "which-pm-runs": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "env-ci": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.0.2.tgz", + "integrity": "sha512-Xc41mKvjouTXD3Oy9AqySz1IeyvJvHZ20Twf5ZLYbNpPPIuCnL/qHCmNlD01LoNy0JTunw9HPYVptD19Ac7Mbw==", + "dev": true, + "requires": { + "execa": "^4.0.0", + "java-properties": "^1.0.0" + } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "into-stream": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", - "integrity": "sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==", - "dev": true, - "requires": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", - "dev": true, - "requires": { - "text-extensions": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, - "issue-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", - "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", - "dev": true, - "requires": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - } - }, - "java-properties": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "lint-staged": { - "version": "10.2.11", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.11.tgz", - "integrity": "sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "cli-truncate": "2.1.0", - "commander": "^5.1.0", - "cosmiconfig": "^6.0.0", - "debug": "^4.1.1", - "dedent": "^0.7.0", - "enquirer": "^2.3.5", - "execa": "^4.0.1", - "listr2": "^2.1.0", - "log-symbols": "^4.0.0", - "micromatch": "^4.0.2", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.2.0", - "string-argv": "0.3.1", - "stringify-object": "^3.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "execa": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz", + "integrity": "sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "fast-glob": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", + "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "fastq": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.1.tgz", + "integrity": "sha512-mpIH5sKYueh3YyeJwqtVo8sORi0CgtmkVbK6kZStpQlZBYQuTzG2CZ7idSiJuA7bY0SFCWUc5WIs+oYumGCQNw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } }, - "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "listr2": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.0.tgz", - "integrity": "sha512-nwmqTJYQQ+AsKb4fCXH/6/UmLCEDL1jkRAdSn9M6cEUzoRGrs33YD/3N86gAZQnGZ6hxV18XSdlBcJ1GTmetJA==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "cli-truncate": "^2.1.0", - "figures": "^3.2.0", - "indent-string": "^4.0.0", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^6.6.2", - "through": "^2.3.8" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", - "dev": true - }, - "lodash.capitalize": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=", - "dev": true - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", - "dev": true - }, - "lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, - "lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", - "dev": true - }, - "lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", - "dev": true - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "git-log-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", + "integrity": "sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=", + "dev": true, + "requires": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + }, + "dependencies": { + "split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=", + "dev": true, + "requires": { + "through2": "~2.0.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "globby": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz", + "integrity": "sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true }, "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "hook-std": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", + "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", + "dev": true }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "hosted-git-info": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz", + "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==", + "dev": true, + "requires": { + "lru-cache": "^5.1.1" + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "macos-release": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", - "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", - "dev": true - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", - "dev": true - }, - "marked": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.0.tgz", - "integrity": "sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ==", - "dev": true - }, - "marked-terminal": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.0.0.tgz", - "integrity": "sha512-mzU3VD7aVz12FfGoKFAceijehA6Ocjfg3rVimvJbFAB/NOYCsuzRVtq3PSFdPmWI5mhdGeEh3/aMJ5DSxAz94Q==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cardinal": "^2.1.1", - "chalk": "^3.0.0", - "cli-table": "^0.3.1", - "node-emoji": "^1.10.0", - "supports-hyperlinks": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", + "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^6.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "meow": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", - "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^2.5.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } - }, - "modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", - "dev": true, - "requires": { - "lodash.toarray": "^4.4.0" - } - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.0.0.tgz", - "integrity": "sha512-bAEm2fx8Dq/a35Z6PIRkkBBJvR56BbEJvhpNtvCZ4W9FyORSna77fn+xtYFjqk5JpBS+fMnAOG/wFgkQBmB7hw==", - "dev": true - }, - "npm": { - "version": "6.14.6", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.6.tgz", - "integrity": "sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==", - "dev": true, - "requires": { - "JSONStream": "^1.3.5", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "aproba": "^2.0.0", - "archy": "~1.0.0", - "bin-links": "^1.1.7", - "bluebird": "^3.5.5", - "byte-size": "^5.0.1", - "cacache": "^12.0.3", - "call-limit": "^1.1.1", - "chownr": "^1.1.4", - "ci-info": "^2.0.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.5.1", - "cmd-shim": "^3.0.3", - "columnify": "~1.5.4", - "config-chain": "^1.1.12", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.3.0", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.8", - "iferr": "^1.0.2", - "imurmurhash": "*", - "infer-owner": "^1.0.4", - "inflight": "~1.0.6", - "inherits": "^2.0.4", - "ini": "^1.3.5", - "init-package-json": "^1.10.3", - "is-cidr": "^3.0.0", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^4.0.7", - "libnpm": "^3.0.1", - "libnpmaccess": "^3.0.2", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "libnpx": "^10.2.2", - "lock-verify": "^2.1.0", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^5.1.1", - "meant": "~1.0.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.5", - "move-concurrently": "^1.0.1", - "node-gyp": "^5.1.0", - "nopt": "^4.0.3", - "normalize-package-data": "^2.5.0", - "npm-audit-report": "^1.3.2", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "^3.0.2", - "npm-lifecycle": "^3.1.4", - "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.8", - "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.5", - "npm-user-validate": "~1.0.0", - "npmlog": "~4.1.2", - "once": "~1.4.0", - "opener": "^1.5.1", - "osenv": "^0.1.5", - "pacote": "^9.5.12", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", - "qrcode-terminal": "^0.12.0", - "query-string": "^6.8.2", - "qw": "~1.0.1", - "read": "~1.0.7", - "read-cmd-shim": "^1.0.5", - "read-installed": "~4.0.3", - "read-package-json": "^2.1.1", - "read-package-tree": "^5.3.1", - "readable-stream": "^3.6.0", - "readdir-scoped-modules": "^1.1.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "^2.7.1", - "safe-buffer": "^5.1.2", - "semver": "^5.7.1", - "sha": "^3.0.0", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.1", - "stringify-package": "^1.0.1", - "tar": "^4.4.13", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "^1.1.1", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.3", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.7.0", - "write-file-atomic": "^2.4.3" - }, - "dependencies": { - "JSONStream": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "agent-base": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "bundled": true, - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "5.5.2", - "bundled": true, - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ansi-align": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.0.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "ansistyles": { - "version": "0.1.3", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "archy": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "asap": { - "version": "2.0.6", - "bundled": true, - "dev": true - }, - "asn1": { - "version": "0.2.4", - "bundled": true, - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "bundled": true, - "dev": true - }, - "aws4": { - "version": "1.8.0", - "bundled": true, - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bin-links": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cmd-shim": "^3.0.0", - "gentle-fs": "^2.3.0", - "graceful-fs": "^4.1.15", - "npm-normalize-package-bin": "^1.0.0", - "write-file-atomic": "^2.3.0" - } - }, - "bluebird": { - "version": "3.5.5", - "bundled": true, - "dev": true - }, - "boxen": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-from": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "builtins": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "byline": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "byte-size": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "cacache": { - "version": "12.0.3", - "bundled": true, - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "call-limit": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "chalk": { - "version": "2.4.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chownr": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "cidr-regex": { - "version": "2.0.10", - "bundled": true, - "dev": true, - "requires": { - "ip-regex": "^2.1.0" - } - }, - "cli-boxes": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "cli-columns": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "cli-table3": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - } - }, - "cliui": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "cmd-shim": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" - } - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "color-convert": { - "version": "1.9.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "colors": { - "version": "1.3.3", - "bundled": true, - "dev": true, - "optional": true - }, - "columnify": { - "version": "1.5.4", - "bundled": true, - "dev": true, - "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" - } - }, - "combined-stream": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "bundled": true, - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "configstore": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "create-error-class": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "bundled": true, - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "bundled": true, - "dev": true - } - } - }, - "crypto-random-string": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "bundled": true, - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "debuglog": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true - }, - "defaults": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "detect-indent": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "detect-newline": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "dezalgo": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "dot-prop": { - "version": "4.2.0", - "bundled": true, - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "duplexify": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "editor": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "encoding": { - "version": "0.1.12", - "bundled": true, - "dev": true, - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "env-paths": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "err-code": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "errno": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "bundled": true, - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "bundled": true, - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "execa": { - "version": "0.7.0", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "figgy-pudding": { - "version": "3.5.1", - "bundled": true, - "dev": true - }, - "find-npm-prefix": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "find-up": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flush-write-stream": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true - }, - "form-data": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, - "from2": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs-minipass": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^2.6.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "fs-vacuum": { - "version": "1.2.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "genfun": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "gentle-fs": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.2", - "chownr": "^1.1.2", - "cmd-shim": "^3.0.3", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "infer-owner": "^1.0.4", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, - "get-caller-file": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "global-dirs": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "got": { - "version": "6.7.1", - "bundled": true, - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.2.4", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "har-validator": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "hosted-git-info": { - "version": "2.8.8", - "bundled": true, - "dev": true - }, - "http-cache-semantics": { - "version": "3.8.1", - "bundled": true, - "dev": true - }, - "http-proxy-agent": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, - "http-signature": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.23", - "bundled": true, - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "iferr": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "ignore-walk": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "import-lazy": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true - }, - "init-package-json": { - "version": "1.10.3", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "ip": { - "version": "1.1.5", - "bundled": true, - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "is-ci": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ci-info": "^1.5.0" - }, - "dependencies": { - "ci-info": { - "version": "1.6.0", - "bundled": true, - "dev": true - } - } - }, - "is-cidr": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "cidr-regex": "^2.0.10" - } - }, - "is-date-object": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "is-path-inside": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-redirect": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isexe": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "bundled": true, - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "bundled": true, - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "latest-version": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "lazy-property": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "lcid": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "libcipm": { - "version": "4.0.7", - "bundled": true, - "dev": true, - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "ini": "^1.3.5", - "lock-verify": "^2.0.2", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^9.1.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" - } - }, - "libnpm": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.3", - "find-npm-prefix": "^1.0.2", - "libnpmaccess": "^3.0.2", - "libnpmconfig": "^1.2.1", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmpublish": "^1.1.2", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "lock-verify": "^2.0.2", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.0", - "npmlog": "^4.1.2", - "pacote": "^9.5.3", - "read-package-json": "^2.0.13", - "stringify-package": "^1.0.0" - } - }, - "libnpmaccess": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmconfig": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "find-up": "^3.0.0", - "ini": "^1.3.5" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - } - } - }, - "libnpmhook": { - "version": "5.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmorg": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmpublish": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" - } - }, - "libnpmsearch": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmteam": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpx": { - "version": "10.2.2", - "bundled": true, - "dev": true, - "requires": { - "dotenv": "^5.0.1", - "npm-package-arg": "^6.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.0", - "update-notifier": "^2.3.0", - "which": "^1.3.0", - "y18n": "^4.0.0", - "yargs": "^11.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lock-verify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "npm-package-arg": "^6.1.0", - "semver": "^5.4.1" - } - }, - "lockfile": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "signal-exit": "^3.0.2" - } - }, - "lodash._baseindexof": { - "version": "3.1.0", - "bundled": true, - "dev": true - }, - "lodash._baseuniq": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash._cacheindexof": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "lodash._createcache": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0" - } - }, - "lodash._createset": { - "version": "4.0.3", - "bundled": true, - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "bundled": true, - "dev": true - }, - "lodash._root": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.restparam": { - "version": "3.6.1", - "bundled": true, - "dev": true - }, - "lodash.union": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.without": { - "version": "4.4.0", - "bundled": true, - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "make-fetch-happen": { - "version": "5.0.2", - "bundled": true, - "dev": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "bundled": true, - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "meant": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "mem": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "bundled": true, - "dev": true - } - } - }, - "mime-db": { - "version": "1.35.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.19", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "~1.35.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minizlib": { - "version": "1.3.3", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "mississippi": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "bundled": true, - "dev": true, - "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - } - } - }, - "move-concurrently": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "bundled": true, - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "node-fetch-npm": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-gyp": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.1.2", - "request": "^2.88.0", - "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" - } - }, - "nopt": { - "version": "4.0.3", - "bundled": true, - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "resolve": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "npm-audit-report": { - "version": "1.3.2", - "bundled": true, - "dev": true, - "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" - } - }, - "npm-bundled": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-cache-filename": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "npm-install-checks": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" - } - }, - "npm-lifecycle": { - "version": "3.1.4", - "bundled": true, - "dev": true, - "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.15", - "node-gyp": "^5.0.2", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", - "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" - } - }, - "npm-logical-tree": { - "version": "1.2.1", - "bundled": true, - "dev": true - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "npm-package-arg": { - "version": "6.1.1", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "1.4.8", - "bundled": true, - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-profile": { - "version": "4.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.2 || 2", - "figgy-pudding": "^3.4.1", - "npm-registry-fetch": "^4.0.0" - } - }, - "npm-registry-fetch": { - "version": "4.0.5", - "bundled": true, - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "bundled": true, - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "npm-user-validate": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "object-keys": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.1", - "bundled": true, - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "bundled": true, - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "package-json": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, - "pacote": { - "version": "9.5.12", - "bundled": true, - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-normalize-package-bin": "^1.0.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^3.0.0", - "npm-registry-fetch": "^4.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "parallel-transform": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "path-exists": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "path-key": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "pify": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-retry": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "bundled": true, - "dev": true - } - } - }, - "promzard": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "read": "1" - } - }, - "proto-list": { - "version": "1.2.4", - "bundled": true, - "dev": true - }, - "protoduck": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "prr": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "psl": { - "version": "1.1.29", - "bundled": true, - "dev": true - }, - "pump": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "bundled": true, - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true - }, - "qrcode-terminal": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "qs": { - "version": "6.5.2", - "bundled": true, - "dev": true - }, - "query-string": { - "version": "6.8.2", - "bundled": true, - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "qw": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - } - } - }, - "read": { - "version": "1.0.7", - "bundled": true, - "dev": true, - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-cmd-shim": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "read-installed": { - "version": "4.0.3", - "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" - } - }, - "read-package-json": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-package-tree": { - "version": "5.3.1", - "bundled": true, - "dev": true, - "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "registry-auth-token": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, - "request": { - "version": "2.88.0", - "bundled": true, - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "retry": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-queue": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.7.1", - "bundled": true, - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "sha": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "slide": { - "version": "1.1.6", - "bundled": true, - "dev": true - }, - "smart-buffer": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "socks": { - "version": "2.3.3", - "bundled": true, - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "sorted-object": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "sorted-union-stream": { - "version": "2.1.3", - "bundled": true, - "dev": true, - "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" - }, - "dependencies": { - "from2": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" - } - }, - "isarray": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "bundled": true, - "dev": true - }, - "split-on-first": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "sshpk": { - "version": "1.14.2", - "bundled": true, - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stream-each": { - "version": "1.2.2", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-iterate": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "stream-shift": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strict-uri-encode": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.0", - "bundled": true, - "dev": true - } - } - }, - "stringify-package": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tar": { - "version": "4.4.13", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "term-size": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "execa": "^0.7.0" - } - }, - "text-table": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "through": { - "version": "2.3.8", - "bundled": true, - "dev": true - }, - "through2": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "tiny-relative-date": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "bundled": true, - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "typedarray": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "umask": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "unique-string": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "unzip-response": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "url-parse-lax": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "prepend-http": "^1.0.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "util-extend": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "util-promisify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "uuid": { - "version": "3.3.3", - "bundled": true, - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "verror": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "which": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "widest-line": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "worker-farm": { - "version": "1.7.0", - "bundled": true, - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "write-file-atomic": { - "version": "2.4.3", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "xtend": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "y18n": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true - }, - "yargs": { - "version": "11.1.1", - "bundled": true, - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" - }, - "dependencies": { - "y18n": { - "version": "3.2.1", - "bundled": true, - "dev": true - } - } - }, - "yargs-parser": { - "version": "9.0.2", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "dev": true - }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "dev": true, - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, - "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true - }, - "p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", - "dev": true, - "requires": { - "p-map": "^2.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, - "p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", - "dev": true - }, - "p-retry": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", - "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.12.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", - "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "into-stream": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", + "integrity": "sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==", + "dev": true, + "requires": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, + "issue-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", + "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", + "dev": true, + "requires": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + } + }, + "java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.2.11", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.11.tgz", + "integrity": "sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "cli-truncate": "2.1.0", + "commander": "^5.1.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "enquirer": "^2.3.5", + "execa": "^4.0.1", + "listr2": "^2.1.0", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "listr2": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.0.tgz", + "integrity": "sha512-nwmqTJYQQ+AsKb4fCXH/6/UmLCEDL1jkRAdSn9M6cEUzoRGrs33YD/3N86gAZQnGZ6hxV18XSdlBcJ1GTmetJA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.2", + "through": "^2.3.8" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", + "dev": true + }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", + "dev": true }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "dev": true + }, + "marked": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.0.tgz", + "integrity": "sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ==", + "dev": true + }, + "marked-terminal": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.0.0.tgz", + "integrity": "sha512-mzU3VD7aVz12FfGoKFAceijehA6Ocjfg3rVimvJbFAB/NOYCsuzRVtq3PSFdPmWI5mhdGeEh3/aMJ5DSxAz94Q==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^3.0.0", + "cli-table": "^0.3.1", + "node-emoji": "^1.10.0", + "supports-hyperlinks": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } - } }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", - "dev": true, - "requires": { - "esprima": "~4.0.0" - } - }, - "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", - "dev": true - }, - "registry-auth-token": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", - "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "rxjs": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", - "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semantic-release": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-17.0.4.tgz", - "integrity": "sha512-5y9QRSrZtdvACmlpX5DvEVsvFuKRDUVn7JVJFxPVLGrGofDf1d0M/+hA1wFmCjiJZ+VCY8bYaSqVqF14KCF9rw==", - "dev": true, - "requires": { - "@semantic-release/commit-analyzer": "^8.0.0", - "@semantic-release/error": "^2.2.0", - "@semantic-release/github": "^7.0.0", - "@semantic-release/npm": "^7.0.0", - "@semantic-release/release-notes-generator": "^9.0.0", - "aggregate-error": "^3.0.0", - "cosmiconfig": "^6.0.0", - "debug": "^4.0.0", - "env-ci": "^5.0.0", - "execa": "^4.0.0", - "figures": "^3.0.0", - "find-versions": "^3.0.0", - "get-stream": "^5.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^2.0.0", - "hosted-git-info": "^3.0.0", - "lodash": "^4.17.15", - "marked": "^0.8.0", - "marked-terminal": "^4.0.0", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "p-reduce": "^2.0.0", - "read-pkg-up": "^7.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.1.1", - "semver-diff": "^3.1.1", - "signale": "^1.2.1", - "yargs": "^15.0.1" - } - }, - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", - "dev": true - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "semver-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "dev": true, - "requires": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "dependencies": { - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - } - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "meow": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", + "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "dev": true, + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.1.0.tgz", + "integrity": "sha512-UxHuSWsSAmzSqN+DSjasaZWQ3QPtEisHdlr4y9MJ5zg0RcImv5fQt8QM0izJSCdsdmhJGK+ubcTpJXwVDmwSVQ==", + "dev": true + }, + "npm": { + "version": "6.14.8", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.8.tgz", + "integrity": "sha512-HBZVBMYs5blsj94GTeQZel7s9odVuuSUHy1+AlZh7rPVux1os2ashvEGLy/STNK7vUjbrCg5Kq9/GXisJgdf6A==", + "dev": true, + "requires": { + "JSONStream": "^1.3.5", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "^2.0.0", + "archy": "~1.0.0", + "bin-links": "^1.1.8", + "bluebird": "^3.5.5", + "byte-size": "^5.0.1", + "cacache": "^12.0.3", + "call-limit": "^1.1.1", + "chownr": "^1.1.4", + "ci-info": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.1", + "cmd-shim": "^3.0.3", + "columnify": "~1.5.4", + "config-chain": "^1.1.12", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.3.1", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.8.8", + "iferr": "^1.0.2", + "imurmurhash": "*", + "infer-owner": "^1.0.4", + "inflight": "~1.0.6", + "inherits": "^2.0.4", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^3.0.0", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^4.0.8", + "libnpm": "^3.0.1", + "libnpmaccess": "^3.0.2", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "libnpx": "^10.2.4", + "lock-verify": "^2.1.0", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^5.1.1", + "meant": "^1.0.2", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.5", + "move-concurrently": "^1.0.1", + "node-gyp": "^5.1.0", + "nopt": "^4.0.3", + "normalize-package-data": "^2.5.0", + "npm-audit-report": "^1.3.3", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "^3.0.2", + "npm-lifecycle": "^3.1.5", + "npm-package-arg": "^6.1.1", + "npm-packlist": "^1.4.8", + "npm-pick-manifest": "^3.0.2", + "npm-profile": "^4.0.4", + "npm-registry-fetch": "^4.0.7", + "npm-user-validate": "~1.0.0", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "^1.5.1", + "osenv": "^0.1.5", + "pacote": "^9.5.12", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.8.2", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "^1.0.5", + "read-installed": "~4.0.3", + "read-package-json": "^2.1.1", + "read-package-tree": "^5.3.1", + "readable-stream": "^3.6.0", + "readdir-scoped-modules": "^1.1.0", + "request": "^2.88.0", + "retry": "^0.12.0", + "rimraf": "^2.7.1", + "safe-buffer": "^5.1.2", + "semver": "^5.7.1", + "sha": "^3.0.0", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.1", + "stringify-package": "^1.0.1", + "tar": "^4.4.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "^1.1.1", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.3", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.7.0", + "write-file-atomic": "^2.4.3" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "bundled": true, + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "5.5.2", + "bundled": true, + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "asap": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true, + "dev": true + }, + "aws4": { + "version": "1.8.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.8", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "cmd-shim": "^3.0.0", + "gentle-fs": "^2.3.0", + "graceful-fs": "^4.1.15", + "npm-normalize-package-bin": "^1.0.0", + "write-file-atomic": "^2.3.0" + } + }, + "bluebird": { + "version": "3.5.5", + "bundled": true, + "dev": true + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "byline": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "byte-size": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "cacache": { + "version": "12.0.3", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "cidr-regex": { + "version": "2.0.10", + "bundled": true, + "dev": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "cmd-shim": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "colors": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "dev": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "bundled": true, + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.5", + "bundled": true, + "dev": true, + "requires": { + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true, + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "bundled": true, + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "bundled": true, + "dev": true + } + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true, + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "bundled": true, + "dev": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "err-code": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "bundled": true, + "dev": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "bundled": true, + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "figgy-pudding": { + "version": "3.5.1", + "bundled": true, + "dev": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true + }, + "form-data": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^2.6.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true, + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "gentle-fs": { + "version": "2.3.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.2", + "chownr": "^1.1.2", + "cmd-shim": "^3.0.3", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "infer-owner": "^1.0.4", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true, + "dev": true + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "bundled": true, + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "har-validator": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "bundled": true, + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true, + "dev": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true, + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "is-ci": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ci-info": "^1.5.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "bundled": true, + "dev": true + } + } + }, + "is-cidr": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "cidr-regex": "^2.0.10" + } + }, + "is-date-object": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "bundled": true, + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true, + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "libcipm": { + "version": "4.0.8", + "bundled": true, + "dev": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "ini": "^1.3.5", + "lock-verify": "^2.1.0", + "mkdirp": "^0.5.1", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^9.1.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpm": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.3", + "find-npm-prefix": "^1.0.2", + "libnpmaccess": "^3.0.2", + "libnpmconfig": "^1.2.1", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmpublish": "^1.1.2", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "npm-profile": "^4.0.2", + "npm-registry-fetch": "^4.0.0", + "npmlog": "^4.1.2", + "pacote": "^9.5.3", + "read-package-json": "^2.0.13", + "stringify-package": "^1.0.0" + } + }, + "libnpmaccess": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true, + "dev": true + } + } + }, + "libnpmhook": { + "version": "5.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmorg": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmpublish": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + } + }, + "libnpmsearch": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmteam": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpx": { + "version": "10.2.4", + "bundled": true, + "dev": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^14.2.3" + } + }, + "lock-verify": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "npm-package-arg": "^6.1.0", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true, + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true, + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true, + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true, + "dev": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true, + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true, + "dev": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true, + "dev": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "version": "5.0.2", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "meant": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "mime-db": { + "version": "1.35.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.19", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "bundled": true, + "dev": true, + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + } + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true, + "dev": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.1.2", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "semver": "^5.7.1", + "tar": "^4.4.12", + "which": "^1.3.1" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "npm-audit-report": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "npm-install-checks": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "3.1.5", + "bundled": true, + "dev": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.15", + "node-gyp": "^5.0.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.1" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true, + "dev": true + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npm-package-arg": { + "version": "6.1.1", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "4.0.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.2 || 2", + "figgy-pudding": "^3.4.1", + "npm-registry-fetch": "^4.0.0" + } + }, + "npm-registry-fetch": { + "version": "4.0.7", + "bundled": true, + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "bundled": true, + "dev": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "object-keys": { + "version": "1.0.12", + "bundled": true, + "dev": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.1", + "bundled": true, + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "9.5.12", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-normalize-package-bin": "^1.0.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^3.0.0", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "pify": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true, + "dev": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true, + "dev": true + }, + "protoduck": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "psl": { + "version": "1.1.29", + "bundled": true, + "dev": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "qs": { + "version": "6.5.2", + "bundled": true, + "dev": true + }, + "query-string": { + "version": "6.8.2", + "bundled": true, + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.3.1", + "bundled": true, + "dev": true, + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.88.0", + "bundled": true, + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "retry": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.1" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "sha": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "slide": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "socks": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "dev": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "bundled": true, + "dev": true + }, + "split-on-first": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true, + "dev": true + } + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "through": { + "version": "2.3.8", + "bundled": true, + "dev": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "bundled": true, + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true, + "dev": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true + }, + "umask": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "util-promisify": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.3", + "bundled": true, + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "bundled": true, + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + }, + "yargs": { + "version": "14.2.3", + "bundled": true, + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "find-up": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "15.0.1", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "bundled": true, + "dev": true + } + } + } + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "spawn-error-forwarder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk=", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" - } - }, - "split2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", - "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", - "dev": true, - "requires": { - "through2": "^2.0.2" - }, - "dependencies": { - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "stream-buffers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", - "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", - "dev": true - }, - "stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true - }, - "tempy": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.5.0.tgz", - "integrity": "sha512-VEY96x7gbIRfsxqsafy2l5yVxxp3PhwAGoWMyC2D2Zt5DmEv+2tGiPOrquNRpf21hhGnKLVEsuqleqiZmKG/qw==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.12.0", - "unique-string": "^2.0.0" - }, - "dependencies": { - "type-fest": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", - "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==", - "dev": true - } - } - }, - "text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "dev": true, - "requires": { - "readable-stream": "2 || 3" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", - "dev": true - }, - "trim-newlines": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", - "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", - "dev": true - }, - "trim-off-newlines": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", - "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", - "dev": true - }, - "ts-node": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz", - "integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "3.1.1" - } - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - }, - "typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", - "dev": true - }, - "uglify-js": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", - "integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", - "dev": true, - "optional": true - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universal-user-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz", - "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==", - "dev": true, - "requires": { - "os-name": "^3.1.0" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "dev": true - }, - "windows-release": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", - "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", - "dev": true, - "requires": { - "execa": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "dev": true, + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "p-each-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true + }, + "p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "requires": { + "p-map": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pyright": { + "version": "1.1.66", + "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.66.tgz", + "integrity": "sha512-DbtdoFKrg3JrGAEG9bUjN4dNQQeoj7a/vy61Ppi/6KeA1sEclA/sUFFH8Yyb56UDUEXYg2U1dw78zr6mRw+Cog==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + } + } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", + "dev": true, + "requires": { + "esprima": "~4.0.0" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "registry-auth-token": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", + "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semantic-release": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-17.0.4.tgz", + "integrity": "sha512-5y9QRSrZtdvACmlpX5DvEVsvFuKRDUVn7JVJFxPVLGrGofDf1d0M/+hA1wFmCjiJZ+VCY8bYaSqVqF14KCF9rw==", + "dev": true, + "requires": { + "@semantic-release/commit-analyzer": "^8.0.0", + "@semantic-release/error": "^2.2.0", + "@semantic-release/github": "^7.0.0", + "@semantic-release/npm": "^7.0.0", + "@semantic-release/release-notes-generator": "^9.0.0", + "aggregate-error": "^3.0.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.0.0", + "env-ci": "^5.0.0", + "execa": "^4.0.0", + "figures": "^3.0.0", + "find-versions": "^3.0.0", + "get-stream": "^5.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^2.0.0", + "hosted-git-info": "^3.0.0", + "lodash": "^4.17.15", + "marked": "^0.8.0", + "marked-terminal": "^4.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "p-reduce": "^2.0.0", + "read-pkg-up": "^7.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.1.1", + "semver-diff": "^3.1.1", + "signale": "^1.2.1", + "yargs": "^15.0.1" + } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "dev": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "requires": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "dependencies": { + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "dev": true, + "requires": { + "through2": "^2.0.2" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, + "tempy": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.5.0.tgz", + "integrity": "sha512-VEY96x7gbIRfsxqsafy2l5yVxxp3PhwAGoWMyC2D2Zt5DmEv+2tGiPOrquNRpf21hhGnKLVEsuqleqiZmKG/qw==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.12.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", + "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==", + "dev": true + } + } + }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", + "dev": true + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "ts-node": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz", + "integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "uglify-js": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", + "integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", + "dev": true, + "optional": true + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universal-user-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz", + "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==", + "dev": true, + "requires": { + "os-name": "^3.1.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "windows-release": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", + "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "dev": true, + "requires": { + "execa": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yaml": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.8.2.tgz", - "integrity": "sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.7" - } - }, - "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yaml": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.8.2.tgz", + "integrity": "sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.7" + } + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } }, "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true } - } } diff --git a/package.json b/package.json index 6636a4e..72bb32a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "type": "git", "url": "https://github.com/justindujardin/pathy" }, - "author": "", + "author": "Justin DuJardin", "license": "Apache-2.0", "bugs": { "url": "https://github.com/justindujardin/pathy/issues" @@ -38,6 +38,7 @@ "husky": "^4.2.5", "lint-staged": "^10.2.11", "minimist": "^1.2.5", + "pyright": "^1.1.66", "semantic-release": "^17.0.4", "stream-buffers": "^3.0.2", "ts-node": "^8.0.2", @@ -99,5 +100,6 @@ ], "@semantic-release/github" ] - } + }, + "dependencies": {} } diff --git a/tools/lint.sh b/tools/lint.sh index 9b43570..be14002 100644 --- a/tools/lint.sh +++ b/tools/lint.sh @@ -6,3 +6,4 @@ set -e mypy pathy flake8 pathy tests black pathy tests --check +pyright \ No newline at end of file From 8bcbf661213707b861b128dde591143a342cbd98 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 18:40:02 -0700 Subject: [PATCH 53/75] chore: run with npx --- tools/lint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lint.sh b/tools/lint.sh index be14002..0aab50a 100644 --- a/tools/lint.sh +++ b/tools/lint.sh @@ -6,4 +6,4 @@ set -e mypy pathy flake8 pathy tests black pathy tests --check -pyright \ No newline at end of file +npx pyright \ No newline at end of file From 84a8e31de44f4ccfd428c27ee16d8431afd0858f Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 18:54:01 -0700 Subject: [PATCH 54/75] chore: fix pyright errors in gcs.py --- pathy/gcs.py | 23 ++++++++++++++--------- tools/lint.sh | 6 +++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pathy/gcs.py b/pathy/gcs.py index 05eb8be..a248c11 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -6,10 +6,15 @@ try: from google.api_core import exceptions as gcs_errors from google.auth.exceptions import DefaultCredentialsError - from google.cloud import storage + from google.cloud.storage import Blob as GCSNativeBlob + from google.cloud.storage import Bucket as GCSNativeBucket + from google.cloud.storage import Client as GCSNativeClient has_gcs = True except ImportError: + GCSNativeBlob = Any + GCSNativeBucket = Any + GCSNativeClient = Any storage = None has_gcs = False @@ -23,12 +28,12 @@ """ -class BucketEntryGCS(BucketEntry["BucketGCS", "storage.Blob"]): +class BucketEntryGCS(BucketEntry["BucketGCS", GCSNativeBlob]): ... @dataclass -class BlobGCS(Blob["storage.Bucket", "storage.Blob"]): +class BlobGCS(Blob[GCSNativeBucket, GCSNativeBlob]): def delete(self) -> None: self.raw.delete() @@ -39,7 +44,7 @@ def exists(self) -> bool: @dataclass class BucketGCS(Bucket): name: str - bucket: "storage.Bucket" + bucket: GCSNativeBucket def get_blob(self, blob_name: str) -> Optional[BlobGCS]: assert isinstance( @@ -91,11 +96,11 @@ def exists(self) -> bool: class BucketClientGCS(BucketClient): - client: Optional["storage.Client"] + client: Optional[GCSNativeClient] - def __init__(self, client: Optional["storage.Client"] = None): + def __init__(self, client: Optional[GCSNativeClient] = None): try: - self.client = storage.Client() if storage else None + self.client = GCSNativeClient() if storage else None except (BaseException, DefaultCredentialsError): self.client = None @@ -149,7 +154,7 @@ def get_bucket(self, path: PurePathy) -> BucketGCS: def list_buckets( self, **kwargs: Dict[str, Any] - ) -> Generator["storage.Bucket", None, None]: + ) -> Generator[GCSNativeBucket, None, None]: assert self.client is not None, _MISSING_DEPS return self.client.list_buckets(**kwargs) # type:ignore @@ -162,7 +167,7 @@ def scandir( # type:ignore[override] assert self.client is not None, _MISSING_DEPS continuation_token = None if path is None or not path.root: - gcs_bucket: "storage.Bucket" + gcs_bucket: GCSNativeBucket for gcs_bucket in self.list_buckets(): yield BucketEntryGCS(gcs_bucket.name, is_dir=True, raw=None) return diff --git a/tools/lint.sh b/tools/lint.sh index 0aab50a..84665a2 100644 --- a/tools/lint.sh +++ b/tools/lint.sh @@ -3,7 +3,11 @@ set -e . .env/bin/activate +echo "========================= mypy" mypy pathy +echo "========================= flake8" flake8 pathy tests +echo "========================= black" black pathy tests --check -npx pyright \ No newline at end of file +echo "========================= pyright" +npx pyright pathy tests \ No newline at end of file From 17b10e90d6efeac3214535291155167a544dc1de Mon Sep 17 00:00:00 2001 From: justindujardin Date: Thu, 3 Sep 2020 19:02:19 -0700 Subject: [PATCH 55/75] chore: take two at fixing pyright errors - this is pretty nice. If you don't have the packages installed, the types end up being Any everywhere, but if you do have them installed, you get all the correct types including documentation popups in the IDE and intellisense :tada: --- pathy/gcs.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pathy/gcs.py b/pathy/gcs.py index a248c11..7a8c050 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -4,15 +4,17 @@ from .base import Blob, Bucket, BucketClient, BucketEntry, ClientError, PurePathy try: - from google.api_core import exceptions as gcs_errors - from google.auth.exceptions import DefaultCredentialsError - from google.cloud.storage import Blob as GCSNativeBlob - from google.cloud.storage import Bucket as GCSNativeBucket - from google.cloud.storage import Client as GCSNativeClient + from google.api_core import exceptions as gcs_errors # type:ignore + from google.auth.exceptions import DefaultCredentialsError # type:ignore + from google.cloud.storage import Blob as GCSNativeBlob # type:ignore + from google.cloud.storage import Bucket as GCSNativeBucket # type:ignore + from google.cloud.storage import Client as GCSNativeClient # type:ignore has_gcs = True except ImportError: GCSNativeBlob = Any + DefaultCredentialsError = Any + gcs_errors = Any GCSNativeBucket = Any GCSNativeClient = Any storage = None From 4059aef7d7bad8b4ab4093b92dac3dd80750486e Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sat, 26 Sep 2020 13:59:03 -0700 Subject: [PATCH 56/75] chore: fix issue where GCS installation was not found - storage var is outdated --- README.md | 20 ++++++++++++++++---- pathy/gcs.py | 3 +-- tools/setup.sh | 4 +++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 64b63d7..310948d 100644 --- a/README.md +++ b/README.md @@ -400,10 +400,13 @@ Copy a blob or folder of blobs from one bucket to another. $ cp [OPTIONS] FROM_LOCATION TO_LOCATION ``` -**Options**: +**Arguments**: - `FROM_LOCATION`: [required] - `TO_LOCATION`: [required] + +**Options**: + - `--help`: Show this message and exit. ## `ls` @@ -416,9 +419,12 @@ List the blobs that exist at a given location. $ ls [OPTIONS] LOCATION ``` -**Options**: +**Arguments**: - `LOCATION`: [required] + +**Options**: + - `--help`: Show this message and exit. ## `mv` @@ -431,10 +437,13 @@ Move a blob or folder of blobs from one path to another. $ mv [OPTIONS] FROM_LOCATION TO_LOCATION ``` -**Options**: +**Arguments**: - `FROM_LOCATION`: [required] - `TO_LOCATION`: [required] + +**Options**: + - `--help`: Show this message and exit. ## `rm` @@ -447,9 +456,12 @@ Remove a blob or folder of blobs from a given location. $ rm [OPTIONS] LOCATION ``` -**Options**: +**Arguments**: - `LOCATION`: [required] + +**Options**: + - `-r, --recursive`: Recursively remove files and folders. [default: False] - `-v, --verbose`: Print removed files and folders. [default: False] - `--help`: Show this message and exit. diff --git a/pathy/gcs.py b/pathy/gcs.py index 7a8c050..afe0dd0 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -17,7 +17,6 @@ gcs_errors = Any GCSNativeBucket = Any GCSNativeClient = Any - storage = None has_gcs = False _MISSING_DEPS = """You are using the GCS functionality of Pathy without @@ -102,7 +101,7 @@ class BucketClientGCS(BucketClient): def __init__(self, client: Optional[GCSNativeClient] = None): try: - self.client = GCSNativeClient() if storage else None + self.client = GCSNativeClient() if GCSNativeClient else None except (BaseException, DefaultCredentialsError): self.client = None diff --git a/tools/setup.sh b/tools/setup.sh index 099ebf6..2356087 100644 --- a/tools/setup.sh +++ b/tools/setup.sh @@ -9,6 +9,8 @@ fi . .env/bin/activate echo "Installing/updating requirements..." -pip install -r requirements.txt -r requirements-dev.txt +pip install -r requirements.txt +echo "Installing/updating dev requirements..." +pip install -r requirements-dev.txt pip install -e . From 4b3e959ce1c4c491cb291935e8d47ac537b72485 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sat, 26 Sep 2020 14:00:36 -0700 Subject: [PATCH 57/75] feat: update smart-open to 2.2.0 for minimal deps - to get GCS support, use `pip install pathy[gcs]` --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f5391e3..492fc8b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -smart-open>=1.10.0,<2.0.0 +smart-open>=2.2.0,<3.0.0 typer>=0.3.0,<1.0.0 dataclasses>=0.6,<1.0; python_version < "3.7" \ No newline at end of file From 6c2fee7fc098ce273b5651808997347286973fa0 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sat, 26 Sep 2020 14:05:18 -0700 Subject: [PATCH 58/75] chore: fix fallback type for credentials error --- pathy/gcs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathy/gcs.py b/pathy/gcs.py index afe0dd0..09ed42a 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -13,7 +13,7 @@ has_gcs = True except ImportError: GCSNativeBlob = Any - DefaultCredentialsError = Any + DefaultCredentialsError = BaseException gcs_errors = Any GCSNativeBucket = Any GCSNativeClient = Any From 5fa9994206634140d40fcc3d586f0ff7683bb516 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Sat, 26 Sep 2020 14:08:13 -0700 Subject: [PATCH 59/75] chore: npm audit fix --- package-lock.json | 1642 ++++++++++++++++++++++++--------------------- 1 file changed, 873 insertions(+), 769 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea56e11..866f3af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -649,339 +649,10 @@ } }, "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "meow": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", - "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^2.5.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } - }, - "modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", - "dev": true, - "requires": { - "lodash.toarray": "^4.4.0" - } - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.0.0.tgz", - "integrity": "sha512-bAEm2fx8Dq/a35Z6PIRkkBBJvR56BbEJvhpNtvCZ4W9FyORSna77fn+xtYFjqk5JpBS+fMnAOG/wFgkQBmB7hw==", - "dev": true - }, - "npm": { - "version": "6.14.6", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.6.tgz", - "integrity": "sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==", - "dev": true, - "requires": { - "JSONStream": "^1.3.5", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "aproba": "^2.0.0", - "archy": "~1.0.0", - "bin-links": "^1.1.7", - "bluebird": "^3.5.5", - "byte-size": "^5.0.1", - "cacache": "^12.0.3", - "call-limit": "^1.1.1", - "chownr": "^1.1.4", - "ci-info": "^2.0.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.5.1", - "cmd-shim": "^3.0.3", - "columnify": "~1.5.4", - "config-chain": "^1.1.12", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.3.0", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.8", - "iferr": "^1.0.2", - "imurmurhash": "*", - "infer-owner": "^1.0.4", - "inflight": "~1.0.6", - "inherits": "^2.0.4", - "ini": "^1.3.5", - "init-package-json": "^1.10.3", - "is-cidr": "^3.0.0", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^4.0.7", - "libnpm": "^3.0.1", - "libnpmaccess": "^3.0.2", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "libnpx": "^10.2.2", - "lock-verify": "^2.1.0", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^5.1.1", - "meant": "~1.0.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.5", - "move-concurrently": "^1.0.1", - "node-gyp": "^5.1.0", - "nopt": "^4.0.3", - "normalize-package-data": "^2.5.0", - "npm-audit-report": "^1.3.2", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "^3.0.2", - "npm-lifecycle": "^3.1.4", - "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.8", - "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.5", - "npm-user-validate": "~1.0.0", - "npmlog": "~4.1.2", - "once": "~1.4.0", - "opener": "^1.5.1", - "osenv": "^0.1.5", - "pacote": "^9.5.12", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", - "qrcode-terminal": "^0.12.0", - "query-string": "^6.8.2", - "qw": "~1.0.1", - "read": "~1.0.7", - "read-cmd-shim": "^1.0.5", - "read-installed": "~4.0.3", - "read-package-json": "^2.1.1", - "read-package-tree": "^5.3.1", - "readable-stream": "^3.6.0", - "readdir-scoped-modules": "^1.1.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "^2.7.1", - "safe-buffer": "^5.1.2", - "semver": "^5.7.1", - "sha": "^3.0.0", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.1", - "stringify-package": "^1.0.1", - "tar": "^4.4.13", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "^1.1.1", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.3", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.7.0", - "write-file-atomic": "^2.4.3" - }, - "dependencies": { - "JSONStream": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "colors": { "version": "1.0.3", @@ -2536,9 +2207,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "normalize-package-data": { @@ -2712,7 +2383,8 @@ "dependencies": { "JSONStream": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { "jsonparse": "^1.2.0", @@ -2721,12 +2393,14 @@ }, "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "agent-base": { "version": "4.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -2734,7 +2408,8 @@ }, "agentkeepalive": { "version": "3.5.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", "dev": true, "requires": { "humanize-ms": "^1.2.1" @@ -2742,7 +2417,8 @@ }, "ajv": { "version": "5.5.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { "co": "^4.6.0", @@ -2753,7 +2429,8 @@ }, "ansi-align": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { "string-width": "^2.0.0" @@ -2761,12 +2438,14 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { "version": "3.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -2774,27 +2453,32 @@ }, "ansicolors": { "version": "0.3.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, "ansistyles": { "version": "0.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=", "dev": true }, "aproba": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "dev": true }, "archy": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "requires": { "delegates": "^1.0.0", @@ -2803,7 +2487,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2817,7 +2502,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -2827,12 +2513,14 @@ }, "asap": { "version": "2.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", "dev": true }, "asn1": { "version": "0.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -2840,32 +2528,38 @@ }, "assert-plus": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "asynckit": { "version": "0.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, "aws-sign2": { "version": "0.7.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { "version": "1.8.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "optional": true, "requires": { @@ -2874,7 +2568,8 @@ }, "bin-links": { "version": "1.1.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.8.tgz", + "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==", "dev": true, "requires": { "bluebird": "^3.5.3", @@ -2887,12 +2582,14 @@ }, "bluebird": { "version": "3.5.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "boxen": { "version": "1.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "requires": { "ansi-align": "^2.0.0", @@ -2906,7 +2603,8 @@ }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -2915,27 +2613,32 @@ }, "buffer-from": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", "dev": true }, "builtins": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", "dev": true }, "byline": { "version": "5.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", "dev": true }, "byte-size": { "version": "5.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", "dev": true }, "cacache": { "version": "12.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -2957,27 +2660,32 @@ }, "call-limit": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==", "dev": true }, "camelcase": { "version": "4.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, "capture-stack-trace": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", "dev": true }, "caseless": { "version": "0.12.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, "chalk": { "version": "2.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -2987,17 +2695,20 @@ }, "chownr": { "version": "1.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "ci-info": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, "cidr-regex": { "version": "2.0.10", - "bundled": true, + "resolved": false, + "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==", "dev": true, "requires": { "ip-regex": "^2.1.0" @@ -3005,12 +2716,14 @@ }, "cli-boxes": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, "cli-columns": { "version": "3.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=", "dev": true, "requires": { "string-width": "^2.0.0", @@ -3019,7 +2732,8 @@ }, "cli-table3": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", "dev": true, "requires": { "colors": "^1.1.2", @@ -3029,7 +2743,8 @@ }, "cliui": { "version": "5.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { "string-width": "^3.1.0", @@ -3039,17 +2754,20 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -3059,7 +2777,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -3069,12 +2788,14 @@ }, "clone": { "version": "1.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, "cmd-shim": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -3083,17 +2804,20 @@ }, "co": { "version": "4.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "color-convert": { "version": "1.9.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { "color-name": "^1.1.1" @@ -3101,18 +2825,21 @@ }, "color-name": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "colors": { "version": "1.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true, "optional": true }, "columnify": { "version": "1.5.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", "dev": true, "requires": { "strip-ansi": "^3.0.0", @@ -3121,7 +2848,8 @@ }, "combined-stream": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -3129,12 +2857,14 @@ }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "concat-stream": { "version": "1.6.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -3145,7 +2875,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3159,7 +2890,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3169,7 +2901,8 @@ }, "config-chain": { "version": "1.1.12", - "bundled": true, + "resolved": false, + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, "requires": { "ini": "^1.3.4", @@ -3178,7 +2911,8 @@ }, "configstore": { "version": "3.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", "dev": true, "requires": { "dot-prop": "^4.2.1", @@ -3191,12 +2925,14 @@ }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "copy-concurrently": { "version": "1.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { "aproba": "^1.1.1", @@ -3209,24 +2945,28 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "iferr": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true } } }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, "create-error-class": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { "capture-stack-trace": "^1.0.0" @@ -3234,7 +2974,8 @@ }, "cross-spawn": { "version": "5.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -3244,7 +2985,8 @@ "dependencies": { "lru-cache": { "version": "4.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -3253,24 +2995,28 @@ }, "yallist": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true } } }, "crypto-random-string": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "dev": true }, "cyclist": { "version": "0.2.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", "dev": true }, "dashdash": { "version": "1.14.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -3278,7 +3024,8 @@ }, "debug": { "version": "3.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -3286,34 +3033,40 @@ "dependencies": { "ms": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } }, "debuglog": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", "dev": true }, "decamelize": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "decode-uri-component": { "version": "0.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, "defaults": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { "clone": "^1.0.2" @@ -3321,7 +3074,8 @@ }, "define-properties": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -3329,27 +3083,32 @@ }, "delayed-stream": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true }, "detect-indent": { "version": "5.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", "dev": true }, "detect-newline": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "dev": true }, "dezalgo": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", "dev": true, "requires": { "asap": "^2.0.0", @@ -3358,7 +3117,8 @@ }, "dot-prop": { "version": "4.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "dev": true, "requires": { "is-obj": "^1.0.0" @@ -3366,17 +3126,20 @@ }, "dotenv": { "version": "5.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==", "dev": true }, "duplexer3": { "version": "0.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, "duplexify": { "version": "3.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -3387,7 +3150,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3401,7 +3165,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3411,7 +3176,8 @@ }, "ecc-jsbn": { "version": "0.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "optional": true, "requires": { @@ -3421,17 +3187,20 @@ }, "editor": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=", "dev": true }, "emoji-regex": { "version": "7.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "encoding": { "version": "0.1.12", - "bundled": true, + "resolved": false, + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "dev": true, "requires": { "iconv-lite": "~0.4.13" @@ -3439,7 +3208,8 @@ }, "end-of-stream": { "version": "1.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -3447,17 +3217,20 @@ }, "env-paths": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", "dev": true }, "err-code": { "version": "1.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", "dev": true }, "errno": { "version": "0.1.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { "prr": "~1.0.1" @@ -3465,7 +3238,8 @@ }, "es-abstract": { "version": "1.12.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { "es-to-primitive": "^1.1.1", @@ -3477,7 +3251,8 @@ }, "es-to-primitive": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -3487,12 +3262,14 @@ }, "es6-promise": { "version": "4.2.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { "version": "5.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { "es6-promise": "^4.0.3" @@ -3500,12 +3277,14 @@ }, "escape-string-regexp": { "version": "1.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "execa": { "version": "0.7.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -3519,44 +3298,52 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true } } }, "extend": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extsprintf": { "version": "1.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, "fast-deep-equal": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, "figgy-pudding": { "version": "3.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "dev": true }, "find-npm-prefix": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==", "dev": true }, "flush-write-stream": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -3565,7 +3352,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3579,7 +3367,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3589,12 +3378,14 @@ }, "forever-agent": { "version": "0.6.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true }, "form-data": { "version": "2.3.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -3604,7 +3395,8 @@ }, "from2": { "version": "2.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -3613,7 +3405,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3627,7 +3420,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3637,7 +3431,8 @@ }, "fs-minipass": { "version": "1.2.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "requires": { "minipass": "^2.6.0" @@ -3645,7 +3440,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -3656,7 +3452,8 @@ }, "fs-vacuum": { "version": "1.2.10", - "bundled": true, + "resolved": false, + "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -3666,7 +3463,8 @@ }, "fs-write-stream-atomic": { "version": "1.0.10", - "bundled": true, + "resolved": false, + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -3677,12 +3475,14 @@ "dependencies": { "iferr": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3696,7 +3496,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3706,17 +3507,20 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "function-bind": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { "aproba": "^1.0.3", @@ -3731,12 +3535,14 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -3748,12 +3554,14 @@ }, "genfun": { "version": "5.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", "dev": true }, "gentle-fs": { "version": "2.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.3.1.tgz", + "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==", "dev": true, "requires": { "aproba": "^1.1.2", @@ -3771,24 +3579,28 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "iferr": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true } } }, "get-caller-file": { "version": "2.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-stream": { "version": "4.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -3796,7 +3608,8 @@ }, "getpass": { "version": "0.1.7", - "bundled": true, + "resolved": false, + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -3804,7 +3617,8 @@ }, "glob": { "version": "7.1.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3817,7 +3631,8 @@ }, "global-dirs": { "version": "0.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { "ini": "^1.3.4" @@ -3825,7 +3640,8 @@ }, "got": { "version": "6.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { "create-error-class": "^3.0.0", @@ -3843,24 +3659,28 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true } } }, "graceful-fs": { "version": "4.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "har-schema": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { "version": "5.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "dev": true, "requires": { "ajv": "^5.3.0", @@ -3869,7 +3689,8 @@ }, "has": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -3877,32 +3698,38 @@ }, "has-flag": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "has-symbols": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, "hosted-git-info": { "version": "2.8.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, "http-cache-semantics": { "version": "3.8.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", "dev": true }, "http-proxy-agent": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "dev": true, "requires": { "agent-base": "4", @@ -3911,7 +3738,8 @@ }, "http-signature": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -3921,7 +3749,8 @@ }, "https-proxy-agent": { "version": "2.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", @@ -3930,7 +3759,8 @@ }, "humanize-ms": { "version": "1.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "dev": true, "requires": { "ms": "^2.0.0" @@ -3938,7 +3768,8 @@ }, "iconv-lite": { "version": "0.4.23", - "bundled": true, + "resolved": false, + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -3946,12 +3777,14 @@ }, "iferr": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==", "dev": true }, "ignore-walk": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "requires": { "minimatch": "^3.0.4" @@ -3959,22 +3792,26 @@ }, "import-lazy": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", "dev": true }, "imurmurhash": { "version": "0.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "infer-owner": { "version": "1.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "dev": true }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", @@ -3983,17 +3820,20 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "init-package-json": { "version": "1.10.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", "dev": true, "requires": { "glob": "^7.1.1", @@ -4008,22 +3848,26 @@ }, "ip": { "version": "1.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, "ip-regex": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, "is-callable": { "version": "1.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, "is-ci": { "version": "1.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "requires": { "ci-info": "^1.5.0" @@ -4031,14 +3875,16 @@ "dependencies": { "ci-info": { "version": "1.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", "dev": true } } }, "is-cidr": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==", "dev": true, "requires": { "cidr-regex": "^2.0.10" @@ -4046,12 +3892,14 @@ }, "is-date-object": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -4059,7 +3907,8 @@ }, "is-installed-globally": { "version": "0.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { "global-dirs": "^0.1.0", @@ -4068,17 +3917,20 @@ }, "is-npm": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", "dev": true }, "is-obj": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, "is-path-inside": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { "path-is-inside": "^1.0.1" @@ -4086,12 +3938,14 @@ }, "is-redirect": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", "dev": true }, "is-regex": { "version": "1.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { "has": "^1.0.1" @@ -4099,17 +3953,20 @@ }, "is-retry-allowed": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", "dev": true }, "is-stream": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "is-symbol": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "dev": true, "requires": { "has-symbols": "^1.0.0" @@ -4117,58 +3974,69 @@ }, "is-typedarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isstream": { "version": "0.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, "jsbn": { "version": "0.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true, "optional": true }, "json-parse-better-errors": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "json-schema": { "version": "0.2.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, "json-schema-traverse": { "version": "0.3.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, "jsonparse": { "version": "1.3.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, "jsprim": { "version": "1.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, "requires": { "assert-plus": "1.0.0", @@ -4179,7 +4047,8 @@ }, "latest-version": { "version": "3.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { "package-json": "^4.0.0" @@ -4187,12 +4056,14 @@ }, "lazy-property": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=", "dev": true }, "libcipm": { "version": "4.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/libcipm/-/libcipm-4.0.8.tgz", + "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==", "dev": true, "requires": { "bin-links": "^1.1.2", @@ -4214,7 +4085,8 @@ }, "libnpm": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==", "dev": true, "requires": { "bin-links": "^1.1.2", @@ -4241,7 +4113,8 @@ }, "libnpmaccess": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -4252,7 +4125,8 @@ }, "libnpmconfig": { "version": "1.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -4262,7 +4136,8 @@ "dependencies": { "find-up": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { "locate-path": "^3.0.0" @@ -4270,7 +4145,8 @@ }, "locate-path": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { "p-locate": "^3.0.0", @@ -4279,7 +4155,8 @@ }, "p-limit": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -4287,7 +4164,8 @@ }, "p-locate": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { "p-limit": "^2.0.0" @@ -4295,14 +4173,16 @@ }, "p-try": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true } } }, "libnpmhook": { "version": "5.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -4313,7 +4193,8 @@ }, "libnpmorg": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -4324,7 +4205,8 @@ }, "libnpmpublish": { "version": "1.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -4340,7 +4222,8 @@ }, "libnpmsearch": { "version": "2.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -4350,7 +4233,8 @@ }, "libnpmteam": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==", "dev": true, "requires": { "aproba": "^2.0.0", @@ -4361,7 +4245,8 @@ }, "libnpx": { "version": "10.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/libnpx/-/libnpx-10.2.4.tgz", + "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==", "dev": true, "requires": { "dotenv": "^5.0.1", @@ -4376,7 +4261,8 @@ }, "lock-verify": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==", "dev": true, "requires": { "npm-package-arg": "^6.1.0", @@ -4385,7 +4271,8 @@ }, "lockfile": { "version": "1.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "dev": true, "requires": { "signal-exit": "^3.0.2" @@ -4393,12 +4280,14 @@ }, "lodash._baseindexof": { "version": "3.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=", "dev": true }, "lodash._baseuniq": { "version": "4.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", "dev": true, "requires": { "lodash._createset": "~4.0.0", @@ -4407,17 +4296,20 @@ }, "lodash._bindcallback": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", "dev": true }, "lodash._cacheindexof": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=", "dev": true }, "lodash._createcache": { "version": "3.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=", "dev": true, "requires": { "lodash._getnative": "^3.0.0" @@ -4425,52 +4317,62 @@ }, "lodash._createset": { "version": "4.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=", "dev": true }, "lodash._getnative": { "version": "3.9.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", "dev": true }, "lodash._root": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, "lodash.clonedeep": { "version": "4.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, "lodash.restparam": { "version": "3.6.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", "dev": true }, "lodash.union": { "version": "4.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", "dev": true }, "lodash.uniq": { "version": "4.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, "lodash.without": { "version": "4.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=", "dev": true }, "lowercase-keys": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, "lru-cache": { "version": "5.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { "yallist": "^3.0.2" @@ -4478,7 +4380,8 @@ }, "make-dir": { "version": "1.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { "pify": "^3.0.0" @@ -4486,7 +4389,8 @@ }, "make-fetch-happen": { "version": "5.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", "dev": true, "requires": { "agentkeepalive": "^3.4.1", @@ -4504,17 +4408,20 @@ }, "meant": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.2.tgz", + "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg==", "dev": true }, "mime-db": { "version": "1.35.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", "dev": true }, "mime-types": { "version": "2.1.19", - "bundled": true, + "resolved": false, + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "dev": true, "requires": { "mime-db": "~1.35.0" @@ -4522,7 +4429,8 @@ }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4530,12 +4438,14 @@ }, "minimist": { "version": "1.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "minizlib": { "version": "1.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dev": true, "requires": { "minipass": "^2.9.0" @@ -4543,7 +4453,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -4554,7 +4465,8 @@ }, "mississippi": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { "concat-stream": "^1.5.0", @@ -4571,7 +4483,8 @@ }, "mkdirp": { "version": "0.5.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -4579,14 +4492,16 @@ "dependencies": { "minimist": { "version": "1.2.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true } } }, "move-concurrently": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { "aproba": "^1.1.1", @@ -4599,24 +4514,28 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true } } }, "ms": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "mute-stream": { "version": "0.0.7", - "bundled": true, + "resolved": false, + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, "node-fetch-npm": { "version": "2.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", "dev": true, "requires": { "encoding": "^0.1.11", @@ -4626,7 +4545,8 @@ }, "node-gyp": { "version": "5.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==", "dev": true, "requires": { "env-paths": "^2.2.0", @@ -4644,7 +4564,8 @@ }, "nopt": { "version": "4.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, "requires": { "abbrev": "1", @@ -4653,7 +4574,8 @@ }, "normalize-package-data": { "version": "2.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -4664,7 +4586,8 @@ "dependencies": { "resolve": { "version": "1.10.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -4674,7 +4597,8 @@ }, "npm-audit-report": { "version": "1.3.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-1.3.3.tgz", + "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==", "dev": true, "requires": { "cli-table3": "^0.5.0", @@ -4683,7 +4607,8 @@ }, "npm-bundled": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -4691,12 +4616,14 @@ }, "npm-cache-filename": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=", "dev": true }, "npm-install-checks": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==", "dev": true, "requires": { "semver": "^2.3.0 || 3.x || 4 || 5" @@ -4704,7 +4631,8 @@ }, "npm-lifecycle": { "version": "3.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz", + "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==", "dev": true, "requires": { "byline": "^5.0.0", @@ -4719,17 +4647,20 @@ }, "npm-logical-tree": { "version": "1.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==", "dev": true }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true }, "npm-package-arg": { "version": "6.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { "hosted-git-info": "^2.7.1", @@ -4740,7 +4671,8 @@ }, "npm-packlist": { "version": "1.4.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "requires": { "ignore-walk": "^3.0.1", @@ -4750,7 +4682,8 @@ }, "npm-pick-manifest": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -4760,7 +4693,8 @@ }, "npm-profile": { "version": "4.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==", "dev": true, "requires": { "aproba": "^1.1.2 || 2", @@ -4770,7 +4704,8 @@ }, "npm-registry-fetch": { "version": "4.0.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz", + "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", "dev": true, "requires": { "JSONStream": "^1.3.4", @@ -4784,14 +4719,16 @@ "dependencies": { "safe-buffer": { "version": "5.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true } } }, "npm-run-path": { "version": "2.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { "path-key": "^2.0.0" @@ -4799,12 +4736,14 @@ }, "npm-user-validate": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=", "dev": true }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { "are-we-there-yet": "~1.1.2", @@ -4815,27 +4754,32 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "oauth-sign": { "version": "0.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "object-keys": { "version": "1.0.12", - "bundled": true, + "resolved": false, + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", "dev": true }, "object.getownpropertydescriptors": { "version": "2.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -4844,7 +4788,8 @@ }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -4852,22 +4797,26 @@ }, "opener": { "version": "1.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", "dev": true }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "requires": { "os-homedir": "^1.0.0", @@ -4876,12 +4825,14 @@ }, "p-finally": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "package-json": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { "got": "^6.7.1", @@ -4892,7 +4843,8 @@ }, "pacote": { "version": "9.5.12", - "bundled": true, + "resolved": false, + "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", "dev": true, "requires": { "bluebird": "^3.5.3", @@ -4929,7 +4881,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -4940,7 +4893,8 @@ }, "parallel-transform": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "dev": true, "requires": { "cyclist": "~0.2.2", @@ -4950,7 +4904,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -4964,7 +4919,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -4974,57 +4930,68 @@ }, "path-exists": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-is-inside": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, "path-key": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "performance-now": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "pify": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "prepend-http": { "version": "1.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "promise-inflight": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, "promise-retry": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", "dev": true, "requires": { "err-code": "^1.0.0", @@ -5033,14 +5000,16 @@ "dependencies": { "retry": { "version": "0.10.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", "dev": true } } }, "promzard": { "version": "0.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", "dev": true, "requires": { "read": "1" @@ -5048,12 +5017,14 @@ }, "proto-list": { "version": "1.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, "protoduck": { "version": "5.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", "dev": true, "requires": { "genfun": "^5.0.0" @@ -5061,22 +5032,26 @@ }, "prr": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, "pseudomap": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "psl": { "version": "1.1.29", - "bundled": true, + "resolved": false, + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", "dev": true }, "pump": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5085,7 +5060,8 @@ }, "pumpify": { "version": "1.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { "duplexify": "^3.6.0", @@ -5095,7 +5071,8 @@ "dependencies": { "pump": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5106,22 +5083,26 @@ }, "punycode": { "version": "1.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, "qrcode-terminal": { "version": "0.12.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "dev": true }, "qs": { "version": "6.5.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, "query-string": { "version": "6.8.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==", "dev": true, "requires": { "decode-uri-component": "^0.2.0", @@ -5131,12 +5112,14 @@ }, "qw": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=", "dev": true }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { "deep-extend": "^0.6.0", @@ -5147,7 +5130,8 @@ }, "read": { "version": "1.0.7", - "bundled": true, + "resolved": false, + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", "dev": true, "requires": { "mute-stream": "~0.0.4" @@ -5155,7 +5139,8 @@ }, "read-cmd-shim": { "version": "1.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==", "dev": true, "requires": { "graceful-fs": "^4.1.2" @@ -5163,7 +5148,8 @@ }, "read-installed": { "version": "4.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", "dev": true, "requires": { "debuglog": "^1.0.1", @@ -5177,7 +5163,8 @@ }, "read-package-json": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", "dev": true, "requires": { "glob": "^7.1.1", @@ -5189,7 +5176,8 @@ }, "read-package-tree": { "version": "5.3.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", "dev": true, "requires": { "read-package-json": "^2.0.0", @@ -5199,7 +5187,8 @@ }, "readable-stream": { "version": "3.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -5209,7 +5198,8 @@ }, "readdir-scoped-modules": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "dev": true, "requires": { "debuglog": "^1.0.1", @@ -5220,7 +5210,8 @@ }, "registry-auth-token": { "version": "3.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "dev": true, "requires": { "rc": "^1.1.6", @@ -5229,7 +5220,8 @@ }, "registry-url": { "version": "3.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { "rc": "^1.0.1" @@ -5237,7 +5229,8 @@ }, "request": { "version": "2.88.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -5264,27 +5257,32 @@ }, "require-directory": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve-from": { "version": "4.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "retry": { "version": "0.12.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, "rimraf": { "version": "2.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { "glob": "^7.1.3" @@ -5292,7 +5290,8 @@ }, "run-queue": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { "aproba": "^1.1.1" @@ -5300,29 +5299,34 @@ "dependencies": { "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true } } }, "safe-buffer": { "version": "5.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "semver": { "version": "5.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "semver-diff": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { "semver": "^5.0.3" @@ -5330,12 +5334,14 @@ }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "sha": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", "dev": true, "requires": { "graceful-fs": "^4.1.2" @@ -5343,7 +5349,8 @@ }, "shebang-command": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -5351,27 +5358,32 @@ }, "shebang-regex": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slide": { "version": "1.1.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, "smart-buffer": { "version": "4.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", "dev": true }, "socks": { "version": "2.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", "dev": true, "requires": { "ip": "1.1.5", @@ -5380,7 +5392,8 @@ }, "socks-proxy-agent": { "version": "4.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", "dev": true, "requires": { "agent-base": "~4.2.1", @@ -5389,7 +5402,8 @@ "dependencies": { "agent-base": { "version": "4.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -5399,12 +5413,14 @@ }, "sorted-object": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=", "dev": true }, "sorted-union-stream": { "version": "2.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=", "dev": true, "requires": { "from2": "^1.3.0", @@ -5413,7 +5429,8 @@ "dependencies": { "from2": { "version": "1.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=", "dev": true, "requires": { "inherits": "~2.0.1", @@ -5422,12 +5439,14 @@ }, "isarray": { "version": "0.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, "readable-stream": { "version": "1.1.14", - "bundled": true, + "resolved": false, + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5438,14 +5457,16 @@ }, "string_decoder": { "version": "0.10.31", - "bundled": true, + "resolved": false, + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } } }, "spdx-correct": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -5454,12 +5475,14 @@ }, "spdx-exceptions": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -5468,17 +5491,20 @@ }, "spdx-license-ids": { "version": "3.0.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "split-on-first": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", "dev": true }, "sshpk": { "version": "1.14.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "dev": true, "requires": { "asn1": "~0.2.3", @@ -5494,7 +5520,8 @@ }, "ssri": { "version": "6.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -5502,7 +5529,8 @@ }, "stream-each": { "version": "1.2.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5511,7 +5539,8 @@ }, "stream-iterate": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=", "dev": true, "requires": { "readable-stream": "^2.1.5", @@ -5520,7 +5549,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5534,7 +5564,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -5544,17 +5575,20 @@ }, "stream-shift": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, "strict-uri-encode": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, "string-width": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -5563,17 +5597,20 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "strip-ansi": { "version": "4.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -5583,7 +5620,8 @@ }, "string_decoder": { "version": "1.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { "safe-buffer": "~5.2.0" @@ -5591,19 +5629,22 @@ "dependencies": { "safe-buffer": { "version": "5.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true } } }, "stringify-package": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", "dev": true }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -5611,17 +5652,20 @@ }, "strip-eof": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { "version": "5.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5629,7 +5673,8 @@ }, "tar": { "version": "4.4.13", - "bundled": true, + "resolved": false, + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { "chownr": "^1.1.1", @@ -5643,7 +5688,8 @@ "dependencies": { "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -5654,7 +5700,8 @@ }, "term-size": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { "execa": "^0.7.0" @@ -5662,17 +5709,20 @@ }, "text-table": { "version": "0.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "through": { "version": "2.3.8", - "bundled": true, + "resolved": false, + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "through2": { "version": "2.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { "readable-stream": "^2.1.5", @@ -5681,7 +5731,8 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5695,7 +5746,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -5705,17 +5757,20 @@ }, "timed-out": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, "tiny-relative-date": { "version": "1.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==", "dev": true }, "tough-cookie": { "version": "2.4.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { "psl": "^1.1.24", @@ -5724,7 +5779,8 @@ }, "tunnel-agent": { "version": "0.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -5732,28 +5788,33 @@ }, "tweetnacl": { "version": "0.14.5", - "bundled": true, + "resolved": false, + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true, "optional": true }, "typedarray": { "version": "0.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, "uid-number": { "version": "0.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", "dev": true }, "umask": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=", "dev": true }, "unique-filename": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { "unique-slug": "^2.0.0" @@ -5761,7 +5822,8 @@ }, "unique-slug": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -5769,7 +5831,8 @@ }, "unique-string": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { "crypto-random-string": "^1.0.0" @@ -5777,17 +5840,20 @@ }, "unpipe": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, "unzip-response": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", "dev": true }, "update-notifier": { "version": "2.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { "boxen": "^1.2.1", @@ -5804,7 +5870,8 @@ }, "url-parse-lax": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { "prepend-http": "^1.0.1" @@ -5812,17 +5879,20 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, "util-extend": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=", "dev": true }, "util-promisify": { "version": "2.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", "dev": true, "requires": { "object.getownpropertydescriptors": "^2.0.3" @@ -5830,12 +5900,14 @@ }, "uuid": { "version": "3.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, "validate-npm-package-license": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -5844,7 +5916,8 @@ }, "validate-npm-package-name": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", "dev": true, "requires": { "builtins": "^1.0.3" @@ -5852,7 +5925,8 @@ }, "verror": { "version": "1.10.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -5862,7 +5936,8 @@ }, "wcwidth": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "dev": true, "requires": { "defaults": "^1.0.3" @@ -5870,7 +5945,8 @@ }, "which": { "version": "1.3.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -5878,12 +5954,14 @@ }, "which-module": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "requires": { "string-width": "^1.0.2" @@ -5891,7 +5969,8 @@ "dependencies": { "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -5903,7 +5982,8 @@ }, "widest-line": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { "string-width": "^2.1.1" @@ -5911,7 +5991,8 @@ }, "worker-farm": { "version": "1.7.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "dev": true, "requires": { "errno": "~0.1.7" @@ -5919,7 +6000,8 @@ }, "wrap-ansi": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -5929,17 +6011,20 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -5949,7 +6034,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -5959,12 +6045,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write-file-atomic": { "version": "2.4.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -5974,27 +6062,32 @@ }, "xdg-basedir": { "version": "3.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, "xtend": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, "y18n": { "version": "4.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, "yallist": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true }, "yargs": { "version": "14.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -6012,12 +6105,14 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "find-up": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { "locate-path": "^3.0.0" @@ -6025,12 +6120,14 @@ }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "locate-path": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { "p-locate": "^3.0.0", @@ -6039,7 +6136,8 @@ }, "p-limit": { "version": "2.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6047,7 +6145,8 @@ }, "p-locate": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { "p-limit": "^2.0.0" @@ -6055,12 +6154,14 @@ }, "p-try": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "string-width": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -6070,7 +6171,8 @@ }, "strip-ansi": { "version": "5.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -6080,7 +6182,8 @@ }, "yargs-parser": { "version": "15.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -6089,7 +6192,8 @@ "dependencies": { "camelcase": { "version": "5.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true } } From b5a88a7bbf2553f7daf258ada1b779546c74c681 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 26 Sep 2020 21:13:28 +0000 Subject: [PATCH 60/75] chore(release): 0.3.1 ## [0.3.1](https://github.com/justindujardin/pathy/compare/v0.3.0...v0.3.1) (2020-09-26) ### Features * update smart-open to 2.2.0 for minimal deps ([4b3e959](https://github.com/justindujardin/pathy/commit/4b3e959ce1c4c491cb291935e8d47ac537b72485)) * **ci:** add pyright check to lint step ([10ce34d](https://github.com/justindujardin/pathy/commit/10ce34d13ddc99232b3ce7681db27f561d51c87b)) --- CHANGELOG.md | 8 ++++++++ pathy/about.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ae2177..dbf246a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.3.1](https://github.com/justindujardin/pathy/compare/v0.3.0...v0.3.1) (2020-09-26) + + +### Features + +* update smart-open to 2.2.0 for minimal deps ([4b3e959](https://github.com/justindujardin/pathy/commit/4b3e959ce1c4c491cb291935e8d47ac537b72485)) +* **ci:** add pyright check to lint step ([10ce34d](https://github.com/justindujardin/pathy/commit/10ce34d13ddc99232b3ce7681db27f561d51c87b)) + # [0.3.0](https://github.com/justindujardin/pathy/compare/v0.2.0...v0.3.0) (2020-09-04) diff --git a/pathy/about.py b/pathy/about.py index 1c6599a..bab1599 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.3.0" +__version__ = "0.3.1" __summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 9b80f84ca6431fabcda6dbfaefa3a2cc320df0ae Mon Sep 17 00:00:00 2001 From: justindujardin Date: Mon, 2 Nov 2020 16:06:11 -0800 Subject: [PATCH 61/75] test: add a rglob + unlink test - it seems like a reasonably common pattern, make sure it works like rmdir --- tests/test_base.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_base.py b/tests/test_base.py index 7ddfa4f..22c3dc3 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -696,6 +696,21 @@ def test_api_rmdir(with_adapter, bucket: str): assert not path.exists() +@pytest.mark.parametrize("adapter", TEST_ADAPTERS) +def test_api_rglob_unlink(with_adapter, bucket: str): + files = [f"gs://{bucket}/rglob_and_unlink/{i}.file.txt" for i in range(3)] + for file in files: + Pathy(file).write_text("---") + path = Pathy(f"gs://{bucket}/rglob_and_unlink/") + for blob in path.rglob("*"): + blob.unlink() + # All the files are gone + for file in files: + assert Pathy(file).exists() is False + # The folder is gone + assert not path.exists() + + @pytest.mark.parametrize("adapter", TEST_ADAPTERS) def test_api_mkdir(with_adapter, bucket: str): bucket_name = f"pathy-e2e-test-{uuid4().hex}" From d1d6e50857cec83793669f01774b5bbc97edee93 Mon Sep 17 00:00:00 2001 From: justindujardin Date: Mon, 2 Nov 2020 16:12:46 -0800 Subject: [PATCH 62/75] chore: suppress ugly path open type error - the Path open method has a gross type that changes based on the python version. We'll use our specific type and deal with the consequences. :sunglasses: --- pathy/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathy/base.py b/pathy/base.py index 7051df7..6848bd5 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -606,7 +606,7 @@ def rglob(self: "Pathy", pattern: str) -> Generator["Pathy", None, None]: all matched blobs. Imagine adding "**/" before a call to glob.""" yield from super().rglob(pattern) # type:ignore - def open( + def open( # type:ignore self: "Pathy", mode: str = "r", buffering: int = DEFAULT_BUFFER_SIZE, From fdf083eb711f225e577e9f6199a7a403b49bf2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 12 Nov 2020 20:33:20 +0100 Subject: [PATCH 63/75] fix: upgrade smart-open to >=2.2.0,<4.0.0 (#36) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⬆️ Upgrade smart-open pin, to fix botocore requiring urllib3 < 1.26 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 492fc8b..f948ca7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -smart-open>=2.2.0,<3.0.0 +smart-open>=2.2.0,<4.0.0 typer>=0.3.0,<1.0.0 dataclasses>=0.6,<1.0; python_version < "3.7" \ No newline at end of file From 0f8dbbc246175280575fa6059ce7cd03c8a9e1c6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 12 Nov 2020 19:35:38 +0000 Subject: [PATCH 64/75] chore(release): 0.3.2 ## [0.3.2](https://github.com/justindujardin/pathy/compare/v0.3.1...v0.3.2) (2020-11-12) ### Bug Fixes * upgrade smart-open to >=2.2.0,<4.0.0 ([#36](https://github.com/justindujardin/pathy/issues/36)) ([fdf083e](https://github.com/justindujardin/pathy/commit/fdf083eb711f225e577e9f6199a7a403b49bf2ea)) --- CHANGELOG.md | 7 +++++++ pathy/about.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbf246a..364220a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.3.2](https://github.com/justindujardin/pathy/compare/v0.3.1...v0.3.2) (2020-11-12) + + +### Bug Fixes + +* upgrade smart-open to >=2.2.0,<4.0.0 ([#36](https://github.com/justindujardin/pathy/issues/36)) ([fdf083e](https://github.com/justindujardin/pathy/commit/fdf083eb711f225e577e9f6199a7a403b49bf2ea)) + ## [0.3.1](https://github.com/justindujardin/pathy/compare/v0.3.0...v0.3.1) (2020-09-26) diff --git a/pathy/about.py b/pathy/about.py index bab1599..2946884 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.3.1" +__version__ = "0.3.2" __summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 7a0bd6606daab25483f4e0aabf40616c8bab3775 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Thu, 12 Nov 2020 11:57:39 -0800 Subject: [PATCH 65/75] chore: add BucketStat -> BlobStat to changelog --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 364220a..f91577b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,10 +32,11 @@ * This renames the internal GCS/File adapter classes by removing the prefix Client. -ClientBucketFS -> BucketFS -ClientBlobFS -> BlobFS -ClientBucketGCS -> BucketGCS -ClientBlobGCS -> BlobGCS +- ClientBucketFS -> BucketFS +- ClientBlobFS -> BlobFS +- ClientBucketGCS -> BucketGCS +- ClientBlobGCS -> BlobGCS +- BucketStat -> BlobStat * use_fs, get_fs_client, use_fs_cache, get_fs_cache, and clear_fs_cache moved from pathy.api to pathy.clients # [0.2.0](https://github.com/justindujardin/pathy/compare/v0.1.3...v0.2.0) (2020-08-22) From 80f003670e9ce2515b23813fec9dfdf7a6696fb3 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Thu, 12 Nov 2020 12:58:15 -0800 Subject: [PATCH 66/75] fix: path.scheme would error with schemeless paths (#37) - return "" from file paths as expected rather than error --- pathy/base.py | 4 ++++ pathy/clients.py | 1 + tests/test_base.py | 7 +++++++ tests/test_clients.py | 1 + 4 files changed, 13 insertions(+) diff --git a/pathy/base.py b/pathy/base.py index 6848bd5..0d5c2c5 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -235,8 +235,12 @@ def scheme(self) -> str: ```python assert Pathy("gs://foo/bar").scheme == "gs" assert Pathy("file:///tmp/foo/bar").scheme == "file" + assert Pathy("/dev/null").scheme == "" """ + # If there is no drive, return nothing + if self.drive == "": + return "" # This is an assumption of mine. I think it's fine, but let's # cause an error if it's not the case. assert self.drive[-1] == ":", "drive should end with :" diff --git a/pathy/clients.py b/pathy/clients.py index bd61141..f55f7ee 100644 --- a/pathy/clients.py +++ b/pathy/clients.py @@ -11,6 +11,7 @@ # hardcode it (atleast the base schemes) to get nice types flowing # in cases where they would otherwise be lost. _client_registry: Dict[str, Type[BucketClient]] = { + "": BucketClientFS, "file": BucketClientFS, "gs": BucketClientGCS, } diff --git a/tests/test_base.py b/tests/test_base.py index 22c3dc3..188d095 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -129,6 +129,13 @@ def test_base_repr(): assert bytes(PurePathy("fake_file.txt")) == b"fake_file.txt" +def test_base_scheme_extraction(): + assert PurePathy("gs://var/tests/fake").scheme == "gs" + assert PurePathy("s3://var/tests/fake").scheme == "s3" + assert PurePathy("file://var/tests/fake").scheme == "file" + assert PurePathy("/var/tests/fake").scheme == "" + + @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_base_fspath(): assert os.fspath(PurePathy("/var/tests/fake")) == "/var/tests/fake" diff --git a/tests/test_clients.py b/tests/test_clients.py index 972b97b..4676473 100644 --- a/tests/test_clients.py +++ b/tests/test_clients.py @@ -14,6 +14,7 @@ def test_clients_get_client_works_with_builtin_schems(): assert isinstance(get_client("gs"), BucketClientGCS) assert isinstance(get_client("file"), BucketClientFS) + assert isinstance(get_client(""), BucketClientFS) def test_clients_get_client_errors_with_unknown_scheme(): From dd2e1657e8b6c65967679f16fabc82ea17e8e026 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 12 Nov 2020 21:00:14 +0000 Subject: [PATCH 67/75] chore(release): 0.3.3 ## [0.3.3](https://github.com/justindujardin/pathy/compare/v0.3.2...v0.3.3) (2020-11-12) ### Bug Fixes * path.scheme would error with schemeless paths ([#37](https://github.com/justindujardin/pathy/issues/37)) ([80f0036](https://github.com/justindujardin/pathy/commit/80f003670e9ce2515b23813fec9dfdf7a6696fb3)) --- CHANGELOG.md | 7 +++++++ pathy/about.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f91577b..b1cbf2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.3.3](https://github.com/justindujardin/pathy/compare/v0.3.2...v0.3.3) (2020-11-12) + + +### Bug Fixes + +* path.scheme would error with schemeless paths ([#37](https://github.com/justindujardin/pathy/issues/37)) ([80f0036](https://github.com/justindujardin/pathy/commit/80f003670e9ce2515b23813fec9dfdf7a6696fb3)) + ## [0.3.2](https://github.com/justindujardin/pathy/compare/v0.3.1...v0.3.2) (2020-11-12) diff --git a/pathy/about.py b/pathy/about.py index 2946884..5217516 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.3.2" +__version__ = "0.3.3" __summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 6e06dbdb02b1b28e38d8c4954cea763dc33a64c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Nov 2020 10:47:27 -0800 Subject: [PATCH 68/75] chore(deps-dev): bump semantic-release from 17.0.4 to 17.2.3 (#38) Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.0.4 to 17.2.3. - [Release notes](https://github.com/semantic-release/semantic-release/releases) - [Commits](https://github.com/semantic-release/semantic-release/compare/v17.0.4...v17.2.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 2196 ++++++++++++++++++--------------------------- 1 file changed, 866 insertions(+), 1330 deletions(-) diff --git a/package-lock.json b/package-lock.json index 866f3af..e130297 100644 --- a/package-lock.json +++ b/package-lock.json @@ -292,9 +292,9 @@ } }, "@semantic-release/npm": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.5.tgz", - "integrity": "sha512-D+oEmsx9aHE1q806NFQwSC9KdBO8ri/VO99eEz0wWbX2jyLqVyWr7t0IjKC8aSnkkQswg/4KN/ZjfF6iz1XOpw==", + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.8.tgz", + "integrity": "sha512-8c1TLwKB/xT5E1FNs5l4GFtaNTznHesJk7tw3pGSlVxRqDXa1EZI+DfziZlO58Wk3PpS2ecu661kvBdz9aMgYQ==", "dev": true, "requires": { "@semantic-release/error": "^2.2.0", @@ -304,12 +304,12 @@ "lodash": "^4.17.15", "nerf-dart": "^1.0.0", "normalize-url": "^5.0.0", - "npm": "^6.10.3", + "npm": "^6.14.8", "rc": "^1.2.8", "read-pkg": "^5.0.0", "registry-auth-token": "^4.0.0", "semver": "^7.1.2", - "tempy": "^0.5.0" + "tempy": "^1.0.0" }, "dependencies": { "fs-extra": { @@ -325,13 +325,21 @@ } }, "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } } }, "universalify": { @@ -373,9 +381,9 @@ "dev": true }, "@types/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, "@types/node": { @@ -517,6 +525,12 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "before-after-hook": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", @@ -529,6 +543,16 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "dev": true }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -676,71 +700,40 @@ "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", "dev": true }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, "conventional-changelog-angular": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz", - "integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", + "integrity": "sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==", "dev": true, "requires": { "compare-func": "^2.0.0", "q": "^1.5.1" - }, - "dependencies": { - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - } } }, "conventional-changelog-writer": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz", - "integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz", + "integrity": "sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A==", "dev": true, "requires": { "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.6", + "conventional-commits-filter": "^2.0.7", "dateformat": "^3.0.0", "handlebars": "^4.7.6", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.15", - "meow": "^7.0.0", + "meow": "^8.0.0", "semver": "^6.0.0", "split": "^1.0.0", - "through2": "^3.0.0" + "through2": "^4.0.0" }, "dependencies": { - "conventional-commits-filter": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz", - "integrity": "sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw==", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -750,9 +743,9 @@ } }, "conventional-commits-filter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", - "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", "dev": true, "requires": { "lodash.ismatch": "^4.4.0", @@ -760,120 +753,18 @@ } }, "conventional-commits-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz", - "integrity": "sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz", + "integrity": "sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ==", "dev": true, "requires": { "JSONStream": "^1.0.4", "is-text-path": "^1.0.1", "lodash": "^4.17.15", - "meow": "^7.0.0", + "meow": "^8.0.0", "split2": "^2.0.0", - "through2": "^3.0.0", + "through2": "^4.0.0", "trim-off-newlines": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "map-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", - "dev": true - }, - "meow": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", - "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^2.5.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - } - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "trim-newlines": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", - "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", - "dev": true - }, - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } } }, "core-util-is": { @@ -977,6 +868,53 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + } + } + }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -999,9 +937,9 @@ } }, "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { "is-obj": "^2.0.0" @@ -1144,12 +1082,13 @@ } }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "find-versions": { @@ -1182,6 +1121,18 @@ "universalify": "^0.1.0" } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1238,6 +1189,20 @@ } } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -1286,6 +1251,15 @@ "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1299,12 +1273,12 @@ "dev": true }, "hosted-git-info": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz", - "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", "dev": true, "requires": { - "lru-cache": "^5.1.1" + "lru-cache": "^6.0.0" } }, "http-proxy-agent": { @@ -1443,6 +1417,16 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1471,6 +1455,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1504,6 +1497,18 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -1813,16 +1818,27 @@ "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } } }, "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "lodash": { @@ -1985,12 +2001,12 @@ } }, "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" } }, "macos-release": { @@ -2012,39 +2028,38 @@ "dev": true }, "marked": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.0.tgz", - "integrity": "sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.4.tgz", + "integrity": "sha512-6x5TFGCTKSQBLTZtOburGxCxFEBJEGYVLwCMTBCxzvyuisGcC20UNzDSJhCr/cJ/Kmh6ulfJm10g6WWEAJ3kvg==", "dev": true }, "marked-terminal": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.0.0.tgz", - "integrity": "sha512-mzU3VD7aVz12FfGoKFAceijehA6Ocjfg3rVimvJbFAB/NOYCsuzRVtq3PSFdPmWI5mhdGeEh3/aMJ5DSxAz94Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.1.0.tgz", + "integrity": "sha512-5KllfAOW02WS6hLRQ7cNvGOxvKW1BKuXELH4EtbWfyWgxQhROoMxEvuQ/3fTgkNjledR0J48F4HbapvYp1zWkQ==", "dev": true, "requires": { - "ansi-escapes": "^4.3.0", + "ansi-escapes": "^4.3.1", "cardinal": "^2.1.1", - "chalk": "^3.0.0", + "chalk": "^4.0.0", "cli-table": "^0.3.1", "node-emoji": "^1.10.0", - "supports-hyperlinks": "^2.0.0" + "supports-hyperlinks": "^2.1.0" }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2073,9 +2088,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -2084,9 +2099,9 @@ } }, "meow": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", - "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", "dev": true, "requires": { "@types/minimist": "^1.2.0", @@ -2094,20 +2109,12 @@ "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", - "normalize-package-data": "^2.5.0", + "normalize-package-data": "^3.0.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", "trim-newlines": "^3.0.0", - "type-fest": "^0.13.1", - "yargs-parser": "^18.1.3" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true - } + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" } }, "merge-stream": { @@ -2150,6 +2157,15 @@ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", @@ -2213,29 +2229,15 @@ "dev": true }, "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } } }, "normalize-path": { @@ -2245,9 +2247,9 @@ "dev": true }, "normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.1.0.tgz", - "integrity": "sha512-UxHuSWsSAmzSqN+DSjasaZWQ3QPtEisHdlr4y9MJ5zg0RcImv5fQt8QM0izJSCdsdmhJGK+ubcTpJXwVDmwSVQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.3.0.tgz", + "integrity": "sha512-9/nOVLYYe/dO/eJeQUNaGUF4m4Z5E7cb9oNTKabH+bNf19mqj60txTcveQxL0GlcWLXCxkOu2/LwL8oW0idIDA==", "dev": true }, "npm": { @@ -2383,8 +2385,7 @@ "dependencies": { "JSONStream": { "version": "1.3.5", - "resolved": false, - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "bundled": true, "dev": true, "requires": { "jsonparse": "^1.2.0", @@ -2393,14 +2394,12 @@ }, "abbrev": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "bundled": true, "dev": true }, "agent-base": { "version": "4.3.0", - "resolved": false, - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "bundled": true, "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -2408,8 +2407,7 @@ }, "agentkeepalive": { "version": "3.5.2", - "resolved": false, - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "bundled": true, "dev": true, "requires": { "humanize-ms": "^1.2.1" @@ -2417,8 +2415,7 @@ }, "ajv": { "version": "5.5.2", - "resolved": false, - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "bundled": true, "dev": true, "requires": { "co": "^4.6.0", @@ -2429,8 +2426,7 @@ }, "ansi-align": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "bundled": true, "dev": true, "requires": { "string-width": "^2.0.0" @@ -2438,14 +2434,12 @@ }, "ansi-regex": { "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "bundled": true, "dev": true }, "ansi-styles": { "version": "3.2.1", - "resolved": false, - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "bundled": true, "dev": true, "requires": { "color-convert": "^1.9.0" @@ -2453,32 +2447,27 @@ }, "ansicolors": { "version": "0.3.2", - "resolved": false, - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", + "bundled": true, "dev": true }, "ansistyles": { "version": "0.1.3", - "resolved": false, - "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=", + "bundled": true, "dev": true }, "aproba": { "version": "2.0.0", - "resolved": false, - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "bundled": true, "dev": true }, "archy": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "bundled": true, "dev": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": false, - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "bundled": true, "dev": true, "requires": { "delegates": "^1.0.0", @@ -2487,8 +2476,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2502,8 +2490,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -2513,14 +2500,12 @@ }, "asap": { "version": "2.0.6", - "resolved": false, - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "bundled": true, "dev": true }, "asn1": { "version": "0.2.4", - "resolved": false, - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "bundled": true, "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -2528,38 +2513,32 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "bundled": true, "dev": true }, "asynckit": { "version": "0.4.0", - "resolved": false, - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "bundled": true, "dev": true }, "aws-sign2": { "version": "0.7.0", - "resolved": false, - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "bundled": true, "dev": true }, "aws4": { "version": "1.8.0", - "resolved": false, - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "bundled": true, "dev": true }, "balanced-match": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "bundled": true, "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -2568,8 +2547,7 @@ }, "bin-links": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.8.tgz", - "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==", + "bundled": true, "dev": true, "requires": { "bluebird": "^3.5.3", @@ -2582,14 +2560,12 @@ }, "bluebird": { "version": "3.5.5", - "resolved": false, - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "bundled": true, "dev": true }, "boxen": { "version": "1.3.0", - "resolved": false, - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "bundled": true, "dev": true, "requires": { "ansi-align": "^2.0.0", @@ -2603,8 +2579,7 @@ }, "brace-expansion": { "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "bundled": true, "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -2613,32 +2588,27 @@ }, "buffer-from": { "version": "1.0.0", - "resolved": false, - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "bundled": true, "dev": true }, "builtins": { "version": "1.0.3", - "resolved": false, - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "bundled": true, "dev": true }, "byline": { "version": "5.0.0", - "resolved": false, - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", + "bundled": true, "dev": true }, "byte-size": { "version": "5.0.1", - "resolved": false, - "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", + "bundled": true, "dev": true }, "cacache": { "version": "12.0.3", - "resolved": false, - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "bundled": true, "dev": true, "requires": { "bluebird": "^3.5.5", @@ -2660,32 +2630,27 @@ }, "call-limit": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==", + "bundled": true, "dev": true }, "camelcase": { "version": "4.1.0", - "resolved": false, - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "bundled": true, "dev": true }, "capture-stack-trace": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "bundled": true, "dev": true }, "caseless": { "version": "0.12.0", - "resolved": false, - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "bundled": true, "dev": true }, "chalk": { "version": "2.4.1", - "resolved": false, - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "bundled": true, "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -2695,20 +2660,17 @@ }, "chownr": { "version": "1.1.4", - "resolved": false, - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "bundled": true, "dev": true }, "ci-info": { "version": "2.0.0", - "resolved": false, - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "bundled": true, "dev": true }, "cidr-regex": { "version": "2.0.10", - "resolved": false, - "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==", + "bundled": true, "dev": true, "requires": { "ip-regex": "^2.1.0" @@ -2716,14 +2678,12 @@ }, "cli-boxes": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "bundled": true, "dev": true }, "cli-columns": { "version": "3.1.2", - "resolved": false, - "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=", + "bundled": true, "dev": true, "requires": { "string-width": "^2.0.0", @@ -2732,8 +2692,7 @@ }, "cli-table3": { "version": "0.5.1", - "resolved": false, - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "bundled": true, "dev": true, "requires": { "colors": "^1.1.2", @@ -2743,8 +2702,7 @@ }, "cliui": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "bundled": true, "dev": true, "requires": { "string-width": "^3.1.0", @@ -2754,20 +2712,17 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "bundled": true, "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "bundled": true, "dev": true }, "string-width": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "bundled": true, "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -2777,8 +2732,7 @@ }, "strip-ansi": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "bundled": true, "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -2788,14 +2742,12 @@ }, "clone": { "version": "1.0.4", - "resolved": false, - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "bundled": true, "dev": true }, "cmd-shim": { "version": "3.0.3", - "resolved": false, - "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==", + "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -2804,20 +2756,17 @@ }, "co": { "version": "4.6.0", - "resolved": false, - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "bundled": true, "dev": true }, "code-point-at": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "bundled": true, "dev": true }, "color-convert": { "version": "1.9.1", - "resolved": false, - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "bundled": true, "dev": true, "requires": { "color-name": "^1.1.1" @@ -2825,21 +2774,18 @@ }, "color-name": { "version": "1.1.3", - "resolved": false, - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "bundled": true, "dev": true }, "colors": { "version": "1.3.3", - "resolved": false, - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "bundled": true, "dev": true, "optional": true }, "columnify": { "version": "1.5.4", - "resolved": false, - "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", + "bundled": true, "dev": true, "requires": { "strip-ansi": "^3.0.0", @@ -2848,8 +2794,7 @@ }, "combined-stream": { "version": "1.0.6", - "resolved": false, - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "bundled": true, "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -2857,14 +2802,12 @@ }, "concat-map": { "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "bundled": true, "dev": true }, "concat-stream": { "version": "1.6.2", - "resolved": false, - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "bundled": true, "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -2875,8 +2818,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2890,8 +2832,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -2901,8 +2842,7 @@ }, "config-chain": { "version": "1.1.12", - "resolved": false, - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "bundled": true, "dev": true, "requires": { "ini": "^1.3.4", @@ -2911,8 +2851,7 @@ }, "configstore": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", - "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "bundled": true, "dev": true, "requires": { "dot-prop": "^4.2.1", @@ -2925,14 +2864,12 @@ }, "console-control-strings": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "bundled": true, "dev": true }, "copy-concurrently": { "version": "1.0.5", - "resolved": false, - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "bundled": true, "dev": true, "requires": { "aproba": "^1.1.1", @@ -2945,28 +2882,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "dev": true }, "iferr": { "version": "0.1.5", - "resolved": false, - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "bundled": true, "dev": true } } }, "core-util-is": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "bundled": true, "dev": true }, "create-error-class": { "version": "3.0.2", - "resolved": false, - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "bundled": true, "dev": true, "requires": { "capture-stack-trace": "^1.0.0" @@ -2974,8 +2907,7 @@ }, "cross-spawn": { "version": "5.1.0", - "resolved": false, - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "bundled": true, "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -2985,8 +2917,7 @@ "dependencies": { "lru-cache": { "version": "4.1.5", - "resolved": false, - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "bundled": true, "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -2995,28 +2926,24 @@ }, "yallist": { "version": "2.1.2", - "resolved": false, - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "bundled": true, "dev": true } } }, "crypto-random-string": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "bundled": true, "dev": true }, "cyclist": { "version": "0.2.2", - "resolved": false, - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "bundled": true, "dev": true }, "dashdash": { "version": "1.14.1", - "resolved": false, - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "bundled": true, "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -3024,8 +2951,7 @@ }, "debug": { "version": "3.1.0", - "resolved": false, - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "bundled": true, "dev": true, "requires": { "ms": "2.0.0" @@ -3033,40 +2959,34 @@ "dependencies": { "ms": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "bundled": true, "dev": true } } }, "debuglog": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", + "bundled": true, "dev": true }, "decamelize": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "bundled": true, "dev": true }, "decode-uri-component": { "version": "0.2.0", - "resolved": false, - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "bundled": true, "dev": true }, "deep-extend": { "version": "0.6.0", - "resolved": false, - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "bundled": true, "dev": true }, "defaults": { "version": "1.0.3", - "resolved": false, - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "bundled": true, "dev": true, "requires": { "clone": "^1.0.2" @@ -3074,8 +2994,7 @@ }, "define-properties": { "version": "1.1.3", - "resolved": false, - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "bundled": true, "dev": true, "requires": { "object-keys": "^1.0.12" @@ -3083,32 +3002,27 @@ }, "delayed-stream": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "bundled": true, "dev": true }, "delegates": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "bundled": true, "dev": true }, "detect-indent": { "version": "5.0.0", - "resolved": false, - "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "bundled": true, "dev": true }, "detect-newline": { "version": "2.1.0", - "resolved": false, - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "bundled": true, "dev": true }, "dezalgo": { "version": "1.0.3", - "resolved": false, - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "bundled": true, "dev": true, "requires": { "asap": "^2.0.0", @@ -3117,8 +3031,7 @@ }, "dot-prop": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "bundled": true, "dev": true, "requires": { "is-obj": "^1.0.0" @@ -3126,20 +3039,17 @@ }, "dotenv": { "version": "5.0.1", - "resolved": false, - "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==", + "bundled": true, "dev": true }, "duplexer3": { "version": "0.1.4", - "resolved": false, - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "bundled": true, "dev": true }, "duplexify": { "version": "3.6.0", - "resolved": false, - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "bundled": true, "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -3150,8 +3060,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3165,8 +3074,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3176,8 +3084,7 @@ }, "ecc-jsbn": { "version": "0.1.2", - "resolved": false, - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "bundled": true, "dev": true, "optional": true, "requires": { @@ -3187,20 +3094,17 @@ }, "editor": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=", + "bundled": true, "dev": true }, "emoji-regex": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "bundled": true, "dev": true }, "encoding": { "version": "0.1.12", - "resolved": false, - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "bundled": true, "dev": true, "requires": { "iconv-lite": "~0.4.13" @@ -3208,8 +3112,7 @@ }, "end-of-stream": { "version": "1.4.1", - "resolved": false, - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "bundled": true, "dev": true, "requires": { "once": "^1.4.0" @@ -3217,20 +3120,17 @@ }, "env-paths": { "version": "2.2.0", - "resolved": false, - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "bundled": true, "dev": true }, "err-code": { "version": "1.1.2", - "resolved": false, - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "bundled": true, "dev": true }, "errno": { "version": "0.1.7", - "resolved": false, - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "bundled": true, "dev": true, "requires": { "prr": "~1.0.1" @@ -3238,8 +3138,7 @@ }, "es-abstract": { "version": "1.12.0", - "resolved": false, - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "bundled": true, "dev": true, "requires": { "es-to-primitive": "^1.1.1", @@ -3251,8 +3150,7 @@ }, "es-to-primitive": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "bundled": true, "dev": true, "requires": { "is-callable": "^1.1.4", @@ -3262,14 +3160,12 @@ }, "es6-promise": { "version": "4.2.8", - "resolved": false, - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "bundled": true, "dev": true }, "es6-promisify": { "version": "5.0.0", - "resolved": false, - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "bundled": true, "dev": true, "requires": { "es6-promise": "^4.0.3" @@ -3277,14 +3173,12 @@ }, "escape-string-regexp": { "version": "1.0.5", - "resolved": false, - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "bundled": true, "dev": true }, "execa": { "version": "0.7.0", - "resolved": false, - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "bundled": true, "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -3298,52 +3192,44 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "bundled": true, "dev": true } } }, "extend": { "version": "3.0.2", - "resolved": false, - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "bundled": true, "dev": true }, "extsprintf": { "version": "1.3.0", - "resolved": false, - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "bundled": true, "dev": true }, "fast-deep-equal": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "bundled": true, "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "bundled": true, "dev": true }, "figgy-pudding": { "version": "3.5.1", - "resolved": false, - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "bundled": true, "dev": true }, "find-npm-prefix": { "version": "1.0.2", - "resolved": false, - "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==", + "bundled": true, "dev": true }, "flush-write-stream": { "version": "1.0.3", - "resolved": false, - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "bundled": true, "dev": true, "requires": { "inherits": "^2.0.1", @@ -3352,8 +3238,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3367,8 +3252,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3378,14 +3262,12 @@ }, "forever-agent": { "version": "0.6.1", - "resolved": false, - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "bundled": true, "dev": true }, "form-data": { "version": "2.3.2", - "resolved": false, - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "bundled": true, "dev": true, "requires": { "asynckit": "^0.4.0", @@ -3395,8 +3277,7 @@ }, "from2": { "version": "2.3.0", - "resolved": false, - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "bundled": true, "dev": true, "requires": { "inherits": "^2.0.1", @@ -3405,8 +3286,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3420,8 +3300,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3431,8 +3310,7 @@ }, "fs-minipass": { "version": "1.2.7", - "resolved": false, - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "bundled": true, "dev": true, "requires": { "minipass": "^2.6.0" @@ -3440,8 +3318,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -3452,8 +3329,7 @@ }, "fs-vacuum": { "version": "1.2.10", - "resolved": false, - "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", + "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -3463,8 +3339,7 @@ }, "fs-write-stream-atomic": { "version": "1.0.10", - "resolved": false, - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -3475,14 +3350,12 @@ "dependencies": { "iferr": { "version": "0.1.5", - "resolved": false, - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "bundled": true, "dev": true }, "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3496,8 +3369,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3507,20 +3379,17 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "bundled": true, "dev": true }, "function-bind": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "bundled": true, "dev": true }, "gauge": { "version": "2.7.4", - "resolved": false, - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "bundled": true, "dev": true, "requires": { "aproba": "^1.0.3", @@ -3535,14 +3404,12 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "dev": true }, "string-width": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -3554,14 +3421,12 @@ }, "genfun": { "version": "5.0.0", - "resolved": false, - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "bundled": true, "dev": true }, "gentle-fs": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.3.1.tgz", - "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==", + "bundled": true, "dev": true, "requires": { "aproba": "^1.1.2", @@ -3579,28 +3444,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "dev": true }, "iferr": { "version": "0.1.5", - "resolved": false, - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "bundled": true, "dev": true } } }, "get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "bundled": true, "dev": true }, "get-stream": { "version": "4.1.0", - "resolved": false, - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "bundled": true, "dev": true, "requires": { "pump": "^3.0.0" @@ -3608,8 +3469,7 @@ }, "getpass": { "version": "0.1.7", - "resolved": false, - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "bundled": true, "dev": true, "requires": { "assert-plus": "^1.0.0" @@ -3617,8 +3477,7 @@ }, "glob": { "version": "7.1.6", - "resolved": false, - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "bundled": true, "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3631,8 +3490,7 @@ }, "global-dirs": { "version": "0.1.1", - "resolved": false, - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "bundled": true, "dev": true, "requires": { "ini": "^1.3.4" @@ -3640,8 +3498,7 @@ }, "got": { "version": "6.7.1", - "resolved": false, - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "bundled": true, "dev": true, "requires": { "create-error-class": "^3.0.0", @@ -3659,28 +3516,24 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "bundled": true, "dev": true } } }, "graceful-fs": { "version": "4.2.4", - "resolved": false, - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "bundled": true, "dev": true }, "har-schema": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "bundled": true, "dev": true }, "har-validator": { "version": "5.1.0", - "resolved": false, - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", + "bundled": true, "dev": true, "requires": { "ajv": "^5.3.0", @@ -3689,8 +3542,7 @@ }, "has": { "version": "1.0.3", - "resolved": false, - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "bundled": true, "dev": true, "requires": { "function-bind": "^1.1.1" @@ -3698,38 +3550,32 @@ }, "has-flag": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "bundled": true, "dev": true }, "has-symbols": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "bundled": true, "dev": true }, "has-unicode": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "bundled": true, "dev": true }, "hosted-git-info": { "version": "2.8.8", - "resolved": false, - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "bundled": true, "dev": true }, "http-cache-semantics": { "version": "3.8.1", - "resolved": false, - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "bundled": true, "dev": true }, "http-proxy-agent": { "version": "2.1.0", - "resolved": false, - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "bundled": true, "dev": true, "requires": { "agent-base": "4", @@ -3738,8 +3584,7 @@ }, "http-signature": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "bundled": true, "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -3749,8 +3594,7 @@ }, "https-proxy-agent": { "version": "2.2.4", - "resolved": false, - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "bundled": true, "dev": true, "requires": { "agent-base": "^4.3.0", @@ -3759,8 +3603,7 @@ }, "humanize-ms": { "version": "1.2.1", - "resolved": false, - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "bundled": true, "dev": true, "requires": { "ms": "^2.0.0" @@ -3768,8 +3611,7 @@ }, "iconv-lite": { "version": "0.4.23", - "resolved": false, - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "bundled": true, "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -3777,14 +3619,12 @@ }, "iferr": { "version": "1.0.2", - "resolved": false, - "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==", + "bundled": true, "dev": true }, "ignore-walk": { "version": "3.0.3", - "resolved": false, - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "bundled": true, "dev": true, "requires": { "minimatch": "^3.0.4" @@ -3792,26 +3632,22 @@ }, "import-lazy": { "version": "2.1.0", - "resolved": false, - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "bundled": true, "dev": true }, "imurmurhash": { "version": "0.1.4", - "resolved": false, - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "bundled": true, "dev": true }, "infer-owner": { "version": "1.0.4", - "resolved": false, - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "bundled": true, "dev": true }, "inflight": { "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "bundled": true, "dev": true, "requires": { "once": "^1.3.0", @@ -3820,20 +3656,17 @@ }, "inherits": { "version": "2.0.4", - "resolved": false, - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "bundled": true, "dev": true }, "ini": { "version": "1.3.5", - "resolved": false, - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "bundled": true, "dev": true }, "init-package-json": { "version": "1.10.3", - "resolved": false, - "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", + "bundled": true, "dev": true, "requires": { "glob": "^7.1.1", @@ -3848,26 +3681,22 @@ }, "ip": { "version": "1.1.5", - "resolved": false, - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "bundled": true, "dev": true }, "ip-regex": { "version": "2.1.0", - "resolved": false, - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "bundled": true, "dev": true }, "is-callable": { "version": "1.1.4", - "resolved": false, - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "bundled": true, "dev": true }, "is-ci": { "version": "1.2.1", - "resolved": false, - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "bundled": true, "dev": true, "requires": { "ci-info": "^1.5.0" @@ -3875,16 +3704,14 @@ "dependencies": { "ci-info": { "version": "1.6.0", - "resolved": false, - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "bundled": true, "dev": true } } }, "is-cidr": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==", + "bundled": true, "dev": true, "requires": { "cidr-regex": "^2.0.10" @@ -3892,14 +3719,12 @@ }, "is-date-object": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "bundled": true, "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -3907,8 +3732,7 @@ }, "is-installed-globally": { "version": "0.1.0", - "resolved": false, - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "bundled": true, "dev": true, "requires": { "global-dirs": "^0.1.0", @@ -3917,20 +3741,17 @@ }, "is-npm": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "bundled": true, "dev": true }, "is-obj": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "bundled": true, "dev": true }, "is-path-inside": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "bundled": true, "dev": true, "requires": { "path-is-inside": "^1.0.1" @@ -3938,14 +3759,12 @@ }, "is-redirect": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "bundled": true, "dev": true }, "is-regex": { "version": "1.0.4", - "resolved": false, - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "bundled": true, "dev": true, "requires": { "has": "^1.0.1" @@ -3953,20 +3772,17 @@ }, "is-retry-allowed": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "bundled": true, "dev": true }, "is-stream": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "bundled": true, "dev": true }, "is-symbol": { "version": "1.0.2", - "resolved": false, - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "bundled": true, "dev": true, "requires": { "has-symbols": "^1.0.0" @@ -3974,69 +3790,58 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "bundled": true, "dev": true }, "isarray": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "bundled": true, "dev": true }, "isexe": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "bundled": true, "dev": true }, "isstream": { "version": "0.1.2", - "resolved": false, - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "bundled": true, "dev": true }, "jsbn": { "version": "0.1.1", - "resolved": false, - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "bundled": true, "dev": true, "optional": true }, "json-parse-better-errors": { "version": "1.0.2", - "resolved": false, - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "bundled": true, "dev": true }, "json-schema": { "version": "0.2.3", - "resolved": false, - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "bundled": true, "dev": true }, "json-schema-traverse": { "version": "0.3.1", - "resolved": false, - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "bundled": true, "dev": true }, "json-stringify-safe": { "version": "5.0.1", - "resolved": false, - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "bundled": true, "dev": true }, "jsonparse": { "version": "1.3.1", - "resolved": false, - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "bundled": true, "dev": true }, "jsprim": { "version": "1.4.1", - "resolved": false, - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "bundled": true, "dev": true, "requires": { "assert-plus": "1.0.0", @@ -4047,8 +3852,7 @@ }, "latest-version": { "version": "3.1.0", - "resolved": false, - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "bundled": true, "dev": true, "requires": { "package-json": "^4.0.0" @@ -4056,14 +3860,12 @@ }, "lazy-property": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=", + "bundled": true, "dev": true }, "libcipm": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/libcipm/-/libcipm-4.0.8.tgz", - "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==", + "bundled": true, "dev": true, "requires": { "bin-links": "^1.1.2", @@ -4085,8 +3887,7 @@ }, "libnpm": { "version": "3.0.1", - "resolved": false, - "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==", + "bundled": true, "dev": true, "requires": { "bin-links": "^1.1.2", @@ -4113,8 +3914,7 @@ }, "libnpmaccess": { "version": "3.0.2", - "resolved": false, - "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==", + "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", @@ -4125,8 +3925,7 @@ }, "libnpmconfig": { "version": "1.2.1", - "resolved": false, - "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", + "bundled": true, "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -4136,8 +3935,7 @@ "dependencies": { "find-up": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "bundled": true, "dev": true, "requires": { "locate-path": "^3.0.0" @@ -4145,8 +3943,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "bundled": true, "dev": true, "requires": { "p-locate": "^3.0.0", @@ -4155,8 +3952,7 @@ }, "p-limit": { "version": "2.2.0", - "resolved": false, - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "bundled": true, "dev": true, "requires": { "p-try": "^2.0.0" @@ -4164,8 +3960,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "bundled": true, "dev": true, "requires": { "p-limit": "^2.0.0" @@ -4173,16 +3968,14 @@ }, "p-try": { "version": "2.2.0", - "resolved": false, - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "bundled": true, "dev": true } } }, "libnpmhook": { "version": "5.0.3", - "resolved": false, - "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==", + "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", @@ -4193,8 +3986,7 @@ }, "libnpmorg": { "version": "1.0.1", - "resolved": false, - "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==", + "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", @@ -4205,8 +3997,7 @@ }, "libnpmpublish": { "version": "1.1.2", - "resolved": false, - "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==", + "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", @@ -4222,8 +4013,7 @@ }, "libnpmsearch": { "version": "2.0.2", - "resolved": false, - "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==", + "bundled": true, "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -4233,8 +4023,7 @@ }, "libnpmteam": { "version": "1.0.2", - "resolved": false, - "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==", + "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", @@ -4245,8 +4034,7 @@ }, "libnpx": { "version": "10.2.4", - "resolved": "https://registry.npmjs.org/libnpx/-/libnpx-10.2.4.tgz", - "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==", + "bundled": true, "dev": true, "requires": { "dotenv": "^5.0.1", @@ -4261,8 +4049,7 @@ }, "lock-verify": { "version": "2.1.0", - "resolved": false, - "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==", + "bundled": true, "dev": true, "requires": { "npm-package-arg": "^6.1.0", @@ -4271,8 +4058,7 @@ }, "lockfile": { "version": "1.0.4", - "resolved": false, - "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "bundled": true, "dev": true, "requires": { "signal-exit": "^3.0.2" @@ -4280,14 +4066,12 @@ }, "lodash._baseindexof": { "version": "3.1.0", - "resolved": false, - "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=", + "bundled": true, "dev": true }, "lodash._baseuniq": { "version": "4.6.0", - "resolved": false, - "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", + "bundled": true, "dev": true, "requires": { "lodash._createset": "~4.0.0", @@ -4296,20 +4080,17 @@ }, "lodash._bindcallback": { "version": "3.0.1", - "resolved": false, - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", + "bundled": true, "dev": true }, "lodash._cacheindexof": { "version": "3.0.2", - "resolved": false, - "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=", + "bundled": true, "dev": true }, "lodash._createcache": { "version": "3.1.2", - "resolved": false, - "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=", + "bundled": true, "dev": true, "requires": { "lodash._getnative": "^3.0.0" @@ -4317,62 +4098,52 @@ }, "lodash._createset": { "version": "4.0.3", - "resolved": false, - "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=", + "bundled": true, "dev": true }, "lodash._getnative": { "version": "3.9.1", - "resolved": false, - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "bundled": true, "dev": true }, "lodash._root": { "version": "3.0.1", - "resolved": false, - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "bundled": true, "dev": true }, "lodash.clonedeep": { "version": "4.5.0", - "resolved": false, - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "bundled": true, "dev": true }, "lodash.restparam": { "version": "3.6.1", - "resolved": false, - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "bundled": true, "dev": true }, "lodash.union": { "version": "4.6.0", - "resolved": false, - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", + "bundled": true, "dev": true }, "lodash.uniq": { "version": "4.5.0", - "resolved": false, - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "bundled": true, "dev": true }, "lodash.without": { "version": "4.4.0", - "resolved": false, - "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=", + "bundled": true, "dev": true }, "lowercase-keys": { "version": "1.0.1", - "resolved": false, - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "bundled": true, "dev": true }, "lru-cache": { "version": "5.1.1", - "resolved": false, - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "bundled": true, "dev": true, "requires": { "yallist": "^3.0.2" @@ -4380,8 +4151,7 @@ }, "make-dir": { "version": "1.3.0", - "resolved": false, - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "bundled": true, "dev": true, "requires": { "pify": "^3.0.0" @@ -4389,8 +4159,7 @@ }, "make-fetch-happen": { "version": "5.0.2", - "resolved": false, - "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", + "bundled": true, "dev": true, "requires": { "agentkeepalive": "^3.4.1", @@ -4408,20 +4177,17 @@ }, "meant": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.2.tgz", - "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg==", + "bundled": true, "dev": true }, "mime-db": { "version": "1.35.0", - "resolved": false, - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", + "bundled": true, "dev": true }, "mime-types": { "version": "2.1.19", - "resolved": false, - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "bundled": true, "dev": true, "requires": { "mime-db": "~1.35.0" @@ -4429,8 +4195,7 @@ }, "minimatch": { "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "bundled": true, "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4438,14 +4203,12 @@ }, "minimist": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "bundled": true, "dev": true }, "minizlib": { "version": "1.3.3", - "resolved": false, - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "bundled": true, "dev": true, "requires": { "minipass": "^2.9.0" @@ -4453,8 +4216,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -4465,8 +4227,7 @@ }, "mississippi": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "bundled": true, "dev": true, "requires": { "concat-stream": "^1.5.0", @@ -4483,8 +4244,7 @@ }, "mkdirp": { "version": "0.5.5", - "resolved": false, - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "bundled": true, "dev": true, "requires": { "minimist": "^1.2.5" @@ -4492,16 +4252,14 @@ "dependencies": { "minimist": { "version": "1.2.5", - "resolved": false, - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "bundled": true, "dev": true } } }, "move-concurrently": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "bundled": true, "dev": true, "requires": { "aproba": "^1.1.1", @@ -4514,28 +4272,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "dev": true } } }, "ms": { "version": "2.1.1", - "resolved": false, - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "bundled": true, "dev": true }, "mute-stream": { "version": "0.0.7", - "resolved": false, - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "bundled": true, "dev": true }, "node-fetch-npm": { "version": "2.0.2", - "resolved": false, - "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "bundled": true, "dev": true, "requires": { "encoding": "^0.1.11", @@ -4545,8 +4299,7 @@ }, "node-gyp": { "version": "5.1.0", - "resolved": false, - "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==", + "bundled": true, "dev": true, "requires": { "env-paths": "^2.2.0", @@ -4564,8 +4317,7 @@ }, "nopt": { "version": "4.0.3", - "resolved": false, - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "bundled": true, "dev": true, "requires": { "abbrev": "1", @@ -4574,8 +4326,7 @@ }, "normalize-package-data": { "version": "2.5.0", - "resolved": false, - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "bundled": true, "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -4586,8 +4337,7 @@ "dependencies": { "resolve": { "version": "1.10.0", - "resolved": false, - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "bundled": true, "dev": true, "requires": { "path-parse": "^1.0.6" @@ -4597,8 +4347,7 @@ }, "npm-audit-report": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-1.3.3.tgz", - "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==", + "bundled": true, "dev": true, "requires": { "cli-table3": "^0.5.0", @@ -4607,8 +4356,7 @@ }, "npm-bundled": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "bundled": true, "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -4616,14 +4364,12 @@ }, "npm-cache-filename": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=", + "bundled": true, "dev": true }, "npm-install-checks": { "version": "3.0.2", - "resolved": false, - "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==", + "bundled": true, "dev": true, "requires": { "semver": "^2.3.0 || 3.x || 4 || 5" @@ -4631,8 +4377,7 @@ }, "npm-lifecycle": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz", - "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==", + "bundled": true, "dev": true, "requires": { "byline": "^5.0.0", @@ -4647,20 +4392,17 @@ }, "npm-logical-tree": { "version": "1.2.1", - "resolved": false, - "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==", + "bundled": true, "dev": true }, "npm-normalize-package-bin": { "version": "1.0.1", - "resolved": false, - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "bundled": true, "dev": true }, "npm-package-arg": { "version": "6.1.1", - "resolved": false, - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "bundled": true, "dev": true, "requires": { "hosted-git-info": "^2.7.1", @@ -4671,8 +4413,7 @@ }, "npm-packlist": { "version": "1.4.8", - "resolved": false, - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "bundled": true, "dev": true, "requires": { "ignore-walk": "^3.0.1", @@ -4682,8 +4423,7 @@ }, "npm-pick-manifest": { "version": "3.0.2", - "resolved": false, - "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", + "bundled": true, "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -4693,8 +4433,7 @@ }, "npm-profile": { "version": "4.0.4", - "resolved": false, - "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==", + "bundled": true, "dev": true, "requires": { "aproba": "^1.1.2 || 2", @@ -4704,8 +4443,7 @@ }, "npm-registry-fetch": { "version": "4.0.7", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz", - "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", + "bundled": true, "dev": true, "requires": { "JSONStream": "^1.3.4", @@ -4719,16 +4457,14 @@ "dependencies": { "safe-buffer": { "version": "5.2.1", - "resolved": false, - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "bundled": true, "dev": true } } }, "npm-run-path": { "version": "2.0.2", - "resolved": false, - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "bundled": true, "dev": true, "requires": { "path-key": "^2.0.0" @@ -4736,14 +4472,12 @@ }, "npm-user-validate": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=", + "bundled": true, "dev": true }, "npmlog": { "version": "4.1.2", - "resolved": false, - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "bundled": true, "dev": true, "requires": { "are-we-there-yet": "~1.1.2", @@ -4754,32 +4488,27 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "bundled": true, "dev": true }, "oauth-sign": { "version": "0.9.0", - "resolved": false, - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "bundled": true, "dev": true }, "object-assign": { "version": "4.1.1", - "resolved": false, - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "bundled": true, "dev": true }, "object-keys": { "version": "1.0.12", - "resolved": false, - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "bundled": true, "dev": true }, "object.getownpropertydescriptors": { "version": "2.0.3", - "resolved": false, - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "bundled": true, "dev": true, "requires": { "define-properties": "^1.1.2", @@ -4788,8 +4517,7 @@ }, "once": { "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "dev": true, "requires": { "wrappy": "1" @@ -4797,26 +4525,22 @@ }, "opener": { "version": "1.5.1", - "resolved": false, - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "bundled": true, "dev": true }, "os-homedir": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "bundled": true, "dev": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "bundled": true, "dev": true }, "osenv": { "version": "0.1.5", - "resolved": false, - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "bundled": true, "dev": true, "requires": { "os-homedir": "^1.0.0", @@ -4825,14 +4549,12 @@ }, "p-finally": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "bundled": true, "dev": true }, "package-json": { "version": "4.0.1", - "resolved": false, - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "bundled": true, "dev": true, "requires": { "got": "^6.7.1", @@ -4843,8 +4565,7 @@ }, "pacote": { "version": "9.5.12", - "resolved": false, - "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", + "bundled": true, "dev": true, "requires": { "bluebird": "^3.5.3", @@ -4881,8 +4602,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -4893,8 +4613,7 @@ }, "parallel-transform": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "bundled": true, "dev": true, "requires": { "cyclist": "~0.2.2", @@ -4904,8 +4623,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -4919,8 +4637,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -4930,68 +4647,57 @@ }, "path-exists": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "bundled": true, "dev": true }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "bundled": true, "dev": true }, "path-is-inside": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "bundled": true, "dev": true }, "path-key": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "bundled": true, "dev": true }, "path-parse": { "version": "1.0.6", - "resolved": false, - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "bundled": true, "dev": true }, "performance-now": { "version": "2.1.0", - "resolved": false, - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "bundled": true, "dev": true }, "pify": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "bundled": true, "dev": true }, "prepend-http": { "version": "1.0.4", - "resolved": false, - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "bundled": true, "dev": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "bundled": true, "dev": true }, "promise-inflight": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "bundled": true, "dev": true }, "promise-retry": { "version": "1.1.1", - "resolved": false, - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "bundled": true, "dev": true, "requires": { "err-code": "^1.0.0", @@ -5000,16 +4706,14 @@ "dependencies": { "retry": { "version": "0.10.1", - "resolved": false, - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "bundled": true, "dev": true } } }, "promzard": { "version": "0.3.0", - "resolved": false, - "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", + "bundled": true, "dev": true, "requires": { "read": "1" @@ -5017,14 +4721,12 @@ }, "proto-list": { "version": "1.2.4", - "resolved": false, - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "bundled": true, "dev": true }, "protoduck": { "version": "5.0.1", - "resolved": false, - "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "bundled": true, "dev": true, "requires": { "genfun": "^5.0.0" @@ -5032,26 +4734,22 @@ }, "prr": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "bundled": true, "dev": true }, "pseudomap": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "bundled": true, "dev": true }, "psl": { "version": "1.1.29", - "resolved": false, - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", + "bundled": true, "dev": true }, "pump": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "bundled": true, "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5060,8 +4758,7 @@ }, "pumpify": { "version": "1.5.1", - "resolved": false, - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "bundled": true, "dev": true, "requires": { "duplexify": "^3.6.0", @@ -5071,8 +4768,7 @@ "dependencies": { "pump": { "version": "2.0.1", - "resolved": false, - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "bundled": true, "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5083,26 +4779,22 @@ }, "punycode": { "version": "1.4.1", - "resolved": false, - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "bundled": true, "dev": true }, "qrcode-terminal": { "version": "0.12.0", - "resolved": false, - "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "bundled": true, "dev": true }, "qs": { "version": "6.5.2", - "resolved": false, - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "bundled": true, "dev": true }, "query-string": { "version": "6.8.2", - "resolved": false, - "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==", + "bundled": true, "dev": true, "requires": { "decode-uri-component": "^0.2.0", @@ -5112,14 +4804,12 @@ }, "qw": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=", + "bundled": true, "dev": true }, "rc": { "version": "1.2.8", - "resolved": false, - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "bundled": true, "dev": true, "requires": { "deep-extend": "^0.6.0", @@ -5130,8 +4820,7 @@ }, "read": { "version": "1.0.7", - "resolved": false, - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "bundled": true, "dev": true, "requires": { "mute-stream": "~0.0.4" @@ -5139,8 +4828,7 @@ }, "read-cmd-shim": { "version": "1.0.5", - "resolved": false, - "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==", + "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.2" @@ -5148,8 +4836,7 @@ }, "read-installed": { "version": "4.0.3", - "resolved": false, - "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", + "bundled": true, "dev": true, "requires": { "debuglog": "^1.0.1", @@ -5163,8 +4850,7 @@ }, "read-package-json": { "version": "2.1.1", - "resolved": false, - "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", + "bundled": true, "dev": true, "requires": { "glob": "^7.1.1", @@ -5176,8 +4862,7 @@ }, "read-package-tree": { "version": "5.3.1", - "resolved": false, - "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "bundled": true, "dev": true, "requires": { "read-package-json": "^2.0.0", @@ -5187,8 +4872,7 @@ }, "readable-stream": { "version": "3.6.0", - "resolved": false, - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "bundled": true, "dev": true, "requires": { "inherits": "^2.0.3", @@ -5198,8 +4882,7 @@ }, "readdir-scoped-modules": { "version": "1.1.0", - "resolved": false, - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "bundled": true, "dev": true, "requires": { "debuglog": "^1.0.1", @@ -5210,8 +4893,7 @@ }, "registry-auth-token": { "version": "3.4.0", - "resolved": false, - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "bundled": true, "dev": true, "requires": { "rc": "^1.1.6", @@ -5220,8 +4902,7 @@ }, "registry-url": { "version": "3.1.0", - "resolved": false, - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "bundled": true, "dev": true, "requires": { "rc": "^1.0.1" @@ -5229,8 +4910,7 @@ }, "request": { "version": "2.88.0", - "resolved": false, - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "bundled": true, "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -5257,32 +4937,27 @@ }, "require-directory": { "version": "2.1.1", - "resolved": false, - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "bundled": true, "dev": true }, "require-main-filename": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "bundled": true, "dev": true }, "resolve-from": { "version": "4.0.0", - "resolved": false, - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "bundled": true, "dev": true }, "retry": { "version": "0.12.0", - "resolved": false, - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "bundled": true, "dev": true }, "rimraf": { "version": "2.7.1", - "resolved": false, - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "bundled": true, "dev": true, "requires": { "glob": "^7.1.3" @@ -5290,8 +4965,7 @@ }, "run-queue": { "version": "1.0.3", - "resolved": false, - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "bundled": true, "dev": true, "requires": { "aproba": "^1.1.1" @@ -5299,34 +4973,29 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "dev": true } } }, "safe-buffer": { "version": "5.1.2", - "resolved": false, - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "bundled": true, "dev": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "bundled": true, "dev": true }, "semver": { "version": "5.7.1", - "resolved": false, - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bundled": true, "dev": true }, "semver-diff": { "version": "2.1.0", - "resolved": false, - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "bundled": true, "dev": true, "requires": { "semver": "^5.0.3" @@ -5334,14 +5003,12 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "bundled": true, "dev": true }, "sha": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", + "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.2" @@ -5349,8 +5016,7 @@ }, "shebang-command": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "bundled": true, "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -5358,32 +5024,27 @@ }, "shebang-regex": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "bundled": true, "dev": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "bundled": true, "dev": true }, "slide": { "version": "1.1.6", - "resolved": false, - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "bundled": true, "dev": true }, "smart-buffer": { "version": "4.1.0", - "resolved": false, - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "bundled": true, "dev": true }, "socks": { "version": "2.3.3", - "resolved": false, - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", + "bundled": true, "dev": true, "requires": { "ip": "1.1.5", @@ -5392,8 +5053,7 @@ }, "socks-proxy-agent": { "version": "4.0.2", - "resolved": false, - "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "bundled": true, "dev": true, "requires": { "agent-base": "~4.2.1", @@ -5402,8 +5062,7 @@ "dependencies": { "agent-base": { "version": "4.2.1", - "resolved": false, - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "bundled": true, "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -5413,14 +5072,12 @@ }, "sorted-object": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=", + "bundled": true, "dev": true }, "sorted-union-stream": { "version": "2.1.3", - "resolved": false, - "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=", + "bundled": true, "dev": true, "requires": { "from2": "^1.3.0", @@ -5429,8 +5086,7 @@ "dependencies": { "from2": { "version": "1.3.0", - "resolved": false, - "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=", + "bundled": true, "dev": true, "requires": { "inherits": "~2.0.1", @@ -5439,14 +5095,12 @@ }, "isarray": { "version": "0.0.1", - "resolved": false, - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "bundled": true, "dev": true }, "readable-stream": { "version": "1.1.14", - "resolved": false, - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5457,16 +5111,14 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": false, - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "bundled": true, "dev": true } } }, "spdx-correct": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "bundled": true, "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -5475,14 +5127,12 @@ }, "spdx-exceptions": { "version": "2.1.0", - "resolved": false, - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "bundled": true, "dev": true }, "spdx-expression-parse": { "version": "3.0.0", - "resolved": false, - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "bundled": true, "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -5491,20 +5141,17 @@ }, "spdx-license-ids": { "version": "3.0.5", - "resolved": false, - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "bundled": true, "dev": true }, "split-on-first": { "version": "1.1.0", - "resolved": false, - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "bundled": true, "dev": true }, "sshpk": { "version": "1.14.2", - "resolved": false, - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "bundled": true, "dev": true, "requires": { "asn1": "~0.2.3", @@ -5520,8 +5167,7 @@ }, "ssri": { "version": "6.0.1", - "resolved": false, - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "bundled": true, "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -5529,8 +5175,7 @@ }, "stream-each": { "version": "1.2.2", - "resolved": false, - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "bundled": true, "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5539,8 +5184,7 @@ }, "stream-iterate": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=", + "bundled": true, "dev": true, "requires": { "readable-stream": "^2.1.5", @@ -5549,8 +5193,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5564,8 +5207,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -5575,20 +5217,17 @@ }, "stream-shift": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "bundled": true, "dev": true }, "strict-uri-encode": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "bundled": true, "dev": true }, "string-width": { "version": "2.1.1", - "resolved": false, - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "bundled": true, "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -5597,20 +5236,17 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "bundled": true, "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "bundled": true, "dev": true }, "strip-ansi": { "version": "4.0.0", - "resolved": false, - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "bundled": true, "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -5620,8 +5256,7 @@ }, "string_decoder": { "version": "1.3.0", - "resolved": false, - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.2.0" @@ -5629,22 +5264,19 @@ "dependencies": { "safe-buffer": { "version": "5.2.0", - "resolved": false, - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "bundled": true, "dev": true } } }, "stringify-package": { "version": "1.0.1", - "resolved": false, - "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==", + "bundled": true, "dev": true }, "strip-ansi": { "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -5652,20 +5284,17 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "bundled": true, "dev": true }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "bundled": true, "dev": true }, "supports-color": { "version": "5.4.0", - "resolved": false, - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "bundled": true, "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5673,8 +5302,7 @@ }, "tar": { "version": "4.4.13", - "resolved": false, - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "bundled": true, "dev": true, "requires": { "chownr": "^1.1.1", @@ -5688,8 +5316,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -5700,8 +5327,7 @@ }, "term-size": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "bundled": true, "dev": true, "requires": { "execa": "^0.7.0" @@ -5709,20 +5335,17 @@ }, "text-table": { "version": "0.2.0", - "resolved": false, - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "bundled": true, "dev": true }, "through": { "version": "2.3.8", - "resolved": false, - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "bundled": true, "dev": true }, "through2": { "version": "2.0.3", - "resolved": false, - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "bundled": true, "dev": true, "requires": { "readable-stream": "^2.1.5", @@ -5731,8 +5354,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5746,8 +5368,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -5757,20 +5378,17 @@ }, "timed-out": { "version": "4.0.1", - "resolved": false, - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "bundled": true, "dev": true }, "tiny-relative-date": { "version": "1.3.0", - "resolved": false, - "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==", + "bundled": true, "dev": true }, "tough-cookie": { "version": "2.4.3", - "resolved": false, - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "bundled": true, "dev": true, "requires": { "psl": "^1.1.24", @@ -5779,8 +5397,7 @@ }, "tunnel-agent": { "version": "0.6.0", - "resolved": false, - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "bundled": true, "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -5788,33 +5405,28 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": false, - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "bundled": true, "dev": true, "optional": true }, "typedarray": { "version": "0.0.6", - "resolved": false, - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "bundled": true, "dev": true }, "uid-number": { "version": "0.0.6", - "resolved": false, - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "bundled": true, "dev": true }, "umask": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=", + "bundled": true, "dev": true }, "unique-filename": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "bundled": true, "dev": true, "requires": { "unique-slug": "^2.0.0" @@ -5822,8 +5434,7 @@ }, "unique-slug": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "bundled": true, "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -5831,8 +5442,7 @@ }, "unique-string": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "bundled": true, "dev": true, "requires": { "crypto-random-string": "^1.0.0" @@ -5840,20 +5450,17 @@ }, "unpipe": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "bundled": true, "dev": true }, "unzip-response": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "bundled": true, "dev": true }, "update-notifier": { "version": "2.5.0", - "resolved": false, - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "bundled": true, "dev": true, "requires": { "boxen": "^1.2.1", @@ -5870,8 +5477,7 @@ }, "url-parse-lax": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "bundled": true, "dev": true, "requires": { "prepend-http": "^1.0.1" @@ -5879,20 +5485,17 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "bundled": true, "dev": true }, "util-extend": { "version": "1.0.3", - "resolved": false, - "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=", + "bundled": true, "dev": true }, "util-promisify": { "version": "2.1.0", - "resolved": false, - "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", + "bundled": true, "dev": true, "requires": { "object.getownpropertydescriptors": "^2.0.3" @@ -5900,14 +5503,12 @@ }, "uuid": { "version": "3.3.3", - "resolved": false, - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "bundled": true, "dev": true }, "validate-npm-package-license": { "version": "3.0.4", - "resolved": false, - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "bundled": true, "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -5916,8 +5517,7 @@ }, "validate-npm-package-name": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "bundled": true, "dev": true, "requires": { "builtins": "^1.0.3" @@ -5925,8 +5525,7 @@ }, "verror": { "version": "1.10.0", - "resolved": false, - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "bundled": true, "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -5936,8 +5535,7 @@ }, "wcwidth": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "bundled": true, "dev": true, "requires": { "defaults": "^1.0.3" @@ -5945,8 +5543,7 @@ }, "which": { "version": "1.3.1", - "resolved": false, - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "bundled": true, "dev": true, "requires": { "isexe": "^2.0.0" @@ -5954,14 +5551,12 @@ }, "which-module": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "bundled": true, "dev": true }, "wide-align": { "version": "1.1.2", - "resolved": false, - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "bundled": true, "dev": true, "requires": { "string-width": "^1.0.2" @@ -5969,8 +5564,7 @@ "dependencies": { "string-width": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -5982,8 +5576,7 @@ }, "widest-line": { "version": "2.0.1", - "resolved": false, - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "bundled": true, "dev": true, "requires": { "string-width": "^2.1.1" @@ -5991,8 +5584,7 @@ }, "worker-farm": { "version": "1.7.0", - "resolved": false, - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "bundled": true, "dev": true, "requires": { "errno": "~0.1.7" @@ -6000,8 +5592,7 @@ }, "wrap-ansi": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "bundled": true, "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -6011,20 +5602,17 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "bundled": true, "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "bundled": true, "dev": true }, "string-width": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "bundled": true, "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -6034,8 +5622,7 @@ }, "strip-ansi": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "bundled": true, "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -6045,14 +5632,12 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "bundled": true, "dev": true }, "write-file-atomic": { "version": "2.4.3", - "resolved": false, - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -6062,32 +5647,27 @@ }, "xdg-basedir": { "version": "3.0.0", - "resolved": false, - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "bundled": true, "dev": true }, "xtend": { "version": "4.0.1", - "resolved": false, - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "bundled": true, "dev": true }, "y18n": { "version": "4.0.0", - "resolved": false, - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "bundled": true, "dev": true }, "yallist": { "version": "3.0.3", - "resolved": false, - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "bundled": true, "dev": true }, "yargs": { "version": "14.2.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", - "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "bundled": true, "dev": true, "requires": { "cliui": "^5.0.0", @@ -6105,14 +5685,12 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "bundled": true, "dev": true }, "find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "bundled": true, "dev": true, "requires": { "locate-path": "^3.0.0" @@ -6120,14 +5698,12 @@ }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "bundled": true, "dev": true }, "locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "bundled": true, "dev": true, "requires": { "p-locate": "^3.0.0", @@ -6136,8 +5712,7 @@ }, "p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "bundled": true, "dev": true, "requires": { "p-try": "^2.0.0" @@ -6145,8 +5720,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "bundled": true, "dev": true, "requires": { "p-limit": "^2.0.0" @@ -6154,14 +5728,12 @@ }, "p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "bundled": true, "dev": true }, "string-width": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "bundled": true, "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -6171,8 +5743,7 @@ }, "strip-ansi": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "bundled": true, "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -6182,8 +5753,7 @@ }, "yargs-parser": { "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "bundled": true, "dev": true, "requires": { "camelcase": "^5.0.0", @@ -6192,8 +5762,7 @@ "dependencies": { "camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "bundled": true, "dev": true } } @@ -6244,9 +5813,9 @@ } }, "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", "dev": true }, "p-filter": { @@ -6271,21 +5840,21 @@ "dev": true }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "^2.2.0" } }, "p-map": { @@ -6311,9 +5880,9 @@ } }, "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "parent-module": { @@ -6326,19 +5895,27 @@ } }, "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { + "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { @@ -6379,6 +5956,57 @@ "requires": { "find-up": "^2.0.0", "load-json-file": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } } }, "pkg-dir": { @@ -6508,17 +6136,35 @@ "type-fest": "^0.6.0" }, "dependencies": { - "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true } } }, @@ -6533,87 +6179,6 @@ "type-fest": "^0.8.1" }, "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -6663,9 +6228,9 @@ "dev": true }, "registry-auth-token": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", - "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", "dev": true, "requires": { "rc": "^1.2.8" @@ -6684,11 +6249,12 @@ "dev": true }, "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -6720,6 +6286,15 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", @@ -6742,9 +6317,9 @@ "dev": true }, "semantic-release": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-17.0.4.tgz", - "integrity": "sha512-5y9QRSrZtdvACmlpX5DvEVsvFuKRDUVn7JVJFxPVLGrGofDf1d0M/+hA1wFmCjiJZ+VCY8bYaSqVqF14KCF9rw==", + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-17.2.3.tgz", + "integrity": "sha512-MY1MlowGQrkOR7+leOD8ICkVOC6i1szbwDODdbJ0UdshtMx8Ms0bhpRQmEEliqYKEb5PLv/dqs6zKKuHT7UxTg==", "dev": true, "requires": { "@semantic-release/commit-analyzer": "^8.0.0", @@ -6764,23 +6339,23 @@ "hook-std": "^2.0.0", "hosted-git-info": "^3.0.0", "lodash": "^4.17.15", - "marked": "^0.8.0", + "marked": "^1.0.0", "marked-terminal": "^4.0.0", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", "p-reduce": "^2.0.0", "read-pkg-up": "^7.0.0", "resolve-from": "^5.0.0", - "semver": "^7.1.1", + "semver": "^7.3.2", "semver-diff": "^3.1.1", "signale": "^1.2.1", "yargs": "^15.0.1" } }, "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "semver-compare": { @@ -6928,9 +6503,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -6938,15 +6513,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -6954,9 +6529,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", "dev": true }, "split": { @@ -7110,9 +6685,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -7127,21 +6702,22 @@ "dev": true }, "tempy": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.5.0.tgz", - "integrity": "sha512-VEY96x7gbIRfsxqsafy2l5yVxxp3PhwAGoWMyC2D2Zt5DmEv+2tGiPOrquNRpf21hhGnKLVEsuqleqiZmKG/qw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.0.tgz", + "integrity": "sha512-eLXG5B1G0mRPHmgH2WydPl5v4jH35qEn3y/rA/aahKhIa91Pn119SsU7n7v/433gtT9ONzC8ISvNHIh2JSTm0w==", "dev": true, "requires": { + "del": "^6.0.0", "is-stream": "^2.0.0", "temp-dir": "^2.0.0", - "type-fest": "^0.12.0", + "type-fest": "^0.16.0", "unique-string": "^2.0.0" }, "dependencies": { "type-fest": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", - "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", "dev": true } } @@ -7159,12 +6735,25 @@ "dev": true }, "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, "requires": { - "readable-stream": "2 || 3" + "readable-stream": "3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "to-regex-range": { @@ -7214,9 +6803,9 @@ "dev": true }, "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", "dev": true }, "typescript": { @@ -7226,9 +6815,9 @@ "dev": true }, "uglify-js": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", - "integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.6.tgz", + "integrity": "sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g==", "dev": true, "optional": true }, @@ -7461,9 +7050,9 @@ "dev": true }, "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yaml": { @@ -7476,9 +7065,9 @@ } }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -7491,58 +7080,9 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "yargs-parser": "^18.1.2" }, "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, "yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", @@ -7556,14 +7096,10 @@ } }, "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yn": { "version": "3.1.1", From 84b9987d4a7819ed6b9c8475523036d5809b1b2a Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Sun, 22 Nov 2020 11:35:35 -0800 Subject: [PATCH 69/75] feat(clients): add set_client_params for specifying client-specific args (#39) * feat(clients): add set_client_params for specifying client-specific args - useful for passing credentials and other args to the underlying bucket client library * chore: fix lint * test: add test for set_client_params * chore: fix test * test: run GCS tests during CI build * chore: install all deps for testing * chore: fix credentials detection * chore: update docs * test(clients): add recreate behavior test * chore: use more generous wait in timestamp test * chore: update readme snippets --- README.md | 31 +++++++++++++++++++++------ pathy/__init__.py | 1 + pathy/base.py | 18 ++++++++++++++-- pathy/clients.py | 23 +++++++++++++++++--- pathy/gcs.py | 14 +++++++++++-- tests/conftest.py | 49 +++++++++++++++++++++++++++++++++++++++---- tests/test_clients.py | 45 +++++++++++++++++++++++++++++++++++++-- tools/docs.sh | 2 +- tools/setup.sh | 2 +- 9 files changed, 164 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 310948d..dab35b4 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,14 @@ pip install pathy The package exports the `Pathy` class and utilities for configuring the bucket storage provider to use. By default Pathy prefers GoogleCloudStorage paths of the form `gs://bucket_name/folder/blob_name.txt`. Internally Pathy can convert GCS paths to local files, allowing for a nice developer experience. ```python -from pathy import Pathy - -# Any bucket you have access to will work +from pathy import Pathy, use_fs +# Use the local file-system for quicker development +use_fs() +# Create a bucket +Pathy("gs://my_bucket").mkdir(exist_ok=True) +# An excellent blob greeting = Pathy(f"gs://my_bucket/greeting.txt") -# The blob doesn't exist yet +# But it doesn't exist yet assert not greeting.exists() # Create it by writing some text greeting.write_text("Hello World!") @@ -94,11 +97,13 @@ If you need to use specific implementation details of a type, "narrow" the return of this function to the desired type, e.g. ```python -fluid_path = FluidPath("gs://my_bucket/foo.txt") +from pathy import FluidPath, Pathy + +fluid_path: FluidPath = Pathy.fluid("gs://my_bucket/foo.txt") # Narrow the type to a specific class assert isinstance(fluid_path, Pathy), "must be Pathy" # Use a member specific to that class -print(fluid_path.prefix) +assert fluid_path.prefix == "foo.txt/" ``` ## from_bucket classmethod @@ -111,6 +116,8 @@ Initialize a Pathy from a bucket name. This helper adds a trailing slash and the appropriate prefix. ```python +from pathy import Pathy + assert str(Pathy.from_bucket("one")) == "gs://one/" assert str(Pathy.from_bucket("two")) == "gs://two/" ``` @@ -244,6 +251,8 @@ Pathy.resolve(self, strict: bool = False) -> 'Pathy' Resolve the given path to remove any relative path specifiers. ```python +from pathy import Pathy + path = Pathy("gs://my_bucket/folder/../blob") assert path.resolve() == Pathy("gs://my_bucket/blob") ``` @@ -367,6 +376,16 @@ get_fs_cache() -> Optional[pathlib.Path] Get the folder that holds file-system cached blobs and timestamps. +# set_client_params function + +```python +set_client_params(scheme: str, kwargs: Any) -> None +``` + +Specify args to pass when instantiating a service-specific Client +object. This allows for passing credentials in whatever way your underlying +client library prefers. + # CLI Pathy command line interface. diff --git a/pathy/__init__.py b/pathy/__init__.py index 6d2be60..fd2a8dc 100644 --- a/pathy/__init__.py +++ b/pathy/__init__.py @@ -16,6 +16,7 @@ get_fs_cache, get_fs_client, register_client, + set_client_params, use_fs, use_fs_cache, ) diff --git a/pathy/base.py b/pathy/base.py index 0d5c2c5..03659a1 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -130,6 +130,9 @@ def exists(self) -> bool: class BucketClient: """Base class for a client that interacts with a bucket-based storage system.""" + def recreate(self, **kwargs: Any) -> None: + """Recreate any underlying bucket client adapter with the given kwargs""" + def open( self, path: "Pathy", @@ -140,6 +143,10 @@ def open( errors: Optional[str] = None, newline: Optional[str] = None, ) -> StreamableType: + client_params = {} + if hasattr(self, "client_params"): + client_params = getattr(self, "client_params") + return smart_open.open( self.make_uri(path), mode=mode, @@ -147,6 +154,7 @@ def open( encoding=encoding, errors=errors, newline=newline, + transport_params=client_params, # Disable de/compression based on the file extension ignore_ext=True, ) # type:ignore @@ -475,11 +483,13 @@ def fluid(cls, path_candidate: Union[str, FluidPath]) -> FluidPath: return of this function to the desired type, e.g. ```python - fluid_path = FluidPath("gs://my_bucket/foo.txt") + from pathy import FluidPath, Pathy + + fluid_path: FluidPath = Pathy.fluid("gs://my_bucket/foo.txt") # Narrow the type to a specific class assert isinstance(fluid_path, Pathy), "must be Pathy" # Use a member specific to that class - print(fluid_path.prefix) + assert fluid_path.prefix == "foo.txt/" ``` """ from_path: FluidPath = Pathy(path_candidate) @@ -493,6 +503,8 @@ def from_bucket(cls, bucket_name: str) -> "Pathy": the appropriate prefix. ```python + from pathy import Pathy + assert str(Pathy.from_bucket("one")) == "gs://one/" assert str(Pathy.from_bucket("two")) == "gs://two/" ``` @@ -658,6 +670,8 @@ def resolve(self, strict: bool = False) -> "Pathy": """Resolve the given path to remove any relative path specifiers. ```python + from pathy import Pathy + path = Pathy("gs://my_bucket/folder/../blob") assert path.resolve() == Pathy("gs://my_bucket/blob") ``` diff --git a/pathy/clients.py b/pathy/clients.py index f55f7ee..9e7058f 100644 --- a/pathy/clients.py +++ b/pathy/clients.py @@ -16,29 +16,46 @@ "gs": BucketClientGCS, } +# Hold given client args for a scheme +_client_args_registry: Dict[str, Any] = {} _instance_cache: Dict[str, Any] = {} _fs_client: Optional[BucketClientFS] = None _fs_cache: Optional[pathlib.Path] = None -def register_client() -> None: +def register_client(scheme: str, type: Type[BucketClient]) -> None: """Register a bucket client for use with certain scheme Pathy objects""" global _client_registry + _client_registry[scheme] = type def get_client(scheme: str) -> BucketClientType: """Retrieve the bucket client for use with a given scheme""" - global _client_registry, _instance_cache, _fs_client + global _client_registry, _instance_cache, _fs_client, _client_args_registry if _fs_client is not None: return _fs_client # type: ignore if scheme in _instance_cache: return _instance_cache[scheme] elif scheme in _client_registry: - _instance_cache[scheme] = _client_registry[scheme]() + kwargs = ( + _client_args_registry[scheme] if scheme in _client_args_registry else {} + ) + _instance_cache[scheme] = _client_registry[scheme](**kwargs) # type:ignore return _instance_cache[scheme] raise ValueError(f'There is no client registered to handle "{scheme}" paths') +def set_client_params(scheme: str, **kwargs: Any) -> None: + """Specify args to pass when instantiating a service-specific Client + object. This allows for passing credentials in whatever way your underlying + client library prefers.""" + global _client_registry, _instance_cache, _client_args_registry + _client_args_registry[scheme] = kwargs + if scheme in _instance_cache: + _instance_cache[scheme].recreate(**_client_args_registry[scheme]) + return None + + def use_fs( root: Optional[Union[str, pathlib.Path, bool]] = None ) -> Optional[BucketClientFS]: diff --git a/pathy/gcs.py b/pathy/gcs.py index 09ed42a..3318313 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -99,9 +99,19 @@ def exists(self) -> bool: class BucketClientGCS(BucketClient): client: Optional[GCSNativeClient] - def __init__(self, client: Optional[GCSNativeClient] = None): + @property + def client_params(self) -> Any: + return dict(client=self.client) + + def __init__(self, **kwargs: Any) -> None: + self.recreate(**kwargs) + + def recreate(self, **kwargs: Any) -> None: try: - self.client = GCSNativeClient() if GCSNativeClient else None + creds = kwargs["credentials"] if "credentials" in kwargs else None + if creds is not None: + kwargs["project"] = creds.project_id + self.client = GCSNativeClient(**kwargs) if GCSNativeClient else None except (BaseException, DefaultCredentialsError): self.client = None diff --git a/tests/conftest.py b/tests/conftest.py index 0b6de65..9e81e27 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +import json import os import shutil import tempfile @@ -6,10 +7,10 @@ import pytest from pathy import Pathy, use_fs, use_fs_cache +from pathy.clients import set_client_params from pathy.gcs import has_gcs -# TODO: set this up with a service account for the CI -has_credentials = "CI" not in os.environ +has_credentials = "GCS_CREDENTIALS" in os.environ # Which adapters to use TEST_ADAPTERS = ["gcs", "fs"] if has_credentials and has_gcs else ["fs"] @@ -39,12 +40,52 @@ def with_fs(temp_folder): use_fs(False) +def credentials_from_env(): + """Extract a credentials instance from the GCS_CREDENTIALS env variable. + + You can specify the contents of a credentials JSON file or a file path + that points to a JSON file. + + Raises AssertionError if the value is present but does not point to a file + or valid JSON content.""" + if not has_gcs: + return None + + creds = os.environ.get("GCS_CREDENTIALS", None) + if creds is None: + return None + from google.oauth2 import service_account + + json_creds = None + try: + json_creds = json.loads(creds) + except json.decoder.JSONDecodeError: + pass + + # If not a file path, assume it's JSON content + if json_creds is None: + credentials = service_account.Credentials.from_service_account_file(creds) + else: + fd, path = tempfile.mkstemp() + try: + with os.fdopen(fd, "w") as tmp: + tmp.write(json.dumps(json_creds)) + credentials = service_account.Credentials.from_service_account_file(path) + finally: + os.remove(path) + return credentials + + @pytest.fixture() def with_adapter(adapter: str, bucket: str, other_bucket: str): tmp_dir = None + scheme = "gs" if adapter == "gcs": - # Use GCS (with system credentials) + # Use GCS use_fs(False) + credentials = credentials_from_env() + if credentials is not None: + set_client_params("gs", credentials=credentials) elif adapter == "fs": # Use local file-system in a temp folder tmp_dir = tempfile.mkdtemp() @@ -58,7 +99,7 @@ def with_adapter(adapter: str, bucket: str, other_bucket: str): else: raise ValueError("invalid adapter, nothing is configured") # execute the test - yield + yield scheme if adapter == "fs" and tmp_dir is not None: # Cleanup fs temp folder diff --git a/tests/test_clients.py b/tests/test_clients.py index 4676473..88cdfe2 100644 --- a/tests/test_clients.py +++ b/tests/test_clients.py @@ -4,7 +4,14 @@ import pytest from pathy import Pathy, get_client -from pathy.clients import get_fs_client, use_fs, use_fs_cache +from pathy.base import BucketClient +from pathy.clients import ( + get_fs_client, + register_client, + set_client_params, + use_fs, + use_fs_cache, +) from pathy.file import BucketClientFS from pathy.gcs import BucketClientGCS @@ -17,6 +24,12 @@ def test_clients_get_client_works_with_builtin_schems(): assert isinstance(get_client(""), BucketClientFS) +def test_clients_get_client_respects_use_fs_override(): + use_fs(True) + assert isinstance(get_client("gs"), BucketClientFS) + use_fs(False) + + def test_clients_get_client_errors_with_unknown_scheme(): with pytest.raises(ValueError): get_client("foo") @@ -71,10 +84,38 @@ def test_api_use_fs_cache(with_adapter, with_fs: str, bucket: str): assert orig_cache_time == cached_cache_time, "cached blob timestamps should match" # Update the blob - time.sleep(0.1) + time.sleep(1.0) path.write_text('{ "cool" : true }') # Fetch the updated blob Pathy.to_local(path) updated_cache_time = foo_timestamp.read_text() assert updated_cache_time != orig_cache_time, "cached timestamp did not change" + + +class BucketClientTest(BucketClient): + def __init__(self, required_arg: bool) -> None: + self.recreate(required_arg=required_arg) + + def recreate(self, required_arg: bool) -> None: + self.required_arg = required_arg + + +def test_clients_set_client_params(): + register_client("test", BucketClientTest) + with pytest.raises(TypeError): + get_client("test") + + set_client_params("test", required_arg=True) + client = get_client("test") + assert isinstance(client, BucketClientTest) + + +def test_clients_set_client_params_recreates_client(): + register_client("test", BucketClientTest) + set_client_params("test", required_arg=False) + client: BucketClientTest = get_client("test") + assert client.required_arg is False + set_client_params("test", required_arg=True) + assert client.required_arg is True + assert isinstance(client, BucketClientTest) diff --git a/tools/docs.sh b/tools/docs.sh index 501dd80..26cd165 100644 --- a/tools/docs.sh +++ b/tools/docs.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e . .env/bin/activate -mathy_pydoc pathy.Pathy+ pathy.BlobStat+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache > /tmp/pathy_api.md +mathy_pydoc pathy.Pathy+ pathy.BlobStat+ pathy.use_fs pathy.get_fs_client pathy.use_fs_cache pathy.get_fs_cache pathy.set_client_params > /tmp/pathy_api.md typer pathy.cli utils docs > /tmp/pathy_cli.md python tools/docs.py /tmp/pathy_api.md /tmp/pathy_cli.md README.md diff --git a/tools/setup.sh b/tools/setup.sh index 2356087..33555c5 100644 --- a/tools/setup.sh +++ b/tools/setup.sh @@ -12,5 +12,5 @@ echo "Installing/updating requirements..." pip install -r requirements.txt echo "Installing/updating dev requirements..." pip install -r requirements-dev.txt -pip install -e . +pip install -e ".[all]" From e322083f5eeb2bb69f59d112e424a406c5f56512 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 22 Nov 2020 19:53:23 +0000 Subject: [PATCH 70/75] chore(release): 0.3.4 ## [0.3.4](https://github.com/justindujardin/pathy/compare/v0.3.3...v0.3.4) (2020-11-22) ### Features * **clients:** add set_client_params for specifying client-specific args ([#39](https://github.com/justindujardin/pathy/issues/39)) ([84b9987](https://github.com/justindujardin/pathy/commit/84b9987d4a7819ed6b9c8475523036d5809b1b2a)) --- CHANGELOG.md | 7 +++++++ pathy/about.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1cbf2e..88fe176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.3.4](https://github.com/justindujardin/pathy/compare/v0.3.3...v0.3.4) (2020-11-22) + + +### Features + +* **clients:** add set_client_params for specifying client-specific args ([#39](https://github.com/justindujardin/pathy/issues/39)) ([84b9987](https://github.com/justindujardin/pathy/commit/84b9987d4a7819ed6b9c8475523036d5809b1b2a)) + ## [0.3.3](https://github.com/justindujardin/pathy/compare/v0.3.2...v0.3.3) (2020-11-12) diff --git a/pathy/about.py b/pathy/about.py index 5217516..96f6cde 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.3.3" +__version__ = "0.3.4" __summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin" From 97f51a1d43d9056a0fa9c3efe05cf712c27a4509 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Sun, 22 Nov 2020 12:05:47 -0800 Subject: [PATCH 71/75] Feature/test doc snippets (#40) * test(ci): add readme snippet test runner - gather up and execute the python snippets in the Readme to make sure they work. * chore: update docs and mathy_pydoc version * chore: update docs --- .travis.yml | 1 + README.md | 52 ++++++++++++++++++++++---------------------- requirements-dev.txt | 2 +- tools/test_readme.py | 48 ++++++++++++++++++++++++++++++++++++++++ tools/test_readme.sh | 4 ++++ 5 files changed, 80 insertions(+), 27 deletions(-) create mode 100644 tools/test_readme.py create mode 100644 tools/test_readme.sh diff --git a/.travis.yml b/.travis.yml index b35ed8a..bc168a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ matrix: - sh tools/setup.sh script: - sh tools/lint.sh + - sh tools/test_readme.sh - sh tools/test.sh after_success: - sh tools/codecov.sh diff --git a/README.md b/README.md index dab35b4..a70e422 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ pathy>=0.1.37,<0.2.0 # Pathy class -```python +```python (doc) Pathy(self, args, kwargs) ``` @@ -73,7 +73,7 @@ Subclass of `pathlib.Path` that works with bucket APIs. ## exists method -```python +```python (doc) Pathy.exists(self) -> bool ``` @@ -81,7 +81,7 @@ Returns True if the path points to an existing bucket, blob, or prefix. ## fluid classmethod -```python +```python (doc) Pathy.fluid( path_candidate: Union[str, Pathy, pathlib.Path], ) -> Union[Pathy, pathlib.Path] @@ -108,7 +108,7 @@ assert fluid_path.prefix == "foo.txt/" ## from_bucket classmethod -```python +```python (doc) Pathy.from_bucket(bucket_name: str) -> 'Pathy' ``` @@ -124,7 +124,7 @@ assert str(Pathy.from_bucket("two")) == "gs://two/" ## glob method -```python +```python (doc) Pathy.glob( self: 'Pathy', pattern: str, @@ -136,7 +136,7 @@ blobs. ## is_dir method -```python +```python (doc) Pathy.is_dir(self: 'Pathy') -> bool ``` @@ -148,7 +148,7 @@ Returns False if it points to a blob or the path doesn't exist. ## is_file method -```python +```python (doc) Pathy.is_file(self: 'Pathy') -> bool ``` @@ -160,7 +160,7 @@ exist. ## iterdir method -```python +```python (doc) Pathy.iterdir( self: 'Pathy', ) -> Generator[Pathy, NoneType, NoneType] @@ -170,7 +170,7 @@ Iterate over the blobs found in the given bucket or blob prefix path. ## mkdir method -```python +```python (doc) Pathy.mkdir( self, mode: int = 511, @@ -193,7 +193,7 @@ Raises FileExistsError if exist_ok is false and the bucket already exists. ## open method -```python +```python (doc) Pathy.open( self: 'Pathy', mode: str = 'r', @@ -210,7 +210,7 @@ providers. ## owner method -```python +```python (doc) Pathy.owner(self: 'Pathy') -> Optional[str] ``` @@ -220,7 +220,7 @@ not supported by the bucket API provider. ## rename method -```python +```python (doc) Pathy.rename(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> None ``` @@ -234,7 +234,7 @@ to match the target prefix. ## replace method -```python +```python (doc) Pathy.replace(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> None ``` @@ -244,7 +244,7 @@ If target points to an existing path, it will be replaced. ## resolve method -```python +```python (doc) Pathy.resolve(self, strict: bool = False) -> 'Pathy' ``` @@ -259,7 +259,7 @@ assert path.resolve() == Pathy("gs://my_bucket/blob") ## rglob method -```python +```python (doc) Pathy.rglob( self: 'Pathy', pattern: str, @@ -271,7 +271,7 @@ all matched blobs. Imagine adding "\*\*/" before a call to glob. ## rmdir method -```python +```python (doc) Pathy.rmdir(self: 'Pathy') -> None ``` @@ -279,7 +279,7 @@ Removes this bucket or blob prefix. It must be empty. ## samefile method -```python +```python (doc) Pathy.samefile( self: 'Pathy', other_path: Union[str, bytes, int, pathlib.Path], @@ -290,7 +290,7 @@ Determine if this path points to the same location as other_path. ## stat method -```python +```python (doc) Pathy.stat(self: 'Pathy') -> pathy.base.BlobStat ``` @@ -298,7 +298,7 @@ Returns information about this bucket path. ## to_local classmethod -```python +```python (doc) Pathy.to_local( blob_path: Union[Pathy, str], recurse: bool = True, @@ -312,7 +312,7 @@ as their updated timestamps change. ## touch method -```python +```python (doc) Pathy.touch(self: 'Pathy', mode: int = 438, exist_ok: bool = True) -> None ``` @@ -324,7 +324,7 @@ FileExistsError is raised. # BlobStat dataclass -```python +```python (doc) BlobStat( self, size: Optional[int], @@ -336,7 +336,7 @@ Stat for a bucket item # use_fs function -```python +```python (doc) use_fs( root: Optional[str, pathlib.Path, bool] = None, ) -> Optional[pathy.file.BucketClientFS] @@ -349,7 +349,7 @@ applications. # get_fs_client function -```python +```python (doc) get_fs_client() -> Optional[pathy.file.BucketClientFS] ``` @@ -357,7 +357,7 @@ Get the file-system client (or None) # use_fs_cache function -```python +```python (doc) use_fs_cache( root: Optional[str, pathlib.Path, bool] = None, ) -> Optional[pathlib.Path] @@ -370,7 +370,7 @@ times, or need to pass a local file path to a third-party library. # get_fs_cache function -```python +```python (doc) get_fs_cache() -> Optional[pathlib.Path] ``` @@ -378,7 +378,7 @@ Get the folder that holds file-system cached blobs and timestamps. # set_client_params function -```python +```python (doc) set_client_params(scheme: str, kwargs: Any) -> None ``` diff --git a/requirements-dev.txt b/requirements-dev.txt index 3b0ecbb..4e46dff 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ black pytest-coverage tox mock -mathy_pydoc +mathy_pydoc>=0.7.22,<0.8.0 typer-cli # test serializing spacy models using Pathy in place of Path diff --git a/tools/test_readme.py b/tools/test_readme.py new file mode 100644 index 0000000..7dd0e22 --- /dev/null +++ b/tools/test_readme.py @@ -0,0 +1,48 @@ +import re +from pathlib import Path +from typing import List + +import typer + + +def extract_code_snippets(lines: List[str]) -> List[str]: + inside_codeblock = False + blocks = [] + current_block: List[str] = [] + while len(lines) > 0: + line = lines.pop(0) + if not inside_codeblock: + inside_codeblock = re.match(r"```(p|P)ython$", line.strip()) + else: + end_block = re.match(r"```", line.strip()) + if end_block: + blocks.append("\n".join(current_block)) + current_block = [] + inside_codeblock = False + else: + current_block.append(line) + + return blocks + + +def exec_snippet(text: str): + try: + exec(text) + except BaseException as identifier: + typer.echo(f"TEST Failed! == ERROR: {identifier}") + typer.echo("SNIPPET") + typer.echo(text) + typer.Exit(1) + raise identifier + + +def main(readme_file: Path): + readme_lines = readme_file.read_text().split("\n") + snippets = extract_code_snippets(readme_lines) + for snip in snippets: + exec_snippet(snip) + typer.echo("All snippets in readme executed without error!") + + +if __name__ == "__main__": + typer.run(main) diff --git a/tools/test_readme.sh b/tools/test_readme.sh new file mode 100644 index 0000000..5a18be2 --- /dev/null +++ b/tools/test_readme.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +. .env/bin/activate +python tools/test_readme.py README.md From 36c8de95572047862557f4009103db1037816f78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jan 2021 14:23:44 -0800 Subject: [PATCH 72/75] chore(deps): bump ini from 1.3.5 to 1.3.8 (#41) Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e130297..3c2982a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1434,9 +1434,9 @@ "dev": true }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "into-stream": { @@ -3659,11 +3659,6 @@ "bundled": true, "dev": true }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true - }, "init-package-json": { "version": "1.10.3", "bundled": true, From a965f40a086ccb305e58936813a30c35f95d5212 Mon Sep 17 00:00:00 2001 From: Justin DuJardin Date: Tue, 2 Feb 2021 12:36:27 -0800 Subject: [PATCH 73/75] fix: python 3.9 compatibility (#46) * fix: python 3.9 compatibility - a change to the way scandir is used causes a KeyError in some code paths - root cause: https://bugs.python.org/issue39916 - solution is to refactor scandir to use a class that can be used as a generator or context manager. * chore: disable broken spacy test until thinc pr lands * chore: make PathyScanDir iterable for py < 3.8 * chore: cleanup from review * chore: drop old tox file * chore: add codecov yml to disable patch coverage --- README.md | 4 +- codecov.yml | 3 ++ pathy/base.py | 66 +++++++++++++++++++++++------ pathy/file.py | 78 +++++++++++++++++++--------------- pathy/gcs.py | 103 ++++++++++++++++++++++++++------------------- tests/test_base.py | 1 + tox.ini | 5 --- 7 files changed, 162 insertions(+), 98 deletions(-) create mode 100644 codecov.yml delete mode 100644 tox.ini diff --git a/README.md b/README.md index a70e422..7c89452 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ not supported by the bucket API provider. ## rename method ```python (doc) -Pathy.rename(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> None +Pathy.rename(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> 'Pathy' ``` Rename this path to the given target. @@ -235,7 +235,7 @@ to match the target prefix. ## replace method ```python (doc) -Pathy.replace(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> None +Pathy.replace(self: 'Pathy', target: Union[str, pathlib.PurePath]) -> 'Pathy' ``` Renames this path to the given target. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..1deea58 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +coverage: + status: + patch: off \ No newline at end of file diff --git a/pathy/base.py b/pathy/base.py index 03659a1..b408107 100644 --- a/pathy/base.py +++ b/pathy/base.py @@ -7,9 +7,11 @@ from typing import ( IO, Any, + ContextManager, Dict, Generator, Generic, + Iterator, List, Optional, Tuple, @@ -194,7 +196,7 @@ def scandir( path: "Pathy" = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, - ) -> Generator[BucketEntry[BucketType, BucketBlobType], None, None]: + ) -> "PathyScanDir": raise NotImplementedError(SUBCLASS_ERROR) def create_bucket(self, path: "Pathy") -> Bucket: @@ -364,12 +366,13 @@ def exists(self, path: "Pathy") -> bool: # Determine if the path exists according to the current adapter return client.exists(path) - def scandir(self, path: "Pathy") -> Generator[BucketEntry, None, None]: + def scandir(self, path: "Pathy") -> "PathyScanDir": return self.client(path).scandir(path, prefix=path.prefix) def listdir(self, path: "Pathy") -> Generator[str, None, None]: - for entry in self.scandir(path): - yield entry.name + with self.scandir(path) as entries: + for entry in entries: + yield entry.name def open( self, @@ -646,7 +649,7 @@ def open( # type:ignore raise ValueError("binary mode doesn't take an encoding argument") # Leftover pathlib internals stuff - if self._closed: # type:ignore + if hasattr(self, "_closed") and self._closed: # type:ignore self._raise_closed() # type:ignore return self._accessor.open( self, @@ -679,7 +682,7 @@ def resolve(self, strict: bool = False) -> "Pathy": self._absolute_path_validation() return self._accessor.resolve(self, strict=strict) - def rename(self: "Pathy", target: Union[str, PurePath]) -> None: + def rename(self: "Pathy", target: Union[str, PurePath]) -> "Pathy": # type:ignore """Rename this path to the given target. If the target exists and is a file, it will be replaced silently if the user @@ -689,16 +692,16 @@ def rename(self: "Pathy", target: Union[str, PurePath]) -> None: to match the target prefix.""" self._absolute_path_validation() self_type = type(self) - if not isinstance(target, self_type): - target = self_type(target) - target._absolute_path_validation() # type:ignore - super().rename(target) + result = target if isinstance(target, self_type) else self_type(target) + result._absolute_path_validation() # type:ignore + super().rename(result) + return result - def replace(self: "Pathy", target: Union[str, PurePath]) -> None: + def replace(self: "Pathy", target: Union[str, PurePath]) -> "Pathy": # type:ignore """Renames this path to the given target. If target points to an existing path, it will be replaced.""" - self.rename(target) + return self.rename(target) def rmdir(self: "Pathy") -> None: """Removes this bucket or blob prefix. It must be empty.""" @@ -821,3 +824,42 @@ def symlink_to( method=self.symlink_to.__qualname__ ) raise NotImplementedError(message) + + +class PathyScanDir(Iterator[Any], ContextManager[Any]): + """A scandir implementation that works for all python 3.x versions. + + Python < 3.7 requires that scandir be iterable so it can be converted + to a list of results. + + Python >= 3.8 requires that scandir work as a context manager. + """ + + def __init__( + self, + client: BucketClient, + path: Optional[PurePathy] = None, + prefix: Optional[str] = None, + delimiter: Optional[str] = None, + ) -> None: + super().__init__() + self._client = client + self._path = path + self._prefix = prefix + self._delimiter = delimiter + self._generator = self.scandir() + + def __enter__(self) -> Generator[BucketEntry, None, None]: + return self._generator + + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + pass + + def __next__(self) -> Generator[BucketEntry, None, None]: + yield from self._generator + + def __iter__(self) -> Generator[BucketEntry, None, None]: + yield from self._generator + + def scandir(self) -> Generator[BucketEntry, None, None]: + raise NotImplementedError("must be implemented in a subclass") diff --git a/pathy/file.py b/pathy/file.py index 8591c8c..c51afac 100644 --- a/pathy/file.py +++ b/pathy/file.py @@ -12,6 +12,7 @@ BucketEntry, ClientError, Pathy, + PathyScanDir, PurePathy, StreamableType, ) @@ -182,44 +183,13 @@ def list_buckets(self, **kwargs: Dict[str, Any]) -> Generator[BucketFS, None, No if f.is_dir(): yield BucketFS(f.name, f) - def scandir( # type:ignore[override] + def scandir( self, path: Pathy = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, - ) -> Generator[BucketEntry[BucketFS, pathlib.Path], None, None]: - if path is None or not path.root: - for bucket in self.list_buckets(): - yield BucketEntryFS(bucket.name, is_dir=True, raw=None) - return - assert path is not None - assert path.root is not None - scan_path = self.root / path.root - if prefix is not None: - scan_path = scan_path / prefix - for dir_entry in scan_path.glob("*"): - if dir_entry.is_dir(): - yield BucketEntryFS(dir_entry.name, is_dir=True, raw=None) - else: - file_path = pathlib.Path(dir_entry) - stat = file_path.stat() - file_size = stat.st_size - updated = int(round(stat.st_mtime_ns * 1000)) - blob: Blob = BlobFS( - self.get_bucket(path), - name=dir_entry.name, - size=file_size, - updated=updated, - owner=None, - raw=file_path, - ) - yield BucketEntryFS( - name=dir_entry.name, - is_dir=False, - size=file_size, - last_modified=updated, - raw=blob, - ) + ) -> PathyScanDir: + return _FSScanDir(client=self, path=path, prefix=prefix, delimiter=delimiter) def list_blobs( self, @@ -233,7 +203,7 @@ def list_blobs( scan_path = self.root / path.root if prefix is not None: scan_path = scan_path / prefix - elif prefix is not None: + elif prefix is not None and path.key is not None: scan_path = scan_path / path.key # Path to a file @@ -265,3 +235,41 @@ def list_blobs( owner=None, raw=file_path, ) + + +class _FSScanDir(PathyScanDir): + _client: BucketClientFS + + def scandir(self) -> Generator[BucketEntry[BucketFS, pathlib.Path], None, None]: + if self._path is None or not self._path.root: + for bucket in self._client.list_buckets(): + yield BucketEntryFS(bucket.name, is_dir=True, raw=None) + return + assert self._path is not None + assert self._path.root is not None + scan_path = self._client.root / self._path.root + if self._prefix is not None: + scan_path = scan_path / self._prefix + for dir_entry in scan_path.glob("*"): + if dir_entry.is_dir(): + yield BucketEntryFS(dir_entry.name, is_dir=True, raw=None) + else: + file_path = pathlib.Path(dir_entry) + stat = file_path.stat() + file_size = stat.st_size + updated = int(round(stat.st_mtime_ns * 1000)) + blob: Blob = BlobFS( + self._client.get_bucket(self._path), + name=dir_entry.name, + size=file_size, + updated=updated, + owner=None, + raw=file_path, + ) + yield BucketEntryFS( + name=dir_entry.name, + is_dir=False, + size=file_size, + last_modified=updated, + raw=blob, + ) diff --git a/pathy/gcs.py b/pathy/gcs.py index 3318313..4617280 100644 --- a/pathy/gcs.py +++ b/pathy/gcs.py @@ -1,7 +1,15 @@ from dataclasses import dataclass from typing import Any, Dict, Generator, List, Optional -from .base import Blob, Bucket, BucketClient, BucketEntry, ClientError, PurePathy +from .base import ( + Blob, + Bucket, + BucketClient, + BucketEntry, + ClientError, + PathyScanDir, + PurePathy, +) try: from google.api_core import exceptions as gcs_errors # type:ignore @@ -174,49 +182,8 @@ def scandir( # type:ignore[override] path: Optional[PurePathy] = None, prefix: Optional[str] = None, delimiter: Optional[str] = None, - ) -> Generator[BucketEntryGCS, None, None]: # type:ignore[override] - assert self.client is not None, _MISSING_DEPS - continuation_token = None - if path is None or not path.root: - gcs_bucket: GCSNativeBucket - for gcs_bucket in self.list_buckets(): - yield BucketEntryGCS(gcs_bucket.name, is_dir=True, raw=None) - return - sep = path._flavour.sep - bucket = self.lookup_bucket(path) - if bucket is None: - return - while True: - if continuation_token: - response = self.client.list_blobs( - bucket.name, - prefix=prefix, - delimiter=sep, - page_token=continuation_token, - ) - else: - response = self.client.list_blobs( - bucket.name, prefix=prefix, delimiter=sep - ) - for page in response.pages: - for folder in list(page.prefixes): - full_name = folder[:-1] if folder.endswith(sep) else folder - name = full_name.split(sep)[-1] - if name: - yield BucketEntryGCS(name, is_dir=True, raw=None) - for item in page: - name = item.name.split(sep)[-1] - if name: - yield BucketEntryGCS( - name=name, - is_dir=False, - size=item.size, - last_modified=item.updated.timestamp(), - raw=item, - ) - if response.next_page_token is None: - break - continuation_token = response.next_page_token + ) -> PathyScanDir: + return _GCSScanDir(client=self, path=path, prefix=prefix, delimiter=delimiter) def list_blobs( self, @@ -255,3 +222,51 @@ def list_blobs( if response.next_page_token is None: break continuation_token = response.next_page_token + + +class _GCSScanDir(PathyScanDir): + _client: BucketClientGCS + + def scandir(self) -> Generator[BucketEntryGCS, None, None]: + assert self._client.client is not None, _MISSING_DEPS + continuation_token = None + if self._path is None or not self._path.root: + gcs_bucket: GCSNativeBucket + for gcs_bucket in self._client.client.list_buckets(): + yield BucketEntryGCS(gcs_bucket.name, is_dir=True, raw=None) + return + sep = self._path._flavour.sep + bucket = self._client.lookup_bucket(self._path) + if bucket is None: + return + while True: + if continuation_token: + response = self._client.client.list_blobs( + bucket.name, + prefix=self._prefix, + delimiter=sep, + page_token=continuation_token, + ) + else: + response = self._client.client.list_blobs( + bucket.name, prefix=self._prefix, delimiter=sep + ) + for page in response.pages: + for folder in list(page.prefixes): + full_name = folder[:-1] if folder.endswith(sep) else folder + name = full_name.split(sep)[-1] + if name: + yield BucketEntryGCS(name, is_dir=True, raw=None) + for item in page: + name = item.name.split(sep)[-1] + if name: + yield BucketEntryGCS( + name=name, + is_dir=False, + size=item.size, + last_modified=item.updated.timestamp(), + raw=item, + ) + if response.next_page_token is None: + break + continuation_token = response.next_page_token diff --git a/tests/test_base.py b/tests/test_base.py index 188d095..17002fa 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -763,6 +763,7 @@ def test_api_raises_with_no_known_bucket_clients_for_a_scheme(temp_folder): assert isinstance(accessor.client(path), BucketClientFS) +@pytest.mark.skip("requires: https://github.com/explosion/thinc/pull/465") def test_api_export_spacy_model(temp_folder): """spaCy model loading is one of the things we need to support""" use_fs(temp_folder) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 5a58ca2..0000000 --- a/tox.ini +++ /dev/null @@ -1,5 +0,0 @@ -[tox] -envlist = py36,py37,py38 - -[testenv] -commands = pytest From 759cd862be15dd1683248cdbdc0873e64f2712da Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Tue, 2 Feb 2021 15:37:41 -0500 Subject: [PATCH 74/75] fix(pypi): add requirements.txt to distribution (#45) --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index db51e4f..edbe785 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ include setup.py include README.md include LICENSE include pathy/py.typed +include requirements.txt From 2bf1cff66561ab79ef35637e60b8beb32ef4332f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 2 Feb 2021 20:40:54 +0000 Subject: [PATCH 75/75] chore(release): 0.3.5 ## [0.3.5](https://github.com/justindujardin/pathy/compare/v0.3.4...v0.3.5) (2021-02-02) ### Bug Fixes * **pypi:** add requirements.txt to distribution ([#45](https://github.com/justindujardin/pathy/issues/45)) ([759cd86](https://github.com/justindujardin/pathy/commit/759cd862be15dd1683248cdbdc0873e64f2712da)) * python 3.9 compatibility ([#46](https://github.com/justindujardin/pathy/issues/46)) ([a965f40](https://github.com/justindujardin/pathy/commit/a965f40a086ccb305e58936813a30c35f95d5212)) --- CHANGELOG.md | 8 ++++++++ pathy/about.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88fe176..d7a99bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.3.5](https://github.com/justindujardin/pathy/compare/v0.3.4...v0.3.5) (2021-02-02) + + +### Bug Fixes + +* **pypi:** add requirements.txt to distribution ([#45](https://github.com/justindujardin/pathy/issues/45)) ([759cd86](https://github.com/justindujardin/pathy/commit/759cd862be15dd1683248cdbdc0873e64f2712da)) +* python 3.9 compatibility ([#46](https://github.com/justindujardin/pathy/issues/46)) ([a965f40](https://github.com/justindujardin/pathy/commit/a965f40a086ccb305e58936813a30c35f95d5212)) + ## [0.3.4](https://github.com/justindujardin/pathy/compare/v0.3.3...v0.3.4) (2020-11-22) diff --git a/pathy/about.py b/pathy/about.py index 96f6cde..8f0dbb9 100644 --- a/pathy/about.py +++ b/pathy/about.py @@ -1,5 +1,5 @@ __title__ = "pathy" -__version__ = "0.3.4" +__version__ = "0.3.5" __summary__ = "pathlib.Path subclasses for local and cloud bucket storage" __uri__ = "https://github.com/justindujardin/pathy" __author__ = "Justin DuJardin"