From 2fd16b575f61132499a73cb849ead9aa2893f299 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 13:32:42 -0400 Subject: [PATCH 01/25] adds support for `ClerkAuth` --- dash_auth_plus/__init__.py | 3 + dash_auth_plus/auth.py | 3 +- dash_auth_plus/clerk_auth.py | 542 +++++++++++++++++++++++++++++++++++ 3 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 dash_auth_plus/clerk_auth.py diff --git a/dash_auth_plus/__init__.py b/dash_auth_plus/__init__.py index e4bcae2..d2eb0d1 100644 --- a/dash_auth_plus/__init__.py +++ b/dash_auth_plus/__init__.py @@ -11,6 +11,7 @@ # oidc auth requires authlib, install with `pip install dash-auth-plus[oidc]` try: from .oidc_auth import OIDCAuth, get_oauth + from .clerk_auth import ClerkAuth, get_clerk_auth except ModuleNotFoundError: pass from .version import __version__, __plotly_dash_auth_version__ @@ -27,6 +28,8 @@ "public_callback", "BasicAuth", "OIDCAuth", + "ClerkAuth", + "get_clerk_auth", "__version__", "__plotly_dash_auth_version__", ] diff --git a/dash_auth_plus/auth.py b/dash_auth_plus/auth.py index d800bd6..d643974 100644 --- a/dash_auth_plus/auth.py +++ b/dash_auth_plus/auth.py @@ -3,7 +3,7 @@ from typing import Optional, Union from dash import Dash -from flask import request +from flask import request, session from .public_routes import ( add_public_routes, @@ -65,7 +65,6 @@ def _protect(self): @server.before_request def before_request_auth(): - public_routes = get_public_routes(self.app) public_callbacks = get_public_callbacks(self.app) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py new file mode 100644 index 0000000..1416d58 --- /dev/null +++ b/dash_auth_plus/clerk_auth.py @@ -0,0 +1,542 @@ +import logging +import os +import re +import traceback +from typing import Dict, List, Optional, Union, Callable, TYPE_CHECKING + +import dash +from authlib.integrations.base_client import OAuthError +from authlib.integrations.flask_client import OAuth +from dash_auth_plus.auth import Auth +from flask import Response, redirect, request, session, url_for, jsonify +from werkzeug.routing import Map, Rule +from dotenv import load_dotenv +from urllib.parse import urljoin, quote +load_dotenv() +from werkzeug.routing import MapAdapter + +if TYPE_CHECKING: + from authlib.integrations.flask_client.apps import FlaskOAuth1App, FlaskOAuth2App + +UserGroups = Dict[str, List[str]] + +# UI/UX Design tokens following best practices from the guide +DESIGN_TOKENS = { + # Colors following proper contrast ratios (WCAG AA) + 'colors': { + 'primary': '#0066cc', + 'primary_hover': '#0052a3', + 'danger': '#dc3545', + 'danger_hover': '#c82333', + 'text_primary': '#212529', # Not pure black as per guide + 'text_secondary': '#6c757d', + 'background': '#ffffff', + 'background_secondary': '#f8f9fa', + 'border': '#dee2e6', + 'shadow': 'rgba(0, 0, 0, 0.1)', # Soft shadows as recommended + }, + # Typography following the guide's recommendations + 'typography': { + 'font_family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + 'font_size_base': '14px', # Readable size + 'font_size_small': '12px', + 'font_weight_normal': '400', + 'font_weight_medium': '500', + 'font_weight_semibold': '600', + 'line_height': '1.5', + }, + # Spacing system using base unit of 4px as recommended + 'spacing': { + 'xs': '4px', + 'sm': '8px', + 'md': '12px', + 'lg': '16px', + 'xl': '24px', + }, + # Border radius for consistency + 'border_radius': { + 'sm': '4px', + 'md': '8px', + 'full': '50%', + }, + # Transitions for smooth interactions + 'transitions': { + 'fast': 'all 0.15s ease', + 'medium': 'all 0.2s ease', + } +} + + +class ClerkAuth(Auth): + """Implements auth via Clerk.""" + def __init__( + self, + app: dash.Dash, + secret_key: str = Optional[None], + force_https_callback: Optional[Union[bool, str]] = None, + clerk_secret_key: str = os.environ.get('CLERK_SECRET_KEY'), + clerk_domain: str = os.environ.get('CLERK_DOMAIN'), + clerk_publishable_key: str = os.environ.get('CLERK_PUBLISHABLE_KEY'), + allowed_parties: Optional[str] = os.environ.get('CLERK_ALLOWED_PARTIES'), + log_signins: bool = False, + public_routes: Optional[list] = None, + logout_page: Union[str, Response] = None, + secure_session: bool = False, + user_groups: Optional[Union[UserGroups, Callable[[str], List[str]]]] = None, + login_user_callback: Callable = None, + auth_protect_layouts: Optional[bool] = False, + auth_protect_layouts_kwargs: Optional[dict] = None, + page_container: Optional[str] = None, + ): + """Secure a Dash app through OpenID Connect. + + Parameters + ---------- + app : Dash + The Dash app to secure + secret_key : str, optional + A string to protect the Flask session, by default None. + Generate a secret key in your Python session + with the following commands: + >>> import os + >>> import base64 + >>> base64.b64encode(os.urandom(30)).decode('utf-8') + Note that you should not do this dynamically: + you should create a key and then assign the value of + that key in your code. + force_https_callback : Union[bool, str], optional + Whether to force redirection to https, by default None + This is useful when the HTTPS termination is upstream of the server + If a string is passed, this will check for the existence of + an envvar with that name and force https callback if it exists. + login_route : str, optional + The route for the login function, it requires a + placeholder, by default "/oidc//login". + logout_route : str, optional + The route for the logout function, by default "/oidc/logout". + callback_route : str, optional + The route for the OIDC redirect URI, it requires a + placeholder, by default "/oidc//callback". + log_signins : bool, optional + Whether to log signins, by default False + public_routes : list, optional + List of public routes, routes should follow the + Flask route syntax + logout_page : str or Response, optional + Page seen by the user after logging out, + by default None which will default to a simple logged out message + secure_session: bool, optional + Whether to ensure the session is secure, setting the flask config + SESSION_COOKIE_SECURE and SESSION_COOKIE_HTTPONLY to True, + by default False + user_groups: a dict or a function returning a dict + Optional group for each user, allowing to protect routes and + callbacks depending on user groups + login_user_callback: python function accepting two arguments + (userinfo, idp), where userinfo is normally a dict + (request form or results from the idp). + This must return a flask response or redirect. + :param auth_protect_layouts: bool, defaults to False. + If true, runs protect_layout() + :param auth_protect_layouts_kwargs: dict, if provided is passed to the + protect_layout as kwargs + :param page_container: string, id of the page container in the app. + If not provided, this will set the page_container_test to True, + meaning all pathname callbacks will be judged. + + Raises + ------ + Exception + Raise an exception if the app.server.secret_key is not defined + """ + super().__init__( + app, + public_routes=public_routes, + auth_protect_layouts=auth_protect_layouts, + auth_protect_layouts_kwargs=auth_protect_layouts_kwargs, + page_container=page_container, + ) + + try: + from clerk_backend_api import Clerk + from clerk_backend_api.jwks_helpers import AuthenticateRequestOptions + except ImportError: + raise ImportError( + "clerk-backend-api is required for dash-clerk-auth. " + "Install it with: pip install clerk-backend-api" + ) + + if isinstance(force_https_callback, str): + self.force_https_callback = force_https_callback in os.environ + elif force_https_callback is not None: + self.force_https_callback = force_https_callback + else: + self.force_https_callback = False + + self.initialized = False + self.clerk_secret_key = clerk_secret_key + self.clerk_domain = clerk_domain + self.clerk_publishable_key = clerk_publishable_key + self.log_signins = log_signins + self.logout_page = logout_page + self._user_groups = user_groups + self.login_user_callback = login_user_callback + self.login_route = '/login' + self.logout_route = '/logout' + self.authenticate_request_options = AuthenticateRequestOptions + self.allowed_parties = allowed_parties + self.callback_route = '/auth_callback' + + # Validate required configuration + if not self.clerk_secret_key: + raise ValueError("clerk_secret_key is required (set CLERK_SECRET_KEY env var)") + if not self.clerk_publishable_key: + raise ValueError("clerk_publishable_key is required (set CLERK_PUBLISHABLE_KEY env var)") + if not self.clerk_domain: + raise ValueError("clerk_domain is required (set CLERK_SIGN_IN_URL env var)") + + self.clerk_client = Clerk(bearer_auth=self.clerk_secret_key) + self.initialized = True + + if secret_key is not None: + app.server.secret_key = secret_key + + if app.server.secret_key is None: + raise RuntimeError( + """ + app.server.secret_key is missing. + Generate a secret key in your Python session + with the following commands: + >>> import os + >>> import base64 + >>> base64.b64encode(os.urandom(30)).decode('utf-8') + and assign it to the property app.server.secret_key + (where app is your dash app instance), or pass is as + the secret_key argument to OIDCAuth.__init__. + Note that you should not do this dynamically: + you should create a key and then assign the value of + that key in your code/via a secret. + """ + ) + + if secure_session: + app.server.config["SESSION_COOKIE_SECURE"] = True + app.server.config["SESSION_COOKIE_HTTPONLY"] = True + + self.oauth = OAuth(app.server) + + app.server.add_url_rule( + self.logout_route, + endpoint="oidc_logout", + view_func=self.logout, + methods=["GET"], + ) + + app.server.add_url_rule( + self.callback_route, + endpoint="oidc_callback", + view_func=self.check_clerk_auth, + methods=["GET", "POST"], + ) + + clerk_script = f''' + ''' + + # Enhanced initialization with smart auth checking + init_script = ''' + + ''' + + self.clerk_script = f'{clerk_script}\n{init_script}\n' + + if dash.__version__ >= "3.0": + # Use the new OAuth2App class for Dash 3+ + @dash.hooks.layout() + def append_clerk_url(layout): + return [dash.dcc.Location(id='_clerk_login_url', refresh=True), layout] + + @dash.hooks.index() + def add_clerk_script(index_string): + """Inject Clerk script into the HTML head""" + if not self.initialized: + return index_string + + + # Add enhanced styles for hover effects and transitions + enhanced_styles = f''' + + ''' + + if clerk_script and '' in index_string: + # Inject scripts and styles before closing head tag + index_string = index_string.replace('', + f'{self.clerk_script}\n{enhanced_styles}\n ') + + return index_string + else: + app.index_string = app.index_string.split('')[0] + clerk_script + '' + + def _create_redirect_uri(self): + """Create the redirect uri based on callback endpoint and idp.""" + kwargs = {"_external": True} + if self.force_https_callback: + kwargs["_scheme"] = "https" + + redirect_uri = urljoin(self.clerk_domain, '/sign-in?redirect_url=' + + quote(request.host_url[:-1] + self.callback_route, safe='')) + session['url'] = request.url if request.method == 'GET' else request.headers.get('referer', request.host_url) + if request.headers.get("X-Forwarded-Host"): + host = request.headers.get("X-Forwarded-Host") + redirect_uri = redirect_uri.replace(request.host, host, 1) + return redirect_uri + + def login_request(self): + """Start the login process.""" + if request.method == "POST": + return jsonify({ + 'multi': True, + 'sideUpdate': { + '_clerk_login_url': {'href': self._create_redirect_uri()} + } + }) + return redirect(self._create_redirect_uri()) + + def logout(self): # pylint: disable=C0116 + """Logout the user.""" + if 'user' in session: + try: + self.clerk_client.sessions.revoke( + session_id=session.get('user', {}).get('session_id', '') + ) + except: + print(traceback.format_exc()) + pass + session.clear() + response = Response( + self.logout_page + or f""" +
+
Logged out successfully
+ +
+ {self.clerk_script} + """, + mimetype="text/html" + ) + for cookie in request.cookies: + response.delete_cookie(cookie) + return response + + def after_logged_in(self, user: Optional[dict], sid): + """ + Post-login actions after successful OIDC authentication. + For example, allows to pass custom attributes to the user session: + class MyOIDCAuth(OIDCAuth): + def after_logged_in(self, user, idp, token): + if user: + user["params"] = value1 + return super().after_logged_in(user, idp, token) + """ + if self.login_user_callback: + return self.login_user_callback(user, 'clerk') + elif user: + email = [x.email_address for x in user.email_addresses + if x.id == user.primary_email_address_id][0] \ + if user.email_addresses else None + session['user'] = { + 'clerk_user_id': user.id, + 'userid': user.username, + 'email': email, + 'session_id': sid + } + if callable(self._user_groups): + session["user"]["groups"] = self._user_groups(user.get("email")) + ( + session["user"].get("groups") or [] + ) + elif self._user_groups: + session["user"]["groups"] = self._user_groups.get( + user.get("email"), [] + ) + (session["user"].get("groups") or []) + if self.log_signins: + logging.info("User %s is logging in.", session['user'].get("email")) + if session.get('url'): + url = session['url'] + del session['url'] + return redirect(url) + + + + def check_clerk_auth(self): + """Pulls Clerk user data from the request and stores it in the session.""" + try: + request_state = self.clerk_client.authenticate_request( + request, + self.authenticate_request_options( + authorized_parties=self.allowed_parties, + ) + ) + + if request_state.is_signed_in: + sid = request_state.payload.get('sid') + sess = self.clerk_client.sessions.get(session_id=sid) + user_data = self.clerk_client.users.get(user_id=sess.user_id) + return self.after_logged_in(user_data, sid) + except: + print(traceback.format_exc()) + pass ## outside the authenticate_request method is applicable in the context of Backend APIs only. + return f"""
logging in...
{self.clerk_script}""" + + def is_authorized(self): # pylint: disable=C0116 + """Check whether the user is authenticated.""" + + map_adapter = Map( + [ + Rule(x) + for x in [ + self.login_route, + self.logout_route, + self.callback_route + ] + if x + ] + ).bind("") + + if ( + "user" in session + or map_adapter.test(request.path) + or self.clerk_domain in request.url + or request.path.startswith('/.well-known/') + ): + return True + return False + + +def get_clerk_auth(app: dash.Dash = None) -> OAuth: + """Retrieve the Clerk object. + + :param app: dash.Dash + Dash app or None, if None the current app is used + calling `dash.get_app()` + """ + if app is None: + app = dash.get_app() + + auth = getattr(app.server, "extensions", {}).get( + "authlib.integrations.flask_client" + ) + if auth is not None: + return auth + + raise RuntimeError( + "Clerk object is not yet defined. `ClerkAuth(app, **kwargs)` needs " + "to be run before `get_clerk_auth` is called." + ) From 1c39d79b5f915a17404273fbdc7009c7ee85f434 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:02:43 -0400 Subject: [PATCH 02/25] fixing for lint --- dash_auth_plus/clerk_auth.py | 205 +++++++++++++++++++---------------- 1 file changed, 112 insertions(+), 93 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 1416d58..20e23df 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -12,6 +12,7 @@ from werkzeug.routing import Map, Rule from dotenv import load_dotenv from urllib.parse import urljoin, quote + load_dotenv() from werkzeug.routing import MapAdapter @@ -23,61 +24,62 @@ # UI/UX Design tokens following best practices from the guide DESIGN_TOKENS = { # Colors following proper contrast ratios (WCAG AA) - 'colors': { - 'primary': '#0066cc', - 'primary_hover': '#0052a3', - 'danger': '#dc3545', - 'danger_hover': '#c82333', - 'text_primary': '#212529', # Not pure black as per guide - 'text_secondary': '#6c757d', - 'background': '#ffffff', - 'background_secondary': '#f8f9fa', - 'border': '#dee2e6', - 'shadow': 'rgba(0, 0, 0, 0.1)', # Soft shadows as recommended + "colors": { + "primary": "#0066cc", + "primary_hover": "#0052a3", + "danger": "#dc3545", + "danger_hover": "#c82333", + "text_primary": "#212529", # Not pure black as per guide + "text_secondary": "#6c757d", + "background": "#ffffff", + "background_secondary": "#f8f9fa", + "border": "#dee2e6", + "shadow": "rgba(0, 0, 0, 0.1)", # Soft shadows as recommended }, # Typography following the guide's recommendations - 'typography': { - 'font_family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', - 'font_size_base': '14px', # Readable size - 'font_size_small': '12px', - 'font_weight_normal': '400', - 'font_weight_medium': '500', - 'font_weight_semibold': '600', - 'line_height': '1.5', + "typography": { + "font_family": '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + "font_size_base": "14px", # Readable size + "font_size_small": "12px", + "font_weight_normal": "400", + "font_weight_medium": "500", + "font_weight_semibold": "600", + "line_height": "1.5", }, # Spacing system using base unit of 4px as recommended - 'spacing': { - 'xs': '4px', - 'sm': '8px', - 'md': '12px', - 'lg': '16px', - 'xl': '24px', + "spacing": { + "xs": "4px", + "sm": "8px", + "md": "12px", + "lg": "16px", + "xl": "24px", }, # Border radius for consistency - 'border_radius': { - 'sm': '4px', - 'md': '8px', - 'full': '50%', + "border_radius": { + "sm": "4px", + "md": "8px", + "full": "50%", }, # Transitions for smooth interactions - 'transitions': { - 'fast': 'all 0.15s ease', - 'medium': 'all 0.2s ease', - } + "transitions": { + "fast": "all 0.15s ease", + "medium": "all 0.2s ease", + }, } class ClerkAuth(Auth): """Implements auth via Clerk.""" + def __init__( self, app: dash.Dash, secret_key: str = Optional[None], force_https_callback: Optional[Union[bool, str]] = None, - clerk_secret_key: str = os.environ.get('CLERK_SECRET_KEY'), - clerk_domain: str = os.environ.get('CLERK_DOMAIN'), - clerk_publishable_key: str = os.environ.get('CLERK_PUBLISHABLE_KEY'), - allowed_parties: Optional[str] = os.environ.get('CLERK_ALLOWED_PARTIES'), + clerk_secret_key: str = os.environ.get("CLERK_SECRET_KEY"), + clerk_domain: str = os.environ.get("CLERK_DOMAIN"), + clerk_publishable_key: str = os.environ.get("CLERK_PUBLISHABLE_KEY"), + allowed_parties: Optional[str] = os.environ.get("CLERK_ALLOWED_PARTIES"), log_signins: bool = False, public_routes: Optional[list] = None, logout_page: Union[str, Response] = None, @@ -181,17 +183,21 @@ def __init__( self.logout_page = logout_page self._user_groups = user_groups self.login_user_callback = login_user_callback - self.login_route = '/login' - self.logout_route = '/logout' + self.login_route = "/login" + self.logout_route = "/logout" self.authenticate_request_options = AuthenticateRequestOptions self.allowed_parties = allowed_parties - self.callback_route = '/auth_callback' + self.callback_route = "/auth_callback" # Validate required configuration if not self.clerk_secret_key: - raise ValueError("clerk_secret_key is required (set CLERK_SECRET_KEY env var)") + raise ValueError( + "clerk_secret_key is required (set CLERK_SECRET_KEY env var)" + ) if not self.clerk_publishable_key: - raise ValueError("clerk_publishable_key is required (set CLERK_PUBLISHABLE_KEY env var)") + raise ValueError( + "clerk_publishable_key is required (set CLERK_PUBLISHABLE_KEY env var)" + ) if not self.clerk_domain: raise ValueError("clerk_domain is required (set CLERK_SIGN_IN_URL env var)") @@ -239,17 +245,17 @@ def __init__( methods=["GET", "POST"], ) - clerk_script = f''' + clerk_script = f""" ''' + """ # Enhanced initialization with smart auth checking - init_script = ''' + init_script = """ - ''' + """ - self.clerk_script = f'{clerk_script}\n{init_script}\n' + self.clerk_script = f"{clerk_script}\n{init_script}\n" if dash.__version__ >= "3.0": # Use the new OAuth2App class for Dash 3+ @dash.hooks.layout() def append_clerk_url(layout): - return [dash.dcc.Location(id='_clerk_login_url', refresh=True), layout] + return [dash.dcc.Location(id="_clerk_login_url", refresh=True), layout] @dash.hooks.index() def add_clerk_script(index_string): @@ -329,9 +335,8 @@ def add_clerk_script(index_string): if not self.initialized: return index_string - # Add enhanced styles for hover effects and transitions - enhanced_styles = f''' + enhanced_styles = f""" - ''' + """ - if clerk_script and '' in index_string: + if clerk_script and "" in index_string: # Inject scripts and styles before closing head tag - index_string = index_string.replace('', - f'{self.clerk_script}\n{enhanced_styles}\n ') + index_string = index_string.replace( + "", + f"{self.clerk_script}\n{enhanced_styles}\n ", + ) return index_string + else: - app.index_string = app.index_string.split('')[0] + clerk_script + '' + app.index_string = ( + app.index_string.split("")[0] + clerk_script + "" + ) def _create_redirect_uri(self): """Create the redirect uri based on callback endpoint and idp.""" @@ -390,9 +400,16 @@ def _create_redirect_uri(self): if self.force_https_callback: kwargs["_scheme"] = "https" - redirect_uri = urljoin(self.clerk_domain, '/sign-in?redirect_url=' + - quote(request.host_url[:-1] + self.callback_route, safe='')) - session['url'] = request.url if request.method == 'GET' else request.headers.get('referer', request.host_url) + redirect_uri = urljoin( + self.clerk_domain, + "/sign-in?redirect_url=" + + quote(request.host_url[:-1] + self.callback_route, safe=""), + ) + session["url"] = ( + request.url + if request.method == "GET" + else request.headers.get("referer", request.host_url) + ) if request.headers.get("X-Forwarded-Host"): host = request.headers.get("X-Forwarded-Host") redirect_uri = redirect_uri.replace(request.host, host, 1) @@ -401,20 +418,22 @@ def _create_redirect_uri(self): def login_request(self): """Start the login process.""" if request.method == "POST": - return jsonify({ - 'multi': True, - 'sideUpdate': { - '_clerk_login_url': {'href': self._create_redirect_uri()} + return jsonify( + { + "multi": True, + "sideUpdate": { + "_clerk_login_url": {"href": self._create_redirect_uri()} + }, } - }) + ) return redirect(self._create_redirect_uri()) def logout(self): # pylint: disable=C0116 """Logout the user.""" - if 'user' in session: + if "user" in session: try: self.clerk_client.sessions.revoke( - session_id=session.get('user', {}).get('session_id', '') + session_id=session.get("user", {}).get("session_id", "") ) except: print(traceback.format_exc()) @@ -430,7 +449,7 @@ def logout(self): # pylint: disable=C0116 {self.clerk_script} """, - mimetype="text/html" + mimetype="text/html", ) for cookie in request.cookies: response.delete_cookie(cookie) @@ -447,16 +466,22 @@ def after_logged_in(self, user, idp, token): return super().after_logged_in(user, idp, token) """ if self.login_user_callback: - return self.login_user_callback(user, 'clerk') + return self.login_user_callback(user, "clerk") elif user: - email = [x.email_address for x in user.email_addresses - if x.id == user.primary_email_address_id][0] \ - if user.email_addresses else None - session['user'] = { - 'clerk_user_id': user.id, - 'userid': user.username, - 'email': email, - 'session_id': sid + email = ( + [ + x.email_address + for x in user.email_addresses + if x.id == user.primary_email_address_id + ][0] + if user.email_addresses + else None + ) + session["user"] = { + "clerk_user_id": user.id, + "userid": user.username, + "email": email, + "session_id": sid, } if callable(self._user_groups): session["user"]["groups"] = self._user_groups(user.get("email")) + ( @@ -467,14 +492,12 @@ def after_logged_in(self, user, idp, token): user.get("email"), [] ) + (session["user"].get("groups") or []) if self.log_signins: - logging.info("User %s is logging in.", session['user'].get("email")) - if session.get('url'): - url = session['url'] - del session['url'] + logging.info("User %s is logging in.", session["user"].get("email")) + if session.get("url"): + url = session["url"] + del session["url"] return redirect(url) - - def check_clerk_auth(self): """Pulls Clerk user data from the request and stores it in the session.""" try: @@ -482,17 +505,17 @@ def check_clerk_auth(self): request, self.authenticate_request_options( authorized_parties=self.allowed_parties, - ) + ), ) if request_state.is_signed_in: - sid = request_state.payload.get('sid') + sid = request_state.payload.get("sid") sess = self.clerk_client.sessions.get(session_id=sid) user_data = self.clerk_client.users.get(user_id=sess.user_id) return self.after_logged_in(user_data, sid) except: print(traceback.format_exc()) - pass ## outside the authenticate_request method is applicable in the context of Backend APIs only. + pass ## outside the authenticate_request method is applicable in the context of Backend APIs only. return f"""
logging in...
{self.clerk_script}""" def is_authorized(self): # pylint: disable=C0116 @@ -501,20 +524,16 @@ def is_authorized(self): # pylint: disable=C0116 map_adapter = Map( [ Rule(x) - for x in [ - self.login_route, - self.logout_route, - self.callback_route - ] + for x in [self.login_route, self.logout_route, self.callback_route] if x ] ).bind("") if ( - "user" in session - or map_adapter.test(request.path) - or self.clerk_domain in request.url - or request.path.startswith('/.well-known/') + "user" in session + or map_adapter.test(request.path) + or self.clerk_domain in request.url + or request.path.startswith("/.well-known/") ): return True return False From f656ecc6bca70112a286035a10495098fa6507fb Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:10:15 -0400 Subject: [PATCH 03/25] adjustments for lint --- dash_auth_plus/auth.py | 2 +- dash_auth_plus/clerk_auth.py | 51 +++++++++++++++--------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/dash_auth_plus/auth.py b/dash_auth_plus/auth.py index d643974..6d4ec57 100644 --- a/dash_auth_plus/auth.py +++ b/dash_auth_plus/auth.py @@ -3,7 +3,7 @@ from typing import Optional, Union from dash import Dash -from flask import request, session +from flask import request from .public_routes import ( add_public_routes, diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 20e23df..9c965ad 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -1,23 +1,17 @@ import logging import os -import re import traceback -from typing import Dict, List, Optional, Union, Callable, TYPE_CHECKING +from typing import Dict, List, Optional, Union, Callable import dash -from authlib.integrations.base_client import OAuthError from authlib.integrations.flask_client import OAuth from dash_auth_plus.auth import Auth -from flask import Response, redirect, request, session, url_for, jsonify +from flask import Response, redirect, request, session, jsonify from werkzeug.routing import Map, Rule from dotenv import load_dotenv from urllib.parse import urljoin, quote load_dotenv() -from werkzeug.routing import MapAdapter - -if TYPE_CHECKING: - from authlib.integrations.flask_client.apps import FlaskOAuth1App, FlaskOAuth2App UserGroups = Dict[str, List[str]] @@ -364,15 +358,15 @@ def add_clerk_script(index_string): }} /* Button hover effects */ - #clerk-login-button:hover, - #clerk-profile-button:hover, + #clerk-login-button:hover, + #clerk-profile-button:hover, #clerk-logout-menu-item:hover {{ transform: translateY(-1px); box-shadow: 0 2px 8px {DESIGN_TOKENS['colors']['shadow']}; }} - #clerk-login-button:active, - #clerk-profile-button:active, + #clerk-login-button:active, + #clerk-profile-button:active, #clerk-logout-menu-item:active {{ transform: translateY(0); box-shadow: 0 1px 4px {DESIGN_TOKENS['colors']['shadow']}; @@ -435,9 +429,10 @@ def logout(self): # pylint: disable=C0116 self.clerk_client.sessions.revoke( session_id=session.get("user", {}).get("session_id", "") ) - except: - print(traceback.format_exc()) - pass + except Exception as e: + logging.error( + "Failed to revoke Clerk session: %s\n%s", e, traceback.format_exc() + ) session.clear() response = Response( self.logout_page @@ -500,22 +495,18 @@ def after_logged_in(self, user, idp, token): def check_clerk_auth(self): """Pulls Clerk user data from the request and stores it in the session.""" - try: - request_state = self.clerk_client.authenticate_request( - request, - self.authenticate_request_options( - authorized_parties=self.allowed_parties, - ), - ) + request_state = self.clerk_client.authenticate_request( + request, + self.authenticate_request_options( + authorized_parties=self.allowed_parties, + ), + ) - if request_state.is_signed_in: - sid = request_state.payload.get("sid") - sess = self.clerk_client.sessions.get(session_id=sid) - user_data = self.clerk_client.users.get(user_id=sess.user_id) - return self.after_logged_in(user_data, sid) - except: - print(traceback.format_exc()) - pass ## outside the authenticate_request method is applicable in the context of Backend APIs only. + if request_state.is_signed_in: + sid = request_state.payload.get("sid") + sess = self.clerk_client.sessions.get(session_id=sid) + user_data = self.clerk_client.users.get(user_id=sess.user_id) + return self.after_logged_in(user_data, sid) return f"""
logging in...
{self.clerk_script}""" def is_authorized(self): # pylint: disable=C0116 From 8cb6cec0e5287f4b7620efee8a6836135a599e60 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:09:07 -0400 Subject: [PATCH 04/25] adding test workflow as well as adding the hosted domain of the app as an allowed party --- .github/workflows/python-test.yml | 2 + README.md | 74 ++++++++++++++++++++- dash_auth_plus/clerk_auth.py | 7 +- tests/test_clerk_auth.py | 104 ++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 tests/test_clerk_auth.py diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 3d25f82..c5de365 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -14,6 +14,8 @@ on: jobs: test: runs-on: ubuntu-latest + environment: + name: test strategy: matrix: python-version: ["3.8", "3.12"] diff --git a/README.md b/README.md index 468552a..a194941 100644 --- a/README.md +++ b/README.md @@ -489,4 +489,76 @@ not protect the layouts of public routes. Passing `auth_protect_layouts_kwargs` is the same are the additional `kwargs` passed to the function By default, the app will check any non-public callback that has the `pathname` as an input, when you pass `page_container` as the `id` of your container element for a page container, -it will only check the route if it is an output. \ No newline at end of file +it will only check the route if it is an output. + +## Example Usage with ClerkAuth + +```python +from dash import Dash, html, dcc, page_container +from dash_auth_plus import ClerkAuth, public_callback +from dash import Input, Output, register_page + +app = Dash(__name__, use_pages=True, pages_folder='', suppress_callback_exceptions=True) + +# Initialize ClerkAuth with public routes +auth = ClerkAuth( + app, + secret_key="aStaticSecretKey!", + log_signins=True, + auth_protect_layouts=True, + page_container='_pages_content', + public_routes=['/', '/user//public'], +) + +# Main layout with navigation +app.layout = html.Div( + [ + html.Div( + [ + dcc.Link("Home", href="/"), + dcc.Link("John Doe", href="/user/john_doe/public"), + dcc.Link('Logout', href='/logout', refresh=True), + ], + style={"display": "flex", "gap": "1rem", "background": "lightgray", "padding": "0.5rem 1rem"}, + ), + page_container, + ], + style={"display": "flex", "flexDirection": "column"}, +) + +# Home page (public) +home_layout = [ + html.H1("Home Page"), + html.Button("Click me", id="home-button"), + html.Div(id="home-contents"), +] +register_page('home', "/", layout=home_layout) + +@public_callback( + Output("home-contents", "children"), + Input("home-button", "n_clicks"), +) +def home(n_clicks): + if not n_clicks: + return "You haven't clicked the button." + return f"You clicked the button {n_clicks} times" + +# Public user page +def user_layout(user_id: str, **kwargs): + return [ + html.H1(f"User {user_id} (public)"), + dcc.Link("Authenticated user content", href=f"/user/{user_id}/private"), + ] +register_page('user', path_template="/user//public", layout=user_layout) + +# Private user page (protected) +def user_private(user_id: str, **kwargs): + return [ + html.H1(f"User {user_id} (authenticated only)"), + html.Div("Members-only information"), + ] +register_page('private', path_template="/user/{user_id}/private", layout=user_private) + +if __name__ == "__main__": + app.run(debug=True) +``` \ No newline at end of file diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 9c965ad..841ae4a 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -73,7 +73,7 @@ def __init__( clerk_secret_key: str = os.environ.get("CLERK_SECRET_KEY"), clerk_domain: str = os.environ.get("CLERK_DOMAIN"), clerk_publishable_key: str = os.environ.get("CLERK_PUBLISHABLE_KEY"), - allowed_parties: Optional[str] = os.environ.get("CLERK_ALLOWED_PARTIES"), + allowed_parties: Optional[List[str]] = os.environ.get("CLERK_ALLOWED_PARTIES", "").split(",") if os.environ.get("CLERK_ALLOWED_PARTIES") else [], log_signins: bool = False, public_routes: Optional[list] = None, logout_page: Union[str, Response] = None, @@ -180,7 +180,9 @@ def __init__( self.login_route = "/login" self.logout_route = "/logout" self.authenticate_request_options = AuthenticateRequestOptions - self.allowed_parties = allowed_parties + host = app.server.config.get("SERVER_NAME", "127.0.0.1") + port = app.server.config.get("SERVER_PORT", 8050) + self.allowed_parties = allowed_parties.append(f"http://{host}:{port}/") if allowed_parties else [f"http://{host}:{port}/"] self.callback_route = "/auth_callback" # Validate required configuration @@ -442,7 +444,6 @@ def logout(self): # pylint: disable=C0116
Logged out successfully
- {self.clerk_script} """, mimetype="text/html", ) diff --git a/tests/test_clerk_auth.py b/tests/test_clerk_auth.py new file mode 100644 index 0000000..4b6095a --- /dev/null +++ b/tests/test_clerk_auth.py @@ -0,0 +1,104 @@ +from selenium.webdriver.common.keys import Keys +from dash import Dash, html, dcc, page_container +from dash_auth_plus import ClerkAuth +import os + +app = Dash(__name__, use_pages=True, pages_folder='', suppress_callback_exceptions=True) + +auth = ClerkAuth(app, secret_key="aStaticSecretKey!", log_signins=True, + auth_protect_layouts=True, page_container='_pages_content', public_routes=['/', '/user//public']) + +app.layout = html.Div( + [ + html.Div( + [ + dcc.Link("Home", href="/"), + dcc.Link("John Doe", href="/user/john_doe/public"), + dcc.Link('Logout', href='/logout', refresh=True), + ], + style={"display": "flex", "gap": "1rem", "background": "lightgray", "padding": "0.5rem 1rem"}, + ), + page_container, + ], + style={"display": "flex", "flexDirection": "column"}, +) + +from dash import Input, Output, html, register_page +from dash_auth_plus import public_callback + +home_layout = [ + html.H1("Home Page"), + html.Button("Click me", id="home-button"), + html.Div(id="home-contents"), +] + +register_page('home',"/", layout=home_layout) + +# Note the use of public callback here rather than the default Dash callback +@public_callback( + Output("home-contents", "children"), + Input("home-button", "n_clicks"), +) +def home(n_clicks): + if not n_clicks: + return "You haven't clicked the button." + return "You clicked the button {} times".format(n_clicks) + +from dash import html, dcc, register_page + +def user_layout(user_id: str, **kwargs): + return [ + html.H1(f"User {user_id} (public)"), + dcc.Link("Authenticated user content", href=f"/user/{user_id}/private"), + ] + +register_page('user', path_template="/user//public", layout=user_layout) + + + +from dash import html, register_page + +def user_private(user_id: str, **kwargs): + return [ + html.H1(f"User {user_id} (authenticated only)"), + html.Div("Members-only information"), + ] + +register_page('private', path_template="/user//private", layout=user_private) + +def test_clerk_auth_flow(dash_duo): + dash_duo.start_server(app) + + # Go to the home page (public) + dash_duo.wait_for_text_to_equal("h1", "Home Page", timeout=10) + + # Navigate to the protected page + dash_duo.driver.get(dash_duo.server_url + "/user/john_doe/private") + + # Should be redirected to Clerk login (look for a known Clerk element or URL) + assert "clerk" in dash_duo.driver.page_source.lower() or "sign-in" in dash_duo.driver.current_url + + dash_duo.wait_for_text_to_equal("form button.cl-formButtonPrimary", "Continue") + + dash_duo.find_element("#identifier-field").send_keys(os.getenv('CLERK_TEST_USER')) + dash_duo.find_element("#identifier-field").send_keys(Keys.RETURN) + + dash_duo.wait_for_text_to_equal("form a.cl-formFieldAction", "Forgot password?") + dash_duo.find_element("#password-field").send_keys(os.getenv('CLERK_TEST_PASSWORD')) + dash_duo.find_element("#password-field").send_keys(Keys.RETURN) + + dash_duo.wait_for_text_to_equal("body", "logging in...") + dash_duo.wait_for_text_to_equal("#_pages_content h1", "User john_doe (authenticated only)") + + dash_duo.find_element('a[href="/logout"]').click() + dash_duo.wait_for_text_to_equal("a[href='/']", "Go back") + dash_duo.find_element("a[href='/']").click() + + # Go to the home page (public) + dash_duo.wait_for_text_to_equal("h1", "Home Page", timeout=10) + + # Navigate to the protected page + dash_duo.driver.get(dash_duo.server_url + "/user/john_doe/private") + + # Should be redirected to Clerk login (look for a known Clerk element or URL) + assert "clerk" in dash_duo.driver.page_source.lower() or "sign-in" in dash_duo.driver.current_url \ No newline at end of file From be31cc6c44640b20a1b889f7ceb93332d7f9d10f Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:09:54 -0400 Subject: [PATCH 05/25] fixing for lint --- dash_auth_plus/clerk_auth.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 841ae4a..c8e7a33 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -73,7 +73,11 @@ def __init__( clerk_secret_key: str = os.environ.get("CLERK_SECRET_KEY"), clerk_domain: str = os.environ.get("CLERK_DOMAIN"), clerk_publishable_key: str = os.environ.get("CLERK_PUBLISHABLE_KEY"), - allowed_parties: Optional[List[str]] = os.environ.get("CLERK_ALLOWED_PARTIES", "").split(",") if os.environ.get("CLERK_ALLOWED_PARTIES") else [], + allowed_parties: Optional[List[str]] = ( + os.environ.get("CLERK_ALLOWED_PARTIES", "").split(",") + if os.environ.get("CLERK_ALLOWED_PARTIES") + else [] + ), log_signins: bool = False, public_routes: Optional[list] = None, logout_page: Union[str, Response] = None, @@ -182,7 +186,11 @@ def __init__( self.authenticate_request_options = AuthenticateRequestOptions host = app.server.config.get("SERVER_NAME", "127.0.0.1") port = app.server.config.get("SERVER_PORT", 8050) - self.allowed_parties = allowed_parties.append(f"http://{host}:{port}/") if allowed_parties else [f"http://{host}:{port}/"] + self.allowed_parties = ( + allowed_parties.append(f"http://{host}:{port}/") + if allowed_parties + else [f"http://{host}:{port}/"] + ) self.callback_route = "/auth_callback" # Validate required configuration From 2541ac9b7b79631ece7e1b29dfe5bda5d4f40070 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:18:55 -0400 Subject: [PATCH 06/25] adjustments for missing imports --- dev-requirements.txt | 3 ++- setup.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 95075ec..7564640 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,4 +5,5 @@ flask werkzeug pytest authlib -black \ No newline at end of file +black +clerk-sdk \ No newline at end of file diff --git a/setup.py b/setup.py index ce1ffa2..5ba8c0a 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,8 @@ ], extras_require={ "oidc": ["authlib"], + "clerk": ["clerk-sdk"], + "all": ["authlib", "clerk-sdk"] }, python_requires=">=3.8", include_package_data=True, From 19dc9eae80ced4232a8f7f6e6eccafab0a158507 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:20:29 -0400 Subject: [PATCH 07/25] bump to v3.9 python for test --- .github/workflows/python-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index c5de365..70740cd 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -18,7 +18,7 @@ jobs: name: test strategy: matrix: - python-version: ["3.8", "3.12"] + python-version: ["3.9", "3.12"] steps: - name: Checkout code uses: actions/checkout@v3 From 1a78d283e762bfb335817e290dfa8e48760c25b4 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:25:03 -0400 Subject: [PATCH 08/25] locking version for backend-api to 3.0.1 --- dev-requirements.txt | 3 ++- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 7564640..5a3bd54 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -6,4 +6,5 @@ werkzeug pytest authlib black -clerk-sdk \ No newline at end of file +clerk-sdk +clerk-backend-api==3.0.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 5ba8c0a..b183747 100644 --- a/setup.py +++ b/setup.py @@ -24,8 +24,8 @@ ], extras_require={ "oidc": ["authlib"], - "clerk": ["clerk-sdk"], - "all": ["authlib", "clerk-sdk"] + "clerk": ["clerk-sdk", "clerk-backend-api==3.0.1"], + "all": ["authlib", "clerk-sdk", "clerk-backend-api==3.0.1"] }, python_requires=">=3.8", include_package_data=True, From 1cccdf32baf31aa750aa224c55cc0d2744c890dc Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:26:56 -0400 Subject: [PATCH 09/25] bump to 3.10 --- .github/workflows/python-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 70740cd..f0cfb4f 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -18,7 +18,7 @@ jobs: name: test strategy: matrix: - python-version: ["3.9", "3.12"] + python-version: ["3.10", "3.12"] steps: - name: Checkout code uses: actions/checkout@v3 From f5a326c80ef13a3dfcb88ac022a4b094680cee88 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:33:18 -0400 Subject: [PATCH 10/25] adding missing imports --- dev-requirements.txt | 3 ++- setup.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 5a3bd54..3ac1ed0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,4 +7,5 @@ pytest authlib black clerk-sdk -clerk-backend-api==3.0.1 \ No newline at end of file +clerk-backend-api==3.0.1 +dotenv \ No newline at end of file diff --git a/setup.py b/setup.py index b183747..ca66840 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ "dash>=1.1.1", "flask", "werkzeug", + "dotenv" ], extras_require={ "oidc": ["authlib"], From e1aca301bdbfe4a40492f65181302f100f16a540 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:40:36 -0400 Subject: [PATCH 11/25] adjusting for test variables --- .github/workflows/python-test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index f0cfb4f..78c7260 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -14,6 +14,7 @@ on: jobs: test: runs-on: ubuntu-latest + environment: name: test strategy: @@ -56,6 +57,12 @@ jobs: npm run lint - name: Run tests + env: + CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }} + CLERK_DOMAIN: ${{ secrets.CLERK_DOMAIN }} + CLERK_PUBLISHABLE_KEY: ${{ secrets.CLERK_PUBLISHABLE_KEY }} + CLERK_TEST_USER: ${{ secrets.CLERK_TEST_USER }} + CLERK_TEST_PASSWORD: ${{ secrets.CLERK_TEST_PASSWORD }} run: | source .venv/bin/activate pytest --headless \ No newline at end of file From 289629246a6196791081ead21bfc42c98e8e367c Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:49:30 -0400 Subject: [PATCH 12/25] fixing issue with auto allowed_parties --- dash_auth_plus/clerk_auth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index c8e7a33..8a85f7b 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -184,12 +184,12 @@ def __init__( self.login_route = "/login" self.logout_route = "/logout" self.authenticate_request_options = AuthenticateRequestOptions - host = app.server.config.get("SERVER_NAME", "127.0.0.1") + host = app.server.config.get("SERVER_NAME") or "127.0.0.1" port = app.server.config.get("SERVER_PORT", 8050) self.allowed_parties = ( - allowed_parties.append(f"http://{host}:{port}/") + allowed_parties + [f"http://{host}:{port}", f"http://localhost:{port}"] if allowed_parties - else [f"http://{host}:{port}/"] + else [f"http://{host}:{port}", f"http://localhost:{port}"] ) self.callback_route = "/auth_callback" From 6c98ef33917635f1bc4d093359a553381bd6a3cb Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:04:50 -0400 Subject: [PATCH 13/25] fixing issue with missing `allowed_parties` for test environment --- dash_auth_plus/clerk_auth.py | 13 ++++- tests/test_clerk_auth.py | 108 ++++++++++++++++++----------------- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 8a85f7b..cf1c5d1 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -187,9 +187,18 @@ def __init__( host = app.server.config.get("SERVER_NAME") or "127.0.0.1" port = app.server.config.get("SERVER_PORT", 8050) self.allowed_parties = ( - allowed_parties + [f"http://{host}:{port}", f"http://localhost:{port}"] + allowed_parties + + [ + f"http://{host}:{port}", + f"http://localhost:{port}", + f"https://localhost:{port}", + ] if allowed_parties - else [f"http://{host}:{port}", f"http://localhost:{port}"] + else [ + f"http://{host}:{port}", + f"http://localhost:{port}", + f"https://localhost:{port}", + ] ) self.callback_route = "/auth_callback" diff --git a/tests/test_clerk_auth.py b/tests/test_clerk_auth.py index 4b6095a..be99fa7 100644 --- a/tests/test_clerk_auth.py +++ b/tests/test_clerk_auth.py @@ -3,71 +3,77 @@ from dash_auth_plus import ClerkAuth import os -app = Dash(__name__, use_pages=True, pages_folder='', suppress_callback_exceptions=True) +def spinup_app(): + from dash import Dash, html, dcc, page_container + app = Dash(__name__, use_pages=True, pages_folder='', suppress_callback_exceptions=True) -auth = ClerkAuth(app, secret_key="aStaticSecretKey!", log_signins=True, - auth_protect_layouts=True, page_container='_pages_content', public_routes=['/', '/user//public']) + auth = ClerkAuth(app, secret_key="aStaticSecretKey!", log_signins=True, + public_routes=['/', '/user//public']) -app.layout = html.Div( - [ - html.Div( + app.layout = html.Div( [ - dcc.Link("Home", href="/"), - dcc.Link("John Doe", href="/user/john_doe/public"), - dcc.Link('Logout', href='/logout', refresh=True), + html.Div( + [ + dcc.Link("Home", href="/"), + dcc.Link("John Doe", href="/user/john_doe/public"), + dcc.Link('Logout', href='/logout', refresh=True), + ], + style={"display": "flex", "gap": "1rem", "background": "lightgray", "padding": "0.5rem 1rem"}, + ), + page_container, ], - style={"display": "flex", "gap": "1rem", "background": "lightgray", "padding": "0.5rem 1rem"}, - ), - page_container, - ], - style={"display": "flex", "flexDirection": "column"}, -) - -from dash import Input, Output, html, register_page -from dash_auth_plus import public_callback - -home_layout = [ - html.H1("Home Page"), - html.Button("Click me", id="home-button"), - html.Div(id="home-contents"), -] - -register_page('home',"/", layout=home_layout) - -# Note the use of public callback here rather than the default Dash callback -@public_callback( - Output("home-contents", "children"), - Input("home-button", "n_clicks"), -) -def home(n_clicks): - if not n_clicks: - return "You haven't clicked the button." - return "You clicked the button {} times".format(n_clicks) - -from dash import html, dcc, register_page - -def user_layout(user_id: str, **kwargs): - return [ - html.H1(f"User {user_id} (public)"), - dcc.Link("Authenticated user content", href=f"/user/{user_id}/private"), + style={"display": "flex", "flexDirection": "column"}, + ) + + from dash import Input, Output, html, register_page + from dash_auth_plus import public_callback + + home_layout = [ + html.H1("Home Page"), + html.Button("Click me", id="home-button"), + html.Div(id="home-contents"), ] -register_page('user', path_template="/user//public", layout=user_layout) + register_page('home',"/", layout=home_layout) + # Note the use of public callback here rather than the default Dash callback + @public_callback( + Output("home-contents", "children"), + Input("home-button", "n_clicks"), + ) + def home(n_clicks): + if not n_clicks: + return "You haven't clicked the button." + return "You clicked the button {} times".format(n_clicks) + from dash import html, dcc, register_page -from dash import html, register_page + def user_layout(user_id: str, **kwargs): + return [ + html.H1(f"User {user_id} (public)"), + dcc.Link("Authenticated user content", href=f"/user/{user_id}/private"), + ] + + register_page('user', path_template="/user//public", layout=user_layout) -def user_private(user_id: str, **kwargs): - return [ - html.H1(f"User {user_id} (authenticated only)"), - html.Div("Members-only information"), - ] -register_page('private', path_template="/user//private", layout=user_private) + + from dash import html, register_page + + def user_private(user_id: str, **kwargs): + return [ + html.H1(f"User {user_id} (authenticated only)"), + html.Div("Members-only information"), + ] + + register_page('private', path_template="/user//private", layout=user_private) + return app, auth def test_clerk_auth_flow(dash_duo): + app, auth = spinup_app() + dash_duo.start_server(app) + auth.allowed_parties = [dash_duo.server_url] # Go to the home page (public) dash_duo.wait_for_text_to_equal("h1", "Home Page", timeout=10) From 33ef9b2ec67f12fb067cd914d1deac39c52bf3ea Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:10:29 -0400 Subject: [PATCH 14/25] `dotenv` -> `python-dotenv` --- dev-requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 3ac1ed0..865041b 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -8,4 +8,4 @@ authlib black clerk-sdk clerk-backend-api==3.0.1 -dotenv \ No newline at end of file +python-dotenv \ No newline at end of file diff --git a/setup.py b/setup.py index ca66840..3184dca 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ "dash>=1.1.1", "flask", "werkzeug", - "dotenv" + "python-dotenv" ], extras_require={ "oidc": ["authlib"], From a13739a4babd10308f5afe6293deec6261393b1d Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:42:22 -0400 Subject: [PATCH 15/25] adding flow for logging out user if they are forced out of clerk remotely --- dash_auth_plus/clerk_auth.py | 62 ++++++++---------------------------- 1 file changed, 13 insertions(+), 49 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index cf1c5d1..6f6c180 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -281,13 +281,13 @@ def __init__( // CRITICAL: Always call load() to ensure Clerk initializes properly window.Clerk.load().then(() => { - console.log('[Clerk] Ready'); // Set up session sync listener if (window.Clerk.addListener) { window.Clerk.addListener((resources) => { // Store auth state in localStorage for persistence if (resources.user && resources.session) { + window._clerk_logged_in = true; if (window.location.pathname === '/auth_callback') { fetch(window.location.pathname, { method: 'POST', @@ -303,6 +303,14 @@ def __init__( }); } } + else if (window._clerk_logged_in) { + window._clerk_logged_in = false; + console.log('session ended, logging out'); + """ + f"""window.location.pathname = '{self.logout_route}';""" + """ + } + else { + window._clerk_logged_in = false; + } }); } @@ -348,63 +356,19 @@ def add_clerk_script(index_string): if not self.initialized: return index_string - # Add enhanced styles for hover effects and transitions - enhanced_styles = f""" - - """ - if clerk_script and "" in index_string: # Inject scripts and styles before closing head tag index_string = index_string.replace( "", - f"{self.clerk_script}\n{enhanced_styles}\n ", + f"{self.clerk_script}\n", ) return index_string else: - app.index_string = ( - app.index_string.split("")[0] + clerk_script + "" + app.index_string.replace( + "", + f"{self.clerk_script}\n", ) def _create_redirect_uri(self): From 7f06d9ff358442f666b2f35585c9aee8a59d5110 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:47:17 -0400 Subject: [PATCH 16/25] fix for lint --- dash_auth_plus/clerk_auth.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 6f6c180..c140147 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -268,7 +268,8 @@ def __init__( """ # Enhanced initialization with smart auth checking - init_script = """ + init_script = ( + """ """ + ) self.clerk_script = f"{clerk_script}\n{init_script}\n" From dfea8aa0e03d8250314364cf1640f0b004fbba01 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:31:01 -0400 Subject: [PATCH 17/25] adjustments for groups and cases where the user has a session but the store says its not there, in order to catch issues where its not trying to log out if the session is remotely terminated --- dash_auth_plus/clerk_auth.py | 53 ++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index c140147..79d1d4a 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -4,12 +4,13 @@ from typing import Dict, List, Optional, Union, Callable import dash +import flask from authlib.integrations.flask_client import OAuth from dash_auth_plus.auth import Auth from flask import Response, redirect, request, session, jsonify from werkzeug.routing import Map, Rule from dotenv import load_dotenv -from urllib.parse import urljoin, quote +from urllib.parse import urljoin, quote, unquote load_dotenv() @@ -184,6 +185,7 @@ def __init__( self.login_route = "/login" self.logout_route = "/logout" self.authenticate_request_options = AuthenticateRequestOptions + self.app.server.after_request(self.set_loggedin_if_user_session) host = app.server.config.get("SERVER_NAME") or "127.0.0.1" port = app.server.config.get("SERVER_PORT", 8050) self.allowed_parties = ( @@ -282,37 +284,44 @@ def __init__( // CRITICAL: Always call load() to ensure Clerk initializes properly window.Clerk.load().then(() => { - + // Set up session sync listener if (window.Clerk.addListener) { window.Clerk.addListener((resources) => { + var clerk_logged_in = JSON.parse(localStorage.getItem('clerk_logged_in', 'false')); // Store auth state in localStorage for persistence if (resources.user && resources.session) { - window._clerk_logged_in = true; - if (window.location.pathname === '/auth_callback') { - fetch(window.location.pathname, { + if (!clerk_logged_in) { + var callbackUrl = window.location.origin + (window.pathname == '/auth_callback' ? window.pathname : '/auth_callback?redirect_url=' + encodeURIComponent(window.location.href)) + fetch(callbackUrl, { method: 'POST', redirect: 'follow', credentials: 'same-origin' }).then(response => { + localStorage.setItem('clerk_logged_in', true); if (response.redirected) { window.location.href = response.url; - } else if (response.ok) { - // Optionally reload or handle the response - window.location.reload(); } }); + } else { + console.log('Clerk session updated'); } } - else if (window._clerk_logged_in) { - window._clerk_logged_in = false; + else if (clerk_logged_in) { + localStorage.setItem('clerk_logged_in', false); console.log('session ended, logging out'); + document.cookie.split(";").forEach((cookie) => { + const eqPos = cookie.indexOf("="); + const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie; + document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/"; + }); """ - + f"""window.location.pathname = '{self.logout_route}';""" + + f"""newLoc = window.location.origin + '{self.logout_route}';""" + """ + window.location.href = newLoc; } else { - window._clerk_logged_in = false; + localStorage.setItem('clerk_logged_in', false); } }); @@ -352,7 +361,8 @@ def __init__( # Use the new OAuth2App class for Dash 3+ @dash.hooks.layout() def append_clerk_url(layout): - return [dash.dcc.Location(id="_clerk_login_url", refresh=True), layout] + return [dash.dcc.Location(id="_clerk_login_url", refresh=True), + dash.dcc.Store(id='clerk_logged_in', storage_type='local'), layout] @dash.hooks.index() def add_clerk_script(index_string): @@ -465,12 +475,12 @@ def after_logged_in(self, user, idp, token): "session_id": sid, } if callable(self._user_groups): - session["user"]["groups"] = self._user_groups(user.get("email")) + ( + session["user"]["groups"] = self._user_groups(email) + ( session["user"].get("groups") or [] ) elif self._user_groups: session["user"]["groups"] = self._user_groups.get( - user.get("email"), [] + email, [] ) + (session["user"].get("groups") or []) if self.log_signins: logging.info("User %s is logging in.", session["user"].get("email")) @@ -478,9 +488,14 @@ def after_logged_in(self, user, idp, token): url = session["url"] del session["url"] return redirect(url) + return {'status': 'ok', 'content': 'User logged in successfully.'} def check_clerk_auth(self): """Pulls Clerk user data from the request and stores it in the session.""" + if request.args.get("redirect_url"): + # If redirect_uri is provided, use it + session['url'] = unquote(request.args.get("redirect_url")) + request_state = self.clerk_client.authenticate_request( request, self.authenticate_request_options( @@ -515,6 +530,14 @@ def is_authorized(self): # pylint: disable=C0116 return True return False + def set_loggedin_if_user_session(self, response: Response): + ''' Set the response data to indicate if the user is logged in.''' + if session.get("user") and request.path == '/_dash-update-component': + response_data = response.json + sideUpdate = response_data.get("sideUpdate", {}) + response_data['sideUpdate'] = {**sideUpdate, "clerk_logged_in": {"data": True}} + return flask.make_response(response_data) + return response def get_clerk_auth(app: dash.Dash = None) -> OAuth: """Retrieve the Clerk object. From fba5335c1acea423810b9ea6b76360244b150eaf Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:34:43 -0400 Subject: [PATCH 18/25] fix for lint --- dash_auth_plus/clerk_auth.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 79d1d4a..dc1470f 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -361,8 +361,11 @@ def __init__( # Use the new OAuth2App class for Dash 3+ @dash.hooks.layout() def append_clerk_url(layout): - return [dash.dcc.Location(id="_clerk_login_url", refresh=True), - dash.dcc.Store(id='clerk_logged_in', storage_type='local'), layout] + return [ + dash.dcc.Location(id="_clerk_login_url", refresh=True), + dash.dcc.Store(id="clerk_logged_in", storage_type="local"), + layout, + ] @dash.hooks.index() def add_clerk_script(index_string): @@ -479,22 +482,22 @@ def after_logged_in(self, user, idp, token): session["user"].get("groups") or [] ) elif self._user_groups: - session["user"]["groups"] = self._user_groups.get( - email, [] - ) + (session["user"].get("groups") or []) + session["user"]["groups"] = self._user_groups.get(email, []) + ( + session["user"].get("groups") or [] + ) if self.log_signins: logging.info("User %s is logging in.", session["user"].get("email")) if session.get("url"): url = session["url"] del session["url"] return redirect(url) - return {'status': 'ok', 'content': 'User logged in successfully.'} + return {"status": "ok", "content": "User logged in successfully."} def check_clerk_auth(self): """Pulls Clerk user data from the request and stores it in the session.""" if request.args.get("redirect_url"): # If redirect_uri is provided, use it - session['url'] = unquote(request.args.get("redirect_url")) + session["url"] = unquote(request.args.get("redirect_url")) request_state = self.clerk_client.authenticate_request( request, @@ -531,14 +534,18 @@ def is_authorized(self): # pylint: disable=C0116 return False def set_loggedin_if_user_session(self, response: Response): - ''' Set the response data to indicate if the user is logged in.''' - if session.get("user") and request.path == '/_dash-update-component': + """Set the response data to indicate if the user is logged in.""" + if session.get("user") and request.path == "/_dash-update-component": response_data = response.json sideUpdate = response_data.get("sideUpdate", {}) - response_data['sideUpdate'] = {**sideUpdate, "clerk_logged_in": {"data": True}} + response_data["sideUpdate"] = { + **sideUpdate, + "clerk_logged_in": {"data": True}, + } return flask.make_response(response_data) return response + def get_clerk_auth(app: dash.Dash = None) -> OAuth: """Retrieve the Clerk object. From b98a373ffdd953211d4f7807b709b42652a91ffc Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:03:55 -0400 Subject: [PATCH 19/25] fixing issue where logging out wouldnt clear the `clerk_logged_in` in localstorage --- dash_auth_plus/clerk_auth.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index dc1470f..64cfcff 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -273,6 +273,11 @@ def __init__( init_script = ( """ +``` +- The `Clerk` api is available in the browser, so you have access to all the api methods available in the Clerk documentation. \ No newline at end of file From 630d3dad1513806866123a1626bcc9da073c0225 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:44:59 -0400 Subject: [PATCH 23/25] adds method to pull the user_data to be used in Dash. --- dash_auth_plus/__init__.py | 3 +- dash_auth_plus/clerk_auth.py | 79 ++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/dash_auth_plus/__init__.py b/dash_auth_plus/__init__.py index d2eb0d1..d0401fb 100644 --- a/dash_auth_plus/__init__.py +++ b/dash_auth_plus/__init__.py @@ -11,7 +11,7 @@ # oidc auth requires authlib, install with `pip install dash-auth-plus[oidc]` try: from .oidc_auth import OIDCAuth, get_oauth - from .clerk_auth import ClerkAuth, get_clerk_auth + from .clerk_auth import ClerkAuth except ModuleNotFoundError: pass from .version import __version__, __plotly_dash_auth_version__ @@ -29,7 +29,6 @@ "BasicAuth", "OIDCAuth", "ClerkAuth", - "get_clerk_auth", "__version__", "__plotly_dash_auth_version__", ] diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 0e1427c..7a9557d 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -279,7 +279,7 @@ def __init__( localStorage.setItem('clerk_logged_in', false) } // Helper to ensure Clerk is ready - window.waitForClerk = function() { + var waitForClerk = function() { return new Promise((resolve) => { let attempts = 0; const interval = setInterval(() => { @@ -292,10 +292,13 @@ def __init__( // Set up session sync listener if (window.Clerk.addListener) { window.Clerk.addListener((resources) => { - var clerk_logged_in = JSON.parse(localStorage.getItem('clerk_logged_in', 'false')); + var clerk_logged_in = JSON.parse(localStorage.getItem('clerk_logged_in')) || false; // Store auth state in localStorage for persistence if (resources.user && resources.session) { + window?.dash_clientside?.set_props('clerk_user_update', {data: new Date().toISOString()}); if (!clerk_logged_in) { + console.log('logging in Clerk user'); + setTimeout(() => { var callbackUrl = window.location.origin + (window.location.pathname == '/auth_callback' ? window.location.pathname : '/auth_callback?redirect_url=' + encodeURIComponent(window.location.href)) fetch(callbackUrl, { method: 'POST', @@ -303,10 +306,9 @@ def __init__( credentials: 'same-origin' }).then(response => { localStorage.setItem('clerk_logged_in', true); - if (response.redirected) { - window.location.href = response.url; - } + window.location.href = response.url; }); + }, 400); } else { console.log('Clerk session updated'); } @@ -314,11 +316,6 @@ def __init__( else if (clerk_logged_in) { localStorage.setItem('clerk_logged_in', false); console.log('session ended, logging out'); - document.cookie.split(";").forEach((cookie) => { - const eqPos = cookie.indexOf("="); - const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie; - document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/"; - }); """ + f"""newLoc = window.location.origin + '{self.logout_route}';""" + """ @@ -333,12 +330,14 @@ def __init__( resolve(window.Clerk); }).catch(err => { + console.error('Clerk load failed:', err); // Clerk load failed resolve(null); }); } if (attempts > 100) { clearInterval(interval); + console.warn('Clerk not initialized after multiple attempts'); // Clerk not found after timeout resolve(null); } @@ -348,7 +347,7 @@ def __init__( // Initialize on load document.addEventListener('DOMContentLoaded', () => { - window.waitForClerk().then(clerk => { + waitForClerk().then(clerk => { if (clerk) { // Dispatch event to trigger initial auth check window.dispatchEvent(new Event('clerk-loaded')); @@ -368,6 +367,7 @@ def append_clerk_url(layout): return [ dash.dcc.Location(id="_clerk_login_url", refresh=True), dash.dcc.Store(id="clerk_logged_in", storage_type="local"), + dash.dcc.Store(id="clerk_user_update", storage_type="local"), layout, ] @@ -540,34 +540,35 @@ def is_authorized(self): # pylint: disable=C0116 def set_loggedin_if_user_session(self, response: Response): """Set the response data to indicate if the user is logged in.""" - if session.get("user") and request.path == "/_dash-update-component": - response_data = response.json - sideUpdate = response_data.get("sideUpdate", {}) - response_data["sideUpdate"] = { - **sideUpdate, - "clerk_logged_in": {"data": True}, - } - return flask.make_response(response_data) + try: + if session.get("user") and request.path == "/_dash-update-component": + response_data = response.json + sideUpdate = response_data.get("sideUpdate", {}) + response_data["sideUpdate"] = { + **sideUpdate, + "clerk_logged_in": {"data": True}, + } + return flask.make_response(response_data) + except: + logging.error( + "Error setting logged in state: %s\n%s", + traceback.format_exc(), + exc_info=True, + ) + pass return response + def get_user_data(self): + request_state = self.clerk_client.authenticate_request( + request, + self.authenticate_request_options( + authorized_parties=self.allowed_parties, + ), + ) -def get_clerk_auth(app: dash.Dash = None) -> OAuth: - """Retrieve the Clerk object. - - :param app: dash.Dash - Dash app or None, if None the current app is used - calling `dash.get_app()` - """ - if app is None: - app = dash.get_app() - - auth = getattr(app.server, "extensions", {}).get( - "authlib.integrations.flask_client" - ) - if auth is not None: - return auth - - raise RuntimeError( - "Clerk object is not yet defined. `ClerkAuth(app, **kwargs)` needs " - "to be run before `get_clerk_auth` is called." - ) + if request_state.is_signed_in: + sid = request_state.payload.get("sid") + sess = self.clerk_client.sessions.get(session_id=sid) + user_data = self.clerk_client.users.get(user_id=sess.user_id) + return user_data.__dict__ + return False # "user not authenticated" From b93bbd70eef0a331f5efa03dac9cc076de45cf80 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:47:39 -0400 Subject: [PATCH 24/25] fixing for lint --- dash_auth_plus/clerk_auth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index 7a9557d..daf47a1 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -542,14 +542,14 @@ def set_loggedin_if_user_session(self, response: Response): """Set the response data to indicate if the user is logged in.""" try: if session.get("user") and request.path == "/_dash-update-component": - response_data = response.json + response_data = response.get_json() sideUpdate = response_data.get("sideUpdate", {}) response_data["sideUpdate"] = { **sideUpdate, "clerk_logged_in": {"data": True}, } return flask.make_response(response_data) - except: + except Exception: logging.error( "Error setting logged in state: %s\n%s", traceback.format_exc(), From 13f6f819aededafae50efe8cf2e0d6692d8d6a39 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 11 Jun 2025 10:37:48 -0400 Subject: [PATCH 25/25] adding components for Clerk UserProfile --- .gitignore | 5 + dash_auth_plus/DashAuthComponents/__init__.py | 49 + dash_auth_plus/__init__.py | 2 + dash_auth_plus/clerk_auth.py | 4 +- dash_auth_plus/public_routes.py | 16 +- dash_auth_plus_components/__init__.py | 46 + package-lock.json | 3394 ++++++++++++++++- package.json | 38 +- setup.py | 2 +- src/ts/components/ClerkProvider.tsx | 19 + src/ts/components/UserProfile.tsx | 43 + src/ts/components/UserProfilePage.tsx | 21 + src/ts/index.ts | 6 + src/ts/utils.ts | 84 + tsconfig.json | 16 + webpack.config.js | 36 + 16 files changed, 3615 insertions(+), 166 deletions(-) create mode 100644 dash_auth_plus/DashAuthComponents/__init__.py create mode 100644 dash_auth_plus_components/__init__.py create mode 100644 src/ts/components/ClerkProvider.tsx create mode 100644 src/ts/components/UserProfile.tsx create mode 100644 src/ts/components/UserProfilePage.tsx create mode 100644 src/ts/index.ts create mode 100644 src/ts/utils.ts create mode 100644 tsconfig.json create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore index dab7335..647b537 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,8 @@ com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties +R/ +man/ +src/jl/ +*.jl +deps/ diff --git a/dash_auth_plus/DashAuthComponents/__init__.py b/dash_auth_plus/DashAuthComponents/__init__.py new file mode 100644 index 0000000..6181a06 --- /dev/null +++ b/dash_auth_plus/DashAuthComponents/__init__.py @@ -0,0 +1,49 @@ +from __future__ import print_function as _ + +import os as _os +import sys as _sys +import json + +import dash as _dash + +# noinspection PyUnresolvedReferences +from ._imports_ import * # noqa: F403,F401 +from ._imports_ import __all__ # noqa: F401 + +if not hasattr(_dash, "__plotly_dash") and not hasattr(_dash, "development"): + print( + "Dash was not successfully imported. " + "Make sure you don't have a file " + 'named \n"dash.py" in your current directory.', + file=_sys.stderr, + ) + _sys.exit(1) + +_basepath = _os.path.dirname(__file__) +_filepath = _os.path.abspath(_os.path.join(_basepath, "package-info.json")) +with open(_filepath) as f: + package = json.load(f) + +package_name = package["name"].replace(" ", "_").replace("-", "_") +__version__ = package["version"] + +_current_path = _os.path.dirname(_os.path.abspath(__file__)) + +_this_module = _sys.modules[__name__] + +_unpkg = f"https://unpkg.com/dash-auth-plus@{__version__}/dash_auth_plus/" + +_js_dist = [ + { + "relative_package_path": "DashAuthComponents/dash_auth_plus.js", + "external_url": f"{_unpkg}dash_auth_plus.js", + "namespace": package_name, + }, +] + +_css_dist = [] + + +for _component in __all__: + setattr(locals()[_component], "_js_dist", _js_dist) + setattr(locals()[_component], "_css_dist", _css_dist) diff --git a/dash_auth_plus/__init__.py b/dash_auth_plus/__init__.py index d0401fb..2ba2c02 100644 --- a/dash_auth_plus/__init__.py +++ b/dash_auth_plus/__init__.py @@ -12,6 +12,7 @@ try: from .oidc_auth import OIDCAuth, get_oauth from .clerk_auth import ClerkAuth + import DashAuthComponents except ModuleNotFoundError: pass from .version import __version__, __plotly_dash_auth_version__ @@ -29,6 +30,7 @@ "BasicAuth", "OIDCAuth", "ClerkAuth", + "DashAuthComponents", "__version__", "__plotly_dash_auth_version__", ] diff --git a/dash_auth_plus/clerk_auth.py b/dash_auth_plus/clerk_auth.py index daf47a1..32dc510 100644 --- a/dash_auth_plus/clerk_auth.py +++ b/dash_auth_plus/clerk_auth.py @@ -267,7 +267,8 @@ def __init__( data-clerk-publishable-key="{self.clerk_publishable_key}" src="{self.clerk_domain}/npm/@clerk/clerk-js@5/dist/clerk.browser.js" type="text/javascript"> - """ + + """ # Enhanced initialization with smart auth checking init_script = ( @@ -368,6 +369,7 @@ def append_clerk_url(layout): dash.dcc.Location(id="_clerk_login_url", refresh=True), dash.dcc.Store(id="clerk_logged_in", storage_type="local"), dash.dcc.Store(id="clerk_user_update", storage_type="local"), + dash.dcc.Store(id="show_user_profile", data=False), layout, ] diff --git a/dash_auth_plus/public_routes.py b/dash_auth_plus/public_routes.py index 6a133cb..c2b76e8 100644 --- a/dash_auth_plus/public_routes.py +++ b/dash_auth_plus/public_routes.py @@ -71,15 +71,15 @@ def public_callback(*callback_args, **callback_kwargs): def decorator(func): wrapped_func = callback(*callback_args, **callback_kwargs)(func) - callback_id = next( - ( - k - for k, v in GLOBAL_CALLBACK_MAP.items() - if inspect.getsource(v["callback"]) == inspect.getsource(func) - ), - None, - ) try: + callback_id = next( + ( + k + for k, v in GLOBAL_CALLBACK_MAP.items() + if inspect.getsource(v["callback"]) == inspect.getsource(func) + ), + None, + ) app = get_app() app.server.config[PUBLIC_CALLBACKS] = get_public_callbacks(app) + [ callback_id diff --git a/dash_auth_plus_components/__init__.py b/dash_auth_plus_components/__init__.py new file mode 100644 index 0000000..cb73525 --- /dev/null +++ b/dash_auth_plus_components/__init__.py @@ -0,0 +1,46 @@ +from __future__ import print_function as _ + +import os as _os +import sys as _sys +import json + +import dash as _dash + +# noinspection PyUnresolvedReferences +from ._imports_ import * # noqa: F403,F401 +from ._imports_ import __all__ # noqa: F401 + +if not hasattr(_dash, '__plotly_dash') and not hasattr(_dash, 'development'): + print('Dash was not successfully imported. ' + 'Make sure you don\'t have a file ' + 'named \n"dash.py" in your current directory.', file=_sys.stderr) + _sys.exit(1) + +_basepath = _os.path.dirname(__file__) +_filepath = _os.path.abspath(_os.path.join(_basepath, 'package-info.json')) +with open(_filepath) as f: + package = json.load(f) + +package_name = package['name'].replace(' ', '_').replace('-', '_') +__version__ = package['version'] + +_current_path = _os.path.dirname(_os.path.abspath(__file__)) + +_this_module = _sys.modules[__name__] + +_unpkg = f'https://unpkg.com/dash-auth-plus@{__version__}/dash_auth_plus/' + +_js_dist = [ + { + 'relative_package_path': 'DashAuthComponents/dash_auth_plus.js', + 'external_url': f'{_unpkg}dash_auth_plus.js', + 'namespace': package_name + }, +] + +_css_dist = [] + + +for _component in __all__: + setattr(locals()[_component], '_js_dist', _js_dist) + setattr(locals()[_component], '_css_dist', _css_dist) diff --git a/package-lock.json b/package-lock.json index 4bffe1c..7d97903 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,404 @@ { "name": "dash_auth_plus", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dash_auth_plus", + "version": "0.1.0", "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, "devDependencies": { + "@babel/core": "^7.24.5", + "@clerk/clerk-react": "^5.2.10", + "@types/ramda": "^0.30.1", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "babel-loader": "^9.1.3", + "dash-extensions-js": "^0.0.8", "npm-run-all": "4.1.5", - "rimraf": "^5.0.5" + "ramda": "^0.30.1", + "react": "^18.3.1", + "react-docgen": "^5.4.0", + "react-dom": "^18.3.1", + "rimraf": "^5.0.5", + "ts-loader": "^9.5.1", + "typescript": "^5.4.5", + "webpack": "^5.96.1", + "webpack-cli": "^6.0.1" }, "engines": { "node": ">=8.11.0", "npm": ">=6.1.0" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@clerk/clerk-react": { + "version": "5.31.9", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.31.9.tgz", + "integrity": "sha512-jP+qygYcChVDKM3pMtChOGNrGV4QAOYQvVyiitzQu5xgyVsFN3AnSdIj0u73lxOLZubfv9cOHjFwc41s31f1pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.9.6", + "@clerk/types": "^4.60.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + } + }, + "node_modules/@clerk/shared": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.9.6.tgz", + "integrity": "sha512-zScvDbNKBcGfkD7Db4LCoEbB8qZ/WFwuB77xqRgXiHDa+pBzEPyFB5nQSt1zQfLqOK3POng0GsPBoXEWKb4Ikw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@clerk/types": "^4.60.0", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.5", + "std-env": "^3.9.0", + "swr": "^2.3.3" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/types": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.60.0.tgz", + "integrity": "sha512-60u/Z3VD0lgepsySUPyFM1MV5cwhMwouN63na5g9+qm3PpaTE2kN4DeW9Nq6t1YB0TFpVEKIb1r8U6EOWPa01A==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -33,15 +417,463 @@ "node": ">=12" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz", + "integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ramda": { + "version": "0.30.2", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz", + "integrity": "sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "types-ramda": "^0.30.1" + } + }, + "node_modules/@types/react": { + "version": "18.3.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", + "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" } }, "node_modules/ansi-regex": { @@ -109,6 +941,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -135,6 +980,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/babel-loader": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -152,6 +1015,248 @@ "balanced-match": "^1.0.0" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/c8": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", + "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/c8/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/c8/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/c8/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/c8/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/c8/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -202,6 +1307,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001721", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", + "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -209,44 +1335,160 @@ "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/chalk/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -267,6 +1509,27 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -274,6 +1537,13 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -289,6 +1559,20 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dash-extensions-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/dash-extensions-js/-/dash-extensions-js-0.0.8.tgz", + "integrity": "sha512-9n3TqhvLatbbzUWQGopxRFc22wqfYb9jTDauY2mHefaqjJPsWeaZMBdk/NlyoYwYCspmA01qA8CDWkVxOMD0JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -343,6 +1627,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -379,6 +1681,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -401,6 +1726,13 @@ "dev": true, "license": "MIT" }, + "node_modules/electron-to-chromium": { + "version": "1.5.166", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", + "integrity": "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -408,6 +1740,33 @@ "dev": true, "license": "MIT" }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -481,87 +1840,365 @@ "which-typed-array": "^1.1.19" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-to-babel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/estree-to-babel/-/estree-to-babel-3.2.1.tgz", + "integrity": "sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.1.6", + "@babel/types": "^7.2.0", + "c8": "^7.6.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "p-limit": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "find-up": "^6.3.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=0.8.0" + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" } }, "node_modules/for-each": { @@ -597,6 +2234,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -638,6 +2282,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -716,6 +2380,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -854,6 +2535,52 @@ "dev": true, "license": "ISC" }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -869,6 +2596,16 @@ "node": ">= 0.4" } }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -1082,6 +2819,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", @@ -1099,6 +2846,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -1258,6 +3018,78 @@ "dev": true, "license": "ISC" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -1274,12 +3106,119 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/load-json-file": { "version": "4.0.0", @@ -1297,6 +3236,41 @@ "node": ">=4" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -1304,6 +3278,35 @@ "dev": true, "license": "ISC" }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1323,6 +3326,60 @@ "node": ">= 0.10.0" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -1349,6 +3406,20 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -1356,6 +3427,50 @@ "dev": true, "license": "MIT" }, + "node_modules/node-dir": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", + "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.2" + }, + "engines": { + "node": ">= 0.10.5" + } + }, + "node_modules/node-dir/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/node-dir/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1512,6 +3627,15 @@ "which": "bin/which" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -1556,6 +3680,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -1574,6 +3708,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -1595,6 +3768,26 @@ "node": ">=4" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1642,6 +3835,26 @@ "node": ">=4" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pidtree": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", @@ -1665,6 +3878,19 @@ "node": ">=4" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -1675,6 +3901,96 @@ "node": ">= 0.4" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/ramda": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", + "integrity": "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-docgen": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-5.4.3.tgz", + "integrity": "sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.7.5", + "@babel/generator": "^7.12.11", + "@babel/runtime": "^7.7.6", + "ast-types": "^0.14.2", + "commander": "^2.19.0", + "doctrine": "^3.0.0", + "estree-to-babel": "^3.1.0", + "neo-async": "^2.6.1", + "node-dir": "^0.1.10", + "strip-indent": "^3.0.0" + }, + "bin": { + "react-docgen": "bin/react-docgen.js" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -1690,6 +4006,19 @@ "node": ">=4" } }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -1734,6 +4063,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -1749,10 +4098,33 @@ "resolve": "bin/resolve" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/rimraf": { @@ -1791,6 +4163,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -1826,6 +4219,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -1836,6 +4259,16 @@ "semver": "bin/semver" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -1885,6 +4318,19 @@ "node": ">= 0.4" } }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2010,6 +4456,27 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -2046,6 +4513,13 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -2159,124 +4633,403 @@ "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swr": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.42.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz", + "integrity": "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { - "node": ">= 0.4" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/ts-loader/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "MIT", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "node_modules/typed-array-buffer": { "version": "1.0.3", @@ -2356,6 +5109,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/types-ramda": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.30.1.tgz", + "integrity": "sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-toolbelt": "^9.6.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -2375,6 +5152,69 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -2386,6 +5226,146 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", + "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2491,6 +5471,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -2588,6 +5575,117 @@ "engines": { "node": ">=8" } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index bfe2d9a..f4865eb 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,43 @@ { "name": "dash_auth_plus", + "version": "0.1.0", "scripts": { - "private::format.black": "black dash_auth_plus --exclude metadata_test.py", - "format": "run-s private::format.*", - "private::lint.black": "black dash_auth_plus --exclude metadata_test.py --check", - "private::lint.flake8": "flake8 dash_auth_plus --exclude=metadata_test.py", - "lint": "run-s private::lint.*", - "dist": "run-s lint && rimraf dist build && python setup.py sdist bdist_wheel" - }, + "private::format.black": "black dash_auth_plus --exclude metadata_test.py", + "format": "run-s private::format.*", + "private::lint.black": "black dash_auth_plus --exclude metadata_test.py --check", + "private::lint.flake8": "flake8 dash_auth_plus --exclude=metadata_test.py", + "lint": "run-s private::lint.*", + "dist": "run-s lint && rimraf dist build && python setup.py sdist bdist_wheel", + "build:backends": "dash-generate-components ./src/ts/components dash_auth_plus_components -p package-info.json --jl-prefix 'components' --ignore \\.test\\. && cp dash_auth_plus_components/** dash_auth_plus/DashAuthComponents ", + "build": "npm run build:js && npm run build:backends", + "build:js": "webpack --mode production" + }, "author": "Bryan Schroeder ", "license": "MIT", "devDependencies": { + "@babel/core": "^7.24.5", + "@clerk/clerk-react": "^5.2.10", + "@types/ramda": "^0.30.1", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "babel-loader": "^9.1.3", + "dash-extensions-js": "^0.0.8", "npm-run-all": "4.1.5", - "rimraf": "^5.0.5" + "ramda": "^0.30.1", + "react": "^18.3.1", + "react-docgen": "^5.4.0", + "react-dom": "^18.3.1", + "rimraf": "^5.0.5", + "ts-loader": "^9.5.1", + "typescript": "^5.4.5", + "webpack": "^5.96.1", + "webpack-cli": "^6.0.1" }, "engines": { "node": ">=8.11.0", "npm": ">=6.1.0" + }, + "dependencies": { + "prop-types": "^15.8.1" } } diff --git a/setup.py b/setup.py index 3184dca..de5979c 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ version=main_ns["__version__"], author="Bryan Schroeder", author_email="bryan.ri.schroeder@gmail.com", - packages=["dash_auth_plus"], + packages=["dash_auth_plus", "dash_auth_plus.DashAuthComponents"], license="MIT", description="Dash Authorization Package.", long_description=long_description, diff --git a/src/ts/components/ClerkProvider.tsx b/src/ts/components/ClerkProvider.tsx new file mode 100644 index 0000000..4bd879c --- /dev/null +++ b/src/ts/components/ClerkProvider.tsx @@ -0,0 +1,19 @@ +import React, { PropsWithChildren } from "react"; +import { ClerkProvider as ClerkClerkProvider } from '@clerk/clerk-react' + +export interface ClerkProviderProps { + PUBLISHABLE_KEY: string; + afterSignOutUrl?: string; + children: React.ReactNode; +} + + +const ClerkProvider: React.FC> = ({ PUBLISHABLE_KEY, afterSignOutUrl, children, ...others }) => { + return ( + + {children} + + ); +}; + +export default ClerkProvider \ No newline at end of file diff --git a/src/ts/components/UserProfile.tsx b/src/ts/components/UserProfile.tsx new file mode 100644 index 0000000..025f271 --- /dev/null +++ b/src/ts/components/UserProfile.tsx @@ -0,0 +1,43 @@ +import React, { PropsWithChildren } from "react"; +import { UserProfile as ClerkUserProfile } from "@clerk/clerk-react"; +import { resolveChildProps, newRenderDashComponent } from "../utils"; + +interface CustomPageProps { + children: React.ReactNode; + label: string; + labelIcon?: React.ReactNode; + url: string; + [key: string]: any; +} + +function customPage(props: CustomPageProps, index?: number) { + const { children, label, labelIcon, url, ...otherProps } = props; + return ( + + {newRenderDashComponent(children)} + + ); +} + +interface UserProfileProps extends PropsWithChildren {} + +const UserProfile: React.FC = ({ children, ...others }) => { + const customPages = React.Children.toArray(children).map((child, index) => { + const childProps = resolveChildProps(child); + return customPage(childProps as CustomPageProps, index); + }); + + return ( + + {customPages} + + ); +}; + +export default UserProfile; \ No newline at end of file diff --git a/src/ts/components/UserProfilePage.tsx b/src/ts/components/UserProfilePage.tsx new file mode 100644 index 0000000..db7d2cd --- /dev/null +++ b/src/ts/components/UserProfilePage.tsx @@ -0,0 +1,21 @@ +import React, { PropsWithChildren } from "react"; +import {UserProfile} from "@clerk/clerk-react" + + +export interface UserProfilePageProps { + children: React.ReactNode; + label: string; + url: string; + labelIcon: React.ReactNode; +} + +const UserProfilePage: React.FC> = ({ children, label, labelIcon, url, ...others }) => { + return ( + + {children} + + ); + }; + +export default UserProfilePage; + diff --git a/src/ts/index.ts b/src/ts/index.ts new file mode 100644 index 0000000..ce3d09c --- /dev/null +++ b/src/ts/index.ts @@ -0,0 +1,6 @@ +import UserProfilePage from "./components/UserProfilePage"; +import UserProfile from "./components/UserProfile"; +import ClerkProvider from "./components/ClerkProvider"; + +export { UserProfilePage, UserProfile, ClerkProvider }; + diff --git a/src/ts/utils.ts b/src/ts/utils.ts new file mode 100644 index 0000000..a54b0ad --- /dev/null +++ b/src/ts/utils.ts @@ -0,0 +1,84 @@ +/** + * Utility functions to support both Dash 2 and Dash 3 components + * + * For more details, refer to the Dash documentation: + * Dash 3 for Component Developers - https://dash.plotly.com/dash-3-for-component-developers + */ +import React, { useState, createElement } from "react"; +import {dissoc, has, includes, isEmpty, isNil, mergeRight, type} from "ramda"; + +const SIMPLE_COMPONENT_TYPES = ['String', 'Number', 'Null', 'Boolean']; +const isSimpleComponent = component => includes(type(component), SIMPLE_COMPONENT_TYPES); + + +export const resolveChildProps = (child: any) => { + if (child.props.componentPath) { + // props are coming from Dash + return (window as any).dash_component_api?.getLayout([ + ...child.props.componentPath, + 'props' + ]); + } + // else props are coming from React (e.g. Demo.js, or Tabs.test.js) + return child.props; +}; + +/** check for dash version */ +export const isDash3 = (): boolean => { + return !!(window as any).dash_component_api; +}; + + + // stringifies object ids used in pattern matching callbacks +export const stringifyId = (id: any): string => { + if (isDash3) { + return (window as any).dash_component_api.stringifyId(id) + } + + if (typeof id !== 'object' || id === null) { + return id; + } + + const stringifyVal = (v: any) => (v && v.wild) || JSON.stringify(v); + + const parts = Object.keys(id) + .sort() + .map((key) => JSON.stringify(key) + ':' + stringifyVal(id[key])); + + return '{' + parts.join(',') + '}'; +}; + + +export const newRenderDashComponent = (component: any, index?: number | null, basePath?: any[]) => { + if (!isDash3() || isEmpty(basePath) || !basePath) { + const dash_extensions = require('dash-extensions-js'); + const {renderDashComponent} = dash_extensions; + return renderDashComponent(component, index) + } + + // Nothing to render. + if (isNil(component) || isEmpty(component)) { + return null; + } + + // Simple stuff such as strings. + if (isSimpleComponent(component)) { + return component; + } + + // Array of stuff. + if (Array.isArray(component)) { + return component.map((item, i) => newRenderDashComponent(item, i, [...(basePath || []), i, 'props'])); + } + + // Merge props. + const allProps = { + component, + componentPath: [...(basePath || [])], + key: index !== null ? index : Math.random().toString(36).substr(2, 9), + temp: true + }; + + // Render the component. + return createElement((window as any).dash_component_api.ExternalWrapper, allProps); +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c36d18e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "jsx": "react", + "baseUrl": "src/ts", + "inlineSources": true, + "sourceMap": true, + "esModuleInterop": true, + "moduleResolution": "node", + "module": "esnext", + "importHelpers": true, + "lib": ["dom"], + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..90cb130 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,36 @@ +const path = require('path'); + +module.exports = { + entry: './src/ts/index.ts', + output: { + path: path.resolve(__dirname, 'dash_auth_plus_components'), + filename: 'dash_auth_plus.js', + library: 'dash_auth_plus_components', + libraryTarget: 'umd', + umdNamedDefine: true, + globalObject: 'this', + }, + resolve: { + extensions: ['.ts', '.tsx', '.js', '.jsx'], + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + { + test: /\.(js|jsx)$/, + use: 'babel-loader', + exclude: /node_modules/, + }, + ], + }, + externals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, + devtool: 'source-map', +}; +