From 4c6a97d0692452b11cfb698a8ffeee1ecf171f50 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Wed, 26 Oct 2022 15:37:11 -0700 Subject: [PATCH 1/5] Update docstrings for latest Sphinx Now that we're using a new-enough version of Sphinx, drop the types from all docstrings as much as possible and let Sphinx pick them up from the type annotations. Adjust the return value type string to work better with automatic linking. Delete some notes about default values where Sphinx figures this out from the signature and autogenerates the documentation. Add some parameter documentation that was missing and make a few minor documentation fixes I noticed on the way through. Add proper docstrings for some enum values. --- docs/documenteer.toml | 1 + src/gafaelfawr/auth.py | 41 ++-- src/gafaelfawr/cache.py | 80 ++++--- src/gafaelfawr/config.py | 6 +- src/gafaelfawr/dependencies/auth.py | 51 +++-- src/gafaelfawr/dependencies/config.py | 2 +- src/gafaelfawr/dependencies/context.py | 4 +- src/gafaelfawr/dependencies/return_url.py | 27 ++- src/gafaelfawr/exceptions.py | 14 +- src/gafaelfawr/factory.py | 61 ++--- src/gafaelfawr/keypair.py | 18 +- src/gafaelfawr/main.py | 2 +- src/gafaelfawr/middleware/state.py | 18 +- src/gafaelfawr/models/github.py | 2 +- src/gafaelfawr/models/history.py | 44 +++- src/gafaelfawr/models/kubernetes.py | 22 +- src/gafaelfawr/models/link.py | 13 +- src/gafaelfawr/models/oidc.py | 8 +- src/gafaelfawr/models/state.py | 10 +- src/gafaelfawr/models/token.py | 45 ++-- src/gafaelfawr/operator/startup.py | 17 +- src/gafaelfawr/operator/tokens.py | 20 +- src/gafaelfawr/providers/base.py | 22 +- src/gafaelfawr/providers/github.py | 75 ++++--- src/gafaelfawr/providers/oidc.py | 101 +++++---- src/gafaelfawr/services/admin.py | 34 +-- src/gafaelfawr/services/firestore.py | 20 +- src/gafaelfawr/services/kubernetes.py | 22 +- src/gafaelfawr/services/ldap.py | 28 +-- src/gafaelfawr/services/oidc.py | 58 ++--- src/gafaelfawr/services/token.py | 261 ++++++++++++---------- src/gafaelfawr/services/token_cache.py | 79 ++++--- src/gafaelfawr/services/userinfo.py | 72 +++--- src/gafaelfawr/slack.py | 16 +- src/gafaelfawr/storage/admin.py | 10 +- src/gafaelfawr/storage/base.py | 35 +-- src/gafaelfawr/storage/firestore.py | 78 +++---- src/gafaelfawr/storage/history.py | 38 ++-- src/gafaelfawr/storage/kubernetes.py | 31 ++- src/gafaelfawr/storage/ldap.py | 81 +++---- src/gafaelfawr/storage/oidc.py | 20 +- src/gafaelfawr/storage/token.py | 101 ++++----- src/gafaelfawr/util.py | 36 +-- 43 files changed, 949 insertions(+), 775 deletions(-) diff --git a/docs/documenteer.toml b/docs/documenteer.toml index 2a9349833..3cd196cb5 100644 --- a/docs/documenteer.toml +++ b/docs/documenteer.toml @@ -29,6 +29,7 @@ nitpick_ignore = [ ["py:exc", "fastapi.exceptions.RequestValidationError"], ["py:exc", "httpx.HTTPError"], ["py:obj", "fastapi.routing.APIRoute"], + ["py:class", "kubernetes_asyncio.client.V1Secret"], ["py:class", "kubernetes_asyncio.client.api_client.ApiClient"], ["py:class", "kubernetes_asyncio.client.models.v1_secret.V1Secret"], ["py:class", "pydantic.env_settings.BaseSettings"], diff --git a/src/gafaelfawr/auth.py b/src/gafaelfawr/auth.py index 1b0a3c8cd..4fee84026 100644 --- a/src/gafaelfawr/auth.py +++ b/src/gafaelfawr/auth.py @@ -32,7 +32,10 @@ class AuthType(str, Enum): """Authentication types for the WWW-Authenticate header.""" Basic = "basic" + """HTTP Basic Authentication (RFC 7617).""" + Bearer = "bearer" + """HTTP Bearer Authentication (RFC 6750).""" class AuthError(Enum): @@ -61,7 +64,7 @@ def as_header(self) -> str: Returns ------- - header : `str` + str Contents of the WWW-Authenticate header. """ return f'{self.auth_type.name} realm="{self.realm}"' @@ -72,20 +75,20 @@ class AuthErrorChallenge(AuthChallenge): """Represents a ``WWW-Authenticate`` header for an error challenge.""" error: AuthError - """The value of the error attribute if present.""" + """Short error code.""" error_description: str - """The value of the error description attribute if present.""" + """Human-readable error description.""" scope: Optional[str] = None - """The value of the scope attribute if present.""" + """Scope required to access this URL.""" def as_header(self) -> str: """Construct the WWW-Authenticate header for this challenge. Returns ------- - header : `str` + str Contents of the WWW-Authenticate header. """ if self.auth_type == AuthType.Basic: @@ -112,13 +115,13 @@ def generate_challenge( Parameters ---------- - request : `gafaelfawr.dependencies.context.RequestContext` + context The context of the incoming request. - auth_type : `AuthType` + auth_type The type of authentication to request. - exc : `gafaelfawr.exceptions.OAuthBearerError` + exc An exception representing a bearer token error. - scopes : Set[`str`], optional + scopes Optional scopes to include in the challenge, primarily intended for `~gafaelfawr.exceptions.InsufficientScopeError` exceptions. @@ -163,20 +166,20 @@ def generate_unauthorized_challenge( Parameters ---------- - context : `gafaelfawr.dependencies.context.RequestContext` + context The incoming request. - auth_type : `AuthType` + auth_type The type of authentication to request. - exc : `gafaelfawr.exceptions.OAuthBearerError`, optional + exc An exception representing a bearer token error. If not present, assumes that no token was provided and there was no error. - ajax_forbidden : `bool`, optional + ajax_forbidden If set to `True`, check to see if the request was sent via AJAX (see - Notes) and, if so, convert it to a 403 error. The default is `False`. + Notes) and, if so, convert it to a 403 error. Returns ------- - exception : ``fastapi.HTTPException`` + ``fastapi.HTTPException`` The exception to raise, either a 403 (for AJAX) or a 401. Notes @@ -245,17 +248,17 @@ def parse_authorization(context: RequestContext) -> Optional[str]: Parameters ---------- - context : `gafaelfawr.dependencies.context.RequestContext` + context The context of the incoming request. Returns ------- - handle_or_token : `str` or `None` - The handle or token if one was found, otherwise None. + str or None + The handle or token if one was found, otherwise `None`. Raises ------ - gafaelfawr.exceptions.InvalidRequestError + InvalidRequestError If the Authorization header is malformed. Notes diff --git a/src/gafaelfawr/cache.py b/src/gafaelfawr/cache.py index d21e82d75..a5c3e42ec 100644 --- a/src/gafaelfawr/cache.py +++ b/src/gafaelfawr/cache.py @@ -92,12 +92,12 @@ def get(self, name: str) -> Optional[int]: Parameters ---------- - name : `str` + name Username or group name. Returns ------- - id : `int` or `None` + int or None UID or GID if the name is in the cache, else `None`. """ return self._cache.get(name) @@ -109,7 +109,7 @@ def lock(self) -> asyncio.Lock: Returns ------- - lock : `asyncio.Lock` + asyncio.Lock The lock for the cache. """ return self._lock @@ -117,6 +117,13 @@ def lock(self) -> asyncio.Lock: def store(self, name: str, id: int) -> None: """Store the UID or GID for a user or group in the cache. + Parameters + ---------- + name + Name of the user or group. + id + UID or GID to store. + Examples -------- .. code-block:: python @@ -126,13 +133,6 @@ def store(self, name: str, id: int) -> None: if not uid: # do something to allocate a UID id_cache.store(username, uid) - - Parameters - ---------- - name : `str` - Name of the user or group. - id : `int` - UID or GID to store. """ self._cache[name] = id @@ -146,9 +146,9 @@ class UserLockManager: Parameters ---------- - general_lock : `asyncio.Lock` + general_lock Lock protecting the per-user locks. - user_lock : `asyncio.Lock` + user_lock Per-user lock for a given user. """ @@ -237,12 +237,12 @@ async def lock(self, username: str) -> UserLockManager: Parameters ---------- - username : `str` + username Per-user lock to hold. Returns ------- - lock : `UserLockManager` + UserLockManager Async context manager that will take the user lock. """ async with self._lock: @@ -257,7 +257,7 @@ class LDAPCache(PerUserCache, Generic[S]): Parameters ---------- - content : `typing.Type` + content The type of object being stored. """ @@ -271,12 +271,12 @@ def get(self, username: str) -> Optional[S]: Parameters ---------- - username : `str` + username Username for which to retrieve data. Returns ------- - data : `typing.Any` or `None` + Any or None The cached data or `None` if there is no data in the cache. """ return self._cache.get(username) @@ -292,9 +292,9 @@ def store(self, username: str, data: S) -> None: Parameters ---------- - username : `str` + username Username for which to store data. - data : `typing.Any` + data Data to store. """ self._cache[username] = data @@ -323,16 +323,16 @@ def get( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - service : `str` + service The service of the internal token. - scopes : List[`str`] + scopes The scopes the internal token should have. Returns ------- - token : `gafaelfawr.models.token.Token` or `None` + Token or None The cached token or `None` if there is no matching token in the cache. @@ -358,13 +358,13 @@ def store( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - service : `str` + service The service of the internal token. - scopes : List[`str`] + scopes The scopes the internal token should have. - token : `gafaelfawr.models.token.Token` + token The token to cache. """ key = self._build_key(token_data, service, scopes) @@ -377,12 +377,17 @@ def _build_key( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - service : `str` + service The service of the internal token. - scopes : List[`str`] + scopes The scopes the internal token should have. + + Returns + ------- + Tuple + An object suitable for use as a hash key for this internal token. """ expires = str(token_data.expires) if token_data.expires else "None" scope = ",".join(sorted(scopes)) @@ -397,12 +402,12 @@ def get(self, token_data: TokenData) -> Optional[Token]: Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. Returns ------- - token : `gafaelfawr.models.token.Token` or `None` + Token or None The cached token or `None` if there is no matching token in the cache. @@ -422,9 +427,9 @@ def store(self, token_data: TokenData, token: Token) -> None: Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - token : `gafaelfawr.models.token.Token` + token The token to cache. """ key = self._build_key(token_data) @@ -435,8 +440,13 @@ def _build_key(self, token_data: TokenData) -> Tuple[str, ...]: Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. + + Returns + ------- + Tuple + An object suitable for use as a hash key for this internal token. """ expires = str(token_data.expires) if token_data.expires else "None" return (token_data.token.key, expires) diff --git a/src/gafaelfawr/config.py b/src/gafaelfawr/config.py index e05826818..ddc9b7bf0 100644 --- a/src/gafaelfawr/config.py +++ b/src/gafaelfawr/config.py @@ -728,13 +728,13 @@ def from_file(cls, path: str) -> Config: Parameters ---------- - path : `str` + path Path to the settings file in YAML. Returns ------- - config : `Config` - The corresponding Config object. + Config + The corresponding `Config` object. """ with open(path, "r") as f: raw_settings = yaml.safe_load(f) diff --git a/src/gafaelfawr/dependencies/auth.py b/src/gafaelfawr/dependencies/auth.py index 6e246b2ce..70c7cdef1 100644 --- a/src/gafaelfawr/dependencies/auth.py +++ b/src/gafaelfawr/dependencies/auth.py @@ -39,24 +39,22 @@ class for `AuthenticateRead` and `AuthenticateWrite`, which provide Parameters ---------- - require_session : `bool`, optional + require_session Require that the credentials come from a cookie, not an - ``Authorization`` header. The default is `False`. - require_scope : `str`, optional + ``Authorization`` header. + require_scope If set, access will be denied if the authentication token does not have this scope. - redirect_if_unauthenticated : `bool`, optional + redirect_if_unauthenticated If the request is unauthenticated, redirect it to the ``/login`` route - rather than returning a challenge. The default is `False`. - allow_bootstrap_token : `bool`, optional - Allow use of the bootstrap token to authenticate to this route. The - default is `False`. - auth_type : `gafaelfawr.auth.AuthType` - The type of the challenge if the user is not authenticated. The - default is `gafaelfawr.auth.AuthType.Bearer`. - ajax_forbidden : `bool`, optional + rather than returning a challenge. + allow_bootstrap_token + Allow use of the bootstrap token to authenticate to this route. + auth_type + The type of the challenge if the user is not authenticated. + ajax_forbidden If set to `True`, check to see if the request was sent via AJAX (see - Notes) and, if so, convert it to a 403 error. The default is `False`. + Notes) and, if so, convert it to a 403 error. """ def __init__( @@ -91,20 +89,20 @@ async def authenticate( Parameters ---------- - context : `gafaelfawr.dependencies.context.RequestContext` + context The request context. - x_csrf_token : `str`, optional + x_csrf_token The value of the ``X-CSRF-Token`` header, if provided. Returns ------- - data : `gafaelfawr.models.token.TokenData` + TokenData The data associated with the verified token. Raises ------ fastapi.HTTPException - If authentication is not provided or is not valid. + Raised if authentication is not provided or is not valid. """ token = context.state.token if token: @@ -161,7 +159,7 @@ def _redirect_or_error(self, context: RequestContext) -> HTTPException: Returns ------- - exc : ``fastapi.HTTPException`` + exc The redirect. """ if not self.redirect_if_unauthenticated: @@ -184,8 +182,8 @@ def _verify_csrf( Raises ------ fastapi.HTTPException - If no CSRF token was provided or if it was incorrect, and the - method was something other than GET or OPTIONS. + Raised if no CSRF token was provided or if it was incorrect, and + the method was something other than GET or OPTIONS. """ if context.request.method in ("GET", "OPTIONS"): return @@ -200,7 +198,10 @@ def _verify_csrf( class AuthenticateRead(Authenticate): - """Authenticate a read API.""" + """Authenticate a read API. + + Should be used as a FastAPI dependency. + """ async def __call__( self, context: RequestContext = Depends(context_dependency) @@ -209,7 +210,10 @@ async def __call__( class AuthenticateWrite(Authenticate): - """Authenticate a write API.""" + """Authenticate a write API. + + Should be used as a FastAPI dependency. + """ async def __call__( self, @@ -235,7 +239,8 @@ async def verified_oidc_token( Raises ------ fastapi.HTTPException - An authorization challenge if no token is provided. + Raised if no token is provided or if the token is not valid. Contains + an authorization challenge. """ try: encoded_token = parse_authorization(context) diff --git a/src/gafaelfawr/dependencies/config.py b/src/gafaelfawr/dependencies/config.py index fae44faaa..bd5b8a692 100644 --- a/src/gafaelfawr/dependencies/config.py +++ b/src/gafaelfawr/dependencies/config.py @@ -47,7 +47,7 @@ def set_settings_path(self, path: str) -> None: Parameters ---------- - path : `str` + path The new configuration path. """ self._settings_path = path diff --git a/src/gafaelfawr/dependencies/context.py b/src/gafaelfawr/dependencies/context.py index 896805ff6..68f3da50d 100644 --- a/src/gafaelfawr/dependencies/context.py +++ b/src/gafaelfawr/dependencies/context.py @@ -65,7 +65,7 @@ def rebind_logger(self, **values: Any) -> None: Parameters ---------- - **values : `typing.Any` + **values Additional values that should be added to the logging context. """ self.logger = self.logger.bind(**values) @@ -125,7 +125,7 @@ async def initialize(self, config: Config) -> None: Parameters ---------- - config : `gafaelfawr.config.Config` + config Gafaelfawr configuration. """ if self._process_context: diff --git a/src/gafaelfawr/dependencies/return_url.py b/src/gafaelfawr/dependencies/return_url.py index 866521565..5127c1e15 100644 --- a/src/gafaelfawr/dependencies/return_url.py +++ b/src/gafaelfawr/dependencies/return_url.py @@ -16,7 +16,11 @@ from ..exceptions import InvalidReturnURLError from .context import RequestContext, context_dependency -__all__ = ["parsed_redirect_uri", "return_url", "return_url_with_header"] +__all__ = [ + "parsed_redirect_uri", + "return_url", + "return_url_with_header", +] def _check_url(url: str, param: str, context: RequestContext) -> ParseResult: @@ -24,17 +28,17 @@ def _check_url(url: str, param: str, context: RequestContext) -> ParseResult: Parameters ---------- - url : `str` + url The URL to check. - param : `str` + param The name of the query parameter in which the URL was found, for error reporting purposes. - context : `gafaelfawr.dependencies.RequestContext` + context The context of the request. Returns ------- - parsed_url : `urllib.parse.ParseResult` + urllib.parse.ParseResult The parsed URL. Raises @@ -73,7 +77,7 @@ async def return_url( Returns ------- - return_url : `str` or `None` + urllib.parse.ParseResult The verified return URL, or `None` if none was given. Raises @@ -108,13 +112,13 @@ async def return_url_with_header( ) -> Optional[str]: """Validate a return URL in an ``rd`` parameter or header. - Same as :py:func:`return_url` except also accepts a return URL in the + Same as `return_url` except also accepts a return URL in the ``X-Auth-Request-Redirect`` header if the ``rd`` query parameter was not set. Returns ------- - return_url : `str` or `None` + urllib.parse.ParseResult The verified return URL, or `None` if none was given. Raises @@ -140,13 +144,12 @@ async def parsed_redirect_uri( ) -> ParseResult: """Validate a return URL in a ``redirect_uri`` parameter. - Same as :py:func:`return_url` except expects the URL in a ``return_uri`` - parameter instead of ``rd`` and returns a parsed URL instead of the `str` - form. + Same as `return_url` except expects the URL in a ``return_uri`` parameter + instead of ``rd`` and returns a parsed URL instead of the `str` form. Returns ------- - redirect_uri : `urllib.parse.ParseResult` + urllib.parse.ParseResult The verified, parsed redirect URI. Raises diff --git a/src/gafaelfawr/exceptions.py b/src/gafaelfawr/exceptions.py index 7efb8b823..49ba29cf7 100644 --- a/src/gafaelfawr/exceptions.py +++ b/src/gafaelfawr/exceptions.py @@ -73,11 +73,11 @@ class ValidationError(SlackIgnoredException, kopf.PermanentError): Parameters ---------- - message : `str` + message The error message (used as the ``msg`` key). - location : `ErrorLocation` + location The part of the request giving rise to the error. - field : `str` + field The field within that part of the request giving rise to the error. Notes @@ -107,8 +107,12 @@ def __init__( def to_dict(self) -> Dict[str, Union[List[str], str]]: """Convert the exception to a dictionary suitable for the exception. - The return value is intended to be passed as the ``detail`` parameter - to a `fastapi.HTTPException`. + Returns + ------- + dict + Serialized error emssage to pass as the ``detail`` parameter to a + ``fastapi.HTTPException``. It is designed to produce the same + JSON structure as native FastAPI errors. """ return { "loc": [self.location.value, self.field], diff --git a/src/gafaelfawr/factory.py b/src/gafaelfawr/factory.py index c19259499..0a0a7066d 100644 --- a/src/gafaelfawr/factory.py +++ b/src/gafaelfawr/factory.py @@ -97,12 +97,12 @@ async def from_config(cls, config: Config) -> ProcessContext: Parameters ---------- - config : `gafaelfawr.config.Config` + config The Gafaelfawr configuration. Returns ------- - context : `ProcessContext` + ProcessContext Shared context for a Gafaelfawr process. """ ldap_pool = None @@ -159,11 +159,11 @@ class Factory: Parameters ---------- - context : `ProcessContext` + context Shared process context. - session : `sqlalchemy.ext.asyncio.async_scoped_session` + session Database session. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use for errors. """ @@ -185,17 +185,17 @@ async def create( Parameters ---------- - config : `gafaelfawr.config.Config` + config Gafaelfawr configuration. - engine : `sqlalchemy.ext.asyncio.AsyncEngine` + engine Database engine to use for connections. - check_db : `bool`, optional + check_db If set to `True`, check database connectivity before returning by doing a simple query. Returns ------- - factory : `gafaelfawr.factory.Factory` + Factory Newly-created factory. The caller must call `aclose` on the returned object during shutdown. """ @@ -222,17 +222,17 @@ async def standalone( Parameters ---------- - config : `gafaelfawr.config.Config` + config Gafaelfawr configuration. - engine : `sqlalchemy.ext.asyncio.AsyncEngine` + engine Database engine to use for connections. - check_db : `bool`, optional + check_db If set to `True`, check database connectivity before returning by doing a simple query. Yields ------ - factory : `Factory` + Factory The factory. Must be used as an async context manager. Examples @@ -279,7 +279,7 @@ def create_admin_service(self) -> AdminService: Returns ------- - admin_service : `gafaelfawr.services.admin.AdminService` + AdminService The new token administrator manager. """ admin_store = AdminStore(self.session) @@ -291,7 +291,7 @@ def create_firestore_service(self) -> FirestoreService: Returns ------- - firestore : `gafaelfawr.services.firestore.FirestoreService` + FirestoreService Newly-created Firestore service. """ storage = self.create_firestore_storage() @@ -309,7 +309,7 @@ def create_firestore_storage(self) -> FirestoreStorage: Returns ------- - firestore : `gafaelfawr.storage.firestore.FirestoreStorage` + FirestoreStorage Newly-created Firestore storage. """ if not self._context.config.firestore: @@ -323,12 +323,12 @@ def create_kubernetes_token_service( Parameters ---------- - api_client : ``kubernetes_asyncio.client.ApiClient`` + api_client The Kubernetes client. Returns ------- - service : `gafaelfawr.services.kubernetes.KubernetesTokenService` + KubernetesTokenService Newly-created Kubernetes service. """ storage = KubernetesTokenStorage(api_client, self._logger) @@ -345,7 +345,7 @@ def create_oidc_service(self) -> OIDCService: Returns ------- - oidc_service : `gafaelfawr.services.oidc.OIDCService` + OIDCService A new OpenID Connect server. """ if not self._context.config.oidc_server: @@ -371,13 +371,14 @@ def create_oidc_user_info_service(self) -> OIDCUserInfoService: Returns ------- - user_info_service : `gafaelfawr.services.userinfo.OIDCUserInfoService` + OIDCUserInfoService A new user information service. Raises ------ - gafaelfawr.exceptions.NotConfiguredError - The configured authentication provider is not OpenID Connect. + NotConfiguredError + Raised if the configured authentication provider is not OpenID + Connect. """ if not self._context.config.oidc: raise NotConfiguredError("OpenID Connect is not configured") @@ -414,7 +415,7 @@ def create_oidc_token_verifier(self) -> OIDCTokenVerifier: Returns ------- - verifier : `gafaelfawr.providers.oidc.OIDCTokenVerifier` + OIDCTokenVerifier A new JWT token verifier. """ if not self._context.config.oidc: @@ -434,13 +435,13 @@ def create_provider(self) -> Provider: Returns ------- - provider : `gafaelfawr.providers.base.Provider` + Provider A new Provider. Raises ------ NotImplementedError - None of the authentication providers are configured. + Raised if none of the authentication providers are configured. """ if self._context.config.github: return GitHubProvider( @@ -467,7 +468,7 @@ def create_token_cache_service(self) -> TokenCacheService: Returns ------- - cache : `gafaelfawr.services.token_cache.TokenCacheService` + TokenCacheService A new token cache. """ key = self._context.config.session_secret @@ -490,7 +491,7 @@ def create_token_service(self) -> TokenService: Returns ------- - token_service : `gafaelfawr.services.token.TokenService` + TokenService The new token manager. """ token_db_store = TokenDatabaseStore(self.session) @@ -526,7 +527,7 @@ def create_user_info_service(self) -> UserInfoService: Returns ------- - info_service : `gafaelfawr.services.userinfo.UserInfoService` + UserInfoService Newly created service. """ firestore = None @@ -561,7 +562,7 @@ def set_context(self, context: ProcessContext) -> None: Parameters ---------- - context : `ProcessContext` + context New process context. """ self._context = context @@ -574,7 +575,7 @@ def set_logger(self, logger: BoundLogger) -> None: Parameters ---------- - logger : `structlog.stdlib.BoundLogger` + logger New logger. """ self._logger = logger diff --git a/src/gafaelfawr/keypair.py b/src/gafaelfawr/keypair.py index 6c6fd8185..61a79da7a 100644 --- a/src/gafaelfawr/keypair.py +++ b/src/gafaelfawr/keypair.py @@ -37,18 +37,18 @@ def from_pem(cls, pem: bytes) -> RSAKeyPair: Parameters ---------- - pem : `bytes` + pem The PEM-encoded key (must not be password-protected). Returns ------- - keypair : `RSAKeyPair` + RSAKeyPair The corresponding key pair. Raises ------ cryptography.exceptions.UnsupportedAlgorithm - The provided key is not an RSA private key. + Raised if the provided key is not an RSA private key. """ private_key = load_pem_private_key( pem, password=None, backend=default_backend() @@ -63,7 +63,7 @@ def generate(cls) -> RSAKeyPair: Returns ------- - keypair : `RSAKeyPair` + RSAKeyPair Newly-generated key pair. """ private_key = rsa.generate_private_key( @@ -83,7 +83,7 @@ def private_key_as_pem(self) -> bytes: Returns ------- - key : `bytes` + bytes Private key encoded using PKCS#8 with no encryption. """ if not self._private_key_as_pem: @@ -97,13 +97,13 @@ def public_key_as_jwks(self, kid: Optional[str] = None) -> JWKS: Parameters ---------- - kid : `str`, optional + kid The key ID. If not included, the kid will be omitted, making the result invalid JWKS. Returns ------- - key : `gafaelfawr.models.oidc.JWKS` + JWKS The public key in JWKS format. """ public_numbers = self.public_numbers() @@ -122,7 +122,7 @@ def public_key_as_pem(self) -> bytes: Returns ------- - public_key : `bytes` + bytes The public key in PEM encoding and SubjectPublicKeyInfo format. """ if not self._public_key_as_pem: @@ -137,7 +137,7 @@ def public_numbers(self) -> rsa.RSAPublicNumbers: Returns ------- - nums : `cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers` + cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers The public numbers. """ return self.private_key.public_key().public_numbers() diff --git a/src/gafaelfawr/main.py b/src/gafaelfawr/main.py index 10facc92f..e9222f3db 100644 --- a/src/gafaelfawr/main.py +++ b/src/gafaelfawr/main.py @@ -41,7 +41,7 @@ def create_app(*, load_config: bool = True) -> FastAPI: Parameters ---------- - load_config : `bool`, optional + load_config If set to `False`, do not try to load the configuration. Configure `~safir.middleware.x_forwarded.XForwardedMiddleware` with the default set of proxy IP addresses. This is used primarily for OpenAPI diff --git a/src/gafaelfawr/middleware/state.py b/src/gafaelfawr/middleware/state.py index ae4afb164..75159e70d 100644 --- a/src/gafaelfawr/middleware/state.py +++ b/src/gafaelfawr/middleware/state.py @@ -28,14 +28,14 @@ async def from_cookie(cls, cookie: str, request: Request) -> BaseState: Parameters ---------- - cookie : `str` + cookie The encrypted cookie value. - request : ``fastapi.Request`` + request The request, used for logging. Returns ------- - state : `BaseState` + BaseState The state represented by the cookie. """ @@ -45,7 +45,7 @@ async def as_cookie(self) -> str: Returns ------- - cookie : `str` + str The encrypted cookie value. """ @@ -69,11 +69,11 @@ class StateMiddleware(BaseHTTPMiddleware): Parameters ---------- - app : ``fastapi.FastAPI`` + app The ASGI application. - cookie_name : `str` + cookie_name The name of the state cookie. - state_class : `BaseState` + state_class The class to use to parse the cookie. Must be derived from `BaseState`. """ @@ -120,12 +120,12 @@ def is_cookie_secure(request: Request) -> bool: Parameters ---------- - request : ``fastapi.Request`` + request The incoming request. Returns ------- - secure : `bool` + bool Whether to mark the cookie as secure. Notes diff --git a/src/gafaelfawr/models/github.py b/src/gafaelfawr/models/github.py index 8c6af459d..05b77cf8d 100644 --- a/src/gafaelfawr/models/github.py +++ b/src/gafaelfawr/models/github.py @@ -38,7 +38,7 @@ def group_name(self) -> str: Returns ------- - group_name : `str` + str The name of the group. Notes diff --git a/src/gafaelfawr/models/history.py b/src/gafaelfawr/models/history.py index 5c2ab4819..eda072c28 100644 --- a/src/gafaelfawr/models/history.py +++ b/src/gafaelfawr/models/history.py @@ -105,7 +105,23 @@ class HistoryCursor: @classmethod def from_str(cls, cursor: str) -> HistoryCursor: - """Build cursor from the string serialization form.""" + """Build cursor from the string serialization form. + + Parameters + ---------- + cursor + Serialized form of the cursor. + + Returns + ------- + HistoryCursor + The cursor represented as an object. + + Raises + ------ + InvalidCursorError + Raised if the cursor is not valid. + """ previous = cursor.startswith("p") if previous: cursor = cursor[1:] @@ -121,7 +137,18 @@ def from_str(cls, cursor: str) -> HistoryCursor: @classmethod def invert(cls, cursor: HistoryCursor) -> HistoryCursor: - """Return the inverted cursor (going the opposite direction).""" + """Return the inverted cursor (going the opposite direction). + + Parameters + ---------- + cursor + Cursor to invert. + + Returns + ------- + HistoryCursor + The inverted cursor. + """ return cls( time=cursor.time, id=cursor.id, previous=not cursor.previous ) @@ -156,7 +183,13 @@ class PaginatedHistory(Generic[E]): """Cursor for the previous batch of entries.""" def link_header(self, base_url: URL) -> str: - """Construct an RFC 8288 ``Link`` header for a paginated result.""" + """Construct an RFC 8288 ``Link`` header for a paginated result. + + Parameters + ---------- + base_url + The starting URL of the current group of entries. + """ first_url = base_url.remove_query_params("cursor") header = f' <{str(first_url)}>; rel="first"' params = parse_qs(first_url.query) @@ -344,6 +377,11 @@ def reduced_dict(self) -> Dict[str, Any]: Excludes the ``old_`` fields for changes other than edits, and when the edit doesn't change those fields. + Returns + ------- + dict + Dictionary representation of the object. + Notes ----- Knowing which fields to exclude requires understanding the semantics diff --git a/src/gafaelfawr/models/kubernetes.py b/src/gafaelfawr/models/kubernetes.py index a09f92f91..f2af4d454 100644 --- a/src/gafaelfawr/models/kubernetes.py +++ b/src/gafaelfawr/models/kubernetes.py @@ -62,7 +62,7 @@ def from_dict(cls, obj: Mapping[str, Any]) -> GafaelfawrServiceToken: Parameters ---------- - obj : Dict[`str`, Any] + obj The object as returned by the Kubernetes API. Raises @@ -102,7 +102,7 @@ def from_dict(cls, obj: Mapping[str, Any]) -> GafaelfawrServiceToken: @property def key(self) -> str: - """Return a unique key for this custom object.""" + """A unique key for this custom object.""" return f"{self.namespace}/{self.name}" @@ -143,10 +143,15 @@ def failure( Parameters ---------- - service_token : `KubernetesResource` + service_token The object being processed. - message : `str` + message The error message for the failure. + + Returns + ------- + KubernetesResourceStatus + The corresponding status object. """ return cls( message=message, @@ -155,7 +160,14 @@ def failure( ) def to_dict(self) -> Dict[str, Union[str, int]]: - """Convert the status update to a dictionary for Kubernetes.""" + """Convert the status update to a dictionary for Kubernetes. + + Returns + ------- + dict + Information to store in the ``status`` field of the Kubernetes + resource. + """ transition_time = self.timestamp.isoformat().split("+")[0] + "Z" status = "False" if self.reason == StatusReason.Failed else "True" return { diff --git a/src/gafaelfawr/models/link.py b/src/gafaelfawr/models/link.py index 08cc867f6..86d7a1b12 100644 --- a/src/gafaelfawr/models/link.py +++ b/src/gafaelfawr/models/link.py @@ -27,7 +27,18 @@ class LinkData: @classmethod def from_header(cls, header: Optional[str]) -> LinkData: - """Parse an RFC 8288 ``Link`` with pagination URLs.""" + """Parse an RFC 8288 ``Link`` with pagination URLs. + + Parameters + ---------- + header + The contents of an RFC 8288 ``Link`` header. + + Returns + ------- + LinkData + The parsed form of that header. + """ links = {} if header: elements = header.split(",") diff --git a/src/gafaelfawr/models/oidc.py b/src/gafaelfawr/models/oidc.py index fd26aa528..4cc140d30 100644 --- a/src/gafaelfawr/models/oidc.py +++ b/src/gafaelfawr/models/oidc.py @@ -39,18 +39,18 @@ def from_str(cls, code: str) -> OIDCAuthorizationCode: Parameters ---------- - code : `str` + code The serialized code. Returns ------- - decoded_code : `OIDCAuthorizationCode` + OIDCAuthorizationCode The decoded `OIDCAuthorizationCode`. Raises ------ - gafaelfawr.exceptions.InvalidGrantError - The provided string is not a valid authorization code. + InvalidGrantError + Raised if the provided string is not a valid authorization code. """ if not code.startswith("gc-"): msg = "Token does not start with gc-" diff --git a/src/gafaelfawr/models/state.py b/src/gafaelfawr/models/state.py index 1f168b204..295bf693a 100644 --- a/src/gafaelfawr/models/state.py +++ b/src/gafaelfawr/models/state.py @@ -49,17 +49,17 @@ async def from_cookie( Parameters ---------- - cookie : `str` + cookie The encrypted cookie value. - key : `bytes` + key The `~cryptography.fernet.Fernet` key used to decrypt it. - request : ``fastapi.Request`` or `None` + request The request, used for logging. If not provided (primarily for the test suite), invalid state cookies will not be logged. Returns ------- - state : `State` + State The state represented by the cookie. """ config = await config_dependency() @@ -92,7 +92,7 @@ async def as_cookie(self) -> str: Returns ------- - cookie : `str` + str The encrypted cookie value. """ data = {} diff --git a/src/gafaelfawr/models/token.py b/src/gafaelfawr/models/token.py index adc97ab8f..8d1143e4e 100644 --- a/src/gafaelfawr/models/token.py +++ b/src/gafaelfawr/models/token.py @@ -57,17 +57,17 @@ def from_str(cls, token: str) -> Token: Parameters ---------- - token : `str` + token The serialized token. Returns ------- - decoded_token : `Token` + Token The decoded `Token`. Raises ------ - gafaelfawr.exceptions.InvalidTokenError + InvalidTokenError The provided string is not a valid token. """ if not token.startswith("gt-"): @@ -89,27 +89,30 @@ def __str__(self) -> str: class TokenType(Enum): - """The class of token. - - session - An interactive user web session. - user - A user-generated token that may be used programmatically. - notebook - The token delegated to a Jupyter notebook for the user. - internal - A service-to-service token used for internal sub-calls made as part of - processing a user request. - service - A service-to-service token used for internal calls initiated by - services, unrelated to a user request. - """ + """The class of token.""" session = "session" + """An interactive user web session.""" + user = "user" + """A user-generated token that may be used programmatically.""" + notebook = "notebook" + """The token delegated to a Jupyter notebook for the user.""" + internal = "internal" + """Service-to-service token chained from a user request. + + A service-to-service token used for internal sub-calls made as part of + processing a user request. + """ + service = "service" + """Service-to-service token independent of a user request. + + A service-to-service token used for internal calls initiated by + services, unrelated to a user request. + """ class TokenGroup(BaseModel): @@ -317,7 +320,7 @@ def to_userinfo_dict(self) -> Dict[str, Any]: Returns ------- - token_userinfo : Dict[`str`, Any]: + dict Dictionary of information, roughly equivalent to calling ``dict(exclude_none=True)`` on the `TokenUserInfo` object, but ensuring that only its data is included even if called on a @@ -362,7 +365,7 @@ def bootstrap_token(cls) -> TokenData: Returns ------- - data : `gafaelfawr.models.token.TokenData` + TokenData Artificial data for the bootstrap token. """ return cls( @@ -382,7 +385,7 @@ def internal_token(cls) -> TokenData: Returns ------- - data : `gafaelfawr.models.token.TokenData` + TokenData Artificial data for the bootstrap token. """ return cls( diff --git a/src/gafaelfawr/operator/startup.py b/src/gafaelfawr/operator/startup.py index 6edc86389..2eb3ecfb3 100644 --- a/src/gafaelfawr/operator/startup.py +++ b/src/gafaelfawr/operator/startup.py @@ -23,6 +23,13 @@ async def startup(memo: kopf.Memo, **_: Any) -> None: via shallow copy, in the ``memo`` argument to any other handler. Use this to initialize the database and Redis pools, create service objects, and so forth. + + Parameters + ---------- + memo + Holds global state, used to store the + `~gafaelfawr.services.kubernetes.KubernetesTokenService` object and + other state that needs to be freed cleanly during shutdown. """ config = await config_dependency() memo.engine = create_database_engine( @@ -37,7 +44,15 @@ async def startup(memo: kopf.Memo, **_: Any) -> None: @kopf.on.cleanup() async def shutdown(memo: kopf.Memo, **_: Any) -> None: - """Shut down a running Kubernetes operator.""" + """Shut down a running Kubernetes operator. + + Parameters + ---------- + memo + Holds global state, used to store the + `~gafaelfawr.services.kubernetes.KubernetesTokenService` object and + other state that needs to be freed cleanly during shutdown. + """ await memo.api_client.close() await memo.factory.aclose() await memo.engine.dispose() diff --git a/src/gafaelfawr/operator/tokens.py b/src/gafaelfawr/operator/tokens.py index 7a054aa0e..dfe9a81ca 100644 --- a/src/gafaelfawr/operator/tokens.py +++ b/src/gafaelfawr/operator/tokens.py @@ -50,19 +50,19 @@ async def create( Parameters ---------- - name : `str` + name Name of the object. - namespace : `str` + namespace Namespace of the object. - body : `kopf.Body` + body Body of the object in dictionary form. - memo : `kopf.Memo` + memo Holds global state, used to store the `~gafaelfawr.services.kubernetes.KubernetesTokenService` object. Returns ------- - status : Dict[`str`, Union[`int`, `str`]] or `None` + dict or None Status information to record in the object, or `None` if no changes were made. """ @@ -88,19 +88,19 @@ async def periodic( Parameters ---------- - name : `str` + name Name of the object. - namespace : `str` + namespace Namespace of the object. - body : `kopf.Body` + body Body of the object in dictionary form. - memo : `kopf.Memo` + memo Holds global state, used to store the `~gafaelfawr.services.kubernetes.KubernetesTokenService` object. Returns ------- - status : Dict[`str`, Union[`int`, `str`]] or `None` + dict or None Status information to record in the object, or `None` if no changes were made. diff --git a/src/gafaelfawr/providers/base.py b/src/gafaelfawr/providers/base.py index 3525341c7..46147623a 100644 --- a/src/gafaelfawr/providers/base.py +++ b/src/gafaelfawr/providers/base.py @@ -19,12 +19,12 @@ def get_redirect_url(self, state: str) -> str: Parameters ---------- - state : `str` + state A random string used for CSRF protection. Returns ------- - url : `str` + str The encoded URL to which to redirect the user. """ @@ -36,26 +36,26 @@ async def create_user_info( Parameters ---------- - code : `str` + code Code returned by a successful authentication. - state : `str` + state The same random string used for the redirect URL. - session : `gafaelfawr.models.state.State` + session The session state. The provider may also store data used during logout. Returns ------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + TokenUserInfo The user information corresponding to that authentication. Raises ------ httpx.HTTPError - An HTTP client error occurred trying to talk to the authentication - provider. - gafaelfawr.exceptions.ProviderError - The provider responded with an error to a request. + Raised if an HTTP client error occurred trying to talk to the + authentication provider. + ProviderError + Raised if the provider responded with an error to a request. """ @abstractmethod @@ -67,6 +67,6 @@ async def logout(self, session: State) -> None: Parameters ---------- - session : `gafaelfawr.models.state.State` + session The session state before logout. """ diff --git a/src/gafaelfawr/providers/github.py b/src/gafaelfawr/providers/github.py index 82506fd5f..a9f7e0496 100644 --- a/src/gafaelfawr/providers/github.py +++ b/src/gafaelfawr/providers/github.py @@ -27,11 +27,11 @@ class GitHubProvider(Provider): Parameters ---------- - config : `gafaelfawr.config.GitHubConfig` + config Configuration for the GitHub authentication provider. - http_client : ``httpx.AsyncClient`` + http_client Session to use to make HTTP requests. - logger : `structlog.stdlib.BoundLogger` + logger Logger for any log messages. """ @@ -72,12 +72,12 @@ def get_redirect_url(self, state: str) -> str: Parameters ---------- - state : `str` + state A random string used for CSRF protection. Returns ------- - url : `str` + str The encoded URL to which to redirect the user. """ params = { @@ -98,27 +98,27 @@ async def create_user_info( Parameters ---------- - code : `str` + code Code returned by a successful authentication. - state : `str` + state The same random string used for the redirect URL. - session : `gafaelfawr.models.state.State` + session The session state, used to store the GitHub access token. Returns ------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + TokenUserInfo The user information corresponding to that authentication. Raises ------ - gafaelfawr.exceptions.GitHubError - GitHub responded with an error to a request. - gafaelfawr.exceptions.PermissionDeniedError - The GitHub username is not a valid username for Gafaelfawr. - ``httpx.HTTPError`` - An HTTP client error occurred trying to talk to the authentication - provider. + GitHubError + Raised if GitHub responded with an error to a request. + PermissionDeniedError + Raised if the GitHub username is not a valid username for + Gafaelfawr. + httpx.HTTPError + Raised if an HTTP client error occurred trying to talk to GitHub. """ github_token = await self._get_access_token(code, state) user_info = await self._get_user_info(github_token) @@ -180,7 +180,7 @@ async def logout(self, session: State) -> None: Parameters ---------- - session : `gafaelfawr.models.state.State` + session The session state, which contains the GitHub access token. """ if not session.github: @@ -209,23 +209,23 @@ async def _get_access_token(self, code: str, state: str) -> str: Parameters ---------- - code : `str` + code Code returned by a successful authentication. - state : `str` + state The same random string used for the redirect URL. Returns ------- - token : `str` + str Access token used for subsequent API calls. Raises ------ - gafaelfawr.exceptions.GitHubError - GitHub responded with an error to the request for the access - token. - ``httpx.HTTPError`` - An error occurred trying to talk to GitHub. + GitHubError + Raised if GitHub responded with an error to the request for the + access token. + httpx.HTTPError + Raised if an error occurred trying to talk to GitHub. """ data = { "client_id": self._config.client_id, @@ -251,20 +251,20 @@ async def _get_user_info(self, token: str) -> GitHubUserInfo: Parameters ---------- - token : `str` + token The token for that user. Returns ------- - info : `GitHubUserInfo` + info Information about that user. Raises ------ - gafaelfawr.exceptions.GitHubError - User has no primary email address. - ``httpx.HTTPError`` - An error occurred trying to talk to GitHub. + GitHubError + Raised if the user has no primary email address. + httpx.HTTPError + Raised if an error occurred trying to talk to GitHub. """ self._logger.debug("Fetching user data from %s", self._USER_URL) r = await self._http_client.get( @@ -318,21 +318,22 @@ async def _get_user_teams_data(self, token: str) -> List[Dict[str, Any]]: Parameters ---------- - token : `str` + token The token for that user. Returns ------- - team_data : List[Dict[`str`, Any]] + list of dict Team information for that user from GitHub in GitHub's JSON format. Raises ------ - gafaelfawr.exceptions.GitHubError - The next URL from a Link header didn't point to the teams API URL. - ``httpx.HTTPError`` - An error occurred trying to talk to GitHub. + GitHubError + Raised if the next URL from a Link header didn't point to the + teams API URL. + httpx.HTTPError + Raised if an error occurred trying to talk to GitHub. """ self._logger.debug("Fetching user team data from %s", self._TEAMS_URL) r = await self._http_client.get( diff --git a/src/gafaelfawr/providers/oidc.py b/src/gafaelfawr/providers/oidc.py index ff042fdff..5e95c79ae 100644 --- a/src/gafaelfawr/providers/oidc.py +++ b/src/gafaelfawr/providers/oidc.py @@ -38,15 +38,15 @@ class OIDCProvider(Provider): Parameters ---------- - config : `gafaelfawr.config.OIDCConfig` + config OpenID Connect authentication provider configuration. - verifier : `OIDCTokenVerifier` + verifier JWT token verifier for OpenID Connect tokens. - user_info_service : `gafaelfawr.services.userinfo.UserInfoService` + user_info_service Service for retrieving user metadata like UID. - http_client : ``httpx.AsyncClient`` + http_client Session to use to make HTTP requests. - logger : `structlog.stdlib.BoundLogger` + logger Logger for any log messages. """ @@ -70,12 +70,12 @@ def get_redirect_url(self, state: str) -> str: Parameters ---------- - state : `str` + state A random string used for CSRF protection. Returns ------- - url : `str` + str The encoded URL to which to redirect the user. """ scopes = ["openid"] @@ -101,29 +101,31 @@ async def create_user_info( Parameters ---------- - code : `str` + code Code returned by a successful authentication. - state : `str` + state The same random string used for the redirect URL, not used. - session : `gafaelfawr.models.state.State` + session The session state, not used by this provider. Returns ------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + TokenUserInfo The user information corresponding to that authentication. Raises ------ - gafaelfawr.exceptions.FirestoreError - Retrieving or assigning a UID from Firestore failed. - gafaelfawr.exceptions.OIDCError - The OpenID Connect provider responded with an error to a request - or the group membership in the resulting token was not valid. - gafaelfawr.exceptions.LDAPError - Gafaelfawr was configured to get user groups, username, or numeric - UID from LDAP, but the attempt failed due to some error. - ``httpx.HTTPError`` + FirestoreError + Raised if retrieving or assigning a UID from Firestore failed. + LDAPError + Raised if Gafaelfawr was configured to get user groups, username, + or numeric UID from LDAP, but the attempt failed due to some + error. + OIDCError + Raised if the OpenID Connect provider responded with an error to a + request or the group membership in the resulting token was not + valid. + httpx.HTTPError An HTTP client error occurred trying to talk to the authentication provider. """ @@ -181,7 +183,7 @@ async def logout(self, session: State) -> None: Parameters ---------- - session : `gafaelfawr.models.state.State` + session The session state, which contains the GitHub access token. """ pass @@ -192,11 +194,11 @@ class OIDCTokenVerifier: Parameters ---------- - config : `gafaelfawr.config.OIDCConfig` + config OpenID Connect authentication provider configuration. - http_client : ``httpx.AsyncClient`` + http_client Session to use to make HTTP requests. - logger : `structlog.stdlib.BoundLogger` + logger Logger for any log messages. """ @@ -212,20 +214,20 @@ async def verify_token(self, token: OIDCToken) -> OIDCVerifiedToken: Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCToken` + token JWT to verify. Returns ------- - verified_token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + OIDCVerifiedToken The verified token contents. Raises ------ jwt.exceptions.InvalidTokenError - The token is invalid. - gafaelfawr.exceptions.VerifyTokenError - The token failed to verify or was invalid in some way. + Raised if the token is invalid. + VerifyTokenError + Raised if the token failed to verify or was invalid in some way. """ unverified_header = jwt.get_unverified_header(token.encoded) unverified_token = jwt.decode( @@ -273,26 +275,27 @@ async def _get_key_as_pem(self, issuer_url: str, key_id: str) -> str: Parameters ---------- - issuer_url : `str` + issuer_url The URL of the issuer. - key_id : `str` + key_id The key ID to retrieve for the issuer in question. Returns ------- - key : `bytes` + bytes The issuer's key in PEM format. Raises ------ - gafaelfawr.exceptions.FetchKeysError - Unable to retrieve the key set for the specified issuer. - gafaelfawr.exceptions.UnknownAlgorithError - The requested key ID was found, but is for an unsupported - algorithm. - gafaelfawr.exceptions.UnknownKeyIdError - The requested key ID was not present in the issuer configuration - or was not found in that issuer's JWKS. + FetchKeysError + Raised if unable to retrieve the key set for the specified + issuer. + UnknownAlgorithError + Raised if the requested key ID was found, but is for an + unsupported algorithm. + UnknownKeyIdError + Raised if the requested key ID was not present in the issuer + configuration or was not found in that issuer's JWKS. """ self._logger.debug("Getting key %s from %s", key_id, issuer_url) @@ -334,18 +337,18 @@ async def _get_keys(self, issuer_url: str) -> List[Dict[str, str]]: Parameters ---------- - issuer_url : `str` + issuer_url URL of the issuer. Returns ------- - body : List[Dict[`str`, `str`]] + list of dict List of keys (in JWKS format) for the given issuer. Raises ------ - gafaelfawr.exceptions.FetchKeysError - On failure to retrieve a set of keys from the issuer. + FetchKeysError + Raised if failed to retrieve a set of keys from the issuer. """ url = await self._get_jwks_uri(issuer_url) if not url: @@ -375,19 +378,19 @@ async def _get_jwks_uri(self, issuer_url: str) -> Optional[str]: Parameters ---------- - issuer_url : `str` + issuer_url URL of the issuer. Returns ------- - url : `str` or `None` - URI for the JWKS of that issuer, or None if the OpenID Connect + str or None + URI for the JWKS of that issuer, or `None` if the OpenID Connect metadata is not present. Raises ------ - gafaelfawr.exceptions.FetchKeysError - If the OpenID Connect metadata doesn't contain the expected + FetchKeysError + Raised if the OpenID Connect metadata doesn't contain the expected parameter. """ url = issuer_url.rstrip("/") + "/.well-known/openid-configuration" diff --git a/src/gafaelfawr/services/admin.py b/src/gafaelfawr/services/admin.py index beaa477fd..e2903f5c6 100644 --- a/src/gafaelfawr/services/admin.py +++ b/src/gafaelfawr/services/admin.py @@ -21,11 +21,11 @@ class AdminService: Parameters ---------- - admin_store : `gafaelfawr.storage.admin.AdminStore` + admin_store The backing store for token administrators. - admin_history_store : `gafaelfawr.storage.history.AdminHistoryStore` + admin_history_store The backing store for history of changes to token administrators. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use for messages. """ @@ -46,16 +46,16 @@ async def add_admin( Parameters ---------- - username : `str` + username The administrator to delete. - actor : `str` + actor The person doing the deleting. - ip_address : `str` + ip_address The IP address from which the request came. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the actor is not an admin. """ if not await self.is_admin(actor) and actor != "": @@ -81,7 +81,7 @@ async def add_initial_admins(self, admins: Iterable[str]) -> None: Parameters ---------- - admins : List[`str`] + admins Usernames of initial admins. """ if not await self._admin_store.list(): @@ -96,22 +96,22 @@ async def delete_admin( Parameters ---------- - username : `str` + username The administrator to delete. - actor : `str` + actor The person doing the deleting. - ip_address : `str` + ip_address The IP address from which the request came. Returns ------- - success : `bool` + bool `True` if the administrator was found and deleted, `False` if they were not found. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the actor is not an admin. """ if not await self.is_admin(actor) and actor != "": @@ -137,5 +137,11 @@ async def get_admins(self) -> List[Admin]: return await self._admin_store.list() async def is_admin(self, username: str) -> bool: - """Returns whether the given user is a token administrator.""" + """Returns whether the given user is a token administrator. + + Parameters + ---------- + username + Username to check. + """ return any((username == a.username for a in await self.get_admins())) diff --git a/src/gafaelfawr/services/firestore.py b/src/gafaelfawr/services/firestore.py index 4aec56a4c..4d37b1506 100644 --- a/src/gafaelfawr/services/firestore.py +++ b/src/gafaelfawr/services/firestore.py @@ -21,14 +21,14 @@ class FirestoreService: Parameters ---------- - uid_cache : `gafaelfawr.cache.IdCache` + uid_cache The underlying UID and GID cache and locks. - gid_cache : `gafaelfawr.cache.IdCache` + gid_cache The underlying UID and GID cache and locks. - storage : `gafaelfawr.storage.firestore.FirestoreStorage`, optional + storage The underlying Firestore storage for UID and GID assignment, if Firestore was configured. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use. """ @@ -50,17 +50,17 @@ async def get_gid(self, group: str) -> int: Parameters ---------- - group : `str` + group Group of the user. Returns ------- - gid : `int` + int GID of the user. Raises ------ - gafaelfawr.exceptions.NoAvailableGidError + NoAvailableGidError No more GIDs are available in that range. """ gid = self._gid_cache.get(group) @@ -79,17 +79,17 @@ async def get_uid(self, username: str) -> int: Parameters ---------- - username : `str` + username Username of the user. Returns ------- - uid : `int` + int UID of the user. Raises ------ - gafaelfawr.exceptions.NoAvailableUidError + NoAvailableUidError No more UIDs are available in that range. """ uid = self._uid_cache.get(username) diff --git a/src/gafaelfawr/services/kubernetes.py b/src/gafaelfawr/services/kubernetes.py index 3233db008..7598d7406 100644 --- a/src/gafaelfawr/services/kubernetes.py +++ b/src/gafaelfawr/services/kubernetes.py @@ -43,13 +43,13 @@ class KubernetesTokenService: Parameters ---------- - token_service : `gafaelfawr.services.token.TokenService` + token_service Token management service. - storage : `gafaelfawr.storage.kubernetes.KubernetesTokenStorage` + storage Storage layer for the Kubernetes cluster. - session : `sqlalchemy.ext.asyncio.async_scoped_session` + session Database session, used for transaction management. - logger : `structlog.stdlib.BoundLogger` + logger Logger to report issues. """ @@ -73,22 +73,22 @@ async def update( Parameters ---------- - name : `str` + name Name of the ``GafaelfawrServiceToken`` Kubernetes object. - namespace : `str` + namespace Namespace of the ``GafaelfawrServiceToken`` Kubernetes object. - body : `gafaelfawr.models.kubernetes.GafaelfawrServiceToken` + body Contents of the ``GafaelfawrServiceToken`` Kubernetes object. Returns ------- - status : `gafaelfawr.models.kubernetes.KubernetesResourceStatus` + KubernetesResourceStatus or None Information to put into the ``status`` portion of the object, or - `None` if no status update was required. + `None` if no status update is required. Raises ------ - error : `gafaelfawr.exceptions.KubernetesError` + KubernetesError Some error occurred while trying to write to Kubernetes. """ try: @@ -159,7 +159,7 @@ async def _update_secret( Returns ------- - status : `gafaelfawr.models.kubernetes.KubernetesResourceStatus` + KubernetesResourceStatus or None Information to put into the ``status`` field of the ``GafaelfawrServiceToken`` Kubernetes object, or `None` if no status update is required. diff --git a/src/gafaelfawr/services/ldap.py b/src/gafaelfawr/services/ldap.py index 6bccdb74b..7f542e5da 100644 --- a/src/gafaelfawr/services/ldap.py +++ b/src/gafaelfawr/services/ldap.py @@ -22,15 +22,15 @@ class LDAPService: Parameters ---------- - ldap : `gafaelfawr.storage.ldap.LDAPStorage` + ldap The underlying LDAP query layer. - group_cache : `gafaelfawr.cache.LDAPCache` + group_cache Cache of user group information (including GIDs). - group_name_cache : `gafaelfawr.cache.LDAPCache` + group_name_cache Cache of group names. - user_cache : `gafaelfawr.cache.LDAPCache` + user_cache Cache of user information from LDAP. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use. """ @@ -56,9 +56,9 @@ async def get_group_names( Parameters ---------- - username : `str` + username Username of the user. - gid : `int` or `None` + gid Primary GID if set. If not `None`, search for the group with this GID and add it to the user's group memberships. This handles LDAP configurations where the user's primary group is represented only @@ -66,7 +66,7 @@ async def get_group_names( Returns ------- - groups : List[`str`] + List of str The names of the user's groups according to LDAP. """ groups = self._group_name_cache.get(username) @@ -87,9 +87,9 @@ async def get_groups( Parameters ---------- - username : `str` + username Username for which to get information. - gid : `int` or `None` + gid Primary GID if set. If not `None`, the user's groups will be checked for this GID. If it's not found, search for the group with this GID and add it to the user's group memberships. This @@ -98,12 +98,12 @@ async def get_groups( Returns ------- - groups : List[`gafaelfawr.models.token.TokenGroup`] + List of TokenGroup Groups of the user. Raises ------ - gafaelfawr.exceptions.LDAPError + LDAPError An error occurred when retrieving user information from LDAP. """ groups = self._group_cache.get(username) @@ -124,12 +124,12 @@ async def get_data(self, username: str) -> LDAPUserData: Parameters ---------- - username : `str` + username Username of the user. Returns ------- - data : `gafaelfawr.models.ldap.LDAPUserData` + LDAPUserData The retrieved data. """ data = self._user_cache.get(username) diff --git a/src/gafaelfawr/services/oidc.py b/src/gafaelfawr/services/oidc.py index 5508a1f4e..fd6107d26 100644 --- a/src/gafaelfawr/services/oidc.py +++ b/src/gafaelfawr/services/oidc.py @@ -34,13 +34,13 @@ class OIDCService: Parameters ---------- - config : `gafaelfawr.config.OIDCServerConfig` + config OpenID Connect server configuration. - authorization_store : `gafaelfawr.storage.oidc.OIDCAuthorizationStore` + authorization_store The underlying storage for OpenID Connect authorizations. - token_service : `gafaelfawr.services.token.TokenService` + token_service Token manipulation service. - logger : `structlog.stdlib.BoundLogger` + logger Logger for diagnostics. Notes @@ -92,7 +92,13 @@ def get_openid_configuration(self) -> OIDCConfig: ) def is_valid_client(self, client_id: str) -> bool: - """Whether a client_id is a valid registered client.""" + """Whether a client_id is a valid registered client. + + Parameters + ---------- + client_id + ``client_id`` parameter from the client. + """ return any(c.client_id == client_id for c in self._config.clients) async def issue_code( @@ -102,21 +108,21 @@ async def issue_code( Parameters ---------- - client_id : `str` + client_id The client ID with access to this authorization. - redirect_uri : `str` + redirect_uri The intended return URI for this authorization. - token : `gafaelfawr.models.token.Token` + token The underlying authentication token. Returns ------- - code : `gafaelfawr.models.oidc.OIDCAuthorizationCode` + OIDCAuthorizationCode The code for a newly-created and stored authorization. Raises ------ - gafaelfawr.exceptions.UnauthorizedClientError + UnauthorizedClientError The provided client ID is not registered as an OpenID Connect client. """ @@ -136,14 +142,14 @@ def issue_token( Parameters ---------- - user_info : `gafaelfawr.models.token.TokenData` + user_info The token data on which to base the token. - **claims : `str` + **claims Additional claims to add to the token. Returns ------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + OIDCVerifiedToken The new token. """ now = datetime.now(timezone.utc) @@ -184,27 +190,27 @@ async def redeem_code( Parameters ---------- - client_id : `str` + client_id The client ID of the OpenID Connect client. - client_secret : `str` or `None` + client_secret The secret for that client. A secret of `None` will never be valid, but is accepted so that error handling can be unified. - redirect_uri : `str` + redirect_uri The return URI of the OpenID Connect client. - code : `gafaelfawr.models.oidc.OIDCAuthorizationCode` + code The OpenID Connect authorization code. Returns ------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + OIDCVerifiedToken A newly-issued JWT for this client. Raises ------ - gafaelfawr.exceptions.InvalidClientError + InvalidClientError If the client ID is not known or the client secret does not match the client ID. - gafaelfawr.exceptions.InvalidGrantError + InvalidGrantError If the code is not valid, the client is not allowed to use it, or the underlying authorization or session does not exist. """ @@ -249,17 +255,17 @@ def verify_token(self, token: OIDCToken) -> OIDCVerifiedToken: Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCToken` + token An encoded token. Returns ------- - verified_token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + OIDCVerifiedToken The verified token. Raises ------ - gafaelfawr.exceptions.InvalidTokenError + InvalidTokenError The issuer of this token is unknown and therefore the token cannot be verified. """ @@ -288,14 +294,14 @@ def _check_client_secret( Parameters ---------- - client_id : `str` + client_id The OpenID Connect client ID. - client_secret : `str` or `None` + client_secret The secret for that client ID. Raises ------ - gafaelfawr.exceptions.InvalidClientError + InvalidClientError If the client ID isn't known or the secret doesn't match. """ if not client_secret: diff --git a/src/gafaelfawr/services/token.py b/src/gafaelfawr/services/token.py index fe531b926..576a02959 100644 --- a/src/gafaelfawr/services/token.py +++ b/src/gafaelfawr/services/token.py @@ -48,17 +48,17 @@ class TokenService: Parameters ---------- - config : `gafaelfawr.config.Config` + config Gafaelfawr configuration. - token_cache : `gafaelfawr.services.token_cache.TokenCacheService` + token_cache Cache of internal and notebook tokens. - token_db_store : `gafaelfawr.storage.token.TokenDatabaseStore` + token_db_store The database backing store for tokens. - token_redis_store : `gafaelfawr.storage.token.TokenRedisStore` + token_redis_store The Redis backing store for tokens. - token_change_store : `gafaelfawr.storage.history.TokenChangeHistoryStore` + token_change_store The backing store for history of changes to tokens. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use. """ @@ -87,13 +87,13 @@ async def audit(self, fix: bool = False) -> List[str]: Parameters ---------- - fix : `bool`, optional + fix Whether to fix problems that the audit code knows how to fix - (which is not all alerts). Default is `False`. + (which is not all alerts). Returns ------- - alerts : List[`str`] + List of str A list of human-readable alert messages formatted in Markdown. """ alerts = [] @@ -245,21 +245,21 @@ async def create_session_token( Parameters ---------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + user_info The user information to associate with the token. - scopes : List[`str`] + scopes The scopes of the token. - ip_address : `str` + ip_address The IP address from which the request came. Returns ------- - token : `gafaelfawr.models.token.Token` + Token The newly-created token. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the provided username is invalid. """ self._validate_username(user_info.username) @@ -318,33 +318,33 @@ async def create_user_token( Parameters ---------- - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The token data for the authentication token of the user creating a user token. - username : `str` + username The username for which to create a token. - token_name : `str` + token_name The name of the token. - scopes : List[`str`] + scopes The scopes of the token. - expires : `datetime` or `None` + expires When the token should expire. If not given, defaults to the expiration of the authentication token taken from ``data``. - ip_address : `str` + ip_address The IP address from which the request came. Returns ------- - token : `gafaelfawr.models.token.Token` + Token The newly-created token. Raises ------ - gafaelfawr.exceptions.DuplicateTokenNameError + DuplicateTokenNameError A token with this name for this user already exists. - gafaelfawr.exceptions.InvalidExpiresError + InvalidExpiresError The provided expiration time was invalid. - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the given username didn't match the user information in the authentication token, or if the specified username is invalid. @@ -415,26 +415,26 @@ async def create_token_from_admin_request( Parameters ---------- - request : `gafaelfawr.models.token.AdminTokenRequest` + request The incoming request. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The data for the authenticated user making the request. - ip_address : `str` or `None` + ip_address The IP address from which the request came, or `None` for internal requests by Gafaelfawr. Returns ------- - token : `gafaelfawr.models.token.Token` + Token The newly-created token. Raises ------ - gafaelfawr.exceptions.InvalidExpiresError + InvalidExpiresError The provided expiration time is not valid. - gafaelfawr.exceptions.InvalidScopesError + InvalidScopesError The requested scopes are not permitted. - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the provided username is invalid. """ self._check_authorization( @@ -524,20 +524,21 @@ async def delete_token( Parameters ---------- - key : `str` + key The key of the token to delete. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The token data for the authentication token of the user deleting the token. - username : `str` + username Constrain deletions to tokens owned by the given user. - ip_address : `str` + ip_address The IP address from which the request came. Returns ------- - success : `bool` - Whether the token was found and deleted. + bool + `True` if the token has been deleted, `False` if it was not + found. """ info = await self.get_token_info_unchecked(key, username) if not info: @@ -609,42 +610,42 @@ async def get_change_history( Parameters ---------- - auth_data : `gafaelfawr.models.token.TokenData` + auth_data Authentication information for the user making the request. - cursor : `str`, optional + cursor A pagination cursor specifying where to start in the results. - limit : `int`, optional + limit Limit the number of returned results. - since : `datetime.datetime`, optional + since Limit the results to events at or after this time. - until : `datetime.datetime`, optional + until Limit the results to events before or at this time. - username : `str`, optional + username Limit the results to tokens owned by this user. - actor : `str`, optional + actor Limit the results to actions performed by this user. - key : `str`, optional + key Limit the results to this token and any subtokens of this token. Note that this will currently pick up direct subtokens but not subtokens of subtokens. - token : `str`, optional + token Limit the results to only this token. - token_type : `gafaelfawr.models.token.TokenType`, optional + token_type Limit the results to tokens of this type. - ip_or_cidr : `str`, optional + ip_or_cidr Limit the results to changes made from this IPv4 or IPv6 address or CIDR block. Returns ------- - entries : `gafaelfawr.models.history.PaginatedHistory` + PaginatedHistory A list of changes matching the search criteria. Raises ------ - gafaelfawr.exceptions.InvalidCursorError + InvalidCursorError The provided cursor was invalid. - gafaelfawr.exceptions.InvalidIPAddressError + InvalidIPAddressError The provided argument was syntactically invalid for both an IP address and a CIDR block. """ @@ -670,14 +671,14 @@ async def get_data(self, token: Token) -> Optional[TokenData]: Parameters ---------- - token : `gafaelfawr.models.token.Token` + token The token. Returns ------- - data : `gafaelfawr.models.token.TokenData` or `None` - The data underlying the token, or `None` if the token is not - valid. + TokenData or None + The data underlying the token, or `None` if the token is not found + or is invalid. """ return await self._token_redis_store.get_data(token) @@ -694,25 +695,25 @@ async def get_internal_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data on which to base the new token. - service : `str` + service The internal service to which the token is delegated. - scopes : List[`str`] + scopes The scopes the new token should have. - ip_address : `str` + ip_address The IP address from which the request came. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. Returns ------- - token : `gafaelfawr.models.token.Token` + Token The newly-created token. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the username is invalid. """ self._validate_scopes(scopes, token_data) @@ -737,21 +738,21 @@ async def get_notebook_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data on which to base the new token. - ip_address : `str` + ip_address The IP address from which the request came. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. Returns ------- - token : `gafaelfawr.models.token.Token` + Token The newly-created token. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError If the username is invalid. """ self._validate_username(token_data.username) @@ -766,14 +767,27 @@ async def get_token_info( Parameters ---------- - key : `str` + key The key of the token. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The authentication data of the person requesting the token information, used for authorization checks. - username : `str`, optional + username If set, constrain the result to tokens from that user and return `None` if the token exists but is for a different user. + + Returns + ------- + TokenInfo or None + Token information from the database, or `None` if the token was + not found or username was given and the token was for another + user. + + Raises + ------ + PermissionDeniedError + The authenticated user doesn't have permission to manipulate + tokens for that user. """ info = await self.get_token_info_unchecked(key, username) if not info: @@ -788,11 +802,18 @@ async def get_token_info_unchecked( Parameters ---------- - key : `str` + key The key of the token. - username : `str`, optional + username If set, constrain the result to tokens from that user and return `None` if the token exists but is for a different user. + + Returns + ------- + TokenInfo or None + Token information from the database, or `None` if the token was + not found or username was given and the token was for another + user. """ info = await self._token_db_store.get_info(key) if not info: @@ -806,14 +827,14 @@ async def get_user_info(self, token: Token) -> Optional[TokenUserInfo]: Parameters ---------- - token : `gafaelfawr.models.token.Token` + token Data from the authentication token. Returns ------- - user_info : `gafaelfawr.models.token.TokenUserInfo` or `None` + TokenUserInfo or None User information for the holder of that token, or `None` if the - user's token is not valid. + token is not valid. """ data = await self.get_data(token) if not data: @@ -833,20 +854,20 @@ async def list_tokens( Parameters ---------- - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The token data for the authentication token of the user making this modification. - username : `str`, optional + username Limit results to the given username. Returns ------- - info : List[`gafaelfawr.models.token.TokenInfo`] + List of TokenInfo Information for all matching tokens. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError The user whose tokens are being listed does not match the authentication information. """ @@ -875,38 +896,39 @@ async def modify_token( Parameters ---------- - key : `str` + key The key of the token to modify. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The token data for the authentication token of the user making this modification. - username : `str`, optional + username If given, constrain modifications to tokens owned by the given user. - ip_address : `str` + ip_address The IP address from which the request came. - token_name : `str`, optional + token_name The new name for the token. - scopes : List[`str`], optional + scopes The new scopes for the token. - expires : `datetime`, optional + expires The new expiration time for the token. - no_expire : `bool` + no_expire If set, the token should not expire. This is a separate parameter because passing `None` to ``expires`` is ambiguous. Returns ------- - info : `gafaelfawr.models.token.TokenInfo` or `None` - Information for the updated token or `None` if it was not found. + TokenInfo or None + Information for the updated token or `None` if the token was not + found. Raises ------ - gafaelfawr.exceptions.InvalidExpiresError + InvalidExpiresError The provided expiration time was invalid. - gafaelfawr.exceptions.DuplicateTokenNameError + DuplicateTokenNameError A token with this name for this user already exists. - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError The user modifiying the token is not a token administrator. """ info = await self.get_token_info_unchecked(key, username) @@ -1002,22 +1024,22 @@ def _check_authorization( Arguments --------- - username : `str` or `None` + username The user whose tokens are being changed, or `None` if listing all tokens. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The authenticated user changing the tokens. - require_admin : `bool`, optional + require_admin If set to `True`, require the authenticated user have - ``admin:token`` scope. Default is `False`. - require_same_user : `bool`, optional + ``admin:token`` scope. + require_same_user If set to `True`, require that ``username`` match the authenticated user as specified by ``auth_data`` and do not allow - token admins. Default is `False`. + token admins. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError The authenticated user doesn't have permission to manipulate tokens for that user. """ @@ -1049,18 +1071,19 @@ async def _delete_one_token( Parameters ---------- - key : `str` + key The key of the token to delete. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The token data for the authentication token of the user deleting the token. - ip_address : `str` + ip_address The IP address from which the request came. Returns ------- - success : `bool` - Whether the token was found and deleted. + bool + `True` if the token was deleted, `False` if the token was not + found. """ info = await self.get_token_info_unchecked(key) if not info: @@ -1103,15 +1126,15 @@ async def _modify_expires( Parameters ---------- - key : `str` + key The key of the token to update. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The token data for the authentication token of the user changing the expiration. - expires : `datetime.datetime` + expires The new expiration of the parent token. The expiration of the child token will be changed if it's later than this value. - ip_address : `str` + ip_address The IP address from which the request came. """ info = await self.get_token_info_unchecked(key) @@ -1147,13 +1170,13 @@ def _validate_ip_or_cidr(self, ip_or_cidr: Optional[str]) -> None: Arguments --------- - ip_address : `str` or `None` + ip_address `None` or a string representing an IPv4 or IPv6 address or CIDR block. Raises ------ - gafaelfawr.exceptions.InvalidIPAddressError + InvalidIPAddressError The provided argument was syntactically invalid for both an IP address and a CIDR block. """ @@ -1172,12 +1195,12 @@ def _validate_expires(self, expires: Optional[datetime]) -> None: Arguments --------- - expires : `datetime` or `None` + expires The token expiration time. Raises ------ - gafaelfawr.exceptions.InvalidExpiresError + InvalidExpiresError The provided expiration time is not valid. Notes @@ -1202,15 +1225,15 @@ def _validate_scopes( Arguments --------- - scopes : List[`str`] + scopes The requested scopes. - auth_data : `gafaelfawr.models.token.TokenData`, optional + auth_data The token used to authenticate the operation, if the scopes should be checked to ensure they are a subset. Raises ------ - gafaelfawr.exceptions.InvalidScopesError + InvalidScopesError The requested scopes are not permitted. """ if not scopes: @@ -1233,17 +1256,17 @@ def _validate_username(self, username: str) -> None: Arguments --------- - username : `str` + username The user whose tokens are being changed. - auth_data : `gafaelfawr.models.token.TokenData` + auth_data The authenticated user changing the tokens. - same_user : `bool`, optional + same_user Require that ``username`` match the authenticated user as specified by ``auth_data`` and do not allow token admins. Raises ------ - gafaelfawr.exceptions.PermissionDeniedError + PermissionDeniedError The username is invalid or the authenticated user doesn't have permission to manipulate tokens for that user. """ diff --git a/src/gafaelfawr/services/token_cache.py b/src/gafaelfawr/services/token_cache.py index c33cc8017..fa050a10b 100644 --- a/src/gafaelfawr/services/token_cache.py +++ b/src/gafaelfawr/services/token_cache.py @@ -31,19 +31,19 @@ class TokenCacheService: Parameters ---------- - config : `gafaelfawr.config.Config` + config The Gafaelfawr configuration. - internal_cache : `gafaelfawr.cache.InternalTokenCache` + internal_cache Cache for internal tokens. - notebook_cache : `gafaelfawr.cache.NotebookTokenCache` + notebook_cache Cache for notebook tokens. - token_db_store : `gafaelfawr.storage.token.TokenDatabaseStore` + token_db_store The database backing store for tokens. - token_redis_store : `gafaelfawr.storage.token.TokenRedisStore` + token_redis_store The Redis backing store for tokens. - token_change_store : `gafaelfawr.storage.history.TokenChangeHistoryStore` + token_change_store The backing store for history of changes to tokens. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use. Notes @@ -109,20 +109,20 @@ async def get_internal_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - service : `str` + service The service of the internal token. - scopes : List[`str`] + scopes The scopes the internal token should have. - ip_address : `str` + ip_address The IP address from which the request came. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. Returns ------- - token : `gafaelfawr.models.token.Token` + Token The cached token or newly-created token. """ # Awkward code is to convince mypy that token is not None. @@ -160,17 +160,17 @@ async def get_notebook_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - ip_address : `str` + ip_address The IP address from which the request came. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. Returns ------- - token : `gafaelfawr.models.token.Token` or `None` - The cached token or `None` if no matching token is cached. + Token or None + The cached token, or `None` if no matching token is cached. """ token = self._notebook_cache.get(token_data) if token and await self._is_token_valid(token, minimum_lifetime): @@ -201,16 +201,21 @@ async def _create_internal_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - service : `str` + service The service of the internal token. - scopes : List[`str`] + scopes The scopes the internal token should have. - ip_address : `str` + ip_address The IP address from which the request came. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. + + Returns + ------- + Token + The retrieved or newly-created internal token. """ # See if there's already a matching internal token. key = await self._token_db_store.get_internal_token_key( @@ -288,12 +293,17 @@ async def _create_notebook_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The authentication data for the parent token. - ip_address : `str` + ip_address The IP address from which the request came. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. + + Returns + ------- + Token + The retrieved or newly-created notebook token. """ # See if there's already a matching notebook token. key = await self._token_db_store.get_notebook_token_key( @@ -363,19 +373,20 @@ async def _is_token_valid( Parameters ---------- - token : `gafaelfawr.models.token.Token` or `None` - The token to check for validity. - scopes : List[`str`], optional + token + The token to check for validity. `None` is accepted to simplify + type checking, but will always return `False`. + scopes If provided, ensure that the token has scopes that are a subset of this scope list. This is used to force a cache miss if an internal token is requested but the requesting token no longer has the scopes that the internal token provides. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. Returns ------- - valid : `bool` + bool Whether the token is valid. """ if not token: @@ -404,15 +415,15 @@ def _minimum_expiration( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The data for the parent token for which a child token was requested. - minimum_lifetime : `datetime.timedelta` or `None`, optional + minimum_lifetime If set, the minimum required lifetime of the token. Returns ------- - min_expires : `datetime.datetime` + datetime The minimum acceptable expiration time for the child token. If no child tokens with at least this expiration time exist, a new child token should be created. diff --git a/src/gafaelfawr/services/userinfo.py b/src/gafaelfawr/services/userinfo.py index 8bd0b1459..010cef36e 100644 --- a/src/gafaelfawr/services/userinfo.py +++ b/src/gafaelfawr/services/userinfo.py @@ -44,13 +44,13 @@ class UserInfoService: Parameters ---------- - config : `gafaelfawr.config.Config` + config Gafaelfawr configuration. - ldap : `gafaelfawr.services.ldap.LDAPService`, optional + ldap LDAP service for user metadata, if LDAP was configured. - firestore : `gafaelfawr.services.firestore.FirestoreService`, optional + firestore Service for Firestore UID/GID lookups, if Firestore was configured. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use. """ @@ -78,20 +78,20 @@ async def get_user_info_from_token( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data Data from the authentication token. Returns ------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + TokenUserInfo User information for the holder of that token. Raises ------ - gafaelfawr.exceptions.FirestoreError + FirestoreError UID/GID allocation using Firestore failed, probably because the UID or GID space has been exhausted. - gafaelfawr.exceptions.LDAPError + LDAPError Gafaelfawr was configured to get user groups, username, or numeric UID from LDAP, but the attempt failed due to some error. """ @@ -160,12 +160,12 @@ async def get_scopes( Parameters ---------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + TokenUserInfo User information for a user. Returns ------- - scopes : List[`str`] or `None` + List of str or None The scopes generated from the group membership based on the ``group_mapping`` configuration parameter, or `None` if the user was not a member of any known group. @@ -201,13 +201,13 @@ class OIDCUserInfoService(UserInfoService): Parameters ---------- - config : `gafaelfawr.config.Config` + config Gafaelfawr configuration. - ldap : `gafaelfawr.services.ldap.LDAPService`, optional + ldap LDAP service for user metadata, if LDAP was configured. - firestore : `gafaelfawr.services.firestore.FirestoreService`, optional + firestore Service for Firestore UID/GID lookups, if Firestore was configured. - logger : `structlog.stdlib.BoundLogger` + logger Logger to use. """ @@ -243,21 +243,21 @@ async def get_user_info_from_oidc_token( Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + token The verified ID token from the OpenID Connect provider. Returns ------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + TokenUserInfo User information derived from external data sources and the provided token. Raises ------ - gafaelfawr.exceptions.LDAPError + LDAPError Gafaelfawr was configured to get user groups, username, or numeric UID from LDAP, but the attempt failed due to some error. - gafaelfawr.exceptions.VerifyTokenError + VerifyTokenError The token is missing required claims. """ username = self._get_username_from_oidc_token(token) @@ -299,21 +299,21 @@ async def _get_groups_from_oidc_token( Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + token The previously verified token. - username : `str` + username Authenticated username (for error reporting). Returns ------- - groups : List[`gafaelfawr.models.token.TokenGroup`] + List of TokenGroup List of groups derived from the token claim. Raises ------ - gafaelfawr.exceptions.FirestoreError + FirestoreError An error occured obtaining the GID from Firestore. - gafaelfawr.exceptions.InvalidTokenClaimsError + InvalidTokenClaimsError The ``isMemberOf`` claim has an invalid syntax. """ claim = self._oidc_config.groups_claim @@ -364,22 +364,22 @@ def _get_gid_from_oidc_token( Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + token The previously verified token. - username : `str` + username Authenticated username (for error reporting). Returns ------- - gid : `int` or `None` + int or None The primary GID of the user as obtained from the token, or `None` if not configured to get a primary GID from the claims. Raises ------ - gafaelfawr.exceptions.MissingGIDClaimError + MissingGIDClaimError The token is missing the required numeric GID claim. - gafaelfawr.exceptions.InvalidTokenClaimsError + InvalidTokenClaimsError The GID claim contains something that is not a number. """ if not self._oidc_config.gid_claim: @@ -403,21 +403,21 @@ def _get_uid_from_oidc_token( Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + token The previously verified token. - username : `str` + username Authenticated username (for error reporting). Returns ------- - uid : `int` + int The numeric UID of the user as obtained from the token. Raises ------ - gafaelfawr.exceptions.MissingUIDClaimError + MissingUIDClaimError The token is missing the required numeric UID claim. - gafaelfawr.exceptions.InvalidTokenClaimsError + InvalidTokenClaimsError The numeric UID claim contains something that is not a number. """ if self._oidc_config.uid_claim not in token.claims: @@ -437,17 +437,17 @@ def _get_username_from_oidc_token(self, token: OIDCVerifiedToken) -> str: Parameters ---------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + token The previously verified token. Returns ------- - username : `str` + str The username of the user as obtained from the token. Raises ------ - gafaelfawr.exceptions.MissingUsernameClaimError + MissingUsernameClaimError The token is missing the required username claim. """ if self._oidc_config.username_claim not in token.claims: diff --git a/src/gafaelfawr/slack.py b/src/gafaelfawr/slack.py index 894f7fff4..a57f431b3 100644 --- a/src/gafaelfawr/slack.py +++ b/src/gafaelfawr/slack.py @@ -43,11 +43,11 @@ class SlackAlertClient: Parameters ---------- - hook_url : `str` + hook_url The URL of the incoming webhook to use to publish the message. - application : `str` + application Name of the application reporting an error. - logger : `structlog.stdlib.BoundLogger` + logger Logger to which to report errors sending messages to Slack. """ @@ -66,7 +66,7 @@ async def message(self, message: str) -> None: Parameters ---------- - message : `str` + message The message to post, in Markdown format. """ if len(message) > 3000: @@ -90,7 +90,7 @@ async def uncaught_exception(self, exc: Exception) -> None: Parameters ---------- - exc : `Exception` + exc The exception to report. """ date = current_datetime().strftime(SLACK_DATE_FORMAT) @@ -197,11 +197,11 @@ def initialize_slack_alerts( Parameters ---------- - hook_url : `str` + hook_url The URL of the incoming webhook to use to publish the message. - application : `str` + application Name of the application reporting an error. - logger : `structlog.stdlib.BoundLogger` + logger Logger to which to report errors sending messages to Slack. """ global _slack_alert_client diff --git a/src/gafaelfawr/storage/admin.py b/src/gafaelfawr/storage/admin.py index 1b872175d..420760781 100644 --- a/src/gafaelfawr/storage/admin.py +++ b/src/gafaelfawr/storage/admin.py @@ -20,7 +20,7 @@ class AdminStore: Parameters ---------- - session : `sqlalchemy.ext.asyncio.async_scoped_session` + session The database session proxy. """ @@ -32,7 +32,7 @@ async def add(self, admin: Admin) -> None: Parameters ---------- - admin : `gafaelfawr.models.admin.Admin` + admin The administrator to add. """ new = SQLAdmin(username=admin.username) @@ -43,12 +43,12 @@ async def delete(self, admin: Admin) -> bool: Parameters ---------- - admin : `gafaelfawr.models.admin.Admin` + admin The administrator to delete. Returns ------- - result : `bool` + bool `True` if the administrator was found and deleted, `False` otherwise. """ @@ -61,7 +61,7 @@ async def list(self) -> List[Admin]: Returns ------- - admins : List[`gafaelfawr.models.admin.Admin`] + list of Admin Current administrators. """ stmt = select(SQLAdmin).order_by(SQLAdmin.username) diff --git a/src/gafaelfawr/storage/base.py b/src/gafaelfawr/storage/base.py index 18e3e3866..fdaa6eae8 100644 --- a/src/gafaelfawr/storage/base.py +++ b/src/gafaelfawr/storage/base.py @@ -26,11 +26,11 @@ class RedisStorage(Generic[S]): Parameters ---------- - content : `typing.Type` + content The class of object being stored. - key : `str` + key Encryption key. Must be a `~cryptography.fernet.Fernet` key. - redis : ``aioredis.Redis`` + redis A Redis client configured to talk to the backend store. """ @@ -44,12 +44,12 @@ async def delete(self, key: str) -> bool: Parameters ---------- - key : `str` + key The key to delete. Returns ------- - success : `bool` + bool `True` if the key was found and deleted, `False` otherwise. """ count = await self._redis.delete(key) @@ -60,7 +60,7 @@ async def delete_all(self, pattern: str) -> None: Parameters ---------- - pattern : `str` + pattern Glob pattern matching the keys to purge, such as ``oidc:*``. """ async for key in self._redis.scan_iter(pattern): @@ -71,19 +71,20 @@ async def get(self, key: str) -> Optional[S]: Parameters ---------- - key : `str` + key The key for the object. Returns ------- - obj : `Serializable` or `None` + Any or None The deserialized object or `None` if no such object could be found. Raises ------ - gafaelfawr.exceptions.DeserializeError - The stored object could not be decrypted or deserialized. + DeserializeError + Raised if the stored object could not be decrypted or + deserialized. """ encrypted_data = await self._redis.get(key) if not encrypted_data: @@ -109,12 +110,12 @@ async def scan(self, pattern: str) -> AsyncIterator[str]: Parameters ---------- - pattern : `str` + pattern Key pattern to scan for. Yields ------ - key : `str` + str Each key matching that pattern. """ async for key in self._redis.scan_iter(match=pattern): @@ -125,14 +126,14 @@ async def store(self, key: str, obj: S, lifetime: Optional[int]) -> None: Parameters ---------- - key : `str` + key The key for the object. - obj : `Serializable` + obj The object to store. - lifetime : `int` or `None` + lifetime The object lifetime in seconds. The object should expire from the - data store after that many seconds after the current time. - Returns `None` if the object should not expire. + data store after that many seconds after the current time. Pass + `None` if the object should not expire. """ encrypted_data = self._fernet.encrypt(obj.json().encode()) await self._redis.set(key, encrypted_data, ex=lifetime) diff --git a/src/gafaelfawr/storage/firestore.py b/src/gafaelfawr/storage/firestore.py index 7eb4cb4ce..fdf010a85 100644 --- a/src/gafaelfawr/storage/firestore.py +++ b/src/gafaelfawr/storage/firestore.py @@ -41,9 +41,9 @@ class FirestoreStorage: Parameters ---------- - config : `gafaelfawr.config.FirestoreConfig` + config Configuration for Google Firestore. - logger : `structlog.stdlib.BoundLogger` + logger Logger for debug messages and errors. """ @@ -60,20 +60,20 @@ async def get_gid(self, group: str) -> int: Parameters ---------- - group : `str` + group Name of the group. Returns ------- - gid : `int` + int GID of the group. Raises ------ - gafaelfawr.exceptions.FirestoreNotInitializedError - Firestore has not been initialized. - gafaelfawr.exceptions.NoAvailableGidError - No more GIDs are available in that range. + FirestoreNotInitializedError + Raised if Firestore has not been initialized. + NoAvailableGidError + Raised if no more GIDs are available in that range. """ transaction = self._db.transaction() group_ref = self._db.collection("groups").document(group) @@ -94,24 +94,24 @@ async def get_uid(self, username: str, bot: bool = False) -> int: Parameters ---------- - username : `str` + username Name of the user. - bot : `bool`, optional + bot If set to true, this is a bot user and should use the bot user range instead of the regular user range if a UID hasn't already been assigned. Returns ------- - uid : `int` + int UID of the user. Raises ------ - gafaelfawr.exceptions.FirestoreNotInitializedError - Firestore has not been initialized. - gafaelfawr.exceptions.NoAvailableUidError - No more UIDs are available in that range. + FirestoreNotInitializedError + Raised if Firestore has not been initialized. + NoAvailableUidError + Raised if no more UIDs are available in that range. """ transaction = self._db.transaction() user_ref = self._db.collection("users").document(username) @@ -160,28 +160,28 @@ async def _get_or_assign_gid( Parameters ---------- - transaction : `google.cloud.firestore.Transaction` + transaction The open transaction. - group_name : `str` + group_name Name of the group, for logging. - group_ref : `google.cloud.firestore.AsyncDocumentReference` + group_ref Reference to the group's (possibly nonexistent) GID document. - counter_ref : `google.cloud.firestore.AsyncDocumentReference` + counter_ref Reference to the document holding the GID counter. - logger : `structlog.stdlib.BoundLogger` + logger Logger for messages. Returns ------- - gid : `int` + int GID of the group. Raises ------ - gafaelfawr.exceptions.FirestoreNotInitializedError - Firestore has not been initialized. - gafaelfawr.exceptions.NoAvailableGidError - No more UIDs are available in that range. + FirestoreNotInitializedError + Raised if Firestore has not been initialized. + NoAvailableGidError + Raised if no more UIDs are available in that range. """ group = await group_ref.get(transaction=transaction) if group.exists: @@ -215,32 +215,32 @@ async def _get_or_assign_uid( Parameters ---------- - transaction : `google.cloud.firestore.Transaction` + transaction The open transaction. - username : `str` + username Username of user, for logging. - user_ref : `google.cloud.firestore.AsyncDocumentReference` + user_ref Reference to the user's (possibly nonexistent) UID document. - counter_ref : `google.cloud.firestore.AsyncDocumentReference` + counter_ref Reference to the document holding the UID counter. - bot : `bool`, optional + bot If set to true, this is a bot user and should use the bot user range instead of the regular user range if a UID hasn't already been assigned. - logger : `structlog.stdlib.BoundLogger` + logger Logger for messages. Returns ------- - uid : `int` + int UID of the user. Raises ------ - gafaelfawr.exceptions.FirestoreNotInitializedError - Firestore has not been initialized. - gafaelfawr.exceptions.NoAvailableUidError - No more UIDs are available in that range. + FirestoreNotInitializedError + Raised if Firestore has not been initialized. + NoAvailableUidError + Raised if no more UIDs are available in that range. """ user = await user_ref.get(transaction=transaction) if user.exists: @@ -273,11 +273,11 @@ async def _initialize_in_transaction( Parameters ---------- - transaction : `google.cloud.firestore.Transaction` + transaction The open transaction. - counter_refs : Dict[str, `google.cloud.firestore.AsyncDocumentReference`] + counter_refs References to the documents holding the counters. - logger : `structlog.stdlib.BoundLogger` + logger Logger for messages. """ # We have to do this in two passes since the Firestore transaction diff --git a/src/gafaelfawr/storage/history.py b/src/gafaelfawr/storage/history.py index 1125c4855..121c9ec3c 100644 --- a/src/gafaelfawr/storage/history.py +++ b/src/gafaelfawr/storage/history.py @@ -28,7 +28,7 @@ class AdminHistoryStore: Parameters ---------- - session : `sqlalchemy.ext.asyncio.async_scoped_session` + session The database session proxy. """ @@ -36,7 +36,13 @@ def __init__(self, session: async_scoped_session) -> None: self._session = session async def add(self, entry: AdminHistoryEntry) -> None: - """Record a change to the token administrators.""" + """Record a change to the token administrators. + + Parameters + ---------- + entry + The change to record. + """ new = AdminHistory(**entry.dict()) new.event_time = datetime_to_db(entry.event_time) self._session.add(new) @@ -47,7 +53,7 @@ class TokenChangeHistoryStore: Parameters ---------- - session : `sqlalchemy.ext.asyncio.async_scoped_session` + session The database session proxy. """ @@ -59,7 +65,7 @@ async def add(self, entry: TokenChangeHistoryEntry) -> None: Parameters ---------- - entry : `gafaelfawr.models.history.TokenChangeHistoryEntry` + entry New entry to add to the database. """ entry_dict = entry.dict() @@ -81,7 +87,7 @@ async def delete(self, *, older_than: datetime) -> None: Parameters ---------- - older_than : `datetime.datetime` + older_than Delete entries created prior to this date. """ stmt = delete(TokenChangeHistory).where( @@ -107,34 +113,34 @@ async def list( Parameters ---------- - cursor : `gafaelfawr.models.history.HistoryCursor`, optional + cursor A pagination cursor specifying where to start in the results. - limit : `int`, optional + limit Limit the number of returned results. - since : `datetime.datetime`, optional + since Limit the results to events at or after this time. - until : `datetime.datetime`, optional + until Limit the results to events before or at this time. - username : `str`, optional + username Limit the results to tokens owned by this user. - actor : `str`, optional + actor Limit the results to actions performed by this user. - key : `str`, optional + key Limit the results to this token and any subtokens of this token. Note that this will currently pick up direct subtokens but not subtokens of subtokens. - token : `str`, optional + token Limit the results to only this token. - token_type : `gafaelfawr.models.token.TokenType`, optional + token_type Limit the results to tokens of this type. - ip_or_cidr : `str`, optional + ip_or_cidr Limit the results to changes made from this IPv4 or IPv6 address or CIDR block. Unless the underlying database is PostgreSQL, the CIDR block must be on an octet boundary. Returns ------- - entries : List[`gafaelfawr.models.history.TokenChangeHistoryEntry`] + list of TokenChangeHistoryEntry List of change history entries, which may be empty. """ stmt = select(TokenChangeHistory) diff --git a/src/gafaelfawr/storage/kubernetes.py b/src/gafaelfawr/storage/kubernetes.py index 9354faa88..48e80afdf 100644 --- a/src/gafaelfawr/storage/kubernetes.py +++ b/src/gafaelfawr/storage/kubernetes.py @@ -47,6 +47,13 @@ class KubernetesTokenStorage: This abstracts storage of Gafaelfawr service tokens in Kubernetes objects by wrapping the underlying Kubernetes Python client. + + Parameters + ---------- + api_client + Kubernetes async client to use. + logger + Logger to use. """ def __init__(self, api_client: ApiClient, logger: BoundLogger) -> None: @@ -65,14 +72,14 @@ async def create_secret( Parameters ---------- - parent : `gafaelfawr.models.kubernetes.GafaelfawrServiceToken` + parent The parent object for the secret. - token : `gafaelfawr.models.token.Token` + token The token to store. Returns ------- - status : `gafaelfawr.models.kubernetes.KubernetesResourceStatus` + KubernetesResourceStatus Status information to store in the parent object. """ secret = self._build_secret(parent, token) @@ -91,12 +98,12 @@ async def get_secret( Parameters ---------- - parent : `gafaelfawr.models.kubernetes.GafaelfawrServiceToken` + parent The parent object. Returns ------- - secret : ``kubernetes_asyncio.client.V1Secret`` or `None` + kubernetes_asyncio.client.V1Secret or None The Kubernetes secret, or `None` if that secret does not exist. """ try: @@ -118,14 +125,14 @@ async def replace_secret( Parameters ---------- - parent : `gafaelfawr.models.kubernetes.GafaelfawrServiceToken` + parent The parent object for the ``Secret``. - token : `gafaelfawr.models.token.Token` + token The token to store. Returns ------- - status : `gafaelfawr.models.kubernetes.KubernetesResourceStatus` + KubernetesResourceStatus Status information to store in the parent object. """ secret = self._build_secret(parent, token) @@ -146,7 +153,7 @@ async def update_secret_metadata( Parameters ---------- - parent : `gafaelfawr.models.kubernetes.GafaelfawrServiceToken` + parent The parent object for the ``Secret``. """ await self._api.patch_namespaced_secret( @@ -173,14 +180,14 @@ def _build_secret( Parameters ---------- - parent : `gafaelfawr.models.kubernetes.GafaelfawrServiceToken` + parent The parent object. - token : `gafaelfawr.models.token.Token` + token The Gafaelfawr token to store in the secret. Returns ------- - secret : ``kubernetes_asyncio.client.V1Secret`` + kubernetes_asyncio.client.V1Secret Newly created secret. Notes diff --git a/src/gafaelfawr/storage/ldap.py b/src/gafaelfawr/storage/ldap.py index 2b8ab6489..99a55f678 100644 --- a/src/gafaelfawr/storage/ldap.py +++ b/src/gafaelfawr/storage/ldap.py @@ -25,11 +25,11 @@ class LDAPStorage: Parameters ---------- - config : `gafaelfawr.config.LDAPConfig` + config Configuration for LDAP searches. - pool : `bonsai.asyncio.AIOConnectionPool` + pool Connection pool for LDAP searches. - logger : `structlog.stdlib.BoundLogger` + logger Logger for debug messages and errors. """ @@ -47,9 +47,9 @@ async def get_group_names( Parameters ---------- - username : `str` + username Username of the user. - primary_gid : `int` or `None` + primary_gid Primary GID if set. If not `None`, search for the group with this GID and add it to the user's group memberships. This handles LDAP configurations where the user's primary group is represented only @@ -57,13 +57,13 @@ async def get_group_names( Returns ------- - groups : List[`str`] + list of str User's group names from LDAP. Raises ------ - gafaelfawr.exceptions.LDAPError - Some error occurred while doing the LDAP search. + LDAPError + Raised if some error occurred while doing the LDAP search. """ group_class = self._config.group_object_class member_attr = self._config.group_member_attr @@ -130,9 +130,9 @@ async def get_groups( Parameters ---------- - username : `str` + username Username of the user. - primary_gid : `int` or `None` + primary_gid Primary GID if set. If not `None`, the user's groups will be checked for this GID. If it's not found, search for the group with this GID and add it to the user's group memberships. This @@ -141,13 +141,13 @@ async def get_groups( Returns ------- - groups : List[`gafaelfawr.models.token.TokenGroup`] + list of TokenGroup User's groups from LDAP. Raises ------ - gafaelfawr.exceptions.LDAPError - Some error occurred when searching LDAP. + LDAPError + Raised if some error occurred when searching LDAP. """ group_class = self._config.group_object_class member_attr = self._config.group_member_attr @@ -207,21 +207,22 @@ async def get_data(self, username: str) -> LDAPUserData: Parameters ---------- - username : `str` + username Username of the user. Returns ------- - data : `gafaelfawr.models.ldap.LDAPUserData` + LDAPUserData The data for an LDAP user. Which fields are filled in will be determined by the configuration. Raises ------ - gafaelfawr.exceptions.LDAPError - The lookup of ``user_search_attr`` at ``user_base_dn`` in the LDAP - server was not valid (connection to the LDAP server failed, - attribute not found in LDAP, UID result value not an integer). + LDAPError + Raised if the lookup of ``user_search_attr`` at ``user_base_dn`` + in the LDAP server was not valid (connection to the LDAP server + failed, attribute not found in LDAP, UID result value not an + integer). """ if not self._config.user_base_dn: return LDAPUserData(name=None, email=None, uid=None, gid=None) @@ -278,42 +279,42 @@ async def _query( ) -> List[Dict[str, List[str]]]: """Perform an LDAP query using the connection pool. - Notes - ----- - The current bonsai connection pool does not keep track of failed - connections and will keep returning the same connection even if the - LDAP server has stopped responding (due to a firewall timeout, for - example). Working around this requires setting a timeout, catching - the timeout exception, and explicitly closing the connection. A - search is attempted at most twice. - - Be aware that bonsai (seen in 1.4.0, not tested again in 1.5.0) - appears to go into an infinite CPU loop when waiting for results when - run in an asyncio loop without other active coroutines. It's not - clear whether that's true if there are other active coroutines. - Parameters ---------- - base : `str` + base Base DN of the search. - scope : `bonsai.LDAPSearchScope` + scope Scope of the search. - filter_exp : `str` + filter_exp Search filter. - attrlist : List[`str`] + attrlist List of attributes to retrieve. Returns ------- - results : List[Dict[`str`, List[`str`]]] + list of dict List of result entries, each of which is a dictionary of the requested attributes (plus possibly other attributes) to a list of their values. Raises ------ - gafaelfawr.exceptions.LDAPError - Failed to run the search. + LDAPError + Raised if failed to run the search. + + Notes + ----- + The current bonsai connection pool does not keep track of failed + connections and will keep returning the same connection even if the + LDAP server has stopped responding (due to a firewall timeout, for + example). Working around this requires setting a timeout, catching + the timeout exception, and explicitly closing the connection. A + search is attempted at most twice. + + Be aware that bonsai (seen in 1.4.0, not tested again in 1.5.0) + appears to go into an infinite CPU loop when waiting for results when + run in an asyncio loop without other active coroutines. It's not + clear whether that's true if there are other active coroutines. """ logger = self._logger.bind( ldap_attrs=attrlist, ldap_base=base, ldap_search=filter_exp diff --git a/src/gafaelfawr/storage/oidc.py b/src/gafaelfawr/storage/oidc.py index 45690b39b..57d77217e 100644 --- a/src/gafaelfawr/storage/oidc.py +++ b/src/gafaelfawr/storage/oidc.py @@ -18,7 +18,7 @@ class OIDCAuthorizationStore: Parameters ---------- - storage : `gafaelfawr.storage.base.RedisStorage` + storage The underlying storage for `~gafaelfawr.models.oidc.OIDCAuthorization`. """ @@ -32,16 +32,16 @@ async def create( Parameters ---------- - client_id : `str` + client_id The client ID with access to this authorization. - redirect_uri : `str` + redirect_uri The intended return URI for this authorization. - token : `gafaelfawr.models.token.Token` + token The underlying authentication token. Returns ------- - code : `gafaelfawr.models.oidc.OIDCAuthorizationCode` + OIDCAuthorizationCode The code for a newly-created and stored authorization. """ authorization = OIDCAuthorization( @@ -59,7 +59,7 @@ async def delete(self, code: OIDCAuthorizationCode) -> None: Parameters ---------- - code : `gafaelfawr.models.oidc.OIDCAuthorizationCode` + code The authorization code. """ await self._storage.delete(f"oidc:{code.key}") @@ -75,19 +75,19 @@ async def get( Parameters ---------- - code : `gafaelfawr.models.oidc.OIDCAuthorizationCode` + code The authorization code. Returns ------- - authorization : `gafaelfawr.models.oidc.OIDCAuthorization` or `None` + OIDCAuthorization or None The corresponding authorization, or `None` if no such authorization exists. Raises ------ - gafaelfawr.exceptions.DeserializeError - If the authorization exists but cannot be deserialized. + DeserializeError + Raised if the authorization exists but cannot be deserialized. """ authorization = await self._storage.get(f"oidc:{code.key}") if not authorization: diff --git a/src/gafaelfawr/storage/token.py b/src/gafaelfawr/storage/token.py index 7747b59f1..c87c38edb 100644 --- a/src/gafaelfawr/storage/token.py +++ b/src/gafaelfawr/storage/token.py @@ -32,7 +32,7 @@ class TokenDatabaseStore: Parameters ---------- - session : `sqlalchemy.ext.asyncio.async_scoped_session` + session The database session proxy. """ @@ -51,19 +51,19 @@ async def add( Parameters ---------- - data : `gafaelfawr.models.token.TokenData` + data The corresponding data. - token_name : `str`, optional + token_name The human-given name for the token. - service : `str`, optional + service The service for an internal token. - parent : `str`, optional + parent The key of the parent of this token. Raises ------ - gafaelfawr.exceptions.DuplicateTokenNameError - The user already has a token by that name. + DuplicateTokenNameError + Raised if the user already has a token by that name. """ if token_name: await self._check_name_conflict(data.username, token_name) @@ -88,12 +88,12 @@ async def delete(self, key: str) -> bool: Parameters ---------- - token : `str` + token The key of the token to delete. Returns ------- - success : `bool` + bool Whether the token was found to be deleted. """ stmt = delete(SQLToken).where(SQLToken.token == key) @@ -105,7 +105,7 @@ async def delete_expired(self) -> List[TokenInfo]: Returns ------- - deleted : List[`gafaelfawr.models.token.TokenInfo`] + list of TokenInfo The deleted tokens. """ now = datetime.utcnow() @@ -144,12 +144,12 @@ async def get_children(self, key: str) -> List[str]: Parameters ---------- - key : `str` + str The key of the token. Returns ------- - children : List[`str`] + list of str The keys of all child tokens of that token, recursively. The direct child tokens will be at the beginning of the list, and other tokens will be listed in a breadth-first search order. @@ -169,23 +169,20 @@ async def get_info(self, key: str) -> Optional[TokenInfo]: Parameters ---------- - key : `str` + key The key of the token. Returns ------- - info : `gafaelfawr.models.token.TokenInfo` or `None` + TokenInfo or None Information about that token or `None` if it doesn't exist in the database. - - Notes - ----- - There is probably some way to materialize parent as a relationship - field on the ORM Token objects, but that gets into gnarly and - hard-to-understand SQLAlchemy ORM internals. This approach still does - only one database query without fancy ORM mappings at the cost of some - irritating mangling of the return value. """ + # There is probably some way to materialize parent as a relationship + # field on the ORM Token objects, but that gets into gnarly and + # hard-to-understand SQLAlchemy ORM internals. This approach still + # does only one database query without fancy ORM mappings at the cost + # of some irritating mangling of the return value. stmt = ( select(SQLToken, Subtoken.parent) .where(SQLToken.token == key) @@ -210,18 +207,18 @@ async def get_internal_token_key( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The data for the parent token. - service : `str` + service The service to which the internal token is delegated. - scopes : List[`str`] + scopes The scopes of the delegated token. - min_expires : `datetime.datetime` + min_expires The minimum expiration time for the token. Returns ------- - key : `str` or `None` + str or None The key of an existing internal child token with the desired properties, or `None` if none exist. """ @@ -246,14 +243,14 @@ async def get_notebook_token_key( Parameters ---------- - token_data : `gafaelfawr.models.token.TokenData` + token_data The data for the parent token. - min_expires : `datetime.datetime` + min_expires The minimum expiration time for the token. Returns ------- - key : `str` or `None` + str or None The key of an existing notebook child token, or `None` if none exist. """ @@ -274,12 +271,12 @@ async def list(self, *, username: Optional[str] = None) -> List[TokenInfo]: Parameters ---------- - username : `str` or `None` + username Limit the returned tokens to ones for the given username. Returns ------- - tokens : List[`gafaelfawr.models.token.TokenInfo`] + list of TokenInfo Information about the tokens. """ stmt = select(SQLToken) @@ -301,7 +298,7 @@ async def list_orphaned(self) -> List[TokenInfo]: Returns ------- - tokens : List[`gafaelfawr.models.token.TokenInfo`] + list of TokenInfo Information about the tokens. """ stmt = ( @@ -320,7 +317,7 @@ async def list_with_parents(self) -> List[TokenInfo]: Returns ------- - tokens : List[`gafaelfawr.models.token.TokenInfo`] + list of TokenInfo Information about the tokens. """ stmt = ( @@ -354,27 +351,27 @@ async def modify( Parameters ---------- - token : `str` + token The token to modify. - token_name : `str`, optional + token_name The new name for the token. - scopes : List[`str`], optional + scopes The new scopes for the token. - expires : `datetime`, optional + expires The new expiration time for the token. - no_expire : `bool` + no_expire If set, the token should not expire. This is a separate parameter because passing `None` to ``expires`` is ambiguous. Returns ------- - info : `gafaelfawr.models.token.TokenInfo` or `None` + TokenInfo or None Information for the updated token or `None` if it was not found. Raises ------ - gafaelfawr.exceptions.DuplicateTokenNameError - The user already has a token by that name. + DuplicateTokenNameError + Raised if the user already has a token by that name. """ stmt = select(SQLToken).where(SQLToken.token == key) token = await self._session.scalar(stmt) @@ -418,9 +415,9 @@ class TokenRedisStore: Parameters ---------- - storage : `gafaelfawr.storage.base.RedisStorage` + storage The underlying storage. - logger : `structlog.stdlib.BoundLogger` + logger Logger for diagnostics. """ @@ -441,12 +438,12 @@ async def delete(self, key: str) -> bool: Parameters ---------- - key : `str` + key The key portion of the token. Returns ------- - success : `bool` + bool `True` if the token was found and deleted, `False` otherwise. """ return await self._storage.delete(f"token:{key}") @@ -462,12 +459,12 @@ async def get_data(self, token: Token) -> Optional[TokenData]: Parameters ---------- - token : `gafaelfawr.models.token.Token` + token The token. Returns ------- - data : `gafaelfawr.models.token.TokenData` or `None` + TokenData or None The data underlying the token, or `None` if the token is not valid. """ @@ -491,12 +488,12 @@ async def get_data_by_key(self, key: str) -> Optional[TokenData]: Parameters ---------- - key : `str` + key The key of the token. Returns ------- - data : `gafaelfawr.models.token.TokenData` or `None` + TokenData or None The data underlying the token, or `None` if the token is not valid. """ @@ -512,7 +509,7 @@ async def list(self) -> List[str]: Returns ------- - tokens : List[`str`] + list of str The tokens found in Redis (by looking for valid keys). """ keys = [] @@ -525,7 +522,7 @@ async def store_data(self, data: TokenData) -> None: Parameters ---------- - data : `gafaelfawr.models.token.TokenData` + data The data underlying that token. """ lifetime = None diff --git a/src/gafaelfawr/util.py b/src/gafaelfawr/util.py index 44366d308..978a90a9d 100644 --- a/src/gafaelfawr/util.py +++ b/src/gafaelfawr/util.py @@ -29,12 +29,12 @@ def add_padding(encoded: str) -> str: Parameters ---------- - encoded : `str` + encoded A base64-encoded string, possibly with the padding removed. Returns ------- - result : `str` + str A correctly-padded version of the encoded string. """ underflow = len(encoded) % 4 @@ -49,12 +49,12 @@ def base64_to_number(data: str) -> int: Parameters ---------- - data : `str` + data Base64-encoded number, possibly without padding. Returns ------- - result : `int` + int The result converted to a number. Note that Python ints can be arbitrarily large. @@ -77,12 +77,12 @@ def format_datetime_for_logging(date: Optional[datetime]) -> Optional[str]: Parameters ---------- - date : `datetime.datetime` or `None` + date The object to format. Returns ------- - date_str : `str` or `None` + str or None The datetime in ISO format with seconds, or `None` if the input was `None`. """ @@ -93,7 +93,13 @@ def format_datetime_for_logging(date: Optional[datetime]) -> Optional[str]: def is_bot_user(username: str) -> bool: - """Return whether the given username is a bot user.""" + """Return whether the given username is a bot user. + + Parameters + ---------- + username + Username to check. + """ return re.search(BOT_USERNAME_REGEX, username) is not None @@ -107,12 +113,12 @@ def normalize_datetime( Parameters ---------- - v : `int` or `datetime.datetime` or `None` + v The field representing a `datetime` Returns ------- - v : `datetime.datetime` or `None` + datetime.datetime or None The timezone-aware `datetime.datetime` or `None` if the input was `None`. """ @@ -136,12 +142,12 @@ def normalize_ip_address( Parameters ---------- - v : `str` or `ipaddress.IPv4Address` or `ipaddress.IPv6Address` or `None` + v The field representing an IP address. Returns ------- - v : `str` or `None` + str or None The converted IP address. """ if v is None: @@ -163,12 +169,12 @@ def normalize_scopes( Parameters ---------- - v : `str` or List[`str`] or `None` + v The field representing token scopes. Returns ------- - v : List[`str`] or `None` + list of str or None The scopes as a list. """ if v is None: @@ -188,12 +194,12 @@ def number_to_base64(data: int) -> bytes: Parameters ---------- - data : `int` + data Arbitrarily large number Returns ------- - result : `bytes` + bytes The equivalent URL-safe base64-encoded string corresponding to the number in big endian order. """ From 5104800f4f75f42fbc0a0d4b34ac3e7c2a15fb48 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 27 Oct 2022 11:58:08 -0700 Subject: [PATCH 2/5] Simplify docstrings for test code Apply the same changes as the main code to drop types from docstrings. We currently don't generate documentation from the test code, but I use the same conventions. Simplify the docstrings in a few cases. --- tests/conftest.py | 34 ++++------------------- tests/support/cookies.py | 8 +++--- tests/support/firestore.py | 6 ++--- tests/support/github.py | 24 ++++++++--------- tests/support/headers.py | 10 +++---- tests/support/jwt.py | 10 +++---- tests/support/kubernetes.py | 13 ++++++--- tests/support/ldap.py | 10 +++---- tests/support/logging.py | 4 +-- tests/support/oidc.py | 36 ++++++++++++------------- tests/support/selenium.py | 13 ++++++--- tests/support/settings.py | 54 ++++++++++++++++++------------------- tests/support/slack.py | 11 +++++--- tests/support/tokens.py | 18 ++++++------- tests/support/util.py | 8 +++++- 15 files changed, 129 insertions(+), 130 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3f3fe7f8c..f91ffd292 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -76,13 +76,7 @@ def config(tmp_path: Path) -> Config: @pytest.fixture(scope="session") def driver() -> Iterator[webdriver.Chrome]: - """Create a driver for Selenium testing. - - Returns - ------- - driver : `selenium.webdriver.Chrome` - The web driver to use in Selenium tests. - """ + """Create a driver for Selenium testing.""" driver = selenium_driver() try: yield driver @@ -170,25 +164,13 @@ async def initialize_empty_database(engine: AsyncEngine) -> None: @pytest.fixture def mock_firestore(tmp_path: Path) -> Iterator[MockFirestore]: - """Configure for Firestore UID/GID assignment and mock the Firestore API. - - Returns - ------- - mock_firestore : `tests.support.firestore.MockFirestore` - The mocked Firestore API. - """ + """Configure Firestore UID/GID assignment and mock the Firestore API.""" yield from patch_firestore() @pytest.fixture def mock_ldap() -> Iterator[MockLDAP]: - """Replace the bonsai LDAP API with a mock class. - - Returns - ------- - mock_ldap : `tests.support.ldap.MockLDAP` - The mock LDAP API object. - """ + """Replace the bonsai LDAP API with a mock class.""" yield from patch_ldap() @@ -196,13 +178,7 @@ def mock_ldap() -> Iterator[MockLDAP]: def mock_slack( config: Config, respx_mock: respx.Router ) -> Optional[MockSlack]: - """Mock a Slack webhook. - - Returns - ------- - mock_slack : `tests.support.slack.MockSlack` - Object that accumulates posted Slack messages. - """ + """Mock a Slack webhook.""" if not config.slack_webhook: return None return mock_slack_webhook(config.slack_webhook, respx_mock) @@ -220,7 +196,7 @@ async def selenium_config( Returns ------- - config : `tests.support.selenium.SeleniumConfig` + SeleniumConfig Configuration information for the server. """ settings_path = build_settings(tmp_path, "selenium") diff --git a/tests/support/cookies.py b/tests/support/cookies.py index 1b8c7ddfe..e05d9abe4 100644 --- a/tests/support/cookies.py +++ b/tests/support/cookies.py @@ -24,14 +24,14 @@ async def set_session_cookie(client: AsyncClient, token: Token) -> str: Parameters ---------- - client : ``httpx.AsyncClient`` + client The client to add the session cookie to. - token : `gafaelfawr.models.token.Token` + token The token for the client identity to use. Returns ------- - csrf : `str` + str The CSRF token to use in subsequent API requests. """ cookie = await State(token=token).as_cookie() @@ -46,7 +46,7 @@ def clear_session_cookie(client: AsyncClient) -> None: Parameters ---------- - client : `httpx.AsyncClient` + client The client from which to remove the session cookie. """ del client.cookies[COOKIE_NAME] diff --git a/tests/support/firestore.py b/tests/support/firestore.py index c214a7e1f..47ee3cc96 100644 --- a/tests/support/firestore.py +++ b/tests/support/firestore.py @@ -79,11 +79,11 @@ def update(self, ref: MockDocumentRef, data: Dict[str, Any]) -> None: class MockFirestore(Mock): """Mock Firestore API for testing. - This mock should be installed with ``mock_firestore``. + This mock should be installed with `patch_firestore`. Parameters ---------- - config : `gafaelfawr.config.FirestoreConfig` + config Configuration for Google Firestore. """ @@ -105,7 +105,7 @@ def patch_firestore() -> Iterator[MockFirestore]: Returns ------- - mock : `MockFirestore` + MockFirestore The mock Firestore API. """ mock = MockFirestore() diff --git a/tests/support/github.py b/tests/support/github.py index fb128b4de..ba93b9c9d 100644 --- a/tests/support/github.py +++ b/tests/support/github.py @@ -27,15 +27,15 @@ class MockGitHub: Parameters ---------- - config : `gafaelfawr.config.GitHubConfig` + config Configuration of the GitHub provider. - code : `str` + code The code that Gafaelfawr must send to redeem for a token. - user_info : `gafaelfawr.providers.github.GitHubUserInfo` + user_info User information to use to synthesize GitHub API responses. - paginate_teams : `bool` + paginate_teams Whether to paginate the team results. - expect_revoke : `bool` + expect_revoke Whether to expect a revocation of the token after returning all user information. """ @@ -160,17 +160,17 @@ async def mock_github( Parameters ---------- - respx_mock : `respx.Router` + respx_mock The mock router. - code : `str` + code The code that Gafaelfawr must send to redeem a token. - user_info : `gafaelfawr.providers.github.GitHubUserInfo` + user_info User information to use to synthesize GitHub API responses. - paginate_teams : `bool`, optional - Whether to paginate the team results. Default: `False` - expect_revoke : `bool`, optional + paginate_teams + Whether to paginate the team results. + expect_revoke Whether to expect a revocation of the token after returning all user - information. Default: `False` + information. """ config = await config_dependency() assert config.github diff --git a/tests/support/headers.py b/tests/support/headers.py index fd557cd7a..4531e50e2 100644 --- a/tests/support/headers.py +++ b/tests/support/headers.py @@ -30,11 +30,11 @@ def assert_unauthorized_is_correct( Parameters ---------- - r : `httpx.Response` + r The unauthorized response. - config : `gafaelfawr.config.Config` + config The Gafaelfawr configuration. - auth_type : `gafaelfawr.auth.AuthType` + auth_type Expected authentication type. """ assert r.status_code == 401 @@ -103,12 +103,12 @@ def query_from_url(url: str) -> Dict[str, List[str]]: Parameters ---------- - url : `str` + url The URL. Returns ------- - query : Dict[`str`, List[`str`]] + dict The query in the form returned by :py:func:`urllib.parse.parse_qs`. """ parsed_url = urlparse(url) diff --git a/tests/support/jwt.py b/tests/support/jwt.py index aea451a27..5d73ba726 100644 --- a/tests/support/jwt.py +++ b/tests/support/jwt.py @@ -29,16 +29,16 @@ def create_upstream_oidc_jwt( Parameters ---------- - kid : `str`, optional - Key ID for the token header. Default is ``orig-kid``. - groups : List[`str`], optional + kid + Key ID for the token header. + groups Group memberships the generated token should have. - **claims : `str`, optional + **claims Other claims to set or override in the token. Returns ------- - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + OIDCVerifiedToken The new token. """ config = config_dependency.config() diff --git a/tests/support/kubernetes.py b/tests/support/kubernetes.py index 226fecbcf..54d1b6af1 100644 --- a/tests/support/kubernetes.py +++ b/tests/support/kubernetes.py @@ -41,7 +41,7 @@ async def install_crds(api_client: ApiClient) -> None: Parameters ---------- - api_client : ``kubernetes_asyncio.client.ApiClient`` + api_client Kubernetes API client to use. """ extensions_api = ApiextensionsV1Api(api_client) @@ -63,7 +63,7 @@ def operator_running(module: str) -> Iterator[None]: Parameters ---------- - module : `str` + module Name of the module that provides the operator. """ kopf_command = [ @@ -89,9 +89,9 @@ async def run_operator_once(module: str, *, delay: float = 1) -> None: Parameters ---------- - module : `str` + module Name of the module that provides the operator. - delay : `float`, optional + delay How long to wait after the operator has started before shutting it down again. """ @@ -107,6 +107,11 @@ async def temporary_namespace(api_client: ApiClient) -> AsyncIterator[str]: delete namespaces. Try to remove the finalizers on any custom objects in the namespace before deleting it so that Kubernetes will actually clean up. + + Parameters + ---------- + api_client + Kubernetes API client to use. """ core_api = CoreV1Api(api_client) custom_api = CustomObjectsApi(api_client) diff --git a/tests/support/ldap.py b/tests/support/ldap.py index 29be04885..0001122b3 100644 --- a/tests/support/ldap.py +++ b/tests/support/ldap.py @@ -34,13 +34,13 @@ def add_entries_for_test( Parameters ---------- - base_dn : `str` + base_dn The base DN of a search that should return this entry. - attr : `str` + attr The search attribute that will be used to retrieve this entry. - value : `str` + value The value of that search attribute. - entries : List[Dict[`str`, List[`str`]]] + entries The entries returned by that search, which will be filtered by the attribute list. """ @@ -88,7 +88,7 @@ def patch_ldap() -> Iterator[MockLDAP]: Returns ------- - mock : `MockLDAP` + MockLDAP The mock LDAP API. """ mock_ldap = MockLDAP() diff --git a/tests/support/logging.py b/tests/support/logging.py index f4bbfb31b..c86641b24 100644 --- a/tests/support/logging.py +++ b/tests/support/logging.py @@ -17,12 +17,12 @@ def parse_log(caplog: LogCaptureFixture) -> List[Dict[str, Any]]: Parameters ---------- - caplog : `_pytest.logging.LogCaptureFixture` + caplog The log capture fixture. Returns ------- - messages : List[Dict[`str`, Any]] + list of dict List of parsed JSON dictionaries with the common log attributes removed (after validation). """ diff --git a/tests/support/oidc.py b/tests/support/oidc.py index f4cb88ba4..124c55a2f 100644 --- a/tests/support/oidc.py +++ b/tests/support/oidc.py @@ -29,9 +29,9 @@ class MockOIDCConfig: Parameters ---------- - config : `gafaelfawr.config.OIDCConfig` + config Configuration for the OpenID Connect provider. - kid : `str` + kid The key ID to return. """ @@ -56,11 +56,11 @@ class MockOIDCToken: Parameters ---------- - config : `gafaelfawr.config.OIDCConfig` + config Configuration for Gafaelfawr. - code : `str` + code The code that Gafaelfawr must send to redeem for a token. - token : `gafaelfawr.models.oidc.OIDCToken` + token The token to return after authentication. """ @@ -92,9 +92,9 @@ async def mock_oidc_provider_config( Parameters ---------- - respx_mock : `respx.Router` + respx_mock The mock router. - kid : `str` + kid The key ID to return. """ config = await config_dependency() @@ -114,11 +114,11 @@ async def mock_oidc_provider_token( Parameters ---------- - respx_mock : `respx.Router` + respx_mock The mock router. - code : `str` + code The code that Gafaelfawr must send to redeem for a token. - token : `gafaelfawr.models.oidc.OIDCToken` + token The token to return after authentication. """ config = await config_dependency() @@ -141,29 +141,29 @@ async def simulate_oidc_login( Parameters ---------- - client : `httpx.AsyncClient` + client Client to use to make calls to the application. - respx_mock : `respx.Router` + respx_mock Mock for httpx calls. - token : `gafaelfawr.models.oidc.OIDCVerifiedToken` + token Authentication token the upstream OpenID Connect provider should return. - return_url : `str`, optional + return_url The return URL to pass to the login process. If not provided, a simple one will be used. - use_redirect_header : `bool`, optional + use_redirect_header If set to `True`, pass the return URL in a header instead of as a parameter to the ``/login`` route. - callback_route : `str`, optional + callback_route Override the callback route to which the upstream OpenID Connect provider is expected to send the redirect. - expect_enrollment : `bool`, optional + expect_enrollment If set to `True`, expect a redirect to the enrollment URL after login rather than to the return URL. Returns ------- - response : ``httpx.Response`` + httpx.Response The response from the return to the ``/login`` handler. """ config = await config_dependency() diff --git a/tests/support/selenium.py b/tests/support/selenium.py index d271bb59f..4e189295c 100644 --- a/tests/support/selenium.py +++ b/tests/support/selenium.py @@ -53,7 +53,7 @@ def selenium_driver() -> webdriver.Chrome: Returns ------- - driver : `selenium.webdriver.Chrome` + selenium.webdriver.Chrome The web driver to use in Selenium tests. """ options = webdriver.ChromeOptions() @@ -171,12 +171,19 @@ async def run_app( ) -> AsyncIterator[SeleniumConfig]: """Run the application as a separate process for Selenium access. + Must be used as an async context manager. + Parameters ---------- - tmp_path : `pathlib.Path` + tmp_path The temporary directory for testing. - settings_path : `pathlib.Path` + settings_path The path to the settings file. + + Yields + ------ + SeleniumConfig + The Selenium configuration. """ config_dependency.set_settings_path(str(settings_path)) config = await config_dependency() diff --git a/tests/support/settings.py b/tests/support/settings.py index 9a24cc73f..8cee7e934 100644 --- a/tests/support/settings.py +++ b/tests/support/settings.py @@ -38,11 +38,11 @@ def store_secret(tmp_path: Path, name: str, secret: bytes) -> Path: Parameters ---------- - tmp_path : `pathlib.Path` + tmp_path The root of the temporary area. - name : `str` + name The name of the secret to construct nice file names. - secret : `bytes` + secret The value of the secret. """ secret_path = tmp_path / name @@ -57,16 +57,16 @@ def _build_settings_file( Parameters ---------- - tmp_path : `pathlib.Path` + tmp_path The root of the temporary area. - template : `str` + template Name of the configuration template to use. - **kwargs : `str` + **kwargs The values to substitute into the template. Returns ------- - settings_path : `pathlib.Path` + pathlib.Path The path to the newly-constructed configuration file. """ template_file = template + ".yaml.in" @@ -89,18 +89,18 @@ def build_settings( Parameters ---------- - tmp_path : `pathlib.Path` + tmp_path The root of the temporary area. - template : `str` + template Settings template to use. - oidc_clients : List[`gafaelfawr.config.OIDCClient`] or `None` + oidc_clients Configuration information for clients of the OpenID Connect server. - **settings : `str` + **settings Any additional settings to add to the settings file. Returns ------- - settings_path : `pathlib.Path` + pathlib.Path The path of the settings file. """ bootstrap_token = str(Token()).encode() @@ -156,26 +156,26 @@ def configure( This cannot be used to change the database URL because sessions will not be recreated or the database reinitialized. - Notes - ----- - This is used for tests that cannot be async, so itself must not be async. - Parameters ---------- - tmp_path : `pathlib.Path` + tmp_path Root of the test temporary directory, used to write the settings file. - template : `str` + template Settings template to use. - oidc_clients : List[`gafaelfawr.config.OIDCClient`], optional + oidc_clients Configuration information for clients of the OpenID Connect server. - **settings : str, optional + **settings Any additional settings to add to the settings file. Returns ------- - config : `gafaelfawr.config.Config` + Config The new configuration. + + Notes + ----- + This is used for tests that cannot be async, so itself must not be async. """ settings_path = build_settings( tmp_path, @@ -202,21 +202,21 @@ async def reconfigure( Parameters ---------- - tmp_path : `pathlib.Path` + tmp_path Root of the test temporary directory, used to write the settings file. - template : `str` + template Settings template to use. - factory : `gafaelfawr.factory.Factory`, optional + factory The factory to reconfigure. - oidc_clients : List[`gafaelfawr.config.OIDCClient`], optional + oidc_clients Configuration information for clients of the OpenID Connect server. - **settings : str, optional + **settings Any additional settings to add to the settings file. Returns ------- - config : `gafaelfawr.config.Config` + Config The new configuration. """ config = configure( diff --git a/tests/support/slack.py b/tests/support/slack.py index c9c835e00..c3c6e28a8 100644 --- a/tests/support/slack.py +++ b/tests/support/slack.py @@ -16,7 +16,7 @@ class MockSlack: Attributes ---------- - messages : List[Dict[`str`, Any]] + messages The messages that have been posted to the webhook so far. """ @@ -47,10 +47,15 @@ def mock_slack_webhook(hook_url: str, respx_mock: respx.Router) -> MockSlack: Parameters ---------- - hook_url : `str` + hook_url URL for the Slack incoming webhook to mock. - respx_mock : `respx.Router` + respx_mock The mock router. + + Returns + ------- + MockSlack + The mock Slack API object. """ mock = MockSlack() respx_mock.post(hook_url).mock(side_effect=mock.post_webhook) diff --git a/tests/support/tokens.py b/tests/support/tokens.py index 33ba83efc..dac06890c 100644 --- a/tests/support/tokens.py +++ b/tests/support/tokens.py @@ -45,13 +45,13 @@ async def add_expired_session_token( Parameters ---------- - user_info : `gafaelfawr.models.token.TokenUserInfo` + user_info The user information to associate with the token. - scopes : List[`str`] + scopes The scopes of the token. - ip_address : `str` + ip_address The IP address from which the request came. - session : `sqlalchemy.ext.asyncio.AsyncSession` + session The database session. """ token_db_store = TokenDatabaseStore(session) @@ -95,18 +95,18 @@ async def create_session_token( Parameters ---------- - factory : `gafaelfawr.factory.Factory` + factory Factory used to create services to add the token. - username : `str`, optional + username Override the username of the generated token. - group_namess : List[`str`], optional + group_names Group memberships the generated token should have. - scopes : List[`str`], optional + scopes Scope for the generated token. Returns ------- - data : `gafaelfawr.models.token.TokenData` + TokenData The data for the generated token. """ if not username: diff --git a/tests/support/util.py b/tests/support/util.py index e0b7936f0..37e2e9dfa 100644 --- a/tests/support/util.py +++ b/tests/support/util.py @@ -8,6 +8,12 @@ def assert_is_now(date: datetime) -> None: - """Assert that a datetime is reasonably close to the current time.""" + """Assert that a datetime is reasonably close to the current time. + + Parameters + ---------- + date + Datetime to check. + """ now = current_datetime() assert now - timedelta(seconds=5) <= date <= now From bb4c0a9a5fa5ab737e175c517ec3cd26bab7d953 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 27 Oct 2022 12:15:56 -0700 Subject: [PATCH 3/5] Update JavaScript dependencies --- .pre-commit-config.yaml | 6 +- ui/package-lock.json | 1870 ++++++++++++++++++--------------------- 2 files changed, 884 insertions(+), 992 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8e8eb8eff..af4ce03a4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,11 +43,11 @@ repos: additional_dependencies: - '@babel/eslint-parser@7.19.1' - '@babel/preset-react@7.18.6' - - eslint@8.25.0 + - eslint@8.26.0 - eslint-config-airbnb@19.0.4 - eslint-config-prettier@8.5.0 - - eslint-config-wesbos@3.0.2 - - eslint-plugin-html@6.2.0 + - eslint-config-wesbos@3.1.4 + - eslint-plugin-html@7.1.0 - eslint-plugin-import@2.26.0 - eslint-plugin-jsx-a11y@6.6.1 - eslint-plugin-prettier@4.2.1 diff --git a/ui/package-lock.json b/ui/package-lock.json index 54a44dbe5..ed2cee3fd 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -95,28 +95,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", - "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.0.tgz", + "integrity": "sha512-Gt9jszFJYq7qzXVK4slhc6NzJXnOVmRECWcVjF/T23rNXD9NtWQ0W3qxdg+p9wWIB+VQw3GYV/U2Ha9bRTfs4w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", - "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", + "@babel/generator": "^7.19.6", "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.3", - "@babel/types": "^7.19.3", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -149,11 +149,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", - "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.0.tgz", + "integrity": "sha512-GUPcXxWibClgmYJuIwC2Bc2Lg+8b9VjaJ+HlNdACEVt+Wlr1eoU1OPZjZRm7Hzl0gaTsUZNQfeihvZJhG7oc3w==", "dependencies": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -198,11 +198,11 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "dependencies": { - "@babel/compat-data": "^7.19.3", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.21.3", "semver": "^6.3.0" @@ -330,18 +330,18 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -410,11 +410,11 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -470,13 +470,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.0.tgz", + "integrity": "sha512-aGMjYraN0zosCEthoGLdqot1oRsmxVTQRHadsUPz5QM44Zej2PYRz7XiDE7GqnkZnNtLbOuxqoZw42vkU7+XEQ==", "dependencies": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/traverse": "^7.20.0", + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -560,9 +560,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", - "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.0.tgz", + "integrity": "sha512-G9VgAhEaICnz8iiJeGJQyVl6J2nTjbW0xeisva0PK6XcKsga7BIaqm4ZF8Rg1Wbaqmy6znspNqhPaPkyukujzg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -907,11 +907,11 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1040,11 +1040,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1098,9 +1098,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", - "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz", + "integrity": "sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w==", "dependencies": { "@babel/helper-plugin-utils": "^7.19.0" }, @@ -1156,9 +1156,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", - "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz", + "integrity": "sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA==", "dependencies": { "@babel/helper-plugin-utils": "^7.19.0" }, @@ -1287,13 +1287,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -1303,14 +1302,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" }, "engines": { "node": ">=6.9.0" @@ -1320,15 +1318,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", - "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "dependencies": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-module-transforms": "^7.19.6", "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -1515,9 +1512,9 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz", - "integrity": "sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", "dependencies": { "@babel/helper-module-imports": "^7.18.6", "@babel/helper-plugin-utils": "^7.19.0", @@ -1605,13 +1602,13 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", - "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.0.tgz", + "integrity": "sha512-xOAsAFaun3t9hCwZ13Qe7gq423UgMZ6zAgmLxeGGapFqlT/X3L5qT2btjiVLlFn7gWtMaVyceS5VxGAuKbgizw==", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.19.0", "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/plugin-syntax-typescript": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -1788,23 +1785,23 @@ } }, "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.0.tgz", + "integrity": "sha512-NDYdls71fTXoU8TZHfbBWg7DiZfNzClcKui/+kyi6ppD2L1qnWW3VV6CjtaBXSUGGhiTWJ6ereOIkUvenif66Q==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.4.tgz", - "integrity": "sha512-HzjQ8+dzdx7dmZy4DQ8KV8aHi/74AjEbBGTFutBmg/pd3dY5/q1sfuOGPTFGEytlQhWoeVXqcK5BwMgIkRkNDQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.0.tgz", + "integrity": "sha512-v1JH7PeAAGBEyTQM9TqojVl+b20zXtesFKCJHu50xMxZKD1fX0TKaKHPsZfFkXfs7D1M9M6Eeqg1FkJ3a0x2dA==", "dependencies": { "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" }, "engines": { "node": ">=6.9.0" @@ -1824,18 +1821,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", - "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.0.tgz", + "integrity": "sha512-5+cAXQNARgjRUK0JWu2UBwja4JLSO/rBMPJzpsKb+oBF5xlUuCfljQepS4XypBQoiigL0VQjTZy6WiONtUdScQ==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.4", + "@babel/generator": "^7.20.0", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.4", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.0", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1852,9 +1849,9 @@ } }, "node_modules/@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -2041,13 +2038,13 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-codegen/typescript": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.7.4.tgz", - "integrity": "sha512-mjoE3KqmAaBUSlCuKb2WDot3+CTfdJ9eKIcWsDFubPTNdZE0Z8mLoATmVryx8NOBjDtPU/ZziI0rYEGOR4qpAw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.8.0.tgz", + "integrity": "sha512-atQ6NFfMmoknJi0QdVSozPugKcgeJB6t1/cVskbhVtX8tAEFFLjl6H23mpz3t35AH6aGWtvx2LCjUSxHI6P6xA==", "dependencies": { "@graphql-codegen/plugin-helpers": "^2.6.2", "@graphql-codegen/schema-ast": "^2.5.1", - "@graphql-codegen/visitor-plugin-common": "2.12.2", + "@graphql-codegen/visitor-plugin-common": "2.13.0", "auto-bind": "~4.0.0", "tslib": "~2.4.0" }, @@ -2056,13 +2053,13 @@ } }, "node_modules/@graphql-codegen/typescript-operations": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.4.tgz", - "integrity": "sha512-+IA0ouGC6GZiCyvWMyzLc6K8i+oG/DjWLY0q4kc9nqBPRi8rsiYzMq/VHVtXCWxNZiGQhkjwpqNaDsqoVhOiUg==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.5.tgz", + "integrity": "sha512-rH15UA34MRf6cITfvt2EkSEaC/8ULvgMg5kzun6895oucA8PFyAFJaQzcjk9UraeD3ddMu56OKhZGdxd0JWfKw==", "dependencies": { "@graphql-codegen/plugin-helpers": "^2.6.2", - "@graphql-codegen/typescript": "^2.7.4", - "@graphql-codegen/visitor-plugin-common": "2.12.2", + "@graphql-codegen/typescript": "^2.7.5", + "@graphql-codegen/visitor-plugin-common": "2.13.0", "auto-bind": "~4.0.0", "tslib": "~2.4.0" }, @@ -2081,9 +2078,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.12.2.tgz", - "integrity": "sha512-UZJxY3mWwaM7yii7mSbl6Rxb6sJlpwGZc3QAs2Yd8MnYjXFPTBR0OO6aBTr9xuftl0pYwHu/pVK7vTPNnVmPww==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.0.tgz", + "integrity": "sha512-8lKw4l8W6yKaqrxx025eB6+lRMWaBKedbKjC9UyLhXAnqTi3tgaRKOBo4zvl1+KzE6R41Ruov9UcGD7OjgmBrw==", "dependencies": { "@graphql-codegen/plugin-helpers": "^2.6.2", "@graphql-tools/optimize": "^1.3.0", @@ -2106,12 +2103,12 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/code-file-loader": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.3.6.tgz", - "integrity": "sha512-PNWWSwSuQAqANerDwS0zdQ5FPipirv75TjjzBHnY+6AF/WvKq5sQiUQheA2P7B+MZc/KdQ7h/JAGMQOhKNVA+Q==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.3.7.tgz", + "integrity": "sha512-mjlqrA09LpDd0NRLKATLDpubUe6Badt4trhUU5Nv0yRhPFTPCEOLxwuDBeihhEXrXFq4yboPshOh607ySeywGw==", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "7.3.6", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/graphql-tag-pluck": "7.3.7", + "@graphql-tools/utils": "8.13.0", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2126,14 +2123,14 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.3.6.tgz", - "integrity": "sha512-qULgqsOGKY1/PBqmP7fJZqbCg/TzPHKB9Wl51HGA9QjGymrzmrH5EjvsC8RtgdubF8yuTTVVFTz1lmSQ7RPssQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.3.7.tgz", + "integrity": "sha512-hyAYCJiauwgFYTuqzj6rgCTGJGiZiFhWqCyIJmQIyeFDhnL7MzhgqlBQElAgn1AfFkW7sBc2hMowIXG8rAK3Jg==", "dependencies": { "@babel/parser": "^7.16.8", "@babel/traverse": "^7.16.8", "@babel/types": "^7.16.8", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -2146,12 +2143,12 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/load": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-7.7.7.tgz", - "integrity": "sha512-IpI2672zcoAX4FLjcH5kvHc7eqjPyLP1svrIcZKQenv0GRS6dW0HI9E5UCBs0y/yy8yW6s+SvpmNsfIlkMj3Kw==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-7.8.1.tgz", + "integrity": "sha512-ZOvidUgIPMQtvrkNBosYu+a54ASgKoEPKnoj+0xQtdJTCZWsnSyn/pinN83laHTwrGp9KNmFTS5Yc+KvPSCBdg==", "dependencies": { - "@graphql-tools/schema": "9.0.4", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/schema": "9.0.5", + "@graphql-tools/utils": "8.13.0", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -2165,11 +2162,11 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/merge": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.6.tgz", - "integrity": "sha512-uUBokxXi89bj08P+iCvQk3Vew4vcfL5ZM6NTylWi8PIpoq4r5nJ625bRuN8h2uubEdRiH8ntN9M4xkd/j7AybQ==", + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.7.tgz", + "integrity": "sha512-su9cUb0gtbvKTmD3LJ3EoUkdqmJ3KBk1efJGvUoN8tFzVVBdxgfOVxwLGI2GgIHcKRVvfhumkjHsa2aneHSY+Q==", "dependencies": { - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -2198,12 +2195,12 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "6.5.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.6.tgz", - "integrity": "sha512-2KjaWYxD/NC6KtckbDEAbN46QO+74d1SBaZQ26qQjWhyoAjon12xlMW4HWxHEN0d0xuz0cnOVUVc+t4wVXePUg==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.7.tgz", + "integrity": "sha512-IK0N/em48+UxOPUT11x69X1h9xZPmui7SwI/MqfA55aZOkdmVJmQ4QbUbdFJXh2s5VU6SefOW5T9XCi/A9pslg==", "dependencies": { "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -2216,12 +2213,12 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/schema": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.4.tgz", - "integrity": "sha512-B/b8ukjs18fq+/s7p97P8L1VMrwapYc3N2KvdG/uNThSazRRn8GsBK0Nr+FH+mVKiUfb4Dno79e3SumZVoHuOQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.5.tgz", + "integrity": "sha512-JPxFumaPQp6pRnrofy7rWVNW57r/1RISNxBCGOudFmjfRE2PlO6b766pxhDnggm/yMR3yzR+mA/JHpOK+ij+EA==", "dependencies": { - "@graphql-tools/merge": "8.3.6", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/merge": "8.3.7", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0", "value-or-promise": "1.0.11" }, @@ -2235,9 +2232,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@graphql-tools/utils": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.12.0.tgz", - "integrity": "sha512-TeO+MJWGXjUTS52qfK4R8HiPoF/R7X+qmgtOYd8DTH0l6b+5Y/tlg5aGeUJefqImRq7nvi93Ms40k/Uz4D5CWw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.13.0.tgz", + "integrity": "sha512-cI4LdXElgVK2jFoq0DpANlvk4Di6kIapHsJI63RCepQ6xZe+mLI1mDrGdesG37s2BaABqV3RdTzeO/sPnTtyxQ==", "dependencies": { "tslib": "^2.4.0" }, @@ -2351,9 +2348,9 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -3543,11 +3540,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-4.2.0.tgz", + "integrity": "sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw==", + "deprecated": "This is a stub types definition. keyv provides its own type definitions, so you do not need this installed.", "dependencies": { - "@types/node": "*" + "keyv": "*" } }, "node_modules/@types/lodash": { @@ -3569,9 +3567,9 @@ } }, "node_modules/@types/node": { - "version": "18.8.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.5.tgz", - "integrity": "sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q==" + "version": "18.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.7.tgz", + "integrity": "sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==" }, "node_modules/@types/node-fetch": { "version": "2.6.2", @@ -3601,9 +3599,9 @@ } }, "node_modules/@types/react": { - "version": "18.0.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.21.tgz", - "integrity": "sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA==", + "version": "18.0.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", + "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3650,6 +3648,67 @@ "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "dependencies": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/@typescript-eslint/experimental-utils": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", @@ -3673,7 +3732,33 @@ "eslint": "*" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "dependencies": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", @@ -3689,7 +3774,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/types": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", @@ -3701,7 +3786,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", @@ -3727,23 +3812,7 @@ } } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/lru-cache": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", @@ -3754,7 +3823,7 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", @@ -3768,11 +3837,27 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/yallist": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vercel/webpack-asset-relocator-loader": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@vercel/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-1.7.3.tgz", @@ -3940,9 +4025,9 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "bin": { "acorn": "bin/acorn" }, @@ -4299,9 +4384,9 @@ } }, "node_modules/axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.0.tgz", + "integrity": "sha512-4+rr8eQ7+XXS5nZrKcMO/AikHL0hVqy+lHWAnE3xdHl+aguag8SOQ6eEqLexwLNWgXIMfunGuD3ON1/6Kyet0A==", "engines": { "node": ">=4" } @@ -4936,9 +5021,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001419", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001419.tgz", - "integrity": "sha512-aFO1r+g6R7TW+PNQxKzjITwLOyDhVRLjW0LcwS/HCZGUUKTGNp9+IwLC4xyDSZBygVL/mxaFR3HIV6wEKQuSzw==", + "version": "1.0.30001426", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz", + "integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==", "funding": [ { "type": "opencollective", @@ -5533,9 +5618,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-js": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", - "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", + "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5543,9 +5628,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", - "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz", + "integrity": "sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A==", "dependencies": { "browserslist": "^4.21.4" }, @@ -5555,9 +5640,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", - "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", + "integrity": "sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5767,6 +5852,54 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-select/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-select/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/css-select/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/css-to-react-native": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", @@ -6244,26 +6377,19 @@ } }, "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, "funding": { "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -6276,11 +6402,12 @@ ] }, "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, "dependencies": { - "domelementtype": "^2.2.0" + "domelementtype": "^2.3.0" }, "engines": { "node": ">= 4" @@ -6290,13 +6417,14 @@ } }, "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" @@ -6356,9 +6484,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.281", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.281.tgz", - "integrity": "sha512-yer0w5wCYdFoZytfmbNhwiGI/3cW06+RV7E23ln4490DVMxs7PvYpbsrSmAiBn/V6gode8wvJlST2YfWgvzWIg==" + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -6756,6 +6884,38 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-config-react-app": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", + "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", + "dependencies": { + "confusing-browser-globals": "^1.0.10" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0", + "@typescript-eslint/parser": "^4.0.0", + "babel-eslint": "^10.0.0", + "eslint": "^7.5.0", + "eslint-plugin-flowtype": "^5.2.0", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jest": "^24.0.0", + "eslint-plugin-jsx-a11y": "^6.3.1", + "eslint-plugin-react": "^7.20.3", + "eslint-plugin-react-hooks": "^4.0.8", + "eslint-plugin-testing-library": "^3.9.0" + }, + "peerDependenciesMeta": { + "eslint-plugin-jest": { + "optional": true + }, + "eslint-plugin-testing-library": { + "optional": true + } + } + }, "node_modules/eslint-config-wesbos": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/eslint-config-wesbos/-/eslint-config-wesbos-3.1.4.tgz", @@ -6823,6 +6983,21 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-flowtype": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz", + "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==", + "dependencies": { + "lodash": "^4.17.15", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.1.0" + } + }, "node_modules/eslint-plugin-html": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", @@ -8008,9 +8183,9 @@ } }, "node_modules/gatsby": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-4.24.4.tgz", - "integrity": "sha512-jUC5X3xspNKTGp5ADlwP7q1I9kZai9mS+cPkEZJvSetdAB5AZjYY867OxwBgj0awt/E1+f8AG255zanK7Nvwtg==", + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-4.24.5.tgz", + "integrity": "sha512-f47r+RQldDkBpmcQqixmvafwEqUNUKvtmov+n04uCKOdfkuiiSMPm2pGAWx3vesfut2Kf7nIVgZLZz9sRjXqKA==", "hasInstallScript": true, "dependencies": { "@babel/code-frame": "^7.14.0", @@ -8637,209 +8812,13 @@ "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/gatsby/node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/gatsby/node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "dependencies": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "dependencies": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/gatsby/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "strip-json-comments": "^3.1.1" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/gatsby/node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "node_modules/gatsby/node_modules/@eslint/eslintrc/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", @@ -8855,20 +8834,33 @@ } } }, - "node_modules/gatsby/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "node_modules/gatsby/node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": ">=10.10.0" + } + }, + "node_modules/gatsby/node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/gatsby/node_modules/acorn": { @@ -8962,53 +8954,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/gatsby/node_modules/eslint-config-react-app": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", - "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", - "dependencies": { - "confusing-browser-globals": "^1.0.10" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0", - "@typescript-eslint/parser": "^4.0.0", - "babel-eslint": "^10.0.0", - "eslint": "^7.5.0", - "eslint-plugin-flowtype": "^5.2.0", - "eslint-plugin-import": "^2.22.0", - "eslint-plugin-jest": "^24.0.0", - "eslint-plugin-jsx-a11y": "^6.3.1", - "eslint-plugin-react": "^7.20.3", - "eslint-plugin-react-hooks": "^4.0.8", - "eslint-plugin-testing-library": "^3.9.0" - }, - "peerDependenciesMeta": { - "eslint-plugin-jest": { - "optional": true - }, - "eslint-plugin-testing-library": { - "optional": true - } - } - }, - "node_modules/gatsby/node_modules/eslint-plugin-flowtype": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz", - "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==", - "dependencies": { - "lodash": "^4.17.15", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.1.0" - } - }, "node_modules/gatsby/node_modules/eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -9384,14 +9329,11 @@ } }, "node_modules/graphql-compose": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/graphql-compose/-/graphql-compose-9.0.9.tgz", - "integrity": "sha512-kEXdwuBk7GKaThWY7eKDP+GLF9pGrGuMpYLu3O5w+lwDsgsfdiUCEC8jTsuPjm1qz9AxsspZHqc3jA4vKwyiNg==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/graphql-compose/-/graphql-compose-9.0.10.tgz", + "integrity": "sha512-UsVoxfi2+c8WbHl2pEB+teoRRZoY4mbWBoijeLDGpAZBSPChnqtSRjp+T9UcouLCwGr5ooNyOQLoI3OVzU1bPQ==", "dependencies": { "graphql-type-json": "0.3.2" - }, - "peerDependencies": { - "graphql": "^14.2.0 || ^15.0.0 || ^16.0.0" } }, "node_modules/graphql-playground-html": { @@ -9639,49 +9581,6 @@ "entities": "^4.3.0" } }, - "node_modules/htmlparser2/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/htmlparser2/node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", - "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -9772,9 +9671,9 @@ } }, "node_modules/immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==", + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", + "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -10019,9 +9918,9 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dependencies": { "has": "^1.0.3" }, @@ -10441,9 +10340,9 @@ "integrity": "sha512-b2A3rRT1TITzqmaO70U2/uunCh43BQVq7BfRwGPkD5xj8/WZsR3sPTy9DENt+dNZGsel3zBEm1UtYegUxjZW7A==" }, "node_modules/joi": { - "version": "17.6.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.3.tgz", - "integrity": "sha512-YlQsIaS9MHYekzf1Qe11LjTkNzx9qhYluK3172z38RxYoAUf82XMX1p1DG1H4Wtk2ED/vPdSn9OggqtDu+aTow==", + "version": "17.6.4", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.4.tgz", + "integrity": "sha512-tPzkTJHZQjSFCc842QpdVpOZ9LI2txApboNUbW70qgnRB14Lzl+oWQOPdF2N4yqyiY14wBGe8lc7f/2hZxbGmw==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -10661,9 +10560,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -11223,9 +11122,9 @@ "integrity": "sha512-YTzGAJOo/B6hkodeT5SKKHpOhAzjMfkUCCXjLJwjWk2F4/InIg+HbdH9kmT7bKpleDuqLZDTRy2OdNtAj0IVyQ==" }, "node_modules/node-abi": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.26.0.tgz", - "integrity": "sha512-jRVtMFTChbi2i/jqo/i2iP9634KMe+7K1v35mIdj3Mn59i5q27ZYhn+sW6npISM/PQg7HrP2kwtRBMmh5Uvzdg==", + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.28.0.tgz", + "integrity": "sha512-fRlDb4I0eLcQeUvGq7IY3xHrSb0c9ummdvDSYWfT9+LKP+3jCKw/tKoqaM7r1BAoiAC6GtwyjaGnOz6B3OtF+A==", "dependencies": { "semver": "^7.3.5" }, @@ -13434,25 +13333,14 @@ } }, "node_modules/recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", - "dependencies": { - "minimatch": "3.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dependencies": { - "brace-expansion": "^1.1.7" + "minimatch": "^3.0.5" }, "engines": { - "node": "*" + "node": ">=6.0.0" } }, "node_modules/redux": { @@ -13624,6 +13512,46 @@ "node": ">=0.10.0" } }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/renderkid/node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -15141,9 +15069,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", "funding": [ { "type": "opencollective", @@ -16137,25 +16065,25 @@ } }, "@babel/compat-data": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", - "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==" + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.0.tgz", + "integrity": "sha512-Gt9jszFJYq7qzXVK4slhc6NzJXnOVmRECWcVjF/T23rNXD9NtWQ0W3qxdg+p9wWIB+VQw3GYV/U2Ha9bRTfs4w==" }, "@babel/core": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", - "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", + "@babel/generator": "^7.19.6", "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.3", - "@babel/types": "^7.19.3", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -16174,11 +16102,11 @@ } }, "@babel/generator": { - "version": "7.19.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", - "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.0.tgz", + "integrity": "sha512-GUPcXxWibClgmYJuIwC2Bc2Lg+8b9VjaJ+HlNdACEVt+Wlr1eoU1OPZjZRm7Hzl0gaTsUZNQfeihvZJhG7oc3w==", "requires": { - "@babel/types": "^7.19.4", + "@babel/types": "^7.20.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -16213,11 +16141,11 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", - "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", "requires": { - "@babel/compat-data": "^7.19.3", + "@babel/compat-data": "^7.20.0", "@babel/helper-validator-option": "^7.18.6", "browserslist": "^4.21.3", "semver": "^6.3.0" @@ -16306,18 +16234,18 @@ } }, "@babel/helper-module-transforms": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", - "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" } }, "@babel/helper-optimise-call-expression": { @@ -16365,11 +16293,11 @@ } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -16407,13 +16335,13 @@ } }, "@babel/helpers": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", - "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.0.tgz", + "integrity": "sha512-aGMjYraN0zosCEthoGLdqot1oRsmxVTQRHadsUPz5QM44Zej2PYRz7XiDE7GqnkZnNtLbOuxqoZw42vkU7+XEQ==", "requires": { "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.4", - "@babel/types": "^7.19.4" + "@babel/traverse": "^7.20.0", + "@babel/types": "^7.20.0" } }, "@babel/highlight": { @@ -16478,9 +16406,9 @@ } }, "@babel/parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", - "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==" + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.0.tgz", + "integrity": "sha512-G9VgAhEaICnz8iiJeGJQyVl6J2nTjbW0xeisva0PK6XcKsga7BIaqm4ZF8Rg1Wbaqmy6znspNqhPaPkyukujzg==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -16693,11 +16621,11 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-syntax-json-strings": { @@ -16781,11 +16709,11 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", - "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-arrow-functions": { @@ -16815,9 +16743,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", - "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz", + "integrity": "sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w==", "requires": { "@babel/helper-plugin-utils": "^7.19.0" } @@ -16854,9 +16782,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", - "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz", + "integrity": "sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA==", "requires": { "@babel/helper-plugin-utils": "^7.19.0" } @@ -16931,36 +16859,33 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", - "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "requires": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-module-transforms": "^7.19.6", "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { @@ -17069,9 +16994,9 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz", - "integrity": "sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", "requires": { "@babel/helper-module-imports": "^7.18.6", "@babel/helper-plugin-utils": "^7.19.0", @@ -17123,13 +17048,13 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", - "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.0.tgz", + "integrity": "sha512-xOAsAFaun3t9hCwZ13Qe7gq423UgMZ6zAgmLxeGGapFqlT/X3L5qT2btjiVLlFn7gWtMaVyceS5VxGAuKbgizw==", "requires": { "@babel/helper-create-class-features-plugin": "^7.19.0", "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.18.6" + "@babel/plugin-syntax-typescript": "^7.20.0" } }, "@babel/plugin-transform-unicode-escapes": { @@ -17267,20 +17192,20 @@ } }, "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.0.tgz", + "integrity": "sha512-NDYdls71fTXoU8TZHfbBWg7DiZfNzClcKui/+kyi6ppD2L1qnWW3VV6CjtaBXSUGGhiTWJ6ereOIkUvenif66Q==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" } }, "@babel/runtime-corejs3": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.4.tgz", - "integrity": "sha512-HzjQ8+dzdx7dmZy4DQ8KV8aHi/74AjEbBGTFutBmg/pd3dY5/q1sfuOGPTFGEytlQhWoeVXqcK5BwMgIkRkNDQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.0.tgz", + "integrity": "sha512-v1JH7PeAAGBEyTQM9TqojVl+b20zXtesFKCJHu50xMxZKD1fX0TKaKHPsZfFkXfs7D1M9M6Eeqg1FkJ3a0x2dA==", "requires": { "core-js-pure": "^3.25.1", - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" } }, "@babel/template": { @@ -17294,18 +17219,18 @@ } }, "@babel/traverse": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", - "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.0.tgz", + "integrity": "sha512-5+cAXQNARgjRUK0JWu2UBwja4JLSO/rBMPJzpsKb+oBF5xlUuCfljQepS4XypBQoiigL0VQjTZy6WiONtUdScQ==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.4", + "@babel/generator": "^7.20.0", "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.4", - "@babel/types": "^7.19.4", + "@babel/parser": "^7.20.0", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -17318,9 +17243,9 @@ } }, "@babel/types": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", - "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", "requires": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -17483,13 +17408,13 @@ } }, "@graphql-codegen/typescript": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.7.4.tgz", - "integrity": "sha512-mjoE3KqmAaBUSlCuKb2WDot3+CTfdJ9eKIcWsDFubPTNdZE0Z8mLoATmVryx8NOBjDtPU/ZziI0rYEGOR4qpAw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.8.0.tgz", + "integrity": "sha512-atQ6NFfMmoknJi0QdVSozPugKcgeJB6t1/cVskbhVtX8tAEFFLjl6H23mpz3t35AH6aGWtvx2LCjUSxHI6P6xA==", "requires": { "@graphql-codegen/plugin-helpers": "^2.6.2", "@graphql-codegen/schema-ast": "^2.5.1", - "@graphql-codegen/visitor-plugin-common": "2.12.2", + "@graphql-codegen/visitor-plugin-common": "2.13.0", "auto-bind": "~4.0.0", "tslib": "~2.4.0" }, @@ -17502,13 +17427,13 @@ } }, "@graphql-codegen/typescript-operations": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.4.tgz", - "integrity": "sha512-+IA0ouGC6GZiCyvWMyzLc6K8i+oG/DjWLY0q4kc9nqBPRi8rsiYzMq/VHVtXCWxNZiGQhkjwpqNaDsqoVhOiUg==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.5.tgz", + "integrity": "sha512-rH15UA34MRf6cITfvt2EkSEaC/8ULvgMg5kzun6895oucA8PFyAFJaQzcjk9UraeD3ddMu56OKhZGdxd0JWfKw==", "requires": { "@graphql-codegen/plugin-helpers": "^2.6.2", - "@graphql-codegen/typescript": "^2.7.4", - "@graphql-codegen/visitor-plugin-common": "2.12.2", + "@graphql-codegen/typescript": "^2.7.5", + "@graphql-codegen/visitor-plugin-common": "2.13.0", "auto-bind": "~4.0.0", "tslib": "~2.4.0" }, @@ -17521,9 +17446,9 @@ } }, "@graphql-codegen/visitor-plugin-common": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.12.2.tgz", - "integrity": "sha512-UZJxY3mWwaM7yii7mSbl6Rxb6sJlpwGZc3QAs2Yd8MnYjXFPTBR0OO6aBTr9xuftl0pYwHu/pVK7vTPNnVmPww==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.0.tgz", + "integrity": "sha512-8lKw4l8W6yKaqrxx025eB6+lRMWaBKedbKjC9UyLhXAnqTi3tgaRKOBo4zvl1+KzE6R41Ruov9UcGD7OjgmBrw==", "requires": { "@graphql-codegen/plugin-helpers": "^2.6.2", "@graphql-tools/optimize": "^1.3.0", @@ -17545,12 +17470,12 @@ } }, "@graphql-tools/code-file-loader": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.3.6.tgz", - "integrity": "sha512-PNWWSwSuQAqANerDwS0zdQ5FPipirv75TjjzBHnY+6AF/WvKq5sQiUQheA2P7B+MZc/KdQ7h/JAGMQOhKNVA+Q==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.3.7.tgz", + "integrity": "sha512-mjlqrA09LpDd0NRLKATLDpubUe6Badt4trhUU5Nv0yRhPFTPCEOLxwuDBeihhEXrXFq4yboPshOh607ySeywGw==", "requires": { - "@graphql-tools/graphql-tag-pluck": "7.3.6", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/graphql-tag-pluck": "7.3.7", + "@graphql-tools/utils": "8.13.0", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -17564,14 +17489,14 @@ } }, "@graphql-tools/graphql-tag-pluck": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.3.6.tgz", - "integrity": "sha512-qULgqsOGKY1/PBqmP7fJZqbCg/TzPHKB9Wl51HGA9QjGymrzmrH5EjvsC8RtgdubF8yuTTVVFTz1lmSQ7RPssQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.3.7.tgz", + "integrity": "sha512-hyAYCJiauwgFYTuqzj6rgCTGJGiZiFhWqCyIJmQIyeFDhnL7MzhgqlBQElAgn1AfFkW7sBc2hMowIXG8rAK3Jg==", "requires": { "@babel/parser": "^7.16.8", "@babel/traverse": "^7.16.8", "@babel/types": "^7.16.8", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0" }, "dependencies": { @@ -17583,12 +17508,12 @@ } }, "@graphql-tools/load": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-7.7.7.tgz", - "integrity": "sha512-IpI2672zcoAX4FLjcH5kvHc7eqjPyLP1svrIcZKQenv0GRS6dW0HI9E5UCBs0y/yy8yW6s+SvpmNsfIlkMj3Kw==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-7.8.1.tgz", + "integrity": "sha512-ZOvidUgIPMQtvrkNBosYu+a54ASgKoEPKnoj+0xQtdJTCZWsnSyn/pinN83laHTwrGp9KNmFTS5Yc+KvPSCBdg==", "requires": { - "@graphql-tools/schema": "9.0.4", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/schema": "9.0.5", + "@graphql-tools/utils": "8.13.0", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -17601,11 +17526,11 @@ } }, "@graphql-tools/merge": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.6.tgz", - "integrity": "sha512-uUBokxXi89bj08P+iCvQk3Vew4vcfL5ZM6NTylWi8PIpoq4r5nJ625bRuN8h2uubEdRiH8ntN9M4xkd/j7AybQ==", + "version": "8.3.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.7.tgz", + "integrity": "sha512-su9cUb0gtbvKTmD3LJ3EoUkdqmJ3KBk1efJGvUoN8tFzVVBdxgfOVxwLGI2GgIHcKRVvfhumkjHsa2aneHSY+Q==", "requires": { - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0" }, "dependencies": { @@ -17632,12 +17557,12 @@ } }, "@graphql-tools/relay-operation-optimizer": { - "version": "6.5.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.6.tgz", - "integrity": "sha512-2KjaWYxD/NC6KtckbDEAbN46QO+74d1SBaZQ26qQjWhyoAjon12xlMW4HWxHEN0d0xuz0cnOVUVc+t4wVXePUg==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.7.tgz", + "integrity": "sha512-IK0N/em48+UxOPUT11x69X1h9xZPmui7SwI/MqfA55aZOkdmVJmQ4QbUbdFJXh2s5VU6SefOW5T9XCi/A9pslg==", "requires": { "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0" }, "dependencies": { @@ -17649,12 +17574,12 @@ } }, "@graphql-tools/schema": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.4.tgz", - "integrity": "sha512-B/b8ukjs18fq+/s7p97P8L1VMrwapYc3N2KvdG/uNThSazRRn8GsBK0Nr+FH+mVKiUfb4Dno79e3SumZVoHuOQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.5.tgz", + "integrity": "sha512-JPxFumaPQp6pRnrofy7rWVNW57r/1RISNxBCGOudFmjfRE2PlO6b766pxhDnggm/yMR3yzR+mA/JHpOK+ij+EA==", "requires": { - "@graphql-tools/merge": "8.3.6", - "@graphql-tools/utils": "8.12.0", + "@graphql-tools/merge": "8.3.7", + "@graphql-tools/utils": "8.13.0", "tslib": "^2.4.0", "value-or-promise": "1.0.11" }, @@ -17667,9 +17592,9 @@ } }, "@graphql-tools/utils": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.12.0.tgz", - "integrity": "sha512-TeO+MJWGXjUTS52qfK4R8HiPoF/R7X+qmgtOYd8DTH0l6b+5Y/tlg5aGeUJefqImRq7nvi93Ms40k/Uz4D5CWw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.13.0.tgz", + "integrity": "sha512-cI4LdXElgVK2jFoq0DpANlvk4Di6kIapHsJI63RCepQ6xZe+mLI1mDrGdesG37s2BaABqV3RdTzeO/sPnTtyxQ==", "requires": { "tslib": "^2.4.0" }, @@ -17762,9 +17687,9 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "requires": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -18537,11 +18462,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, "@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-4.2.0.tgz", + "integrity": "sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw==", "requires": { - "@types/node": "*" + "keyv": "*" } }, "@types/lodash": { @@ -18563,9 +18488,9 @@ } }, "@types/node": { - "version": "18.8.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.5.tgz", - "integrity": "sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q==" + "version": "18.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.7.tgz", + "integrity": "sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==" }, "@types/node-fetch": { "version": "2.6.2", @@ -18595,9 +18520,9 @@ } }, "@types/react": { - "version": "18.0.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.21.tgz", - "integrity": "sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA==", + "version": "18.0.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", + "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -18634,16 +18559,54 @@ "@types/node": "*" } }, - "@types/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-gVC1InwyVrO326wbBZw+AO3u2vRXz/iRWq9jYhpG4W8LXyIgDv3ZmcLQ5Q4Gs+gFMyqx+viFoFT+l3p61QFCmQ==" - }, - "@types/yoga-layout": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", - "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==" - }, + "@types/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-gVC1InwyVrO326wbBZw+AO3u2vRXz/iRWq9jYhpG4W8LXyIgDv3ZmcLQ5Q4Gs+gFMyqx+viFoFT+l3p61QFCmQ==" + }, + "@types/yoga-layout": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", + "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "requires": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "@typescript-eslint/experimental-utils": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", @@ -18655,45 +18618,47 @@ "@typescript-eslint/typescript-estree": "4.33.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "requires": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "requires": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" + } + }, + "@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "requires": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - } - }, - "@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==" - }, - "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "requires": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -18717,6 +18682,15 @@ } } }, + "@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "requires": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + } + }, "@vercel/webpack-asset-relocator-loader": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@vercel/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-1.7.3.tgz", @@ -18881,9 +18855,9 @@ } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" }, "acorn-import-assertions": { "version": "1.8.0", @@ -19115,9 +19089,9 @@ } }, "axe-core": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", - "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==" + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.0.tgz", + "integrity": "sha512-4+rr8eQ7+XXS5nZrKcMO/AikHL0hVqy+lHWAnE3xdHl+aguag8SOQ6eEqLexwLNWgXIMfunGuD3ON1/6Kyet0A==" }, "axios": { "version": "0.21.4", @@ -19601,9 +19575,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001419", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001419.tgz", - "integrity": "sha512-aFO1r+g6R7TW+PNQxKzjITwLOyDhVRLjW0LcwS/HCZGUUKTGNp9+IwLC4xyDSZBygVL/mxaFR3HIV6wEKQuSzw==" + "version": "1.0.30001426", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz", + "integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==" }, "capital-case": { "version": "1.0.4", @@ -20083,22 +20057,22 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "core-js": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", - "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==" + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", + "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==" }, "core-js-compat": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", - "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz", + "integrity": "sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A==", "requires": { "browserslist": "^4.21.4" } }, "core-js-pure": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", - "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==" + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.0.tgz", + "integrity": "sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA==" }, "core-util-is": { "version": "1.0.3", @@ -20238,6 +20212,41 @@ "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" + }, + "dependencies": { + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } } }, "css-to-react-native": { @@ -20589,20 +20598,14 @@ } }, "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - } + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" } }, "domelementtype": { @@ -20611,21 +20614,23 @@ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" }, "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, "requires": { - "domelementtype": "^2.2.0" + "domelementtype": "^2.3.0" } }, "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" } }, "dot-case": { @@ -20678,9 +20683,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.281", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.281.tgz", - "integrity": "sha512-yer0w5wCYdFoZytfmbNhwiGI/3cW06+RV7E23ln4490DVMxs7PvYpbsrSmAiBn/V6gode8wvJlST2YfWgvzWIg==" + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "emoji-regex": { "version": "9.2.2", @@ -21006,6 +21011,14 @@ "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", "dev": true }, + "eslint-config-react-app": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", + "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", + "requires": { + "confusing-browser-globals": "^1.0.10" + } + }, "eslint-config-wesbos": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/eslint-config-wesbos/-/eslint-config-wesbos-3.1.4.tgz", @@ -21049,6 +21062,15 @@ } } }, + "eslint-plugin-flowtype": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz", + "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==", + "requires": { + "lodash": "^4.17.15", + "string-natural-compare": "^3.0.1" + } + }, "eslint-plugin-html": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", @@ -21905,9 +21927,9 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "gatsby": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-4.24.4.tgz", - "integrity": "sha512-jUC5X3xspNKTGp5ADlwP7q1I9kZai9mS+cPkEZJvSetdAB5AZjYY867OxwBgj0awt/E1+f8AG255zanK7Nvwtg==", + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-4.24.5.tgz", + "integrity": "sha512-f47r+RQldDkBpmcQqixmvafwEqUNUKvtmov+n04uCKOdfkuiiSMPm2pGAWx3vesfut2Kf7nIVgZLZz9sRjXqKA==", "requires": { "@babel/code-frame": "^7.14.0", "@babel/core": "^7.15.5", @@ -22122,104 +22144,6 @@ } } }, - "@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "requires": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" - } - } - }, - "@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "requires": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - } - }, - "@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==" - }, - "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "requires": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - } - }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -22311,23 +22235,6 @@ } } }, - "eslint-config-react-app": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", - "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", - "requires": { - "confusing-browser-globals": "^1.0.10" - } - }, - "eslint-plugin-flowtype": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz", - "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==", - "requires": { - "lodash": "^4.17.15", - "string-natural-compare": "^3.0.1" - } - }, "eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -22906,9 +22813,9 @@ "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==" }, "graphql-compose": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/graphql-compose/-/graphql-compose-9.0.9.tgz", - "integrity": "sha512-kEXdwuBk7GKaThWY7eKDP+GLF9pGrGuMpYLu3O5w+lwDsgsfdiUCEC8jTsuPjm1qz9AxsspZHqc3jA4vKwyiNg==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/graphql-compose/-/graphql-compose-9.0.10.tgz", + "integrity": "sha512-UsVoxfi2+c8WbHl2pEB+teoRRZoY4mbWBoijeLDGpAZBSPChnqtSRjp+T9UcouLCwGr5ooNyOQLoI3OVzU1bPQ==", "requires": { "graphql-type-json": "0.3.2" } @@ -23096,39 +23003,6 @@ "domhandler": "^5.0.2", "domutils": "^3.0.1", "entities": "^4.3.0" - }, - "dependencies": { - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", - "dev": true, - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" - } - } } }, "http-cache-semantics": { @@ -23186,9 +23060,9 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" }, "immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", + "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==" }, "immutable": { "version": "3.7.6", @@ -23361,9 +23235,9 @@ } }, "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "requires": { "has": "^1.0.3" } @@ -23660,9 +23534,9 @@ "integrity": "sha512-b2A3rRT1TITzqmaO70U2/uunCh43BQVq7BfRwGPkD5xj8/WZsR3sPTy9DENt+dNZGsel3zBEm1UtYegUxjZW7A==" }, "joi": { - "version": "17.6.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.3.tgz", - "integrity": "sha512-YlQsIaS9MHYekzf1Qe11LjTkNzx9qhYluK3172z38RxYoAUf82XMX1p1DG1H4Wtk2ED/vPdSn9OggqtDu+aTow==", + "version": "17.6.4", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.4.tgz", + "integrity": "sha512-tPzkTJHZQjSFCc842QpdVpOZ9LI2txApboNUbW70qgnRB14Lzl+oWQOPdF2N4yqyiY14wBGe8lc7f/2hZxbGmw==", "requires": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -23838,9 +23712,9 @@ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" }, "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -24289,9 +24163,9 @@ "integrity": "sha512-YTzGAJOo/B6hkodeT5SKKHpOhAzjMfkUCCXjLJwjWk2F4/InIg+HbdH9kmT7bKpleDuqLZDTRy2OdNtAj0IVyQ==" }, "node-abi": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.26.0.tgz", - "integrity": "sha512-jRVtMFTChbi2i/jqo/i2iP9634KMe+7K1v35mIdj3Mn59i5q27ZYhn+sW6npISM/PQg7HrP2kwtRBMmh5Uvzdg==", + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.28.0.tgz", + "integrity": "sha512-fRlDb4I0eLcQeUvGq7IY3xHrSb0c9ummdvDSYWfT9+LKP+3jCKw/tKoqaM7r1BAoiAC6GtwyjaGnOz6B3OtF+A==", "requires": { "semver": "^7.3.5" }, @@ -25836,21 +25710,11 @@ } }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "requires": { - "minimatch": "3.0.4" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^3.0.5" } }, "redux": { @@ -25988,6 +25852,34 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, "entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -27124,9 +27016,9 @@ } }, "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==" + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==" }, "unbox-primitive": { "version": "1.0.2", From 407552e50684f3e1a7d02249bf8da89bc8934d3e Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 27 Oct 2022 12:26:06 -0700 Subject: [PATCH 4/5] Update Python dependencies --- requirements/dev.txt | 38 ++++---- requirements/main.txt | 213 ++++++++++++++++++++++++------------------ 2 files changed, 140 insertions(+), 111 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index 07f7853f3..19b6f8f01 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -310,10 +310,12 @@ docutils==0.19 \ # sphinx # sphinx-click # sphinxcontrib-bibtex -exceptiongroup==1.0.0rc9 \ - --hash=sha256:2e3c3fc1538a094aab74fad52d6c33fc94de3dfee3ee01f187c0e0c72aec5337 \ - --hash=sha256:9086a4a21ef9b31c72181c77c040a074ba0889ee56a7b289ff0afb0d97655f96 - # via trio +exceptiongroup==1.0.0 \ + --hash=sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41 \ + --hash=sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad + # via + # pytest + # trio filelock==3.8.0 \ --hash=sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc \ --hash=sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4 @@ -437,9 +439,9 @@ hyperframe==6.0.1 \ # via # h2 # selenium-wire -identify==2.5.6 \ - --hash=sha256:6c32dbd747aa4ceee1df33f25fed0b0f6e0d65721b15bd151307ff7056d50245 \ - --hash=sha256:b276db7ec52d7e89f5bc4653380e33054ddc803d25875952ad90b0f012cbcdaa +identify==2.5.7 \ + --hash=sha256:5b8fd1e843a6d4bf10685dd31f4520a7f1c7d0e14e9bc5d34b1d6f111cabc011 \ + --hash=sha256:7a67b2a6208d390fd86fd04fb3def94a3a8b7f0bcbd1d1fcd6736f4defe26390 # via pre-commit idna==3.4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ @@ -606,10 +608,6 @@ pre-commit==2.20.0 \ --hash=sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7 \ --hash=sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959 # via -r requirements/dev.in -py==1.11.0 \ - --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \ - --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 - # via pytest pyasn1==0.4.8 \ --hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \ --hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba @@ -723,9 +721,9 @@ pysocks==1.7.1 \ # via # selenium-wire # urllib3 -pytest==7.1.3 \ - --hash=sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7 \ - --hash=sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39 +pytest==7.2.0 \ + --hash=sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71 \ + --hash=sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59 # via # -r requirements/dev.in # pytest-asyncio @@ -1014,9 +1012,9 @@ types-cachetools==5.2.1 \ --hash=sha256:069cfc825697cd51445c1feabbe4edc1fae2b2315870e7a9a179a7c4a5851bee \ --hash=sha256:b496b7e364ba050c4eaadcc6582f2c9fbb04f8ee7141eb3b311a8589dbd4506a # via -r requirements/dev.in -types-pyyaml==6.0.12 \ - --hash=sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15 \ - --hash=sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa +types-pyyaml==6.0.12.1 \ + --hash=sha256:70ccaafcf3fb404d57bffc1529fdd86a13e8b4f2cf9fc3ee81a6408ce0ad59d2 \ + --hash=sha256:aaf5e51444c13bd34104695a89ad9c48412599a4f615d65a60e649109714f608 # via -r requirements/dev.in typing-extensions==4.4.0 \ --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \ @@ -1038,9 +1036,9 @@ urllib3[socks]==1.26.12 \ # -c requirements/main.txt # requests # selenium -virtualenv==20.16.5 \ - --hash=sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da \ - --hash=sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27 +virtualenv==20.16.6 \ + --hash=sha256:186ca84254abcbde98180fd17092f9628c5fe742273c02724972a1d8a2035108 \ + --hash=sha256:530b850b523c6449406dfba859d6345e48ef19b8439606c5d74d7d3c9e14d76e # via pre-commit wsproto==1.2.0 \ --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ diff --git a/requirements/main.txt b/requirements/main.txt index c27bc0ffb..f937a876f 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -120,33 +120,43 @@ async-timeout==4.0.2 \ # via # aiohttp # aioredis -asyncpg==0.26.0 \ - --hash=sha256:03f44926fa7ff7ccd59e98f05c7e227e9de15332a7da5bbcef3654bf468ee597 \ - --hash=sha256:050e339694f8c5d9aebcf326ca26f6622ef23963a6a3a4f97aeefc743954afd5 \ - --hash=sha256:0de408626cfc811ef04f372debfcdd5e4ab5aeb358f2ff14d1bdc246ed6272b5 \ - --hash=sha256:235205b60d4d014921f7b1cdca0e19669a9a8978f7606b3eb8237ca95f8e716e \ - --hash=sha256:2ed3880b3aec8bda90548218fe0914d251d641f798382eda39a17abfc4910af0 \ - --hash=sha256:3ecbe8ed3af4c739addbfbd78f7752866cce2c4e9cc3f953556e4960349ae360 \ - --hash=sha256:49fc7220334cc31d14866a0b77a575d6a5945c0fa3bb67f17304e8b838e2a02b \ - --hash=sha256:4b4051012ca75defa9a1dc6b78185ca58cdc3a247187eb76a6bcf55dfaa2fad4 \ - --hash=sha256:6d60f15a0ac18c54a6ca6507c28599c06e2e87a0901e7b548f15243d71905b18 \ - --hash=sha256:7129bd809990fd119e8b2b9982e80be7712bb6041cd082be3e415e60e5e2e98f \ - --hash=sha256:77e684a24fee17ba3e487ca982d0259ed17bae1af68006f4cf284b23ba20ea2c \ - --hash=sha256:838e4acd72da370ad07243898e886e93d3c0c9413f4444d600ba60a5cc206014 \ - --hash=sha256:868a71704262834065ca7113d80b1f679609e2df77d837747e3d92150dd5a39b \ - --hash=sha256:8e1e79f0253cbd51fc43c4d0ce8804e46ee71f6c173fdc75606662ad18756b52 \ - --hash=sha256:9acb22a7b6bcca0d80982dce3d67f267d43e960544fb5dd934fd3abe20c48014 \ - --hash=sha256:a254d09a3a989cc1839ba2c34448b879cdd017b528a0cda142c92fbb6c13d957 \ - --hash=sha256:b0c3f39ebfac06848ba3f1e280cb1fada7cc1229538e3dad3146e8d1f9deb92a \ - --hash=sha256:b1f7b173af649b85126429e11a628d01a5b75973d2a55d64dba19ad8f0e9f904 \ - --hash=sha256:d156e53b329e187e2dbfca8c28c999210045c45ef22a200b50de9b9e520c2694 \ - --hash=sha256:d96cf93e01df9fb03cef5f62346587805e6c0ca6f654c23b8d35315bdc69af59 \ - --hash=sha256:e550d8185f2c4725c1e8d3c555fe668b41bd092143012ddcc5343889e1c2a13d \ - --hash=sha256:e5bd99ee7a00e87df97b804f178f31086e88c8106aca9703b1d7be5078999e68 \ - --hash=sha256:ede1a3a2c377fe12a3930f4b4dd5340e8b32929541d5db027a21816852723438 \ - --hash=sha256:efe056fd22fc6ed5c1ab353b6510808409566daac4e6f105e2043797f17b8dad \ - --hash=sha256:f3ce7d8c0ab4639bbf872439eba86ef62dd030b245ad0e17c8c675d93d7a6b2d \ - --hash=sha256:f92d501bf213b16fabad4fbb0061398d2bceae30ddc228e7314c28dcc6641b79 +asyncpg==0.27.0 \ + --hash=sha256:16ba8ec2e85d586b4a12bcd03e8d29e3d99e832764d6a1d0b8c27dbbe4a2569d \ + --hash=sha256:18f77e8e71e826ba2d0c3ba6764930776719ae2b225ca07e014590545928b576 \ + --hash=sha256:1b6499de06fe035cf2fa932ec5617ed3f37d4ebbf663b655922e105a484a6af9 \ + --hash=sha256:20b596d8d074f6f695c13ffb8646d0b6bb1ab570ba7b0cfd349b921ff03cfc1e \ + --hash=sha256:2232ebae9796d4600a7819fc383da78ab51b32a092795f4555575fc934c1c89d \ + --hash=sha256:4750f5cf49ed48a6e49c6e5aed390eee367694636c2dcfaf4a273ca832c5c43c \ + --hash=sha256:4bb366ae34af5b5cabc3ac6a5347dfb6013af38c68af8452f27968d49085ecc0 \ + --hash=sha256:5710cb0937f696ce303f5eed6d272e3f057339bb4139378ccecafa9ee923a71c \ + --hash=sha256:609054a1f47292a905582a1cfcca51a6f3f30ab9d822448693e66fdddde27920 \ + --hash=sha256:62932f29cf2433988fcd799770ec64b374a3691e7902ecf85da14d5e0854d1ea \ + --hash=sha256:69aa1b443a182b13a17ff926ed6627af2d98f62f2fe5890583270cc4073f63bf \ + --hash=sha256:71cca80a056ebe19ec74b7117b09e650990c3ca535ac1c35234a96f65604192f \ + --hash=sha256:720986d9a4705dd8a40fdf172036f5ae787225036a7eb46e704c45aa8f62c054 \ + --hash=sha256:768e0e7c2898d40b16d4ef7a0b44e8150db3dd8995b4652aa1fe2902e92c7df8 \ + --hash=sha256:7a6206210c869ebd3f4eb9e89bea132aefb56ff3d1b7dd7e26b102b17e27bbb1 \ + --hash=sha256:7d8585707ecc6661d07367d444bbaa846b4e095d84451340da8df55a3757e152 \ + --hash=sha256:8113e17cfe236dc2277ec844ba9b3d5312f61bd2fdae6d3ed1c1cdd75f6cf2d8 \ + --hash=sha256:879c29a75969eb2722f94443752f4720d560d1e748474de54ae8dd230bc4956b \ + --hash=sha256:88b62164738239f62f4af92567b846a8ef7cf8abf53eddd83650603de4d52163 \ + --hash=sha256:8934577e1ed13f7d2d9cea3cc016cc6f95c19faedea2c2b56a6f94f257cea672 \ + --hash=sha256:9654085f2b22f66952124de13a8071b54453ff972c25c59b5ce1173a4283ffd9 \ + --hash=sha256:975a320baf7020339a67315284a4d3bf7460e664e484672bd3e71dbd881bc692 \ + --hash=sha256:9a3a4ff43702d39e3c97a8786314123d314e0f0e4dabc8367db5b665c93914de \ + --hash=sha256:a7a94c03386bb95456b12c66026b3a87d1b965f0f1e5733c36e7229f8f137747 \ + --hash=sha256:ab0f21c4818d46a60ca789ebc92327d6d874d3b7ccff3963f7af0a21dc6cff52 \ + --hash=sha256:bb71211414dd1eeb8d31ec529fe77cff04bf53efc783a5f6f0a32d84923f45cf \ + --hash=sha256:bf21ebf023ec67335258e0f3d3ad7b91bb9507985ba2b2206346de488267cad0 \ + --hash=sha256:bfc3980b4ba6f97138b04f0d32e8af21d6c9fa1f8e6e140c07d15690a0a99279 \ + --hash=sha256:c2232d4625c558f2aa001942cac1d7952aa9f0dbfc212f63bc754277769e1ef2 \ + --hash=sha256:ccddb9419ab4e1c48742457d0c0362dbdaeb9b28e6875115abfe319b29ee225d \ + --hash=sha256:d20dea7b83651d93b1eb2f353511fe7fd554752844523f17ad30115d8b9c8cd6 \ + --hash=sha256:e56ac8a8237ad4adec97c0cd4728596885f908053ab725e22900b5902e7f8e69 \ + --hash=sha256:eb4b2fdf88af4fb1cc569781a8f933d2a73ee82cd720e0cb4edabbaecf2a905b \ + --hash=sha256:eca01eb112a39d31cc4abb93a5aef2a81514c23f70956729f42fb83b11b3483f \ + --hash=sha256:fca608d199ffed4903dce1bcd97ad0fe8260f405c1c225bdf0002709132171c2 \ + --hash=sha256:fddcacf695581a8d856654bc4c8cfb73d5c9df26d5f55201722d3e6a699e9629 # via # -r requirements/main.in # safir @@ -698,21 +708,21 @@ proto-plus==1.22.1 \ --hash=sha256:6c7dfd122dfef8019ff654746be4f5b1d9c80bba787fe9611b508dd88be3a2fa \ --hash=sha256:ea8982669a23c379f74495bc48e3dcb47c822c484ce8ee1d1d7beb339d4e34c5 # via google-cloud-firestore -protobuf==4.21.8 \ - --hash=sha256:0f236ce5016becd989bf39bd20761593e6d8298eccd2d878eda33012645dc369 \ - --hash=sha256:2c92a7bfcf4ae76a8ac72e545e99a7407e96ffe52934d690eb29a8809ee44d7b \ - --hash=sha256:427426593b55ff106c84e4a88cac855175330cb6eb7e889e85aaa7b5652b686d \ - --hash=sha256:4761201b93e024bb70ee3a6a6425d61f3152ca851f403ba946fb0cde88872661 \ - --hash=sha256:809ca0b225d3df42655a12f311dd0f4148a943c51f1ad63c38343e457492b689 \ - --hash=sha256:89d641be4b5061823fa0e463c50a2607a97833e9f8cfb36c2f91ef5ccfcc3861 \ - --hash=sha256:a55545ce9eec4030cf100fcb93e861c622d927ef94070c1a3c01922902464278 \ - --hash=sha256:b02eabb9ebb1a089ed20626a90ad7a69cee6bcd62c227692466054b19c38dd1f \ - --hash=sha256:b37b76efe84d539f16cba55ee0036a11ad91300333abd213849cbbbb284b878e \ - --hash=sha256:bbececaf3cfea9ea65ebb7974e6242d310d2a7772a6f015477e0d79993af4511 \ - --hash=sha256:bc471cf70a0f53892fdd62f8cd4215f0af8b3f132eeee002c34302dff9edd9b6 \ - --hash=sha256:c252c55ee15175aa1b21b7b9896e6add5162d066d5202e75c39f96136f08cce3 \ - --hash=sha256:c5f94911dd8feb3cd3786fc90f7565c9aba7ce45d0f254afd625b9628f578c3f \ - --hash=sha256:f2d55ff22ec300c4d954d3b0d1eeb185681ec8ad4fbecff8a5aee6a1cdd345ba +protobuf==4.21.9 \ + --hash=sha256:2c9c2ed7466ad565f18668aa4731c535511c5d9a40c6da39524bccf43e441719 \ + --hash=sha256:48e2cd6b88c6ed3d5877a3ea40df79d08374088e89bedc32557348848dff250b \ + --hash=sha256:5b0834e61fb38f34ba8840d7dcb2e5a2f03de0c714e0293b3963b79db26de8ce \ + --hash=sha256:61f21493d96d2a77f9ca84fefa105872550ab5ef71d21c458eb80edcf4885a99 \ + --hash=sha256:6e0be9f09bf9b6cf497b27425487706fa48c6d1632ddd94dab1a5fe11a422392 \ + --hash=sha256:6e312e280fbe3c74ea9e080d9e6080b636798b5e3939242298b591064470b06b \ + --hash=sha256:7eb8f2cc41a34e9c956c256e3ac766cf4e1a4c9c925dc757a41a01be3e852965 \ + --hash=sha256:84ea107016244dfc1eecae7684f7ce13c788b9a644cd3fca5b77871366556444 \ + --hash=sha256:9227c14010acd9ae7702d6467b4625b6fe853175a6b150e539b21d2b2f2b409c \ + --hash=sha256:a419cc95fca8694804709b8c4f2326266d29659b126a93befe210f5bbc772536 \ + --hash=sha256:a7d0ea43949d45b836234f4ebb5ba0b22e7432d065394b532cdca8f98415e3cf \ + --hash=sha256:b5ab0b8918c136345ff045d4b3d5f719b505b7c8af45092d7f45e304f55e50a1 \ + --hash=sha256:e575c57dc8b5b2b2caa436c16d44ef6981f2235eb7179bfc847557886376d740 \ + --hash=sha256:f9eae277dd240ae19bb06ff4e2346e771252b0e619421965504bd1b1bba7c5fa # via # google-api-core # google-cloud-firestore @@ -998,55 +1008,76 @@ watchfiles==0.18.0 \ --hash=sha256:d5d799614d4c56d29c5ba56f4f619f967210dc10a0d6965b62d326b9e2f72c9e \ --hash=sha256:fd4215badad1e3d1ad5fb79f21432dd5157e2e7b0765d27a19dc2a28580c6979 # via uvicorn -websockets==10.3 \ - --hash=sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af \ - --hash=sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c \ - --hash=sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76 \ - --hash=sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47 \ - --hash=sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69 \ - --hash=sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079 \ - --hash=sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c \ - --hash=sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55 \ - --hash=sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02 \ - --hash=sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559 \ - --hash=sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3 \ - --hash=sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e \ - --hash=sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978 \ - --hash=sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98 \ - --hash=sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae \ - --hash=sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755 \ - --hash=sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d \ - --hash=sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991 \ - --hash=sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1 \ - --hash=sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680 \ - --hash=sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247 \ - --hash=sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f \ - --hash=sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2 \ - --hash=sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7 \ - --hash=sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4 \ - --hash=sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667 \ - --hash=sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb \ - --hash=sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094 \ - --hash=sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36 \ - --hash=sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79 \ - --hash=sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500 \ - --hash=sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e \ - --hash=sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582 \ - --hash=sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442 \ - --hash=sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd \ - --hash=sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6 \ - --hash=sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731 \ - --hash=sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4 \ - --hash=sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d \ - --hash=sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8 \ - --hash=sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f \ - --hash=sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677 \ - --hash=sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8 \ - --hash=sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9 \ - --hash=sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e \ - --hash=sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b \ - --hash=sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916 \ - --hash=sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4 +websockets==10.4 \ + --hash=sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41 \ + --hash=sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96 \ + --hash=sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4 \ + --hash=sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72 \ + --hash=sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576 \ + --hash=sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63 \ + --hash=sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b \ + --hash=sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d \ + --hash=sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032 \ + --hash=sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393 \ + --hash=sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50 \ + --hash=sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631 \ + --hash=sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f \ + --hash=sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c \ + --hash=sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6 \ + --hash=sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4 \ + --hash=sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6 \ + --hash=sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0 \ + --hash=sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8 \ + --hash=sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112 \ + --hash=sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94 \ + --hash=sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4 \ + --hash=sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb \ + --hash=sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331 \ + --hash=sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c \ + --hash=sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c \ + --hash=sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193 \ + --hash=sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b \ + --hash=sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b \ + --hash=sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038 \ + --hash=sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089 \ + --hash=sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa \ + --hash=sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9 \ + --hash=sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56 \ + --hash=sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4 \ + --hash=sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179 \ + --hash=sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c \ + --hash=sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882 \ + --hash=sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28 \ + --hash=sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1 \ + --hash=sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a \ + --hash=sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033 \ + --hash=sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1 \ + --hash=sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13 \ + --hash=sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8 \ + --hash=sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c \ + --hash=sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74 \ + --hash=sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab \ + --hash=sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3 \ + --hash=sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588 \ + --hash=sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485 \ + --hash=sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342 \ + --hash=sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48 \ + --hash=sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf \ + --hash=sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0 \ + --hash=sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a \ + --hash=sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea \ + --hash=sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf \ + --hash=sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8 \ + --hash=sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df \ + --hash=sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc \ + --hash=sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f \ + --hash=sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269 \ + --hash=sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3 \ + --hash=sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c \ + --hash=sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46 \ + --hash=sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f \ + --hash=sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106 \ + --hash=sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f # via uvicorn yarl==1.8.1 \ --hash=sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb \ From 6bff9baa876584f8f85e4e009879b3c4fb88124b Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 27 Oct 2022 12:54:49 -0700 Subject: [PATCH 5/5] Finalize change log for 7.0.0 release --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a871cea..555e59bd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Versioning follows [semver](https://semver.org/). Versioning assumes that Gafael Dependencies are updated to the latest available version during each release. Those changes are not noted here explicitly. -## 7.0.0 (unreleased) +## 7.0.0 (2022-10-27) ### Backwards-incompatible changes @@ -15,6 +15,10 @@ Dependencies are updated to the latest available version during each release. Th - While the Kubernetes operator is running, all `Secret` objects created from `GafaelfawrServiceToken` objects are checked for validity every half-hour and replaced if needed. +### Other changes + +- Drop types from docstrings where possible and take advantage of the new support in Sphinx for type annotations when rendering internal API documentation. This produces higher-quality output in many cases. + ## 6.2.0 (2022-10-13) ### New features