From 1df5c1815426cded8f37e9f72989f03fd0c84f29 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Fri, 15 Aug 2025 19:56:17 -0400 Subject: [PATCH 01/14] updated installation instructions --- CHANGELOG.md | 11 +++++++++++ README.md | 14 ++++++++++++-- doc/installation.md | 16 +++++++++++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c6264..b10c6f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## Version 0.8.0 + +### Features + +### Bug Fixes + +### Documentation & Infrastructure + +- Updated installation instructions to reflect PyPI install option. + + ## Version 0.7.0 ### Features diff --git a/README.md b/README.md index b80a077..a8e8fce 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,11 @@ To run the unit and integration tests, you will need: ## Installation -Older versions of this library featured a two-step build process. This has since -been simplified. To install the package run pip: +The easiest way for users to install this library is via pip using the PyPI package: + + pip install philote-mdo + +If you need or want to install the package from the repository, you can doe this using pip: pip install @@ -60,6 +63,13 @@ command: pip install . +Unlike earlier version, the package is distributed with generated gRPC python files. This means that you do not need to +have grpciotools or protoc installed when using Philote-Python. If you are doing development work, specifically when you +are adding new gRPC features, you will need to regenerate the gRPC files. To do this, run the following command from the +repository root directory: + + python utils/compile_proto.py + ## License diff --git a/doc/installation.md b/doc/installation.md index 0de301d..1716620 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -25,8 +25,11 @@ To run the unit and integration tests, you will need: ## Compiling Definitions and Installation -Older versions of this library featured a two-step build process. This has since -been simplified. To install the package run pip: +The easiest way for users to install this library is via pip using the PyPI package: + + pip install philote-mdo + +If you need or want to install the package from the repository, you can doe this using pip: pip install @@ -39,4 +42,11 @@ repository root directory (the one containing pyproject.toml). Often, people install packages when located in that directory, making the corresponding command: - pip install . \ No newline at end of file + pip install . + +Unlike earlier version, the package is distributed with generated gRPC python files. This means that you do not need to +have grpciotools or protoc installed when using Philote-Python. If you are doing development work, specifically when you +are adding new gRPC features, you will need to regenerate the gRPC files. To do this, run the following command from the +repository root directory: + + python utils/compile_proto.py \ No newline at end of file From 79d973f2f507e518d24826d6533efb62aa670030 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Fri, 15 Aug 2025 21:34:58 -0400 Subject: [PATCH 02/14] added openmdao subproblem documentation --- doc/_toc.yml | 1 + doc/tutorials/openmdao_groups.md | 186 ++++++++++++++++++++++++++ philote_mdo/openmdao/group.py | 221 ++++++++++++++++++++++++++++--- 3 files changed, 392 insertions(+), 16 deletions(-) create mode 100644 doc/tutorials/openmdao_groups.md diff --git a/doc/_toc.yml b/doc/_toc.yml index 9416ca9..b1c7851 100644 --- a/doc/_toc.yml +++ b/doc/_toc.yml @@ -18,6 +18,7 @@ parts: - caption: Working with OpenMDAO chapters: - file: tutorials/openmdao + - file: tutorials/openmdao_groups #- caption: API Reference # chapters: diff --git a/doc/tutorials/openmdao_groups.md b/doc/tutorials/openmdao_groups.md new file mode 100644 index 0000000..4557887 --- /dev/null +++ b/doc/tutorials/openmdao_groups.md @@ -0,0 +1,186 @@ +# OpenMDAO Group Wrapping Discipline + +The `OpenMdaoSubProblem` class allows you to wrap existing OpenMDAO groups as Philote disciplines, enabling integration of OpenMDAO models into Philote workflows. This wrapper creates a bridge between OpenMDAO's group-based architecture and Philote's discipline-based framework. + +## Overview + +The `OpenMdaoSubProblem` class (`philote_mdo.openmdao.group.OpenMdaoSubProblem`) is a Philote explicit discipline that internally contains and executes an OpenMDAO group. While the Philote discipline interface is explicit, the underlying OpenMDAO group may contain cycles that require nonlinear solvers. + +### Key Features + +- Wraps any OpenMDAO group as a Philote discipline +- Maps variables between Philote and OpenMDAO namespaces +- Supports automatic partial derivative computation using OpenMDAO's `compute_totals` +- Handles units and multi-dimensional arrays +- Preserves OpenMDAO solver capabilities for cyclic groups + +## Basic Usage + +### 1. Create and Configure the Wrapper + +```python +import openmdao.api as om +from philote_mdo.openmdao.group import OpenMdaoSubProblem + +# Create the wrapper discipline +subprob = OpenMdaoSubProblem() + +# Add your OpenMDAO group +my_group = MyOpenMDAOGroup() +subprob.add_group(my_group) +``` + +### 2. Map Variables + +Map inputs and outputs between the Philote discipline and the OpenMDAO group: + +```python +# Map inputs: (philote_name, openmdao_name, shape, units) +subprob.add_mapped_input('x_local', 'x', shape=(1,), units='m') +subprob.add_mapped_input('design_vars', 'z', shape=(2,), units='') + +# Map outputs: (philote_name, openmdao_name, shape, units) +subprob.add_mapped_output('objective', 'obj', shape=(1,), units='') +subprob.add_mapped_output('constraint1', 'con1', shape=(1,), units='') +``` + +### 3. Declare Partial Derivatives + +If you need partial derivatives, declare them for each output-input pair: + +```python +# Declare partials: (output_name, input_name) +subprob.declare_subproblem_partial('objective', 'x_local') +subprob.declare_subproblem_partial('objective', 'design_vars') +subprob.declare_subproblem_partial('constraint1', 'x_local') +``` + +## Complete Example: Simple Linear Function + +```python +import numpy as np +import openmdao.api as om +from philote_mdo.openmdao.group import OpenMdaoSubProblem + +# Define a simple OpenMDAO group +class SimpleGroup(om.Group): + def setup(self): + self.add_subsystem('comp', om.ExecComp('y = 2*x + 1'), promotes=['*']) + +# Create and configure the wrapper +subprob = OpenMdaoSubProblem() +subprob.add_group(SimpleGroup()) + +# Map variables +subprob.add_mapped_input('input_x', 'x') +subprob.add_mapped_output('output_y', 'y') + +# Declare partials +subprob.declare_subproblem_partial('output_y', 'input_x') + +# Setup the discipline +subprob.setup() + +# Use the discipline +inputs = {'input_x': np.array([3.0])} +outputs = {'output_y': np.array([0.0])} + +subprob.compute(inputs, outputs) +print(f"Result: {outputs['output_y'][0]}") # Should print 7.0 +``` + +## Advanced Example: Sellar Problem + +The Sellar MDA problem demonstrates wrapping a more complex OpenMDAO group with cycles: + +```python +import numpy as np +import openmdao.api as om +from openmdao.test_suite.components.sellar import SellarDis1, SellarDis2 +from philote_mdo.openmdao.group import OpenMdaoSubProblem + +class SellarMDA(om.Group): + def setup(self): + # Create a cycle group + cycle = self.add_subsystem("cycle", om.Group(), promotes=["*"]) + cycle.add_subsystem("d1", SellarDis1(), + promotes_inputs=["x", "z", "y2"], + promotes_outputs=["y1"]) + cycle.add_subsystem("d2", SellarDis2(), + promotes_inputs=["z", "y1"], + promotes_outputs=["y2"]) + + # Set default values + cycle.set_input_defaults("x", 1.0) + cycle.set_input_defaults("z", np.array([5.0, 2.0])) + + # Add solvers for the cycle + cycle.nonlinear_solver = om.NonlinearBlockGS(iprint=0) + cycle.linear_solver = om.LinearBlockGS(iprint=0) + + # Add objective and constraints + self.add_subsystem("obj_cmp", + om.ExecComp("obj = x**2 + z[1] + y1 + exp(-y2)", + z=np.array([0.0, 0.0]), x=0.0), + promotes=["x", "z", "y1", "y2", "obj"]) + + self.add_subsystem("con_cmp1", + om.ExecComp("con1 = 3.16 - y1"), + promotes=["con1", "y1"]) + self.add_subsystem("con_cmp2", + om.ExecComp("con2 = y2 - 24.0"), + promotes=["con2", "y2"]) + +# Create wrapper using inheritance +class SellarGroup(OpenMdaoSubProblem): + def initialize(self): + self.add_group(SellarMDA()) + + # Map all variables + self.add_mapped_input('x', 'x') + self.add_mapped_input('z', 'z', shape=(2,)) + self.add_mapped_output('obj', 'obj') + self.add_mapped_output('con1', 'con1') + self.add_mapped_output('con2', 'con2') + + # Declare partials for optimization + self.declare_subproblem_partial('obj', 'x') + self.declare_subproblem_partial('obj', 'z') + self.declare_subproblem_partial('con1', 'x') + self.declare_subproblem_partial('con1', 'z') + self.declare_subproblem_partial('con2', 'x') + self.declare_subproblem_partial('con2', 'z') +``` + +## API Reference + +```{eval-rst} +.. autoclass:: philote_mdo.openmdao.group.OpenMdaoSubProblem + :members: + :undoc-members: + :show-inheritance: +``` + +## Best Practices + +1. **Variable Mapping**: Always map variables before calling `setup()` +2. **Partial Derivatives**: Only declare partials you actually need to avoid unnecessary computation +3. **Units**: Specify units when mapping variables to ensure proper unit conversion +4. **Shapes**: Correctly specify array shapes for multi-dimensional variables +5. **Inheritance**: Consider inheriting from `OpenMdaoSubProblem` for reusable wrappers +6. **Solver Configuration**: Configure OpenMDAO solvers within your group's `setup()` method + +## Troubleshooting + +### Common Issues + +1. **Missing Variables**: Ensure all mapped variables exist in the OpenMDAO group +2. **Shape Mismatches**: Verify that specified shapes match the actual variable shapes +3. **Unit Inconsistencies**: Check that units are compatible between mapped variables +4. **Solver Convergence**: For cyclic groups, ensure appropriate solvers are configured + +### Debugging Tips + +- Use OpenMDAO's `list_inputs()` and `list_outputs()` to verify available variables +- Check solver convergence with `iprint` settings +- Validate partial derivatives using OpenMDAO's `check_partials()` method \ No newline at end of file diff --git a/philote_mdo/openmdao/group.py b/philote_mdo/openmdao/group.py index b53c16f..4ca6c54 100644 --- a/philote_mdo/openmdao/group.py +++ b/philote_mdo/openmdao/group.py @@ -33,10 +33,48 @@ class OpenMdaoSubProblem(pm.ExplicitDiscipline): """ - Philote explicit discipline that calls an OpenMDAO group. + Philote explicit discipline that wraps an OpenMDAO group. - While the Philote discipline is explicit, the underlying OpenMDAO - group may have cycles that require a nonlinear solver. + This class allows you to wrap existing OpenMDAO groups as Philote disciplines, + enabling integration of OpenMDAO models into Philote workflows. The wrapper + creates a bridge between OpenMDAO's group-based architecture and Philote's + discipline-based framework. + + While the Philote discipline interface is explicit, the underlying OpenMDAO + group may contain cycles that require nonlinear solvers. + + Key Features: + - Wraps any OpenMDAO group as a Philote discipline + - Maps variables between Philote and OpenMDAO namespaces + - Supports automatic partial derivative computation using OpenMDAO's compute_totals + - Handles units and multi-dimensional arrays + - Preserves OpenMDAO solver capabilities for cyclic groups + + Typical Usage: + >>> import openmdao.api as om + >>> from philote_mdo.openmdao.group import OpenMdaoSubProblem + >>> + >>> # Create wrapper and add OpenMDAO group + >>> subprob = OpenMdaoSubProblem() + >>> subprob.add_group(my_openmdao_group) + >>> + >>> # Map variables between namespaces + >>> subprob.add_mapped_input('x_local', 'x', shape=(1,), units='m') + >>> subprob.add_mapped_output('y_local', 'y', shape=(1,), units='kg') + >>> + >>> # Declare partials for derivative computation + >>> subprob.declare_subproblem_partial('y_local', 'x_local') + >>> + >>> # Setup and use + >>> subprob.setup() + >>> subprob.compute(inputs, outputs) + + Attributes: + _prob (om.Problem): Internal OpenMDAO problem object + _model (om.Group): Reference to the OpenMDAO model + _input_map (dict): Mapping from local to OpenMDAO input variable names + _output_map (dict): Mapping from local to OpenMDAO output variable names + _partials_map (dict): Mapping for partial derivative computation """ def __init__(self): @@ -53,15 +91,56 @@ def add_group(self, group): """ Adds an OpenMDAO group to the discipline. - Warning: This will delete any previous problem settings and attached - models. + This method creates a new OpenMDAO Problem with the provided group as the model. + Any previously configured problem will be replaced. + + Parameters + ---------- + group : om.Group + OpenMDAO Group object to wrap as a Philote discipline + + Warning + ------- + This will delete any previous problem settings and attached models. + + Examples + -------- + >>> import openmdao.api as om + >>> from philote_mdo.openmdao.group import OpenMdaoSubProblem + >>> + >>> class MyGroup(om.Group): + ... def setup(self): + ... self.add_subsystem('comp', om.ExecComp('y = 2*x')) + >>> + >>> subprob = OpenMdaoSubProblem() + >>> subprob.add_group(MyGroup()) """ self._prob = om.Problem(model=group) self._model = self._prob.model def add_mapped_input(self, local_var, subprob_var, shape=(1,), units=""): """ - Adds an input that is mapped from the discipline to the sub-problem. + Maps an input variable from the Philote discipline to the OpenMDAO group. + + This creates a mapping between a variable name in the Philote discipline + namespace and a variable name in the OpenMDAO group namespace. + + Parameters + ---------- + local_var : str + Variable name in the Philote discipline namespace + subprob_var : str + Variable name in the OpenMDAO group namespace + shape : tuple, optional + Shape of the variable, by default (1,) + units : str, optional + Units for the variable, by default "" + + Examples + -------- + >>> subprob = OpenMdaoSubProblem() + >>> subprob.add_mapped_input('x_local', 'x', shape=(1,), units='m') + >>> subprob.add_mapped_input('design_vars', 'z', shape=(2,), units='') """ self._input_map[local_var] = { "sub_prob_name": subprob_var, @@ -71,7 +150,27 @@ def add_mapped_input(self, local_var, subprob_var, shape=(1,), units=""): def add_mapped_output(self, local_var, subprob_var, shape=(1,), units=""): """ - Adds an output that is mapped from the discipline to the sub-problem. + Maps an output variable from the Philote discipline to the OpenMDAO group. + + This creates a mapping between a variable name in the Philote discipline + namespace and a variable name in the OpenMDAO group namespace. + + Parameters + ---------- + local_var : str + Variable name in the Philote discipline namespace + subprob_var : str + Variable name in the OpenMDAO group namespace + shape : tuple, optional + Shape of the variable, by default (1,) + units : str, optional + Units for the variable, by default "" + + Examples + -------- + >>> subprob = OpenMdaoSubProblem() + >>> subprob.add_mapped_output('objective', 'obj', shape=(1,), units='') + >>> subprob.add_mapped_output('constraint1', 'con1', shape=(1,), units='N') """ self._output_map[local_var] = { "sub_prob_name": subprob_var, @@ -81,25 +180,47 @@ def add_mapped_output(self, local_var, subprob_var, shape=(1,), units=""): def clear_mapped_variables(self): """ - Clears the variable map and sets it to an empty dictionary. + Clears all variable mappings and resets to empty dictionaries. + + This removes all input and output variable mappings that were previously + configured using add_mapped_input() and add_mapped_output(). + + Examples + -------- + >>> subprob = OpenMdaoSubProblem() + >>> subprob.add_mapped_input('x', 'input_x') + >>> subprob.add_mapped_output('y', 'output_y') + >>> subprob.clear_mapped_variables() # All mappings removed """ self._input_map = {} self._output_map = {} def declare_subproblem_partial(self, local_func, local_var): """ - Declares the partials for this sub-problem. + Declares partial derivatives for the sub-problem. + + This method sets up the mapping for computing partial derivatives of + outputs with respect to inputs using OpenMDAO's compute_totals method. + The partials will be computed automatically during compute_partials(). Parameters ---------- - local_func: str - function name in the local name space - local_var: str - variable name in the local name space + local_func : str + Output variable name in the Philote discipline namespace + local_var : str + Input variable name in the Philote discipline namespace - Returns - ------- - None + Notes + ----- + Both local_func and local_var must have been previously mapped using + add_mapped_output() and add_mapped_input() respectively. + + Examples + -------- + >>> subprob = OpenMdaoSubProblem() + >>> subprob.add_mapped_input('x_local', 'x') + >>> subprob.add_mapped_output('y_local', 'y') + >>> subprob.declare_subproblem_partial('y_local', 'x_local') """ self._partials_map[(local_func, local_var)] = ( self._output_map[local_func]["sub_prob_name"], @@ -107,9 +228,36 @@ def declare_subproblem_partial(self, local_func, local_var): ) def initialize(self): + """ + Initialize the discipline. + + This method is called during discipline setup and can be overridden + by subclasses to perform custom initialization, such as adding groups + and configuring variable mappings. + + Examples + -------- + >>> class MySubProblem(OpenMdaoSubProblem): + ... def initialize(self): + ... self.add_group(MyOpenMDAOGroup()) + ... self.add_mapped_input('x', 'input_x') + ... self.add_mapped_output('y', 'output_y') + """ pass def setup(self): + """ + Set up the discipline by configuring the OpenMDAO problem and Philote variables. + + This method sets up the internal OpenMDAO problem, adds all mapped inputs + and outputs to the Philote discipline, and declares any specified partial + derivatives. + + Notes + ----- + This method is automatically called by the Philote framework and should + not be called directly by users. + """ self._prob.setup() for local, var in self._input_map.items(): @@ -122,6 +270,25 @@ def setup(self): self.declare_partials(pair[0], pair[1]) def compute(self, inputs, outputs): + """ + Execute the OpenMDAO group and transfer results. + + This method transfers input values from the Philote discipline to the + OpenMDAO group, runs the OpenMDAO model, and transfers the output + values back to the Philote discipline. + + Parameters + ---------- + inputs : dict + Dictionary of input values with Philote variable names as keys + outputs : dict + Dictionary to store output values with Philote variable names as keys + + Notes + ----- + This method is automatically called by the Philote framework during + discipline execution and should not be called directly by users. + """ for local, var in self._input_map.items(): sub = var["sub_prob_name"] self._prob[sub] = inputs[local] @@ -133,6 +300,28 @@ def compute(self, inputs, outputs): outputs[local] = self._prob[sub] def compute_partials(self, inputs, partials): + """ + Compute partial derivatives using OpenMDAO's compute_totals method. + + This method transfers input values to the OpenMDAO group, runs the model, + and computes total derivatives for all declared partial derivative pairs + using OpenMDAO's automatic differentiation capabilities. + + Parameters + ---------- + inputs : dict + Dictionary of input values with Philote variable names as keys + partials : dict + Dictionary to store partial derivative values with (output, input) + tuples as keys + + Notes + ----- + This method is automatically called by the Philote framework when + partial derivatives are needed and should not be called directly by users. + Only partials that were declared using declare_subproblem_partial() + will be computed. + """ for local, var in self._input_map.items(): sub = var["sub_prob_name"] self._prob[sub] = inputs[local] From 71d8246110d25018df411469eececa6725f31957 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Fri, 15 Aug 2025 22:13:01 -0400 Subject: [PATCH 03/14] added initial openmdao client documentation --- doc/tutorials/openmdao.md | 354 ++++++++++++++++++++++++++++++- philote_mdo/openmdao/explicit.py | 201 ++++++++++++++++-- philote_mdo/openmdao/implicit.py | 239 +++++++++++++++++++-- 3 files changed, 755 insertions(+), 39 deletions(-) diff --git a/doc/tutorials/openmdao.md b/doc/tutorials/openmdao.md index 2ba640f..e5addad 100644 --- a/doc/tutorials/openmdao.md +++ b/doc/tutorials/openmdao.md @@ -1,4 +1,356 @@ (tutorials:openmdao)= # Calling Philote Disciplines from OpenMDAO -This documentation is currently a work in progress. \ No newline at end of file +This guide explains how to integrate Philote disciplines into OpenMDAO workflows using remote client components. Philote provides OpenMDAO components that act as clients to remote Philote servers, enabling distributed computing and language interoperability. + +## Overview + +Philote offers two main OpenMDAO component types for integrating remote disciplines: + +- **`RemoteExplicitComponent`**: For explicit disciplines where outputs are computed directly from inputs +- **`RemoteImplicitComponent`**: For implicit disciplines that solve residual equations R(inputs, outputs) = 0 + +Both components automatically discover the server's interface and handle all communication transparently, making remote disciplines appear as native OpenMDAO components. + +### Key Benefits + +- **Distributed Computing**: Run expensive analyses on remote servers +- **Language Interoperability**: Use disciplines written in any language supported by Philote +- **Seamless Integration**: Remote disciplines work exactly like local OpenMDAO components +- **Automatic Interface Discovery**: No manual specification of inputs/outputs required +- **Derivative Support**: Automatic partial derivative computation for optimization + +## Prerequisites + +Before using Philote OpenMDAO components, ensure: + +1. A Philote server is running and accessible +2. The gRPC channel can connect to the server +3. OpenMDAO and Philote packages are installed + +## Remote Explicit Components + +### Basic Usage + +Explicit components compute outputs directly from inputs. Here's a complete example: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create gRPC channel to Philote server +channel = grpc.insecure_channel("localhost:50051") + +# Create OpenMDAO problem +prob = om.Problem() +model = prob.model + +# Add remote explicit component +model.add_subsystem( + "analysis", + pmom.RemoteExplicitComponent(channel=channel), + promotes=["*"] +) + +# Setup and run +prob.setup() +prob["x"] = 2.0 +prob["y"] = 3.0 +prob.run_model() + +print(f"Result: {prob['f_xy']}") +``` + +### Advanced Example with Options + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create channel with server options +channel = grpc.insecure_channel("localhost:50051") + +# Create component with server-specific options +component = pmom.RemoteExplicitComponent( + channel=channel, + num_par_fd=4, # OpenMDAO finite difference parallelization + tolerance=1e-8, # Server-specific option + max_iterations=100 # Server-specific option +) + +# Create problem and add component +prob = om.Problem() +prob.model.add_subsystem("remote_analysis", component, promotes=["*"]) + +# Add optimizer for demonstration +prob.driver = om.ScipyOptimizeDriver() +prob.driver.options["optimizer"] = "SLSQP" + +# Add design variable and objective +prob.model.add_design_var("x", lower=-10, upper=10) +prob.model.add_objective("f_xy") + +# Setup and optimize +prob.setup() +prob["x"] = 1.0 +prob["y"] = 1.0 + +prob.run_driver() +print(f"Optimal x: {prob['x']}") +print(f"Optimal objective: {prob['f_xy']}") +``` + +## Remote Implicit Components + +### Basic Usage + +Implicit components solve equations of the form R(inputs, outputs) = 0: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create gRPC channel +channel = grpc.insecure_channel("localhost:50051") + +# Create OpenMDAO problem +prob = om.Problem() +model = prob.model + +# Add remote implicit component +model.add_subsystem( + "implicit_analysis", + pmom.RemoteImplicitComponent(channel=channel), + promotes=["*"] +) + +# Setup and run +prob.setup() + +# Set inputs for quadratic equation: ax^2 + bx + c = 0 +prob["a"] = 1.0 +prob["b"] = -5.0 +prob["c"] = 6.0 + +prob.run_model() +print(f"Solution: x = {prob['x']}") # Should find x = 2 or x = 3 +``` + +### Implicit Component with Nonlinear Solver + +For more complex implicit systems, you may need to configure OpenMDAO solvers: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create problem with nonlinear solver +prob = om.Problem() +model = prob.model + +# Add implicit component +model.add_subsystem( + "implicit_system", + pmom.RemoteImplicitComponent(channel=grpc.insecure_channel("localhost:50051")), + promotes=["*"] +) + +# Configure nonlinear solver for implicit system +model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False) +model.nonlinear_solver.options["iprint"] = 2 +model.nonlinear_solver.options["maxiter"] = 20 +model.linear_solver = om.DirectSolver() + +# Setup and run +prob.setup() +prob["input_param"] = 5.0 +prob.run_model() + +print(f"Converged solution: {prob['output_var']}") +``` + +## Working with Multiple Remote Components + +You can combine multiple remote components in a single OpenMDAO model: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create channels to different servers +channel1 = grpc.insecure_channel("server1:50051") +channel2 = grpc.insecure_channel("server2:50052") + +# Create problem with multiple remote components +prob = om.Problem() +model = prob.model + +# Add multiple remote components +model.add_subsystem( + "aerodynamics", + pmom.RemoteExplicitComponent(channel=channel1), + promotes_inputs=["mach", "alpha", "altitude"], + promotes_outputs=["CL", "CD"] +) + +model.add_subsystem( + "structures", + pmom.RemoteImplicitComponent(channel=channel2), + promotes_inputs=["loads", "material_props"], + promotes_outputs=["stress", "displacement"] +) + +# Connect components +model.connect("CL", "structures.loads") + +# Setup and run coupled analysis +prob.setup() +prob["mach"] = 0.8 +prob["alpha"] = 2.0 +prob["altitude"] = 35000 +prob["material_props"] = [...] + +prob.run_model() +``` + +## Error Handling and Debugging + +### Connection Issues + +```python +import grpc +import philote_mdo.openmdao as pmom + +try: + channel = grpc.insecure_channel("unreachable:50051") + component = pmom.RemoteExplicitComponent(channel=channel) +except grpc.RpcError as e: + print(f"Failed to connect to server: {e}") + print("Check that the server is running and accessible") +except ValueError as e: + print(f"Configuration error: {e}") +``` + +### Server Debugging + +Enable OpenMDAO's debug output to see server communication: + +```python +import openmdao.api as om + +# Enable debug output +prob = om.Problem() +prob.model.add_subsystem("remote", component) + +# Setup with debug info +prob.setup() +prob.set_solver_print(level=2) # Print solver information +prob.run_model() +``` + +## Performance Considerations + +### Parallel Finite Differences + +For components that don't provide analytic derivatives: + +```python +component = pmom.RemoteExplicitComponent( + channel=channel, + num_par_fd=8 # Use 8 parallel processes for finite differences +) +``` + +### Connection Pooling + +For multiple components connecting to the same server: + +```python +# Reuse channels when possible +shared_channel = grpc.insecure_channel("localhost:50051") + +comp1 = pmom.RemoteExplicitComponent(channel=shared_channel) +comp2 = pmom.RemoteExplicitComponent(channel=shared_channel) +``` + +### Server-Side Optimization + +- Ensure servers are running on appropriate hardware +- Use analytic derivatives when possible (server-side implementation) +- Consider caching for expensive computations (server-side) + +## Best Practices + +1. **Error Handling**: Always wrap component creation in try-catch blocks +2. **Connection Management**: Reuse gRPC channels when connecting to the same server +3. **Option Validation**: Server options are discovered automatically but validate important values +4. **Solver Configuration**: Configure appropriate solvers for implicit components +5. **Performance**: Use analytic derivatives when available from the server +6. **Debugging**: Enable OpenMDAO debug output to troubleshoot communication issues + +## Troubleshooting + +### Common Issues + +1. **"No channel provided" Error** + ```python + # Wrong: Missing channel + component = pmom.RemoteExplicitComponent() + + # Correct: Provide valid channel + channel = grpc.insecure_channel("localhost:50051") + component = pmom.RemoteExplicitComponent(channel=channel) + ``` + +2. **Server Connection Failures** + - Verify server is running: `telnet localhost 50051` + - Check firewall settings + - Verify correct hostname/port + +3. **Variable Name Mismatches** + - Use `prob.model.list_inputs()` and `prob.model.list_outputs()` after setup + - Server variables are automatically discovered and named + +4. **Convergence Issues with Implicit Components** + - Check residual values: use OpenMDAO's debug output + - Adjust solver tolerances and iteration limits + - Verify initial guesses are reasonable + +### Debug Tools + +```python +# After problem setup, inspect the remote component +prob.setup() + +# List all inputs and outputs +prob.model.list_inputs(print_arrays=True) +prob.model.list_outputs(print_arrays=True) + +# Check partial derivatives +prob.check_partials(compact_print=True) +``` + +## API Reference + +### RemoteExplicitComponent + +```{eval-rst} +.. autoclass:: philote_mdo.openmdao.explicit.RemoteExplicitComponent + :members: + :undoc-members: + :show-inheritance: +``` + +### RemoteImplicitComponent + +```{eval-rst} +.. autoclass:: philote_mdo.openmdao.implicit.RemoteImplicitComponent + :members: + :undoc-members: + :show-inheritance: +``` \ No newline at end of file diff --git a/philote_mdo/openmdao/explicit.py b/philote_mdo/openmdao/explicit.py index 3ea63de..b22c045 100644 --- a/philote_mdo/openmdao/explicit.py +++ b/philote_mdo/openmdao/explicit.py @@ -35,13 +35,94 @@ class RemoteExplicitComponent(om.ExplicitComponent): """ - OpenMDAO component that acts as a client to an explicit Philote analysis - server. + OpenMDAO explicit component that acts as a client to a remote Philote analysis server. + + This component enables integration of Philote explicit disciplines into OpenMDAO workflows + by connecting to a remote Philote server over gRPC. The component automatically discovers + the server's input/output interface and handles data transfer, computation requests, and + partial derivative computation. + + The component inherits from OpenMDAO's ExplicitComponent and implements the standard + OpenMDAO component interface while internally communicating with a Philote server. + + Key Features: + - Automatic discovery of server variables and options + - Seamless integration with OpenMDAO's analysis and optimization capabilities + - Support for partial derivatives via finite differencing or analytic computation + - Handles unit conversions and data marshaling between OpenMDAO and Philote + - Configurable through server-defined options + + Typical Usage: + >>> import grpc + >>> import openmdao.api as om + >>> import philote_mdo.openmdao as pmom + >>> + >>> # Create gRPC channel to Philote server + >>> channel = grpc.insecure_channel("localhost:50051") + >>> + >>> # Create OpenMDAO problem with remote component + >>> prob = om.Problem() + >>> prob.model.add_subsystem('analysis', + ... pmom.RemoteExplicitComponent(channel=channel), + ... promotes=['*']) + >>> + >>> # Setup and run as normal OpenMDAO component + >>> prob.setup() + >>> prob['x'] = 2.0 + >>> prob.run_model() + >>> print(prob['y']) + + Attributes: + _client (pm.ExplicitClient): Internal Philote client for server communication + + Notes: + - The server must be running and reachable during component initialization + - Server options are automatically queried and declared as OpenMDAO options + - Variable metadata (names, shapes, units) is automatically discovered from server + - The component supports both function evaluation and partial derivative computation """ def __init__(self, channel=None, num_par_fd=1, **kwargs): """ - Initialize the component and client. + Initialize the OpenMDAO component and establish connection to Philote server. + + This constructor creates a Philote explicit client, establishes the gRPC connection, + and configures the component with any provided options. The server must be reachable + during initialization to query available options and variable metadata. + + Parameters + ---------- + channel : grpc.Channel + gRPC channel for connecting to the Philote server. This is required and + should be created using grpc.insecure_channel() or grpc.secure_channel(). + num_par_fd : int, optional + Number of parallel finite difference processes for partial derivatives, + by default 1 + **kwargs : dict + Additional keyword arguments passed as options to the Philote server. + Available options are server-specific and queried automatically. + + Raises + ------ + ValueError + If no channel is provided or channel is None + grpc.RpcError + If unable to connect to the Philote server + + Examples + -------- + >>> import grpc + >>> channel = grpc.insecure_channel("localhost:50051") + >>> component = RemoteExplicitComponent( + ... channel=channel, + ... option1=True, + ... option2=42 + ... ) + + Notes + ----- + The server connection is established immediately during initialization. + If the server is not available, the constructor will raise an exception. """ if not channel: raise ValueError( @@ -55,22 +136,32 @@ def __init__(self, channel=None, num_par_fd=1, **kwargs): # available options on this discipline. self._client = pm.ExplicitClient(channel=channel) - # call the init function of the explicit component - super().__init__(num_par_fd=1, **kwargs) - - # assign and send the option values to the server - # this must be done here and not in initialize, as the values of the - # OpenMDAO options are only set after intialize has been called in the - # init function. That is why the parent init function must be called - # before sending the options values to the philote server. - # options = {} - # for key, val in self.options.items(): - # options[key] = val + # Initialize the parent OpenMDAO ExplicitComponent + super().__init__(num_par_fd=num_par_fd, **kwargs) + + # Send initial options to the server + # This must be done here and not in initialize, as the values of the + # OpenMDAO options are only set after initialize has been called in the + # parent init function. That is why the parent init function must be called + # before sending the option values to the Philote server. self._client.send_options(kwargs) def initialize(self): """ - Define the OpenMDAO component options. + Define OpenMDAO component options based on server-available options. + + This method queries the Philote server for its available options and automatically + declares them as OpenMDAO component options with appropriate types. This allows + the server's configuration to be controlled through OpenMDAO's option system. + + The method is called automatically by OpenMDAO during component setup and should + not be called directly by users. + + Notes + ----- + - Server options are automatically discovered via gRPC + - Option types (bool, int, float, str) are preserved from server metadata + - This method requires an active connection to the Philote server """ # get the available options from the philote discipline self._client.get_available_options() @@ -80,19 +171,67 @@ def initialize(self): def setup(self): """ - Set up the OpenMDAO component. + Set up the OpenMDAO component by declaring inputs and outputs from server metadata. + + This method queries the Philote server for variable metadata and automatically + declares the corresponding inputs and outputs in the OpenMDAO component. Variable + properties including names, shapes, and units are preserved from the server. + + The method is called automatically by OpenMDAO during problem setup and should + not be called directly by users. + + Notes + ----- + - Input and output variables are automatically discovered from server + - Variable shapes and units are preserved from server metadata + - This method uses utility functions to handle the variable declaration process """ utils.client_setup(self) def setup_partials(self): """ - Set up the partials of the OpenMDAO component. + Set up partial derivatives for the OpenMDAO component. + + This method declares partial derivatives for all output-input pairs based on + the server's partial derivative metadata. The component can compute partials + either analytically (if supported by the server) or via finite differencing. + + The method is called automatically by OpenMDAO during component setup and should + not be called directly by users. + + Notes + ----- + - Partial derivative structure is automatically discovered from server + - Both analytic and finite difference methods are supported + - Sparsity patterns are preserved when available from server metadata """ utils.client_setup_partials(self) def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): """ - Compute the function evaluation. + Compute function evaluation by calling the remote Philote server. + + This method transfers input values to the server, requests a function evaluation, + and transfers the computed outputs back to the OpenMDAO component. Data marshaling + and unit conversions are handled automatically. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + outputs : dict + Dictionary to store computed output values with variable names as keys + discrete_inputs : dict, optional + Dictionary of discrete input values (currently unused), by default None + discrete_outputs : dict, optional + Dictionary of discrete output values (currently unused), by default None + + Notes + ----- + - Input/output data is automatically converted between OpenMDAO and Philote formats + - The method handles variable name mapping and unit conversions + - Server communication is synchronous - the method blocks until computation completes + - This method is called automatically by OpenMDAO during model execution """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) out = self._client.run_compute(local_inputs) @@ -100,7 +239,31 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): def compute_partials(self, inputs, partials, discrete_inputs=None, discrete_outputs=None): """ - Compute the gradient evaluation. + Compute partial derivatives by calling the remote Philote server. + + This method transfers input values to the server, requests partial derivative + computation, and transfers the computed Jacobian values back to the OpenMDAO + component. The server may compute derivatives analytically or via finite differencing. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + partials : dict + Dictionary to store computed partial derivatives with (output, input) + tuples as keys + discrete_inputs : dict, optional + Dictionary of discrete input values (currently unused), by default None + discrete_outputs : dict, optional + Dictionary of discrete output values (currently unused), by default None + + Notes + ----- + - Partial derivatives are computed at the current input point + - The method handles conversion between OpenMDAO and Philote Jacobian formats + - Server determines whether to use analytic or finite difference derivatives + - This method is called automatically by OpenMDAO when derivatives are needed + - Sparsity patterns from the server are preserved in the OpenMDAO component """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) jac = self._client.run_compute_partials(local_inputs) diff --git a/philote_mdo/openmdao/implicit.py b/philote_mdo/openmdao/implicit.py index b6e67d4..e4a862f 100644 --- a/philote_mdo/openmdao/implicit.py +++ b/philote_mdo/openmdao/implicit.py @@ -35,12 +35,100 @@ class RemoteImplicitComponent(om.ImplicitComponent): """ - An OpenMDAO component that acts as a client to an implicit analysis server. + OpenMDAO implicit component that acts as a client to a remote Philote implicit analysis server. + + This component enables integration of Philote implicit disciplines into OpenMDAO workflows + by connecting to a remote Philote server over gRPC. Unlike explicit components, implicit + components solve residual equations of the form R(inputs, outputs) = 0, where the outputs + are implicitly defined by the inputs. + + The component inherits from OpenMDAO's ImplicitComponent and implements the standard + OpenMDAO implicit component interface while internally communicating with a Philote server. + It supports both residual evaluation and solving, as well as partial derivative computation + for gradient-based optimization. + + Key Features: + - Automatic discovery of server variables and options + - Residual evaluation via apply_nonlinear method + - Implicit equation solving via solve_nonlinear method + - Jacobian computation for optimization via linearize method + - Seamless integration with OpenMDAO's implicit solving capabilities + - Support for coupled systems and nonlinear solvers + + Typical Usage: + >>> import grpc + >>> import openmdao.api as om + >>> import philote_mdo.openmdao as pmom + >>> + >>> # Create gRPC channel to Philote server + >>> channel = grpc.insecure_channel("localhost:50051") + >>> + >>> # Create OpenMDAO problem with remote implicit component + >>> prob = om.Problem() + >>> prob.model.add_subsystem('implicit_analysis', + ... pmom.RemoteImplicitComponent(channel=channel), + ... promotes=['*']) + >>> + >>> # Setup and run with nonlinear solver if needed + >>> prob.setup() + >>> prob['a'] = 1.0 + >>> prob['b'] = -2.0 + >>> prob['c'] = 1.0 + >>> prob.run_model() + >>> print(prob['x']) # Solution to ax^2 + bx + c = 0 + + Attributes: + _client (pm.ImplicitClient): Internal Philote client for server communication + + Notes: + - The server must be running and reachable during component initialization + - Server options are automatically queried and declared as OpenMDAO options + - Variable metadata (names, shapes, units) is automatically discovered from server + - The component supports residual evaluation, solving, and linearization + - Both inputs and outputs must be provided to the server for residual computation """ def __init__(self, channel=None, num_par_fd=1, **kwargs): """ - Initialize the component and client. + Initialize the OpenMDAO implicit component and establish connection to Philote server. + + This constructor creates a Philote implicit client, establishes the gRPC connection, + and configures the component with any provided options. The server must be reachable + during initialization to query available options and variable metadata. + + Parameters + ---------- + channel : grpc.Channel + gRPC channel for connecting to the Philote server. This is required and + should be created using grpc.insecure_channel() or grpc.secure_channel(). + num_par_fd : int, optional + Number of parallel finite difference processes for partial derivatives, + by default 1 + **kwargs : dict + Additional keyword arguments passed as options to the Philote server. + Available options are server-specific and queried automatically. + + Raises + ------ + ValueError + If no channel is provided or channel is None + grpc.RpcError + If unable to connect to the Philote server + + Examples + -------- + >>> import grpc + >>> channel = grpc.insecure_channel("localhost:50051") + >>> component = RemoteImplicitComponent( + ... channel=channel, + ... tolerance=1e-8, + ... max_iterations=100 + ... ) + + Notes + ----- + The server connection is established immediately during initialization. + If the server is not available, the constructor will raise an exception. """ if not channel: raise ValueError( @@ -55,22 +143,32 @@ def __init__(self, channel=None, num_par_fd=1, **kwargs): # available options on this discipline. self._client = pm.ImplicitClient(channel=channel) - # call the init function of the explicit component - super().__init__(num_par_fd=1, **kwargs) - - # assign and send the option values to the server - # this must be done here and not in initialize, as the values of the - # OpenMDAO options are only set after intialize has been called in the - # init function. That is why the parent init function must be called - # before sending the options values to the philote server. - # options = {} - # for key, val in self.options.items(): - # options[key] = val + # Initialize the parent OpenMDAO ImplicitComponent + super().__init__(num_par_fd=num_par_fd, **kwargs) + + # Send initial options to the server + # This must be done here and not in initialize, as the values of the + # OpenMDAO options are only set after initialize has been called in the + # parent init function. That is why the parent init function must be called + # before sending the option values to the Philote server. self._client.send_options(kwargs) def initialize(self): """ - Define the OpenMDAO component options. + Define OpenMDAO component options based on server-available options. + + This method queries the Philote server for its available options and automatically + declares them as OpenMDAO component options with appropriate types. This allows + the server's configuration to be controlled through OpenMDAO's option system. + + The method is called automatically by OpenMDAO during component setup and should + not be called directly by users. + + Notes + ----- + - Server options are automatically discovered via gRPC + - Option types (bool, int, float, str) are preserved from server metadata + - This method requires an active connection to the Philote server """ # get the available options from the philote discipline self._client.get_available_options() @@ -80,19 +178,72 @@ def initialize(self): def setup(self): """ - Set up the OpenMDAO component. + Set up the OpenMDAO component by declaring inputs and outputs from server metadata. + + This method queries the Philote server for variable metadata and automatically + declares the corresponding inputs and outputs in the OpenMDAO component. For + implicit components, both inputs and outputs are declared, with outputs representing + the unknowns to be solved for. + + The method is called automatically by OpenMDAO during problem setup and should + not be called directly by users. + + Notes + ----- + - Input and output variables are automatically discovered from server + - Variable shapes and units are preserved from server metadata + - This method uses utility functions to handle the variable declaration process + - Outputs represent the unknowns in the implicit equations """ utils.client_setup(self) def setup_partials(self): """ - Set up the partials of the OpenMDAO component. + Set up partial derivatives for the OpenMDAO implicit component. + + This method declares partial derivatives for all residual-input and residual-output + pairs based on the server's partial derivative metadata. For implicit components, + this includes both dR/dinputs and dR/doutputs terms needed for Newton-type solvers. + + The method is called automatically by OpenMDAO during component setup and should + not be called directly by users. + + Notes + ----- + - Partial derivative structure is automatically discovered from server + - Both dR/dinputs and dR/doutputs partials are declared + - Sparsity patterns are preserved when available from server metadata """ utils.client_setup_partials(self) def apply_nonlinear(self, inputs, outputs, residuals, discrete_inputs=None, discrete_outputs=None): """ - Compute the residual evaluation. + Compute residual evaluation by calling the remote Philote server. + + This method transfers both input and output values to the server, requests a + residual evaluation, and transfers the computed residuals back to the OpenMDAO + component. The residuals represent R(inputs, outputs) where the goal is to + find outputs such that R = 0. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + outputs : dict + Dictionary of output values (current guess) with variable names as keys + residuals : dict + Dictionary to store computed residual values with variable names as keys + discrete_inputs : dict, optional + Dictionary of discrete input values (currently unused), by default None + discrete_outputs : dict, optional + Dictionary of discrete output values (currently unused), by default None + + Notes + ----- + - Both inputs and outputs are sent to the server for residual computation + - Residuals are computed as R(inputs, outputs) at the current point + - The goal is to find outputs where residuals are zero + - This method is called automatically by OpenMDAO during residual evaluation """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) local_outputs = utils.create_local_inputs( @@ -104,7 +255,30 @@ def apply_nonlinear(self, inputs, outputs, residuals, discrete_inputs=None, disc def solve_nonlinear(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): """ - Solves the residual for the implicit discipline. + Solve the implicit equations by calling the remote Philote server. + + This method transfers input values to the server, requests the server to solve + the implicit equations R(inputs, outputs) = 0 for the outputs, and transfers + the solved output values back to the OpenMDAO component. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + outputs : dict + Dictionary to store solved output values with variable names as keys + discrete_inputs : dict, optional + Dictionary of discrete input values (currently unused), by default None + discrete_outputs : dict, optional + Dictionary of discrete output values (currently unused), by default None + + Notes + ----- + - The server performs the nonlinear solve internally + - Server may use Newton's method, fixed-point iteration, or other solvers + - Convergence criteria are controlled by server options + - This method is called by OpenMDAO's nonlinear solvers + - Output values are updated with the converged solution """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) out = self._client.run_solve_residuals(local_inputs) @@ -112,7 +286,34 @@ def solve_nonlinear(self, inputs, outputs, discrete_inputs=None, discrete_output def linearize(self, inputs, outputs, partials, discrete_inputs=None, discrete_outputs=None): """ - Computes the residual gradients for the implicit discipline. + Compute partial derivatives of residuals by calling the remote Philote server. + + This method transfers both input and output values to the server, requests + computation of the residual Jacobian (dR/dinputs and dR/doutputs), and transfers + the computed partial derivatives back to the OpenMDAO component. These derivatives + are used by OpenMDAO's linear solvers and optimization algorithms. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + outputs : dict + Dictionary of output values with variable names as keys + partials : dict + Dictionary to store computed partial derivatives with (residual, variable) + tuples as keys + discrete_inputs : dict, optional + Dictionary of discrete input values (currently unused), by default None + discrete_outputs : dict, optional + Dictionary of discrete output values (currently unused), by default None + + Notes + ----- + - Computes both dR/dinputs and dR/doutputs partial derivatives + - Derivatives are computed at the current (inputs, outputs) point + - Server determines whether to use analytic or finite difference derivatives + - This method is called automatically by OpenMDAO when derivatives are needed + - Results are used by linear solvers and optimization algorithms """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) local_outputs = utils.create_local_inputs( From d6bc587b30748247b8df1b427a64949acacd26ed Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Fri, 15 Aug 2025 22:28:29 -0400 Subject: [PATCH 04/14] added implicit discipline documentation --- doc/tutorials/implicit_disciplines.md | 429 +++++++++++++++++++++ philote_mdo/general/implicit_client.py | 234 ++++++++++- philote_mdo/general/implicit_discipline.py | 335 +++++++++++++++- philote_mdo/general/implicit_server.py | 163 +++++++- 4 files changed, 1144 insertions(+), 17 deletions(-) diff --git a/doc/tutorials/implicit_disciplines.md b/doc/tutorials/implicit_disciplines.md index bedef07..6045ba0 100644 --- a/doc/tutorials/implicit_disciplines.md +++ b/doc/tutorials/implicit_disciplines.md @@ -1,2 +1,431 @@ # Creating Implicit Disciplines + +This guide explains how to create, serve, and use implicit disciplines in Philote. Implicit disciplines solve equations of the form R(inputs, outputs) = 0, where outputs are implicitly defined by the inputs through residual equations. Unlike explicit disciplines that compute outputs directly, implicit disciplines require solving nonlinear equations. + +## Overview + +Implicit disciplines are essential for modeling systems where: +- Outputs cannot be computed directly from inputs +- Equilibrium conditions must be satisfied +- Coupled physics require simultaneous solution +- Conservation laws or constraints must be enforced + +### Key Concepts + +**Residual Equations**: Mathematical expressions R(inputs, outputs) that must equal zero at the solution. + +**Implicit Solving**: Finding output values that satisfy R(inputs, outputs) = 0 for given inputs. + +**Jacobian Matrix**: Partial derivatives dR/d[inputs,outputs] used for optimization and sensitivity analysis. + +### Mathematical Formulation + +For implicit disciplines, we solve: +``` +R(x, y) = 0 +``` +Where: +- `x` are the inputs (design variables, parameters) +- `y` are the outputs (state variables, unknowns) +- `R` is the residual function + +The goal is to find `y` such that `R(x, y) = 0` for given inputs `x`. + +## Creating Implicit Disciplines + +### Basic Structure + +All implicit disciplines inherit from `ImplicitDiscipline` and must implement four key methods: + +```python +import numpy as np +import philote_mdo.general as pmdo + +class MyImplicitDiscipline(pmdo.ImplicitDiscipline): + def setup(self): + # Define inputs and outputs + pass + + def setup_partials(self): + # Declare which partial derivatives will be computed + pass + + def compute_residuals(self, inputs, outputs, residuals): + # Compute R(inputs, outputs) + pass + + def solve_residuals(self, inputs, outputs): + # Solve R(inputs, outputs) = 0 for outputs + pass + + def residual_partials(self, inputs, outputs, partials): + # Compute dR/d[inputs,outputs] + pass +``` + +### Example 1: Quadratic Equation Solver + +Let's implement a solver for the quadratic equation ax² + bx + c = 0: + +```python +import numpy as np +import philote_mdo.general as pmdo + +class QuadraticSolver(pmdo.ImplicitDiscipline): + def setup(self): + # Define inputs: coefficients of quadratic equation + self.add_input('a', shape=(1,)) + self.add_input('b', shape=(1,)) + self.add_input('c', shape=(1,)) + + # Define output: solution to the equation + self.add_output('x', shape=(1,)) + + def setup_partials(self): + # Declare all partial derivatives that will be computed + self.declare_partials('x', 'a') # dR/da + self.declare_partials('x', 'b') # dR/db + self.declare_partials('x', 'c') # dR/dc + self.declare_partials('x', 'x') # dR/dx + + def compute_residuals(self, inputs, outputs, residuals): + """Compute residual: R = ax² + bx + c""" + a = inputs['a'] + b = inputs['b'] + c = inputs['c'] + x = outputs['x'] + + residuals['x'] = a * x**2 + b * x + c + + def solve_residuals(self, inputs, outputs): + """Solve quadratic equation analytically""" + a = inputs['a'] + b = inputs['b'] + c = inputs['c'] + + # Use quadratic formula: x = (-b + sqrt(b²-4ac)) / 2a + discriminant = b**2 - 4*a*c + if discriminant < 0: + raise ValueError("No real solution exists") + + outputs['x'] = (-b + np.sqrt(discriminant)) / (2*a) + + def residual_partials(self, inputs, outputs, partials): + """Compute Jacobian of residual equation""" + a = inputs['a'] + b = inputs['b'] + c = inputs['c'] + x = outputs['x'] + + # Partial derivatives of R = ax² + bx + c + partials['x', 'a'] = x**2 # dR/da + partials['x', 'b'] = x # dR/db + partials['x', 'c'] = 1.0 # dR/dc + partials['x', 'x'] = 2*a*x + b # dR/dx +``` + +### Example 2: Coupled System of Equations + +For more complex systems with multiple residuals: + +```python +class CoupledSystem(pmdo.ImplicitDiscipline): + def setup(self): + # Inputs: parameters + self.add_input('p1', shape=(1,)) + self.add_input('p2', shape=(1,)) + + # Outputs: state variables + self.add_output('x1', shape=(1,)) + self.add_output('x2', shape=(1,)) + + def setup_partials(self): + # Declare partials for all residual-variable pairs + self.declare_partials('x1', ['p1', 'x1', 'x2']) + self.declare_partials('x2', ['p2', 'x1', 'x2']) + + def compute_residuals(self, inputs, outputs, residuals): + """ + Solve coupled system: + R1 = x1² + x2 - p1 = 0 + R2 = x1 + x2² - p2 = 0 + """ + p1, p2 = inputs['p1'], inputs['p2'] + x1, x2 = outputs['x1'], outputs['x2'] + + residuals['x1'] = x1**2 + x2 - p1 + residuals['x2'] = x1 + x2**2 - p2 + + def solve_residuals(self, inputs, outputs): + """Solve using Newton's method""" + p1, p2 = inputs['p1'], inputs['p2'] + x1, x2 = outputs['x1'], outputs['x2'] # Initial guess + + max_iter = 20 + tolerance = 1e-8 + + for i in range(max_iter): + # Compute residuals + residuals = {} + self.compute_residuals(inputs, outputs, residuals) + + # Check convergence + r1, r2 = residuals['x1'], residuals['x2'] + if np.abs(r1) < tolerance and np.abs(r2) < tolerance: + break + + # Compute Jacobian + partials = {} + self.residual_partials(inputs, outputs, partials) + + # Newton update: [x1; x2] -= J^(-1) * [r1; r2] + J11 = partials['x1', 'x1'] # dR1/dx1 + J12 = partials['x1', 'x2'] # dR1/dx2 + J21 = partials['x2', 'x1'] # dR2/dx1 + J22 = partials['x2', 'x2'] # dR2/dx2 + + det = J11*J22 - J12*J21 + dx1 = -(J22*r1 - J12*r2) / det + dx2 = -(-J21*r1 + J11*r2) / det + + outputs['x1'] += dx1 + outputs['x2'] += dx2 + else: + raise RuntimeError("Newton method failed to converge") + + def residual_partials(self, inputs, outputs, partials): + """Compute Jacobian matrix""" + x1, x2 = outputs['x1'], outputs['x2'] + + # Partials of R1 = x1² + x2 - p1 + partials['x1', 'p1'] = -1.0 + partials['x1', 'x1'] = 2*x1 + partials['x1', 'x2'] = 1.0 + + # Partials of R2 = x1 + x2² - p2 + partials['x2', 'p2'] = -1.0 + partials['x2', 'x1'] = 1.0 + partials['x2', 'x2'] = 2*x2 +``` + +## Serving Implicit Disciplines + +### Creating a Server + +To serve an implicit discipline over gRPC: + +```python +from concurrent import futures +import grpc +import philote_mdo.general as pmdo + +def run_server(): + # Create the discipline + discipline = QuadraticSolver() + + # Create gRPC server + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + # Create and attach implicit server + impl_server = pmdo.ImplicitServer(discipline=discipline) + impl_server.attach_to_server(server) + + # Start server + server.add_insecure_port('[::]:50051') + server.start() + print('Implicit discipline server started on port 50051') + server.wait_for_termination() + +if __name__ == '__main__': + run_server() +``` + +### Server Configuration + +For production servers, consider: + +```python +def run_production_server(): + discipline = QuadraticSolver() + + # Configure server with more workers for concurrent clients + server = grpc.server( + futures.ThreadPoolExecutor(max_workers=50), + options=[ + ('grpc.keepalive_time_ms', 30000), + ('grpc.keepalive_timeout_ms', 5000), + ('grpc.keepalive_permit_without_calls', True), + ('grpc.http2.max_pings_without_data', 0), + ('grpc.http2.min_time_between_pings_ms', 10000), + ('grpc.http2.min_ping_interval_without_data_ms', 300000) + ] + ) + + impl_server = pmdo.ImplicitServer(discipline=discipline) + impl_server.attach_to_server(server) + + # Use secure connection in production + private_key = open('server.key', 'rb').read() + certificate_chain = open('server.crt', 'rb').read() + credentials = grpc.ssl_server_credentials([(private_key, certificate_chain)]) + + server.add_secure_port('[::]:443', credentials) + server.start() + print('Secure implicit discipline server started on port 443') + server.wait_for_termination() +``` + +## Using Implicit Disciplines (Client Side) + +### Basic Client Usage + +```python +import grpc +import numpy as np +import philote_mdo.general as pmdo + +# Connect to server +channel = grpc.insecure_channel('localhost:50051') +client = pmdo.ImplicitClient(channel) + +# Define inputs for quadratic equation: x² - 5x + 6 = 0 +inputs = { + 'a': np.array([1.0]), + 'b': np.array([-5.0]), + 'c': np.array([6.0]) +} + +# Solve the implicit equations +solution = client.run_solve_residuals(inputs) +print(f"Solution: x = {solution['x'][0]}") # Should be 2.0 or 3.0 + +# Verify solution by computing residuals +outputs = {'x': solution['x']} +residuals = client.run_compute_residuals(inputs, outputs) +print(f"Residual: {residuals['x'][0]}") # Should be close to 0.0 + +# Compute Jacobian for sensitivity analysis +jacobian = client.run_residual_gradients(inputs, outputs) +print(f"dR/da = {jacobian[('x', 'a')][0]}") # x² = 4.0 +print(f"dR/db = {jacobian[('x', 'b')][0]}") # x = 2.0 +print(f"dR/dc = {jacobian[('x', 'c')][0]}") # 1.0 +print(f"dR/dx = {jacobian[('x', 'x')][0]}") # 2ax + b = -1.0 +``` + +### Advanced Client Operations + +```python +def analyze_quadratic_sensitivity(): + """Analyze how solution changes with coefficients""" + channel = grpc.insecure_channel('localhost:50051') + client = pmdo.ImplicitClient(channel) + + # Base case + base_inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([6.0])} + base_solution = client.run_solve_residuals(base_inputs) + base_x = base_solution['x'][0] + + print(f"Base solution: x = {base_x}") + + # Sensitivity to coefficient 'a' + perturbed_inputs = base_inputs.copy() + perturbed_inputs['a'] = np.array([1.1]) # 10% increase + + perturbed_solution = client.run_solve_residuals(perturbed_inputs) + perturbed_x = perturbed_solution['x'][0] + + sensitivity = (perturbed_x - base_x) / (0.1 * base_inputs['a'][0]) + print(f"Finite difference sensitivity dx/da ≈ {sensitivity}") + + # Compare with analytical sensitivity from Jacobian + outputs = {'x': base_solution['x']} + jacobian = client.run_residual_gradients(base_inputs, outputs) + + # For implicit function theorem: dx/da = -(dR/da) / (dR/dx) + dR_da = jacobian[('x', 'a')][0] + dR_dx = jacobian[('x', 'x')][0] + analytical_sensitivity = -dR_da / dR_dx + + print(f"Analytical sensitivity dx/da = {analytical_sensitivity}") + +def solve_parameter_sweep(): + """Solve for multiple parameter values""" + channel = grpc.insecure_channel('localhost:50051') + client = pmdo.ImplicitClient(channel) + + # Sweep parameter 'c' while keeping 'a' and 'b' fixed + c_values = np.linspace(0, 10, 21) + solutions = [] + + for c in c_values: + inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([c])} + + try: + solution = client.run_solve_residuals(inputs) + x = solution['x'][0] + solutions.append((c, x)) + print(f"c = {c:4.1f}, x = {x:6.3f}") + except grpc.RpcError as e: + print(f"c = {c:4.1f}, Failed to solve: {e}") + + return solutions +``` + +### Error Handling + +```python +def robust_client_usage(): + """Demonstrate proper error handling""" + try: + channel = grpc.insecure_channel('localhost:50051') + client = pmdo.ImplicitClient(channel) + + # This should fail - no real solution + inputs = {'a': np.array([1.0]), 'b': np.array([1.0]), 'c': np.array([1.0])} + + solution = client.run_solve_residuals(inputs) + print(f"Unexpected solution: {solution}") + + except grpc.RpcError as e: + print(f"Server error: {e.code()}") + print(f"Details: {e.details()}") + + except Exception as e: + print(f"Other error: {e}") + + finally: + # Clean up connection + if 'channel' in locals(): + channel.close() +``` + + +## API Reference + +### ImplicitDiscipline + +```{eval-rst} +.. autoclass:: philote_mdo.general.implicit_discipline.ImplicitDiscipline + :members: + :undoc-members: + :show-inheritance: +``` + +### ImplicitServer + +```{eval-rst} +.. autoclass:: philote_mdo.general.implicit_server.ImplicitServer + :members: + :undoc-members: + :show-inheritance: +``` + +### ImplicitClient + +```{eval-rst} +.. autoclass:: philote_mdo.general.implicit_client.ImplicitClient + :members: + :undoc-members: + :show-inheritance: +``` diff --git a/philote_mdo/general/implicit_client.py b/philote_mdo/general/implicit_client.py index e6a36d0..d62c2b1 100644 --- a/philote_mdo/general/implicit_client.py +++ b/philote_mdo/general/implicit_client.py @@ -35,18 +35,138 @@ class ImplicitClient(DisciplineClient): """ - Python client for implicit Philote discipline servers. + Python client for connecting to and interacting with implicit Philote discipline servers. + + This class provides a Python interface for communicating with remote implicit disciplines + over gRPC. It handles all the network communication, protocol buffer serialization, + and array streaming automatically, allowing users to interact with remote implicit + disciplines as if they were local Python objects. + + The client supports three main operations for implicit disciplines: + - Residual computation: R(inputs, outputs) evaluation + - Implicit solving: Find outputs such that R(inputs, outputs) = 0 + - Jacobian computation: Compute dR/d[inputs,outputs] for optimization + + Key Features: + - Automatic gRPC connection management + - Efficient streaming of large arrays + - Variable metadata discovery from server + - Option synchronization with server + - Thread-safe operation + - Automatic data type conversion + + Typical Usage: + >>> import grpc + >>> import philote_mdo.general as pmdo + >>> + >>> # Create gRPC channel to server + >>> channel = grpc.insecure_channel('localhost:50051') + >>> client = pmdo.ImplicitClient(channel) + >>> + >>> # Use the client for residual computation + >>> inputs = {'a': [1.0], 'b': [-5.0], 'c': [6.0]} + >>> outputs = {'x': [2.0]} # Current guess + >>> residuals = client.run_compute_residuals(inputs, outputs) + >>> print(f"Residual: {residuals['x']}") # Should be close to 0 + >>> + >>> # Solve the implicit equations + >>> solution = client.run_solve_residuals(inputs) + >>> print(f"Solution: {solution['x']}") # x = 2 or 3 for quadratic + >>> + >>> # Compute Jacobian for optimization + >>> jacobian = client.run_residual_gradients(inputs, outputs) + >>> print(f"dR/dx: {jacobian[('x', 'x')]}") # Jacobian element + + Attributes: + _impl_stub (ImplicitServiceStub): gRPC stub for implicit service calls + + Notes: + - Inherits connection management and metadata handling from DisciplineClient + - All methods are synchronous and block until server responds + - Large arrays are automatically streamed for efficiency + - The server must be running and reachable when creating the client """ def __init__(self, channel): + """ + Initialize the implicit discipline client. + + Creates a connection to the implicit discipline server and sets up the + gRPC stub for making remote procedure calls. + + Parameters + ---------- + channel : grpc.Channel + gRPC channel for connecting to the implicit discipline server. + Should be created with grpc.insecure_channel() or grpc.secure_channel(). + + Examples + -------- + >>> import grpc + >>> channel = grpc.insecure_channel('localhost:50051') + >>> client = ImplicitClient(channel) + + >>> # With secure channel + >>> credentials = grpc.ssl_channel_credentials() + >>> secure_channel = grpc.secure_channel('server:443', credentials) + >>> client = ImplicitClient(secure_channel) + + Notes + ----- + - The server must be running and reachable during initialization + - Variable metadata and options are automatically discovered from server + """ super().__init__(channel=channel) self._impl_stub = disc.ImplicitServiceStub(channel) def run_compute_residuals(self, inputs, outputs): """ - Requests and receives the residual evaluation from the analysis server - for a set of inputs and outputs (sent to the server). + Compute residuals R(inputs, outputs) by calling the remote server. + + This method sends both inputs and outputs to the server and receives the + computed residual values. The residuals represent the constraint equations + that should equal zero at the solution: R(inputs, outputs) = 0. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys and numpy arrays as values + outputs : dict + Dictionary of output values (current guess) with variable names as keys + and numpy arrays as values + + Returns + ------- + dict + Dictionary of computed residual values with variable names as keys + and numpy arrays as values + + Examples + -------- + >>> # Quadratic equation: ax² + bx + c = 0 + >>> inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([6.0])} + >>> outputs = {'x': np.array([2.0])} # Guess x=2 + >>> residuals = client.run_compute_residuals(inputs, outputs) + >>> print(residuals['x']) # Should be [0.0] since 1*4 - 5*2 + 6 = 0 + + >>> # Check a different guess + >>> outputs = {'x': np.array([1.0])} # Guess x=1 + >>> residuals = client.run_compute_residuals(inputs, outputs) + >>> print(residuals['x']) # Should be [2.0] since 1*1 - 5*1 + 6 = 2 + + >>> # Multi-dimensional case + >>> inputs = {'params': np.array([1.0, 2.0, 3.0])} + >>> outputs = {'states': np.array([0.5, 1.5])} + >>> residuals = client.run_compute_residuals(inputs, outputs) + + Notes + ----- + - Both inputs and outputs must be provided to evaluate residuals + - Residual arrays have the same shape as their corresponding output arrays + - Large arrays are automatically streamed for efficiency + - This is typically used for residual evaluation during Newton iterations """ + # Assemble input messages and call server messages = self._assemble_input_messages(inputs, outputs) responses = self._impl_stub.ComputeResiduals(iter(messages)) residuals = self._recover_residuals(responses) @@ -55,9 +175,56 @@ def run_compute_residuals(self, inputs, outputs): def run_solve_residuals(self, inputs): """ - Calls the RPC that solves the residual equations on the remote - discipline server. + Solve implicit equations R(inputs, outputs) = 0 by calling the remote server. + + This method sends inputs to the server and receives the solved output values + that satisfy the residual equations. The server performs the nonlinear solving + internally using its implemented solving strategy. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys and numpy arrays as values + + Returns + ------- + dict + Dictionary of solved output values with variable names as keys + and numpy arrays as values + + Examples + -------- + >>> # Solve quadratic equation: ax² + bx + c = 0 + >>> inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([6.0])} + >>> solution = client.run_solve_residuals(inputs) + >>> print(solution['x']) # Should be [2.0] or [3.0] (roots of x²-5x+6=0) + + >>> # Solve system of equations + >>> inputs = { + ... 'param1': np.array([2.0]), + ... 'param2': np.array([3.0]) + ... } + >>> solution = client.run_solve_residuals(inputs) + >>> print(solution['state1'], solution['state2']) + + >>> # Vector case + >>> inputs = {'design': np.array([1.0, 2.0, 3.0])} + >>> solution = client.run_solve_residuals(inputs) + >>> print(solution['variables']) # Multi-dimensional solution + + Raises + ------ + grpc.RpcError + If the server fails to solve the equations (non-convergence, etc.) + + Notes + ----- + - Only inputs are required; initial guesses are handled by the server + - The server determines convergence criteria and solving strategy + - May raise exceptions for ill-conditioned or non-convergent problems + - Solution quality depends on the server's implementation and input conditioning """ + # Assemble input messages and call server messages = self._assemble_input_messages(inputs) responses = self._impl_stub.SolveResiduals(iter(messages)) outputs = self._recover_outputs(responses) @@ -65,8 +232,63 @@ def run_solve_residuals(self, inputs): def run_residual_gradients(self, inputs, outputs): """ - Calls the RPC to compute the gradients of the residual equations. + Compute Jacobian of residuals dR/d[inputs,outputs] by calling the remote server. + + This method sends both inputs and outputs to the server and receives the + computed partial derivatives of residuals with respect to all inputs and outputs. + These derivatives are essential for gradient-based optimization and sensitivity analysis. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys and numpy arrays as values + outputs : dict + Dictionary of output values with variable names as keys and numpy arrays as values + + Returns + ------- + dict + Dictionary of partial derivatives with (residual_name, variable_name) tuples + as keys and numpy arrays as values + + Examples + -------- + >>> # Jacobian for quadratic equation R = ax² + bx + c + >>> inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([6.0])} + >>> outputs = {'x': np.array([2.0])} + >>> jacobian = client.run_residual_gradients(inputs, outputs) + >>> + >>> # Access individual partial derivatives + >>> print(jacobian[('x', 'a')]) # dR/da = x² = 4.0 + >>> print(jacobian[('x', 'b')]) # dR/db = x = 2.0 + >>> print(jacobian[('x', 'c')]) # dR/dc = 1.0 + >>> print(jacobian[('x', 'x')]) # dR/dx = 2ax + b = -1.0 + + >>> # Multi-residual system + >>> inputs = {'p1': np.array([1.0]), 'p2': np.array([2.0])} + >>> outputs = {'x1': np.array([0.5]), 'x2': np.array([1.0])} + >>> jacobian = client.run_residual_gradients(inputs, outputs) + >>> + >>> # Access cross-derivatives + >>> print(jacobian[('x1', 'p1')]) # dR1/dp1 + >>> print(jacobian[('x1', 'x2')]) # dR1/dx2 (coupling) + >>> print(jacobian[('x2', 'x1')]) # dR2/dx1 (coupling) + + >>> # Vector/matrix case + >>> inputs = {'design': np.array([1.0, 2.0])} + >>> outputs = {'states': np.array([0.5, 1.5, 2.5])} + >>> jacobian = client.run_residual_gradients(inputs, outputs) + >>> print(jacobian[('states', 'design')].shape) # (3, 2) Jacobian block + + Notes + ----- + - Both inputs and outputs must be provided for Jacobian evaluation + - Returns both dR/dinputs and dR/doutputs partial derivatives + - Jacobian elements are computed at the current (inputs, outputs) point + - Used by optimization algorithms and sensitivity analysis tools + - For large problems, consider matrix-free methods if available """ + # Assemble input messages and call server messages = self._assemble_input_messages(inputs, outputs) responses = self._impl_stub.ComputeResidualGradients(iter(messages)) partials = self._recover_partials(responses) diff --git a/philote_mdo/general/implicit_discipline.py b/philote_mdo/general/implicit_discipline.py index 3dd830c..6a106d2 100644 --- a/philote_mdo/general/implicit_discipline.py +++ b/philote_mdo/general/implicit_discipline.py @@ -32,22 +32,351 @@ class ImplicitDiscipline(pmdo.Discipline): """ - Implementation class for implicit disciplines. + Base class for implementing implicit disciplines in Philote. + + Implicit disciplines solve equations of the form R(inputs, outputs) = 0, where the outputs + are implicitly defined by the inputs through residual equations. Unlike explicit disciplines + that compute outputs directly, implicit disciplines require solving nonlinear equations + to find outputs that satisfy the residual constraints. + + This class provides the framework for implementing implicit disciplines that can be served + over gRPC and integrated with optimization frameworks like OpenMDAO. Subclasses must + implement the required methods to define the residual equations, solving strategy, and + derivative computation. + + Key Features: + - Residual equation definition via compute_residuals + - Implicit solving capability via solve_residuals + - Jacobian computation for optimization via residual_partials + - Linear operator support for matrix-free methods via apply_linear + - Automatic gRPC service generation + - Integration with optimization frameworks + + Mathematical Formulation: + For implicit disciplines, we solve: R(x, y) = 0 + Where: + - x are the inputs (design variables, parameters) + - y are the outputs (state variables, unknowns) + - R is the residual function + + The goal is to find y such that R(x, y) = 0 for given inputs x. + + Typical Usage: + >>> import numpy as np + >>> import philote_mdo.general as pmdo + >>> + >>> class QuadraticSolver(pmdo.ImplicitDiscipline): + ... def setup(self): + ... self.add_input('a', shape=(1,)) + ... self.add_input('b', shape=(1,)) + ... self.add_input('c', shape=(1,)) + ... self.add_output('x', shape=(1,)) + ... + ... def compute_residuals(self, inputs, outputs, residuals): + ... a, b, c = inputs['a'], inputs['b'], inputs['c'] + ... x = outputs['x'] + ... residuals['x'] = a * x**2 + b * x + c + ... + ... def solve_residuals(self, inputs, outputs): + ... a, b, c = inputs['a'], inputs['b'], inputs['c'] + ... discriminant = b**2 - 4*a*c + ... outputs['x'] = (-b + np.sqrt(discriminant)) / (2*a) + + Attributes: + _is_implicit (bool): Always True for implicit disciplines + + Notes: + - All methods except __init__ must be implemented by subclasses + - The discipline automatically supports both local and remote execution + - Residual equations should be formulated such that R = 0 at the solution + - Both forward and reverse mode automatic differentiation are supported """ def __init__(self): + """ + Initialize the implicit discipline. + + Sets up the base discipline and marks this as an implicit discipline type. + Subclasses should call super().__init__() and then implement the required methods. + + Examples + -------- + >>> class MyImplicitDiscipline(ImplicitDiscipline): + ... def __init__(self): + ... super().__init__() + ... # Additional initialization if needed + """ super().__init__() self._is_implicit = True def compute_residuals(self, inputs, outputs, residuals): + """ + Compute residual equations R(inputs, outputs) that must equal zero. + + This method evaluates the residual functions given the current values of inputs + and outputs. The residuals represent the constraint equations that must be + satisfied: R(inputs, outputs) = 0. The goal of the implicit solver is to find + output values that drive these residuals to zero. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys and numpy arrays as values + outputs : dict + Dictionary of current output values (current guess) with variable names as keys + residuals : dict + Dictionary to store computed residual values with variable names as keys. + Must be populated by this method. + + Examples + -------- + For a quadratic equation ax² + bx + c = 0: + + >>> def compute_residuals(self, inputs, outputs, residuals): + ... a = inputs['a'] + ... b = inputs['b'] + ... c = inputs['c'] + ... x = outputs['x'] + ... residuals['x'] = a * x**2 + b * x + c + + For a system of coupled equations: + + >>> def compute_residuals(self, inputs, outputs, residuals): + ... x1, x2 = outputs['x1'], outputs['x2'] + ... p1, p2 = inputs['p1'], inputs['p2'] + ... residuals['x1'] = x1**2 + x2 - p1 + ... residuals['x2'] = x1 + x2**2 - p2 + + Notes + ----- + - Residuals should be zero at the solution + - All output variables should have corresponding residual equations + - Residual arrays must have the same shape as their corresponding outputs + - This method is called during both residual evaluation and solving + """ raise NotImplementedError("compute_residuals not implemented") def solve_residuals(self, inputs, outputs): + """ + Solve the implicit equations to find outputs that satisfy R(inputs, outputs) = 0. + + This method implements the nonlinear solving strategy to find output values that + drive the residuals to zero. The specific solving approach (Newton's method, + fixed-point iteration, analytical solution, etc.) depends on the problem + characteristics and implementation choice. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys and numpy arrays as values + outputs : dict + Dictionary of output values to be updated with the solution. Modified in-place. + + Examples + -------- + Analytical solution for quadratic equation: + + >>> def solve_residuals(self, inputs, outputs): + ... a, b, c = inputs['a'], inputs['b'], inputs['c'] + ... discriminant = b**2 - 4*a*c + ... outputs['x'] = (-b + np.sqrt(discriminant)) / (2*a) + + Iterative Newton's method: + + >>> def solve_residuals(self, inputs, outputs): + ... max_iter = 20 + ... tolerance = 1e-8 + ... + ... for i in range(max_iter): + ... residuals = {} + ... self.compute_residuals(inputs, outputs, residuals) + ... + ... if np.abs(residuals['x']) < tolerance: + ... break + ... + ... # Newton update: x_new = x_old - R/dR_dx + ... partials = {} + ... self.residual_partials(inputs, outputs, partials) + ... outputs['x'] -= residuals['x'] / partials['x', 'x'] + + Fixed-point iteration: + + >>> def solve_residuals(self, inputs, outputs): + ... # For equation x = f(x), iterate x_{n+1} = f(x_n) + ... for i in range(100): + ... x_old = outputs['x'].copy() + ... outputs['x'] = self._fixed_point_function(inputs, outputs['x']) + ... if np.abs(outputs['x'] - x_old) < 1e-8: + ... break + + Notes + ----- + - The outputs dictionary is modified in-place + - Convergence criteria and tolerances should be appropriate for the problem + - Consider numerical stability and robustness for different input ranges + - May throw exceptions for non-convergent cases or ill-conditioned problems + - Initial guesses in outputs may affect convergence + """ raise NotImplementedError("solve_residuals not implemented") def residual_partials(self, inputs, outputs, partials): + """ + Compute partial derivatives of residuals with respect to inputs and outputs. + + This method computes the Jacobian matrix elements dR/dx for optimization and + sensitivity analysis. For implicit disciplines, this includes both dR/dinputs + and dR/doutputs terms, which are essential for gradient-based optimization + and coupled system analysis. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + outputs : dict + Dictionary of output values with variable names as keys + partials : dict + Dictionary to store partial derivatives with (residual, variable) tuples as keys. + Must be populated by this method with numpy arrays as values. + + Examples + -------- + For quadratic equation R = ax² + bx + c: + + >>> def residual_partials(self, inputs, outputs, partials): + ... a, b, c = inputs['a'], inputs['b'], inputs['c'] + ... x = outputs['x'] + ... + ... # Partial derivatives of residual w.r.t. inputs + ... partials['x', 'a'] = x**2 # dR/da + ... partials['x', 'b'] = x # dR/db + ... partials['x', 'c'] = 1.0 # dR/dc + ... + ... # Partial derivative of residual w.r.t. output + ... partials['x', 'x'] = 2*a*x + b # dR/dx + + For system of equations: + + >>> def residual_partials(self, inputs, outputs, partials): + ... x1, x2 = outputs['x1'], outputs['x2'] + ... + ... # First residual: R1 = x1² + x2 - p1 + ... partials['x1', 'p1'] = -1.0 + ... partials['x1', 'x1'] = 2*x1 + ... partials['x1', 'x2'] = 1.0 + ... + ... # Second residual: R2 = x1 + x2² - p2 + ... partials['x2', 'p2'] = -1.0 + ... partials['x2', 'x1'] = 1.0 + ... partials['x2', 'x2'] = 2*x2 + + Notes + ----- + - Partial derivatives should be computed at the current (inputs, outputs) point + - All declared partial derivative pairs must be computed + - Use setup_partials() to declare which partials will be computed + - Partials are used for optimization, sensitivity analysis, and coupled solving + - Consider finite differencing if analytical derivatives are too complex + - The dR/doutputs terms are crucial for implicit system solution algorithms + """ raise NotImplementedError("residual_partials not implemented") - def apply_linear(self, inputs, outputs, mode): - """ """ + def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): + """ + Apply the linearized residual operator for matrix-free methods. + + This method implements the action of the residual Jacobian on vectors without + explicitly forming the full Jacobian matrix. It supports both forward and reverse + mode automatic differentiation, enabling efficient gradient computation for + large-scale problems. + + The method computes: + - Forward mode: d_residuals = J * [d_inputs; d_outputs] + - Reverse mode: [d_inputs; d_outputs] += J^T * d_residuals + + Where J is the Jacobian matrix [dR/dinputs, dR/doutputs]. + + Parameters + ---------- + inputs : dict + Dictionary of input values with variable names as keys + outputs : dict + Dictionary of output values with variable names as keys + d_inputs : dict + Dictionary of input perturbations/adjoint variables + d_outputs : dict + Dictionary of output perturbations/adjoint variables + d_residuals : dict + Dictionary of residual perturbations/adjoint variables + mode : str + Differentiation mode: 'fwd' for forward mode, 'rev' for reverse mode + + Examples + -------- + For quadratic equation R = ax² + bx + c: + + >>> def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): + ... a, b, c = inputs['a'], inputs['b'], inputs['c'] + ... x = outputs['x'] + ... + ... if mode == 'fwd': + ... if 'x' in d_residuals: + ... # Forward: dR = (dR/da)*da + (dR/db)*db + (dR/dc)*dc + (dR/dx)*dx + ... if 'a' in d_inputs: + ... d_residuals['x'] += x**2 * d_inputs['a'] + ... if 'b' in d_inputs: + ... d_residuals['x'] += x * d_inputs['b'] + ... if 'c' in d_inputs: + ... d_residuals['x'] += d_inputs['c'] + ... if 'x' in d_outputs: + ... d_residuals['x'] += (2*a*x + b) * d_outputs['x'] + ... + ... elif mode == 'rev': + ... if 'x' in d_residuals: + ... # Reverse: accumulate adjoint contributions + ... if 'a' in d_inputs: + ... d_inputs['a'] += x**2 * d_residuals['x'] + ... if 'b' in d_inputs: + ... d_inputs['b'] += x * d_residuals['x'] + ... if 'c' in d_inputs: + ... d_inputs['c'] += d_residuals['x'] + ... if 'x' in d_outputs: + ... d_outputs['x'] += (2*a*x + b) * d_residuals['x'] + + For coupled system: + + >>> def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): + ... x1, x2 = outputs['x1'], outputs['x2'] + ... + ... if mode == 'fwd': + ... # R1 = x1² + x2 - p1 + ... if 'x1' in d_residuals: + ... if 'x1' in d_outputs: + ... d_residuals['x1'] += 2*x1 * d_outputs['x1'] + ... if 'x2' in d_outputs: + ... d_residuals['x1'] += d_outputs['x2'] + ... if 'p1' in d_inputs: + ... d_residuals['x1'] -= d_inputs['p1'] + ... + ... # R2 = x1 + x2² - p2 + ... if 'x2' in d_residuals: + ... if 'x1' in d_outputs: + ... d_residuals['x2'] += d_outputs['x1'] + ... if 'x2' in d_outputs: + ... d_residuals['x2'] += 2*x2 * d_outputs['x2'] + ... if 'p2' in d_inputs: + ... d_residuals['x2'] -= d_inputs['p2'] + ... + ... elif mode == 'rev': + ... # Reverse mode: transpose operations + ... # ... (similar structure with += operations) + + Notes + ----- + - This method enables matrix-free Newton and quasi-Newton methods + - Essential for large-scale problems where storing full Jacobians is impractical + - Forward mode: multiply Jacobian by vector (useful for directional derivatives) + - Reverse mode: multiply Jacobian transpose by vector (useful for gradients) + - Must be consistent with residual_partials() when implemented + - Used by advanced optimization algorithms and coupled system solvers + """ raise NotImplementedError("apply_linear not implemented") diff --git a/philote_mdo/general/implicit_server.py b/philote_mdo/general/implicit_server.py index 1db1928..21d45e3 100644 --- a/philote_mdo/general/implicit_server.py +++ b/philote_mdo/general/implicit_server.py @@ -35,22 +35,123 @@ class ImplicitServer(pmdo.DisciplineServer, disc.ImplicitServiceServicer): """ - Base class for creating an implicit discipline server. + gRPC server implementation for serving implicit Philote disciplines. + + This class creates a gRPC server that exposes implicit discipline functionality + over the network. It handles client requests for residual computation, implicit + solving, and Jacobian computation by delegating to the underlying implicit discipline. + The server supports streaming of large arrays and handles all protocol buffer + serialization/deserialization automatically. + + The server implements three main RPC methods: + - ComputeResiduals: Evaluate residual equations R(inputs, outputs) + - SolveResiduals: Solve implicit equations R(inputs, outputs) = 0 + - ComputeResidualGradients: Compute Jacobian dR/d[inputs,outputs] + + Key Features: + - Automatic gRPC service implementation from discipline + - Streaming support for large array transfers + - Thread-safe concurrent client handling + - Automatic variable metadata discovery and sharing + - Option handling and configuration + - Error handling and status reporting + + Typical Usage: + >>> from concurrent import futures + >>> import grpc + >>> import philote_mdo.general as pmdo + >>> + >>> # Create your implicit discipline + >>> discipline = MyImplicitDiscipline() + >>> + >>> # Create and configure server + >>> server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + >>> impl_server = pmdo.ImplicitServer(discipline=discipline) + >>> impl_server.attach_to_server(server) + >>> + >>> # Start server + >>> server.add_insecure_port('[::]:50051') + >>> server.start() + >>> print('Server started on port 50051') + >>> server.wait_for_termination() + + Attributes: + _discipline (ImplicitDiscipline): The underlying implicit discipline being served + + Notes: + - Inherits from both DisciplineServer and gRPC ImplicitServiceServicer + - Automatically handles protocol buffer conversion and array streaming + - Thread-safe for concurrent client connections + - The underlying discipline must implement all required implicit methods """ def __init__(self, discipline=None): + """ + Initialize the implicit discipline server. + + Parameters + ---------- + discipline : ImplicitDiscipline, optional + The implicit discipline to serve. Must implement compute_residuals, + solve_residuals, and residual_partials methods. + + Examples + -------- + >>> discipline = MyImplicitDiscipline() + >>> server = ImplicitServer(discipline=discipline) + """ super().__init__(discipline=discipline) def attach_to_server(self, server): """ - Attaches this discipline server class to a gRPC server. + Attach this implicit service to a gRPC server. + + This method registers the implicit discipline service with the gRPC server, + enabling clients to connect and call implicit discipline methods remotely. + + Parameters + ---------- + server : grpc.Server + The gRPC server instance to attach this service to + + Examples + -------- + >>> server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + >>> impl_server = ImplicitServer(discipline=my_discipline) + >>> impl_server.attach_to_server(server) + >>> server.add_insecure_port('[::]:50051') + >>> server.start() """ super().attach_to_server(server) disc.add_ImplicitServiceServicer_to_server(self, server) def ComputeResiduals(self, request_iterator, context): """ - Computes the residuals and sends the results to the client. + Handle client requests for residual computation. + + This gRPC method receives inputs and outputs from the client, calls the + underlying discipline's compute_residuals method, and streams the computed + residuals back to the client. The method handles protocol buffer conversion + and array streaming automatically. + + Parameters + ---------- + request_iterator : Iterator[data.Array] + Stream of input and output arrays from the client + context : grpc.ServicerContext + gRPC context for the request + + Yields + ------ + data.Array + Stream of residual arrays back to the client + + Notes + ----- + - Automatically handles large array streaming + - Converts protocol buffers to numpy arrays for discipline computation + - Streams results back in chunks for efficiency + - This method is called automatically by the gRPC framework """ # inputs and outputs inputs = {} @@ -62,7 +163,7 @@ def ComputeResiduals(self, request_iterator, context): self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) self.process_inputs(request_iterator, flat_inputs, flat_outputs) - # call the user-defined compute_residuals function + # Call the user-defined compute_residuals function self._discipline.compute_residuals(inputs, outputs, residuals) for res_name, value in residuals.items(): @@ -77,7 +178,30 @@ def ComputeResiduals(self, request_iterator, context): def SolveResiduals(self, request_iterator, context): """ - Solves the implicit discipline so that the residuals are driven to zero. + Handle client requests for implicit equation solving. + + This gRPC method receives inputs from the client, calls the underlying + discipline's solve_residuals method to find outputs that satisfy + R(inputs, outputs) = 0, and streams the solved outputs back to the client. + + Parameters + ---------- + request_iterator : Iterator[data.Array] + Stream of input arrays from the client + context : grpc.ServicerContext + gRPC context for the request + + Yields + ------ + data.Array + Stream of solved output arrays back to the client + + Notes + ----- + - The discipline's solve_residuals method performs the actual solving + - May raise exceptions if the solve fails to converge + - Outputs are streamed back in chunks for large arrays + - This method is called automatically by the gRPC framework """ # inputs and outputs inputs = {} @@ -88,7 +212,7 @@ def SolveResiduals(self, request_iterator, context): self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) self.process_inputs(request_iterator, flat_inputs, flat_outputs) - # call the user-defined solve function + # Call the user-defined solve function self._discipline.solve_residuals(inputs, outputs) for output_name, value in outputs.items(): @@ -103,7 +227,30 @@ def SolveResiduals(self, request_iterator, context): def ComputeResidualGradients(self, request_iterator, context): """ - Computes the residual gradients and sends the results to the client. + Handle client requests for residual Jacobian computation. + + This gRPC method receives inputs and outputs from the client, calls the + underlying discipline's residual_partials method to compute the Jacobian + dR/d[inputs,outputs], and streams the partial derivatives back to the client. + + Parameters + ---------- + request_iterator : Iterator[data.Array] + Stream of input and output arrays from the client + context : grpc.ServicerContext + gRPC context for the request + + Yields + ------ + data.Array + Stream of partial derivative arrays back to the client + + Notes + ----- + - Computes both dR/dinputs and dR/doutputs partial derivatives + - Results are streamed back with proper naming for Jacobian reconstruction + - Used for gradient-based optimization and sensitivity analysis + - This method is called automatically by the gRPC framework """ # inputs and outputs inputs = {} @@ -115,7 +262,7 @@ def ComputeResidualGradients(self, request_iterator, context): jac = self.preallocate_partials() self.process_inputs(request_iterator, flat_inputs, flat_outputs) - # call the user-defined residual partials function + # Call the user-defined residual partials function self._discipline.residual_partials(inputs, outputs, jac) for jac, value in jac.items(): From 95647a48944e1608f58b34824370bd9b63d1155a Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Sat, 16 Aug 2025 22:00:34 -0400 Subject: [PATCH 05/14] small implicit discipline doc fixes --- doc/tutorials/implicit_disciplines.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/tutorials/implicit_disciplines.md b/doc/tutorials/implicit_disciplines.md index 6045ba0..c391b17 100644 --- a/doc/tutorials/implicit_disciplines.md +++ b/doc/tutorials/implicit_disciplines.md @@ -6,10 +6,8 @@ This guide explains how to create, serve, and use implicit disciplines in Philot ## Overview Implicit disciplines are essential for modeling systems where: -- Outputs cannot be computed directly from inputs +- Outputs cannot be computed directly from inputs (e.g., iterative problems) - Equilibrium conditions must be satisfied -- Coupled physics require simultaneous solution -- Conservation laws or constraints must be enforced ### Key Concepts From effdeeea4e91c72072a4a943b0e210ba5aa65caa Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Sat, 16 Aug 2025 22:05:12 -0400 Subject: [PATCH 06/14] updated change log --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b10c6f0..8d68886 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ ### Documentation & Infrastructure - Updated installation instructions to reflect PyPI install option. +- Added documentation for implicit disciplines. +- Added documentation for OpenMDAO clients +- Added documentation for the OpenMDOA subproblem discipline. ## Version 0.7.0 From eaaedc4776537025c70027fac12845bff97c066b Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Sat, 16 Aug 2025 23:16:27 -0400 Subject: [PATCH 07/14] moved license and disclaimer to its own docs section --- doc/_config.yml | 14 +++----------- doc/_toc.yml | 1 + doc/license.md | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 doc/license.md diff --git a/doc/_config.yml b/doc/_config.yml index e2e9823..0c3672e 100644 --- a/doc/_config.yml +++ b/doc/_config.yml @@ -4,7 +4,7 @@ title: Philote-Python author: Christopher A. Lupp logo: graphics/philote-python.svg -copyright: 2022-2024 +copyright: 2022-2025 # Force re-execution of notebooks on each build. # See https://jupyterbook.org/content/execute.html @@ -38,16 +38,8 @@ html: favicon: graphics/favicons/favicon.ico use_issues_button: true use_repository_button: true - extra_footer: "This work has been cleared for public release, distribution - unlimited, case number: AFRL-2023-5713. - The views expressed are those of the authors and do not reflect the official - guidance or position of the United States Government, the Department of - Defense or of the United States Air Force. - Statement from DoD: The Appearance of external hyperlinks does not constitute - endorsement by the United States Department of Defense (DoD) of the linked - websites, of the information, products, or services contained therein. The DoD - does not exercise any editorial, security, or other control over the - information you may find at these locations." + show_powered_by: false + extra_navbar: "" # Sphinx options sphinx: diff --git a/doc/_toc.yml b/doc/_toc.yml index b1c7851..5209752 100644 --- a/doc/_toc.yml +++ b/doc/_toc.yml @@ -8,6 +8,7 @@ parts: chapters: - file: installation - file: tutorials/quickstart + - file: license - caption: Tutorials chapters: diff --git a/doc/license.md b/doc/license.md new file mode 100644 index 0000000..477b60a --- /dev/null +++ b/doc/license.md @@ -0,0 +1,34 @@ + +# License + +This package is licensed under the Apache 2 license: + + +> Copyright 2022-2025 Christopher A. Lupp +> +> Licensed under the Apache License, Version 2.0 (the "License"); +> you may not use this file except in compliance with the License. +> You may obtain a copy of the License at +> +> http://www.apache.org/licenses/LICENSE-2.0 +> +> Unless required by applicable law or agreed to in writing, software +> distributed under the License is distributed on an "AS IS" BASIS, +> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +> See the License for the specific language governing permissions and +> limitations under the License. + + + +This work has been cleared for public release, distribution unlimited, case +number: AFRL-2023-5713. + +The views expressed are those of the authors and do not reflect the official +guidance or position of the United States Government, the Department of Defense +or of the United States Air Force. + +Statement from DoD: The Appearance of external hyperlinks does not constitute +endorsement by the United States Department of Defense (DoD) of the linked +websites, of the information, products, or services contained therein. The DoD +does not exercise any editorial, security, or other control over the information +you may find at these locations. From f04180163deda99fe7b4c5131c24c2176e4de4e0 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Tue, 7 Apr 2026 23:06:23 -0400 Subject: [PATCH 08/14] docs: migrate from Jupyter Book to Docusaurus (#57) * docs: migrate from Jupyter Book to Docusaurus Port the existing documentation from Jupyter Book to a Docusaurus site under docs/, mirroring the layout and theming used by Delphi. Replace the gh-pages-based deploy job with the GitHub Pages artifact pipeline, and add a release workflow that handles version bumps, changelog updates, and Docusaurus version snapshots on labeled PRs into main. CHANGELOG is reformatted to Keep-a-Changelog so the release workflow's sed/awk drives the [Unreleased] header rewrite and comparison links. * docs: point repo URLs at MDO-Standards/Philote-Python Update Docusaurus config (url, organizationName, edit/footer links), release workflow REPO variable, and CHANGELOG comparison links to use the canonical MDO-Standards org instead of chrislupp. Also expand the Unreleased CHANGELOG entry with the docs migration, deploy workflow swap, and release workflow additions. * docs: add CLAUDE.md with release workflow documentation * fix: unbreak Sellar and ImplicitDiscipline.apply_linear tests The Sellar example set input defaults on the inner `cycle` subgroup, but `obj_cmp` promoted `x` and `z` to the top level with different defaults; newer OpenMDAO releases reject this during `final_setup`. Move the defaults to the top-level `SellarMDA` group so the promoted values agree. Also update `test_implicit_discipline_apply_linear_not_implemented` to pass the current six-argument signature; the stale three-argument call was raising `TypeError` before the `NotImplementedError` assertion could fire. --- .github/workflows/documentation.yaml | 77 +- .github/workflows/release.yaml | 293 + CHANGELOG.md | 85 +- CLAUDE.md | 156 + docs/.gitignore | 20 + docs/README.md | 29 + docs/docs/about/license.md | 28 + docs/docs/getting-started/installation.md | 57 + docs/docs/getting-started/quickstart.md | 202 + docs/docs/openmdao/openmdao-clients.md | 341 + docs/docs/openmdao/openmdao-groups.md | 182 + docs/docs/tutorials/explicit-disciplines.md | 111 + docs/docs/tutorials/implicit-disciplines.md | 407 + docs/docs/tutorials/units.md | 246 + docs/docusaurus.config.ts | 138 + docs/package-lock.json | 18717 ++++++++++++++++++ docs/package.json | 50 + docs/sidebars.ts | 38 + docs/src/css/custom.css | 104 + docs/src/pages/index.module.css | 410 + docs/src/pages/index.tsx | 391 + docs/static/.nojekyll | 0 docs/static/img/favicon.ico | Bin 0 -> 15086 bytes docs/static/img/logo.svg | 144 + docs/tsconfig.json | 8 + philote_mdo/examples/sellar.py | 9 +- tests/test_abstract_methods.py | 2 +- 27 files changed, 22180 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/release.yaml create mode 100644 CLAUDE.md create mode 100644 docs/.gitignore create mode 100644 docs/README.md create mode 100644 docs/docs/about/license.md create mode 100644 docs/docs/getting-started/installation.md create mode 100644 docs/docs/getting-started/quickstart.md create mode 100644 docs/docs/openmdao/openmdao-clients.md create mode 100644 docs/docs/openmdao/openmdao-groups.md create mode 100644 docs/docs/tutorials/explicit-disciplines.md create mode 100644 docs/docs/tutorials/implicit-disciplines.md create mode 100644 docs/docs/tutorials/units.md create mode 100644 docs/docusaurus.config.ts create mode 100644 docs/package-lock.json create mode 100644 docs/package.json create mode 100644 docs/sidebars.ts create mode 100644 docs/src/css/custom.css create mode 100644 docs/src/pages/index.module.css create mode 100644 docs/src/pages/index.tsx create mode 100644 docs/static/.nojekyll create mode 100644 docs/static/img/favicon.ico create mode 100644 docs/static/img/logo.svg create mode 100644 docs/tsconfig.json diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index e7f1ebb..4e3f224 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -1,47 +1,58 @@ -name: Deploy Documentation +name: Deploy Docs -# Only run this when the master branch changes on: push: - branches: - - main - # If your git repository has the Jupyter Book within some-subfolder next to - # unrelated files, you can make this run only if a file within that specific - # folder has been modified. - # - # paths: - # - some-subfolder/** + branches: [develop] + paths: + - 'docs/**' + - '.github/workflows/documentation.yaml' + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + permissions: - contents: write - -# This job installs dependencies, builds the book, and pushes it to `gh-pages` + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + jobs: - deploy-book: + deploy-docs: runs-on: ubuntu-latest - defaults: - run: - working-directory: doc + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: - - uses: actions/checkout@v2 + - name: Checkout develop + uses: actions/checkout@v4 - # Install dependencies - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - python-version: 3.9 + node-version: '20' + cache: 'npm' + cache-dependency-path: docs/package-lock.json - name: Install dependencies - run: | - pip install -r requirements.txt + run: npm ci + working-directory: docs + + - name: Build Docusaurus + run: npm run build + working-directory: docs - # Build the book - - name: Build the book - run: | - jupyter-book build . + - name: Setup Pages + uses: actions/configure-pages@v5 - # Push the book's HTML to github-pages - - name: GitHub Pages action - uses: peaceiris/actions-gh-pages@v3.6.1 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./doc/_build/html \ No newline at end of file + path: docs/build + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..1e8086c --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,293 @@ +name: Release + +on: + pull_request: + types: [closed] + branches: [main] + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +permissions: + contents: write + +jobs: + release: + if: > + github.event.pull_request.merged == true && + (contains(github.event.pull_request.labels.*.name, 'release') || + contains(github.event.pull_request.labels.*.name, 'prerelease')) + runs-on: ubuntu-latest + + steps: + # ── Checkout ────────────────────────────────────────────────────── + - name: Checkout main + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.RELEASE_TOKEN }} + + # ── GPG setup for signed commits ───────────────────────────────── + - name: Generate ephemeral GPG key + run: | + cat > /tmp/gpg-key-params <> "$GITHUB_ENV" + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.signingkey "${KEY_ID}" + git config commit.gpgsign true + git config tag.gpgsign true + + # ── Extract and validate PR labels ─────────────────────────────── + - name: Extract labels + id: labels + env: + LABELS: ${{ join(github.event.pull_request.labels.*.name, ' ') }} + run: | + echo "Labels on PR: $LABELS" + + # Release type + IS_RELEASE=false + IS_PRERELEASE=false + for l in $LABELS; do + case "$l" in + release) IS_RELEASE=true ;; + prerelease) IS_PRERELEASE=true ;; + esac + done + + if $IS_RELEASE && $IS_PRERELEASE; then + echo "::error::PR must have exactly one of 'release' or 'prerelease', not both." + exit 1 + fi + + # Version bump + BUMP="" + BUMP_COUNT=0 + for l in $LABELS; do + case "$l" in + major|minor|patch) + BUMP="$l" + BUMP_COUNT=$((BUMP_COUNT + 1)) + ;; + esac + done + + if [ "$BUMP_COUNT" -ne 1 ]; then + echo "::error::PR must have exactly one of 'major', 'minor', or 'patch' labels." + exit 1 + fi + + # Pre-release qualifier + QUALIFIER="" + QUAL_COUNT=0 + for l in $LABELS; do + case "$l" in + alpha|beta|rc) + QUALIFIER="$l" + QUAL_COUNT=$((QUAL_COUNT + 1)) + ;; + esac + done + + if $IS_PRERELEASE && [ "$QUAL_COUNT" -ne 1 ]; then + echo "::error::Pre-release PRs must have exactly one of 'alpha', 'beta', or 'rc' labels." + exit 1 + fi + + if $IS_RELEASE && [ "$QUAL_COUNT" -ne 0 ]; then + echo "::error::Stable release PRs must not have 'alpha', 'beta', or 'rc' labels." + exit 1 + fi + + echo "is_release=$IS_RELEASE" >> "$GITHUB_OUTPUT" + echo "is_prerelease=$IS_PRERELEASE" >> "$GITHUB_OUTPUT" + echo "bump=$BUMP" >> "$GITHUB_OUTPUT" + echo "qualifier=$QUALIFIER" >> "$GITHUB_OUTPUT" + + # ── Calculate new version ──────────────────────────────────────── + - name: Calculate version + id: version + run: | + # Read current version from pyproject.toml + CURRENT=$(grep -oP '^version\s*=\s*"\K[0-9]+\.[0-9]+\.[0-9]+' pyproject.toml | head -1) + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" + echo "Current version: ${MAJOR}.${MINOR}.${PATCH}" + + BUMP="${{ steps.labels.outputs.bump }}" + case "$BUMP" in + major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; + minor) MINOR=$((MINOR + 1)); PATCH=0 ;; + patch) PATCH=$((PATCH + 1)) ;; + esac + + NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}" + + if [ "${{ steps.labels.outputs.is_prerelease }}" = "true" ]; then + QUALIFIER="${{ steps.labels.outputs.qualifier }}" + + # Find the latest pre-release tag for this version+qualifier + PRERELEASE_NUM=1 + PATTERN="v${NEW_VERSION}-${QUALIFIER}." + LATEST_TAG=$(git tag -l "${PATTERN}*" | sort -V | tail -n1 || true) + if [ -n "$LATEST_TAG" ]; then + PREV_NUM=$(echo "$LATEST_TAG" | grep -oP "${QUALIFIER}\.\K[0-9]+") + PRERELEASE_NUM=$((PREV_NUM + 1)) + fi + + TAG_VERSION="v${NEW_VERSION}-${QUALIFIER}.${PRERELEASE_NUM}" + IS_PRERELEASE=true + else + TAG_VERSION="v${NEW_VERSION}" + IS_PRERELEASE=false + fi + + echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" + echo "tag_version=${TAG_VERSION}" >> "$GITHUB_OUTPUT" + echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT" + echo "New version: ${NEW_VERSION}, Tag: ${TAG_VERSION}" + + # ── Update pyproject.toml version ──────────────────────────────── + - name: Update pyproject.toml version + run: | + NEW_VERSION="${{ steps.version.outputs.new_version }}" + sed -i 's/^version\s*=\s*"[0-9]\+\.[0-9]\+\.[0-9]\+"/version = "'"$NEW_VERSION"'"/' pyproject.toml + + echo "pyproject.toml updated:" + grep '^version' pyproject.toml + + # ── Update CHANGELOG.md ────────────────────────────────────────── + - name: Update CHANGELOG.md + run: | + TAG="${{ steps.version.outputs.tag_version }}" + VERSION="${{ steps.version.outputs.new_version }}" + DATE=$(date +%Y-%m-%d) + REPO="https://github.com/MDO-Standards/Philote-Python" + + if [ "${{ steps.version.outputs.is_prerelease }}" = "true" ]; then + HEADER="${TAG#v}" + else + HEADER="${VERSION}" + fi + + # Replace [Unreleased] header with version header and add new [Unreleased] + sed -i "s/^## \[Unreleased\]/## [Unreleased]\n\n## [${HEADER}] - ${DATE}/" CHANGELOG.md + + # Update comparison links + # Remove existing [Unreleased] link + sed -i '/^\[Unreleased\]:/d' CHANGELOG.md + + # Add new links at the bottom + echo "[Unreleased]: ${REPO}/compare/${TAG}...HEAD" >> CHANGELOG.md + echo "[${HEADER}]: ${REPO}/releases/tag/${TAG}" >> CHANGELOG.md + + # ── Create docs version snapshot (stable releases only) ──────── + - name: Setup Node.js + if: steps.labels.outputs.is_release == 'true' + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: docs/package-lock.json + + - name: Install docs dependencies + if: steps.labels.outputs.is_release == 'true' + run: npm ci + working-directory: docs + + - name: Create docs version snapshot + if: steps.labels.outputs.is_release == 'true' + run: npx docusaurus docs:version ${{ steps.version.outputs.new_version }} + working-directory: docs + + - name: Update docs default version + if: steps.labels.outputs.is_release == 'true' + run: | + VERSION="${{ steps.version.outputs.new_version }}" + sed -i "s/lastVersion: \"[^\"]*\"/lastVersion: \"${VERSION}\"/" docs/docusaurus.config.ts + + # ── Commit and tag ─────────────────────────────────────────────── + - name: Commit release changes + run: | + TAG="${{ steps.version.outputs.tag_version }}" + VERSION="${TAG#v}" + + git add -A + git commit -S -m "chore: release version ${VERSION}" + + - name: Create annotated tag + run: | + TAG="${{ steps.version.outputs.tag_version }}" + git tag -a "$TAG" -m "Release ${TAG}" + + - name: Push commit and tag + run: | + git push origin main + git push origin "${{ steps.version.outputs.tag_version }}" + + # ── Extract release notes ──────────────────────────────────────── + - name: Extract release notes + id: notes + run: | + TAG="${{ steps.version.outputs.tag_version }}" + VERSION="${TAG#v}" + + # Extract the section for this version from CHANGELOG.md + NOTES=$(awk -v ver="$VERSION" ' + /^## \[/ { + if (found) exit + if (index($0, "[" ver "]")) found=1 + next + } + found { print } + ' CHANGELOG.md) + + # Write to file for the release action + echo "$NOTES" > /tmp/release-notes.md + echo "Release notes:" + cat /tmp/release-notes.md + + # ── Create GitHub Release ──────────────────────────────────────── + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.version.outputs.tag_version }} + name: ${{ steps.version.outputs.tag_version }} + body_path: /tmp/release-notes.md + prerelease: ${{ steps.version.outputs.is_prerelease }} + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + + # ── Merge back to develop ─────────────────────────────────────── + - name: Merge main back to develop + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: | + git fetch origin develop + git checkout develop + + if git merge main --no-edit; then + git push origin develop + echo "Successfully merged main back to develop" + else + git merge --abort + echo "::warning::Auto-merge failed due to conflicts. Creating PR." + gh pr create \ + --base develop \ + --head main \ + --title "chore: merge release ${{ steps.version.outputs.tag_version }} back to develop" \ + --body "Automatic merge of release changes back to develop failed due to conflicts. Please resolve manually." + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d68886..763a855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,47 @@ -# Change Log +# Changelog -## Version 0.8.0 +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] ### Features ### Bug Fixes +- Fixed `SellarMDA` promoted-input ambiguity that newer OpenMDAO releases + reject during `final_setup`. The `x` and `z` defaults were being set on + the inner `cycle` subgroup, but `obj_cmp` promoted the same variables + to the top level with different defaults. Moved the `set_input_defaults` + calls to the top-level `SellarMDA` group so the promoted values agree. +- Updated `test_implicit_discipline_apply_linear_not_implemented` to call + `apply_linear` with its current six-argument signature (`inputs`, + `outputs`, `d_inputs`, `d_outputs`, `d_residuals`, `mode`); the stale + three-argument call raised `TypeError` before the `NotImplementedError` + assertion could fire. + ### Documentation & Infrastructure - Updated installation instructions to reflect PyPI install option. - Added documentation for implicit disciplines. - Added documentation for OpenMDAO clients - Added documentation for the OpenMDOA subproblem discipline. - - -## Version 0.7.0 +- Migrated documentation from Jupyter Book to Docusaurus, mirroring the + Delphi docs setup (dark-mode theme, KaTeX math, custom landing page, + versioned docs). +- Replaced the Jupyter Book `gh-pages` deploy job with a Docusaurus + GitHub Pages artifact pipeline triggered on `develop` for `docs/**`. +- Added a `Release` GitHub Actions workflow mirroring Delphi's release + flow: PR-label-driven version bumps, signed commits, `pyproject.toml` + version update, CHANGELOG rewrite, Docusaurus version snapshots on + stable releases, GitHub Release creation, and auto-merge of `main` + back to `develop`. +- Reformatted `CHANGELOG.md` to the Keep a Changelog convention so the + release workflow can drive header rewrites and comparison links. + +## [0.7.0] ### Features @@ -42,8 +69,7 @@ - Updated copyright statements across the codebase - -## Version 0.6.1 +## [0.6.1] ### Features @@ -55,8 +81,7 @@ installation will fail due to an incompatible grpcio-tools version getting installed at build time. The grpcio-tools version has been fixed for the build at 1.59. As a result the grpcio version also must at least be 1.59 - -## Version 0.6.0 +## [0.6.0] ### Features @@ -70,8 +95,7 @@ - None - -## Version 0.5.3 +## [0.5.3] ### Features @@ -81,8 +105,7 @@ - Added missing function arguments to explicit discipline. - -## Version 0.5.2 +## [0.5.2] ### Features @@ -95,8 +118,7 @@ a platform-specific wheel. The wheel must be platform-specific, because gRPC has C underpinnings. - -## Version 0.5.1 +## [0.5.1] ### Features @@ -114,13 +136,11 @@ a platform-specific wheel. The wheel must be platform-specific, because gRPC has C underpinnings. - -## Version 0.5.0 +## [0.5.0] - yanked due to source distribution issues. All features present in 0.5.1 - -## Version 0.4.0 +## [0.4.0] ### Features @@ -130,8 +150,7 @@ - None - -## Version 0.3.0 +## [0.3.0] This release is one of the biggest changes to the code to date. It contains a fundamental reorganization and adds a number of features. Notably, it adds @@ -151,7 +170,6 @@ unit and integration testing of almost all the code. - Completed implicit discipline functionality and testing. - Fixed unit tests for GetVariableDefinitions and GetPartialsDefinitions. - Added edge case handling for partials of variables that are scalar. -- ### Bug Fixes @@ -162,8 +180,7 @@ unit and integration testing of almost all the code. - Added jupyter book for documentation. - Added a quick start guide. - -## Version 0.2.1 +## [0.2.1] This is purely a bugfix release. Thanks to Alex Xu for finding these bugs and fixing them. @@ -176,8 +193,7 @@ This is purely a bugfix release. Thanks to Alex Xu for finding these bugs and fi - Fixed bug that prevented proper chunking of array data - Fixed flat view of arrays used during variable transfer - -## Version 0.2.0 +## [0.2.0] This version augments the Philote MDO version to 0.3.0. @@ -193,8 +209,7 @@ This version augments the Philote MDO version to 0.3.0. error for n-dimensional arrays, as the slices would not work unless the array was flattened. - -## Version 0.1.0 +## [0.1.0] Initial release of the Philote MDO Python bindings. Includes working remote explicit disciplines. Only the generic API currently works, so there is no @@ -218,3 +233,17 @@ considered pre-release. While they may work in production environments, it is expected that bugs may surface and that several features are still missing. Because of this, the API may still change frequently before version 1.0.0 is released. + +[Unreleased]: https://github.com/MDO-Standards/Philote-Python/compare/v0.7.0...HEAD +[0.7.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.7.0 +[0.6.1]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.6.1 +[0.6.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.6.0 +[0.5.3]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.5.3 +[0.5.2]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.5.2 +[0.5.1]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.5.1 +[0.5.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.5.0 +[0.4.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.4.0 +[0.3.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.3.0 +[0.2.1]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.2.1 +[0.2.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.2.0 +[0.1.0]: https://github.com/MDO-Standards/Philote-Python/releases/tag/v0.1.0 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f219efe --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,156 @@ +# AI Agent Guidelines for Project Philote-Python + +## Project Overview + +Philote-Python is the reference Python implementation of the Philote-MDO standard — a gRPC-based protocol for distributed multidisciplinary analysis and optimization. It provides explicit and implicit discipline base classes, the corresponding servers and clients, and OpenMDAO bindings (`RemoteExplicitComponent`, `RemoteImplicitComponent`, `OpenMdaoSubProblem`). + +## Build System + +### Requirements +- Python 3.9 - 3.12 +- `numpy`, `scipy`, `grpcio`, `protobuf` (installed automatically) +- `openmdao` (only for the OpenMDAO bindings and integration tests) + +### Install +```bash +# From PyPI +pip install philote-mdo + +# From source (editable) +pip install -e . +``` + +### Regenerating gRPC stubs +Generated `*_pb2.py` / `*_pb2_grpc.py` files are committed to the repo. Regenerate only when the `.proto` definitions change: +```bash +python utils/compile_proto.py +``` + +### Running tests +```bash +pytest tests/ +``` +Some tests require OpenMDAO; install it first if you want full coverage. + +## Code Organization + +``` +philote_mdo/ +├── general/ # Core discipline / client / server base classes +├── openmdao/ # OpenMDAO bindings (RemoteExplicit/Implicit, OpenMdaoSubProblem) +├── examples/ # Reference disciplines (Paraboloid, Quadratic, Sellar, Rosenbrock) +├── generated/ # Auto-generated gRPC stubs (do not edit by hand) +└── utils/ # Shared utilities + +tests/ # pytest unit and integration tests (lives outside the package) +proto/ # Philote-MDO .proto definitions +utils/ # Developer scripts (e.g. compile_proto.py) +docs/ # Docusaurus documentation site (see docs/README.md) +``` + +## CI/CD + +### Tests workflow (`.github/workflows/tests.yaml`) +Runs `pytest` against Python 3.9 / 3.10 / 3.11 / 3.12 on every push to `main`, `develop`, `release/*`, `support/*`, and on PRs into `main` / `develop`. + +### Documentation workflow (`.github/workflows/documentation.yaml`) +Builds the Docusaurus site under `docs/` and deploys it to GitHub Pages whenever `docs/**` changes on `develop`. Uses the GitHub Pages artifact pipeline (`actions/configure-pages` + `upload-pages-artifact` + `deploy-pages`). + +### PyPI publish workflow (`.github/workflows/publish-pypi.yaml`) +Triggered when a GitHub Release is published. Builds the source distribution with Poetry and pushes to PyPI via trusted publishing. **The release workflow below is what creates the GitHub Release that triggers this.** + +### Release workflow (`.github/workflows/release.yaml`) + +Label-driven release pipeline mirroring Delphi. Triggered when a labeled PR is **merged** into `main`. + +**Label system** (applied to PRs targeting `main`): + +| Purpose | Labels (pick exactly one per row) | +|---------|-----------------------------------| +| Release type | `release` OR `prerelease` | +| Version bump | `major`, `minor`, or `patch` | +| Pre-release qualifier | `alpha`, `beta`, or `rc` (only with `prerelease`) | + +**What the workflow does on merge:** +1. Generates an ephemeral GPG key and configures signed commits/tags as `github-actions[bot]`. +2. Validates the PR labels (exactly one release type, one bump, optional qualifier on prereleases). +3. Reads the current version from `pyproject.toml` and bumps it per the labels. + - For prereleases, scans existing `vX.Y.Z-.*` tags and increments the suffix counter. +4. Updates `pyproject.toml` (`version = "X.Y.Z"`). +5. Updates `CHANGELOG.md`: + - Replaces `## [Unreleased]` with `## [Unreleased]\n\n## [X.Y.Z] - YYYY-MM-DD`. + - Removes the old `[Unreleased]: ...` link and appends new comparison/release links. +6. **Stable releases only** — cuts a Docusaurus version snapshot: + - `npm ci` in `docs/` + - `npx docusaurus docs:version ` (creates `docs/versioned_docs/version-/`, `docs/versioned_sidebars/`, and updates `docs/versions.json`) + - `sed -i 's/lastVersion: "[^"]*"/lastVersion: ""/'` in `docs/docusaurus.config.ts` +7. Creates a signed commit `chore: release version X.Y.Z` and an annotated tag `vX.Y.Z` (or `vX.Y.Z-.` for prereleases). +8. Pushes the commit and the tag to `main`. +9. Extracts release notes for the new version from `CHANGELOG.md` via `awk`. +10. Creates a GitHub Release (`softprops/action-gh-release@v2`) with `prerelease: true|false` derived from the labels — this is what triggers `publish-pypi.yaml`. +11. Merges `main` back into `develop` automatically. On merge conflict, opens a PR instead. + +**Required setup** (one-time, in repo settings): +- A PAT with `repo` + `workflow` scope stored as the `RELEASE_TOKEN` Actions secret. +- The PR labels listed above must exist on the repo. +- GitHub Pages source must be set to "GitHub Actions". + +### Changelog +`CHANGELOG.md` follows the [Keep a Changelog](https://keepachangelog.com/) format. New changes go under `[Unreleased]`. The release workflow converts this header to a versioned section automatically. + +**Important**: When adding, changing, or removing features, always update `CHANGELOG.md` as part of the same commit. Add a concise entry under the appropriate subsection of `[Unreleased]`: +- `### Features` — new features or capabilities +- `### Bug Fixes` — bug fixes +- `### Documentation & Infrastructure` — docs, CI, build, and tooling changes + +## Documentation + +Documentation lives in `docs/` and is built with [Docusaurus](https://docusaurus.io/) (mirrors the Delphi setup: dark-mode default, KaTeX math, custom landing page, versioned docs). + +### Local development +```bash +cd docs +npm install # first time only +npm run start # live-reload dev server +npm run build # production build into docs/build +``` + +### Adding or editing pages +- Source files live under `docs/docs/` and are organised by sidebar category (`getting-started/`, `tutorials/`, `openmdao/`, `about/`). +- The sidebar layout is defined in `docs/sidebars.ts`. Add new pages there if you want them to appear in the nav. +- Math uses KaTeX (`$...$` inline, `$$...$$` block). +- Admonitions use Docusaurus syntax (`:::note`, `:::warning`, `:::tip`, …). +- Edits to `docs/**` on `develop` trigger an automatic deploy. + +### Versioned docs +- The `docs/docs/` directory is the **"Next"** branch — edits here feed the in-progress documentation. +- `lastVersion` in `docs/docusaurus.config.ts` is updated automatically by the release workflow. +- **Do not run `npx docusaurus docs:version` manually.** The release workflow cuts the snapshot for every stable release. Manual snapshots desync the version dropdown from the actual git tags. + +## Pull Requests + +When creating PRs: +- Target `develop` for feature/bugfix work, `main` only for releases. +- Add a concise `[Unreleased]` entry in `CHANGELOG.md` as part of the same PR. +- For release PRs, add the labels documented in the [Release workflow](#release-workflow-githubworkflowsreleaseyaml) section above. + +## Git Workflow + +- **`main`** — stable releases only. Moves only via the release workflow. +- **`develop`** — integration branch. Feature branches merge here. +- **`feature/*`** — feature branches off `develop`. +- After a release the workflow merges `main` back into `develop` automatically (PR fallback on conflict). + +## Creating a Release + +1. Make sure `CHANGELOG.md` has meaningful entries under `[Unreleased]`. +2. Open a PR from `develop` → `main`. +3. Apply labels: + - One of `release` or `prerelease`. + - One of `major`, `minor`, or `patch`. + - For prereleases, also one of `alpha`, `beta`, or `rc`. +4. Merge the PR. The release workflow does everything else: version bump, changelog rewrite, docs version snapshot (stable releases), signed commit + tag, GitHub Release, PyPI publish (via the published-release trigger), and merge-back into `develop`. + +## License + +Apache-2.0. See `LICENSE` and `docs/docs/about/license.md`. diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..b2d6de3 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..03c4035 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,29 @@ +# Website + +This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. + +## Installation + +```bash +npm install +``` + +## Local Development + +```bash +npm run start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +## Build + +```bash +npm run build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +## Deployment + +The site is deployed automatically to GitHub Pages by the `Deploy Docs` GitHub Actions workflow whenever changes land on the `develop` branch. diff --git a/docs/docs/about/license.md b/docs/docs/about/license.md new file mode 100644 index 0000000..f1dc5db --- /dev/null +++ b/docs/docs/about/license.md @@ -0,0 +1,28 @@ +--- +sidebar_position: 1 +title: "License" +--- + +# License + +This package is licensed under the Apache 2 license: + +> Copyright 2022-2025 Christopher A. Lupp +> +> Licensed under the Apache License, Version 2.0 (the "License"); +> you may not use this file except in compliance with the License. +> You may obtain a copy of the License at +> +>     http://www.apache.org/licenses/LICENSE-2.0 +> +> Unless required by applicable law or agreed to in writing, software +> distributed under the License is distributed on an "AS IS" BASIS, +> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +> See the License for the specific language governing permissions and +> limitations under the License. + +This work has been cleared for public release, distribution unlimited, case number: AFRL-2023-5713. + +The views expressed are those of the authors and do not reflect the official guidance or position of the United States Government, the Department of Defense or of the United States Air Force. + +Statement from DoD: The Appearance of external hyperlinks does not constitute endorsement by the United States Department of Defense (DoD) of the linked websites, of the information, products, or services contained therein. The DoD does not exercise any editorial, security, or other control over the information you may find at these locations. diff --git a/docs/docs/getting-started/installation.md b/docs/docs/getting-started/installation.md new file mode 100644 index 0000000..9c62167 --- /dev/null +++ b/docs/docs/getting-started/installation.md @@ -0,0 +1,57 @@ +--- +sidebar_position: 1 +title: "Installation" +--- + +# Installation + +Philote-Python is a pure Python library. It ships with generated python for gRPC which can be regenerated during the development process. For a standard installation, however, it can just be installed via pip. + +## Requirements + +The development process requires the following tools to be installed: + +- `grpcio-tools` +- `protoletariat` +- `importlib.resources` + +Additionally, the following dependencies are required by Philote-MDO and will be installed automatically during the installation process: + +- `numpy` +- `grpcio` + +To run the unit and integration tests, you will need: + +- [OpenMDAO](https://github.com/OpenMDAO/OpenMDAO) (also installable via pip) + +## Compiling Definitions and Installation + +The easiest way for users to install this library is via pip using the PyPI package: + +```bash +pip install philote-mdo +``` + +If you need or want to install the package from the repository, you can do this using pip: + +```bash +pip install +``` + +or + +```bash +pip install -e +``` + +for an editable install. Note, that `` is the path to the repository root directory (the one containing `pyproject.toml`). Often, people install packages when located in that directory, making the corresponding command: + +```bash +pip install . +``` + +Unlike earlier versions, the package is distributed with generated gRPC python files. This means that you do not need to have `grpcio-tools` or `protoc` installed when using Philote-Python. If you are doing development work, specifically when you are adding new gRPC features, you will need to regenerate the gRPC files. To do this, run the following command from the repository root directory: + +```bash +python utils/compile_proto.py +``` diff --git a/docs/docs/getting-started/quickstart.md b/docs/docs/getting-started/quickstart.md new file mode 100644 index 0000000..aae3fb7 --- /dev/null +++ b/docs/docs/getting-started/quickstart.md @@ -0,0 +1,202 @@ +--- +sidebar_position: 2 +title: "Quick Start" +--- + +# Quick Start + +Client/server interactions might seem difficult when starting out, but the purpose of this library is to help out and abstract away the difficult bits. This quick start guide is intended to familiarize you with the basic operating principles of the Philote-MDO standard and get you started using this Python implementation. + +:::note +This guide attempts to be as user friendly as possible. However, it is likely that some basic understanding of Multidisciplinary Design Analysis and Optimization may be necessary (e.g., what a discipline is, etc.). +::: + +## Disciplines + +Before we take a closer look at clients and servers, we need a discipline that we will attach to a server and call from the client. Philote-Python implements disciplines in a similar way to OpenMDAO. We create a class, inheriting from a base class and then specialize some methods to run the calculations we want. + +To illustrate this, let us take a look at a simple paraboloid problem (the same problem as in the OpenMDAO documentation): + +$$ +f(x,y) = (x-3)^2 + x y + (y+4)^2 - 3 +$$ + +To create a discipline that executes this equation, we create a class and inherit from the `ExplicitDiscipline` class Philote-Python provides: + +```python +class Paraboloid(pmdo.ExplicitDiscipline): + """ + Basic two-dimensional paraboloid example (explicit) discipline. + """ + + def setup(self): + self.add_input("x", shape=(1,), units="m") + self.add_input("y", shape=(1,), units="m") + + self.add_output("f_xy", shape=(1,), units="m**2") + + def setup_partials(self): + self.declare_partials("f_xy", "x") + self.declare_partials("f_xy", "y") + + def compute(self, inputs, outputs): + x = inputs["x"] + y = inputs["y"] + + outputs["f_xy"] = (x - 3.0) ** 2 + x * y + (y + 4.0) ** 2 - 3.0 + + def compute_partials(self, inputs, partials): + x = inputs["x"] + y = inputs["y"] + + partials["f_xy", "x"] = 2.0 * x - 6.0 + y + partials["f_xy", "y"] = 2.0 * y + 8.0 + x +``` + +How to declare and implement this (explicit) discipline is discussed in [Creating Explicit Disciplines](../tutorials/explicit-disciplines.md). For the time being, we will skip over the discipline member functions or what they do in detail. + +Now, the discipline must first be attached to a server to run. + +## Standing Up an Analysis Server + +The discipline created in the previous section serves as the implementation for the analysis server we will stand up in this section. Philote-Python attempts to abstract away most aspects of using gRPC, so that discipline developers can focus on their respective fields, rather than client-server communication. Because of this, a server class is provided, to which analysis disciplines can be attached. + +First, a gRPC channel needs to be generated: + +```python +from concurrent import futures +import grpc +# ... + +server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) +``` + +Next, the **Paraboloid** discipline is attached to the server: + +```python +import philote_mdo.general as pmdo +from philote_mdo.examples import Paraboloid +# ... + +discipline = pmdo.ExplicitServer(discipline=Paraboloid()) +discipline.attach_to_server(server) +``` + +Finally, the port of the server is defined (opening a port is necessary for network communication) and the server is started: + +```python +server.add_insecure_port("[::]:50051") +server.start() +print("Server started. Listening on port 50051.") +server.wait_for_termination() +``` + +In this example, the server waits for a termination signal. Using *Ctrl-C* will kill the server (on Unix and Unix-like systems) when it is no longer needed. + +:::warning +This example uses an insecure port. Production environments should generally always use encrypted network traffic to minimize security vulnerabilities and third parties snooping on data exchanged. The code presented here is a tutorial and not intended for production use. +::: + +The above code snippets were taken slightly out of order for didactical purposes. For completeness, here is the full example server (`paraboloid_explicit.py`): + +```python +from concurrent import futures +import grpc +import philote_mdo.general as pmdo +from philote_mdo.examples import Paraboloid + + +server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + +discipline = pmdo.ExplicitServer(discipline=Paraboloid()) +discipline.attach_to_server(server) + +server.add_insecure_port("[::]:50051") +server.start() +print("Server started. Listening on port 50051.") +server.wait_for_termination() +``` + +## Calling the Discipline Using a Client + +Now that a server is running, it can be queried using a client. Philote-Python offers a number of clients for this purpose, ranging from the general implementation to OpenMDAO and CSDL components. However, under the hood, the OpenMDAO and CSDL components use the general client implementation. + +This example will use the general explicit client implementation. Despite it being fully functional and able to run in scientific workflows, it probably is not a realistic MDO workflow. The [OpenMDAO clients](../openmdao/openmdao-clients.md) tutorial demonstrates calling Philote disciplines from the OpenMDAO framework. + +First, the explicit client must be imported and initialized using a gRPC channel. + +```python +import grpc +import numpy as np +from philote_mdo.general import ExplicitClient + + +client = ExplicitClient(channel=grpc.insecure_channel("localhost:50051")) +``` + +:::warning +The same disclaimer as above applies here. It is not recommended using an insecure channel for production work. Generally, all production work should be encrypted. +::: + +Now the client should be connected to the analysis server. While it may be tempting to just call the function evaluation at this point, hold your horses. There are a few necessary steps that are mandatory to ensure proper behavior. The first is to sync the stream options between the client and server (details of what happens here can be found in **Setting up the Connection**). + +```python +# transfer the stream options to the server +client.send_stream_options() +``` + +Next, the setup function must be run, after which the variable and partials meta data must be retrieved from the server: + +```python +# run setup +client.run_setup() +client.get_variable_definitions() +client.get_partials_definitions() +``` + +Now, the client is ready to call the function evaluation. An input dictionary is defined using the variable name as the key and numpy arrays (size=1) and passed to the `run_compute` function. + +```python +# define some inputs +inputs = {"x": np.array([1.0]), "y": np.array([2.0])} +outputs = {} + +# run a function evaluation +outputs = client.run_compute(inputs) + +print(outputs) +``` + +The script should now have printed out the outputs dictionary. Congratulations, you have run your first client-server analysis using Philote/Philote-Python! + +The full client script (`paraboloid_client.py`) is: + +```python +import grpc +import numpy as np +from philote_mdo.general import ExplicitClient + + +client = ExplicitClient(channel=grpc.insecure_channel("localhost:50051")) + +# transfer the stream options to the server +client.send_stream_options() + +# run setup +client.run_setup() +client.get_variable_definitions() +client.get_partials_definitions() + +# define some inputs +inputs = {"x": np.array([1.0]), "y": np.array([2.0])} +outputs = {} + +# run a function evaluation +outputs = client.run_compute(inputs) + +print(outputs) +``` + +## Potential Pitfalls + +Keep in mind that Philote requires network communication. For the tutorials it is probably best to run both the server and client on the same machine. However, it is possible to call a remote analysis discipline that is separate of the client. This requires opening the appropriate port (in this case, *50051*) and managing permissions. Many of these issues can be avoided by running the examples on the same machine. diff --git a/docs/docs/openmdao/openmdao-clients.md b/docs/docs/openmdao/openmdao-clients.md new file mode 100644 index 0000000..dda467e --- /dev/null +++ b/docs/docs/openmdao/openmdao-clients.md @@ -0,0 +1,341 @@ +--- +sidebar_position: 1 +title: "OpenMDAO Clients" +--- + +# Calling Philote Disciplines from OpenMDAO + +This guide explains how to integrate Philote disciplines into OpenMDAO workflows using remote client components. Philote provides OpenMDAO components that act as clients to remote Philote servers, enabling distributed computing and language interoperability. + +## Overview + +Philote offers two main OpenMDAO component types for integrating remote disciplines: + +- **`RemoteExplicitComponent`**: For explicit disciplines where outputs are computed directly from inputs +- **`RemoteImplicitComponent`**: For implicit disciplines that solve residual equations $R(\text{inputs}, \text{outputs}) = 0$ + +Both components automatically discover the server's interface and handle all communication transparently, making remote disciplines appear as native OpenMDAO components. + +### Key Benefits + +- **Distributed Computing**: Run expensive analyses on remote servers +- **Language Interoperability**: Use disciplines written in any language supported by Philote +- **Seamless Integration**: Remote disciplines work exactly like local OpenMDAO components +- **Automatic Interface Discovery**: No manual specification of inputs/outputs required +- **Derivative Support**: Automatic partial derivative computation for optimization + +## Prerequisites + +Before using Philote OpenMDAO components, ensure: + +1. A Philote server is running and accessible +2. The gRPC channel can connect to the server +3. OpenMDAO and Philote packages are installed + +## Remote Explicit Components + +### Basic Usage + +Explicit components compute outputs directly from inputs. Here's a complete example: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create gRPC channel to Philote server +channel = grpc.insecure_channel("localhost:50051") + +# Create OpenMDAO problem +prob = om.Problem() +model = prob.model + +# Add remote explicit component +model.add_subsystem( + "analysis", + pmom.RemoteExplicitComponent(channel=channel), + promotes=["*"] +) + +# Setup and run +prob.setup() +prob["x"] = 2.0 +prob["y"] = 3.0 +prob.run_model() + +print(f"Result: {prob['f_xy']}") +``` + +### Advanced Example with Options + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create channel with server options +channel = grpc.insecure_channel("localhost:50051") + +# Create component with server-specific options +component = pmom.RemoteExplicitComponent( + channel=channel, + num_par_fd=4, # OpenMDAO finite difference parallelization + tolerance=1e-8, # Server-specific option + max_iterations=100 # Server-specific option +) + +# Create problem and add component +prob = om.Problem() +prob.model.add_subsystem("remote_analysis", component, promotes=["*"]) + +# Add optimizer for demonstration +prob.driver = om.ScipyOptimizeDriver() +prob.driver.options["optimizer"] = "SLSQP" + +# Add design variable and objective +prob.model.add_design_var("x", lower=-10, upper=10) +prob.model.add_objective("f_xy") + +# Setup and optimize +prob.setup() +prob["x"] = 1.0 +prob["y"] = 1.0 + +prob.run_driver() +print(f"Optimal x: {prob['x']}") +print(f"Optimal objective: {prob['f_xy']}") +``` + +## Remote Implicit Components + +### Basic Usage + +Implicit components solve equations of the form $R(\text{inputs}, \text{outputs}) = 0$: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create gRPC channel +channel = grpc.insecure_channel("localhost:50051") + +# Create OpenMDAO problem +prob = om.Problem() +model = prob.model + +# Add remote implicit component +model.add_subsystem( + "implicit_analysis", + pmom.RemoteImplicitComponent(channel=channel), + promotes=["*"] +) + +# Setup and run +prob.setup() + +# Set inputs for quadratic equation: ax^2 + bx + c = 0 +prob["a"] = 1.0 +prob["b"] = -5.0 +prob["c"] = 6.0 + +prob.run_model() +print(f"Solution: x = {prob['x']}") # Should find x = 2 or x = 3 +``` + +### Implicit Component with Nonlinear Solver + +For more complex implicit systems, you may need to configure OpenMDAO solvers: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create problem with nonlinear solver +prob = om.Problem() +model = prob.model + +# Add implicit component +model.add_subsystem( + "implicit_system", + pmom.RemoteImplicitComponent(channel=grpc.insecure_channel("localhost:50051")), + promotes=["*"] +) + +# Configure nonlinear solver for implicit system +model.nonlinear_solver = om.NewtonSolver(solve_subsystems=False) +model.nonlinear_solver.options["iprint"] = 2 +model.nonlinear_solver.options["maxiter"] = 20 +model.linear_solver = om.DirectSolver() + +# Setup and run +prob.setup() +prob["input_param"] = 5.0 +prob.run_model() + +print(f"Converged solution: {prob['output_var']}") +``` + +## Working with Multiple Remote Components + +You can combine multiple remote components in a single OpenMDAO model: + +```python +import grpc +import openmdao.api as om +import philote_mdo.openmdao as pmom + +# Create channels to different servers +channel1 = grpc.insecure_channel("server1:50051") +channel2 = grpc.insecure_channel("server2:50052") + +# Create problem with multiple remote components +prob = om.Problem() +model = prob.model + +# Add multiple remote components +model.add_subsystem( + "aerodynamics", + pmom.RemoteExplicitComponent(channel=channel1), + promotes_inputs=["mach", "alpha", "altitude"], + promotes_outputs=["CL", "CD"] +) + +model.add_subsystem( + "structures", + pmom.RemoteImplicitComponent(channel=channel2), + promotes_inputs=["loads", "material_props"], + promotes_outputs=["stress", "displacement"] +) + +# Connect components +model.connect("CL", "structures.loads") + +# Setup and run coupled analysis +prob.setup() +prob["mach"] = 0.8 +prob["alpha"] = 2.0 +prob["altitude"] = 35000 +prob["material_props"] = [...] + +prob.run_model() +``` + +## Error Handling and Debugging + +### Connection Issues + +```python +import grpc +import philote_mdo.openmdao as pmom + +try: + channel = grpc.insecure_channel("unreachable:50051") + component = pmom.RemoteExplicitComponent(channel=channel) +except grpc.RpcError as e: + print(f"Failed to connect to server: {e}") + print("Check that the server is running and accessible") +except ValueError as e: + print(f"Configuration error: {e}") +``` + +### Server Debugging + +Enable OpenMDAO's debug output to see server communication: + +```python +import openmdao.api as om + +# Enable debug output +prob = om.Problem() +prob.model.add_subsystem("remote", component) + +# Setup with debug info +prob.setup() +prob.set_solver_print(level=2) # Print solver information +prob.run_model() +``` + +## Performance Considerations + +### Parallel Finite Differences + +For components that don't provide analytic derivatives: + +```python +component = pmom.RemoteExplicitComponent( + channel=channel, + num_par_fd=8 # Use 8 parallel processes for finite differences +) +``` + +### Connection Pooling + +For multiple components connecting to the same server: + +```python +# Reuse channels when possible +shared_channel = grpc.insecure_channel("localhost:50051") + +comp1 = pmom.RemoteExplicitComponent(channel=shared_channel) +comp2 = pmom.RemoteExplicitComponent(channel=shared_channel) +``` + +### Server-Side Optimization + +- Ensure servers are running on appropriate hardware +- Use analytic derivatives when possible (server-side implementation) +- Consider caching for expensive computations (server-side) + +## Best Practices + +1. **Error Handling**: Always wrap component creation in try-catch blocks +2. **Connection Management**: Reuse gRPC channels when connecting to the same server +3. **Option Validation**: Server options are discovered automatically but validate important values +4. **Solver Configuration**: Configure appropriate solvers for implicit components +5. **Performance**: Use analytic derivatives when available from the server +6. **Debugging**: Enable OpenMDAO debug output to troubleshoot communication issues + +## Troubleshooting + +### Common Issues + +1. **"No channel provided" Error** + + ```python + # Wrong: Missing channel + component = pmom.RemoteExplicitComponent() + + # Correct: Provide valid channel + channel = grpc.insecure_channel("localhost:50051") + component = pmom.RemoteExplicitComponent(channel=channel) + ``` + +2. **Server Connection Failures** + - Verify server is running: `telnet localhost 50051` + - Check firewall settings + - Verify correct hostname/port + +3. **Variable Name Mismatches** + - Use `prob.model.list_inputs()` and `prob.model.list_outputs()` after setup + - Server variables are automatically discovered and named + +4. **Convergence Issues with Implicit Components** + - Check residual values: use OpenMDAO's debug output + - Adjust solver tolerances and iteration limits + - Verify initial guesses are reasonable + +### Debug Tools + +```python +# After problem setup, inspect the remote component +prob.setup() + +# List all inputs and outputs +prob.model.list_inputs(print_arrays=True) +prob.model.list_outputs(print_arrays=True) + +# Check partial derivatives +prob.check_partials(compact_print=True) +``` diff --git a/docs/docs/openmdao/openmdao-groups.md b/docs/docs/openmdao/openmdao-groups.md new file mode 100644 index 0000000..3b28226 --- /dev/null +++ b/docs/docs/openmdao/openmdao-groups.md @@ -0,0 +1,182 @@ +--- +sidebar_position: 2 +title: "OpenMDAO Group Wrapping" +--- + +# OpenMDAO Group Wrapping Discipline + +The `OpenMdaoSubProblem` class allows you to wrap existing OpenMDAO groups as Philote disciplines, enabling integration of OpenMDAO models into Philote workflows. This wrapper creates a bridge between OpenMDAO's group-based architecture and Philote's discipline-based framework. + +## Overview + +The `OpenMdaoSubProblem` class (`philote_mdo.openmdao.group.OpenMdaoSubProblem`) is a Philote explicit discipline that internally contains and executes an OpenMDAO group. While the Philote discipline interface is explicit, the underlying OpenMDAO group may contain cycles that require nonlinear solvers. + +### Key Features + +- Wraps any OpenMDAO group as a Philote discipline +- Maps variables between Philote and OpenMDAO namespaces +- Supports automatic partial derivative computation using OpenMDAO's `compute_totals` +- Handles units and multi-dimensional arrays +- Preserves OpenMDAO solver capabilities for cyclic groups + +## Basic Usage + +### 1. Create and Configure the Wrapper + +```python +import openmdao.api as om +from philote_mdo.openmdao.group import OpenMdaoSubProblem + +# Create the wrapper discipline +subprob = OpenMdaoSubProblem() + +# Add your OpenMDAO group +my_group = MyOpenMDAOGroup() +subprob.add_group(my_group) +``` + +### 2. Map Variables + +Map inputs and outputs between the Philote discipline and the OpenMDAO group: + +```python +# Map inputs: (philote_name, openmdao_name, shape, units) +subprob.add_mapped_input('x_local', 'x', shape=(1,), units='m') +subprob.add_mapped_input('design_vars', 'z', shape=(2,), units='') + +# Map outputs: (philote_name, openmdao_name, shape, units) +subprob.add_mapped_output('objective', 'obj', shape=(1,), units='') +subprob.add_mapped_output('constraint1', 'con1', shape=(1,), units='') +``` + +### 3. Declare Partial Derivatives + +If you need partial derivatives, declare them for each output-input pair: + +```python +# Declare partials: (output_name, input_name) +subprob.declare_subproblem_partial('objective', 'x_local') +subprob.declare_subproblem_partial('objective', 'design_vars') +subprob.declare_subproblem_partial('constraint1', 'x_local') +``` + +## Complete Example: Simple Linear Function + +```python +import numpy as np +import openmdao.api as om +from philote_mdo.openmdao.group import OpenMdaoSubProblem + +# Define a simple OpenMDAO group +class SimpleGroup(om.Group): + def setup(self): + self.add_subsystem('comp', om.ExecComp('y = 2*x + 1'), promotes=['*']) + +# Create and configure the wrapper +subprob = OpenMdaoSubProblem() +subprob.add_group(SimpleGroup()) + +# Map variables +subprob.add_mapped_input('input_x', 'x') +subprob.add_mapped_output('output_y', 'y') + +# Declare partials +subprob.declare_subproblem_partial('output_y', 'input_x') + +# Setup the discipline +subprob.setup() + +# Use the discipline +inputs = {'input_x': np.array([3.0])} +outputs = {'output_y': np.array([0.0])} + +subprob.compute(inputs, outputs) +print(f"Result: {outputs['output_y'][0]}") # Should print 7.0 +``` + +## Advanced Example: Sellar Problem + +The Sellar MDA problem demonstrates wrapping a more complex OpenMDAO group with cycles: + +```python +import numpy as np +import openmdao.api as om +from openmdao.test_suite.components.sellar import SellarDis1, SellarDis2 +from philote_mdo.openmdao.group import OpenMdaoSubProblem + +class SellarMDA(om.Group): + def setup(self): + # Create a cycle group + cycle = self.add_subsystem("cycle", om.Group(), promotes=["*"]) + cycle.add_subsystem("d1", SellarDis1(), + promotes_inputs=["x", "z", "y2"], + promotes_outputs=["y1"]) + cycle.add_subsystem("d2", SellarDis2(), + promotes_inputs=["z", "y1"], + promotes_outputs=["y2"]) + + # Set default values + cycle.set_input_defaults("x", 1.0) + cycle.set_input_defaults("z", np.array([5.0, 2.0])) + + # Add solvers for the cycle + cycle.nonlinear_solver = om.NonlinearBlockGS(iprint=0) + cycle.linear_solver = om.LinearBlockGS(iprint=0) + + # Add objective and constraints + self.add_subsystem("obj_cmp", + om.ExecComp("obj = x**2 + z[1] + y1 + exp(-y2)", + z=np.array([0.0, 0.0]), x=0.0), + promotes=["x", "z", "y1", "y2", "obj"]) + + self.add_subsystem("con_cmp1", + om.ExecComp("con1 = 3.16 - y1"), + promotes=["con1", "y1"]) + self.add_subsystem("con_cmp2", + om.ExecComp("con2 = y2 - 24.0"), + promotes=["con2", "y2"]) + +# Create wrapper using inheritance +class SellarGroup(OpenMdaoSubProblem): + def initialize(self): + self.add_group(SellarMDA()) + + # Map all variables + self.add_mapped_input('x', 'x') + self.add_mapped_input('z', 'z', shape=(2,)) + self.add_mapped_output('obj', 'obj') + self.add_mapped_output('con1', 'con1') + self.add_mapped_output('con2', 'con2') + + # Declare partials for optimization + self.declare_subproblem_partial('obj', 'x') + self.declare_subproblem_partial('obj', 'z') + self.declare_subproblem_partial('con1', 'x') + self.declare_subproblem_partial('con1', 'z') + self.declare_subproblem_partial('con2', 'x') + self.declare_subproblem_partial('con2', 'z') +``` + +## Best Practices + +1. **Variable Mapping**: Always map variables before calling `setup()` +2. **Partial Derivatives**: Only declare partials you actually need to avoid unnecessary computation +3. **Units**: Specify units when mapping variables to ensure proper unit conversion +4. **Shapes**: Correctly specify array shapes for multi-dimensional variables +5. **Inheritance**: Consider inheriting from `OpenMdaoSubProblem` for reusable wrappers +6. **Solver Configuration**: Configure OpenMDAO solvers within your group's `setup()` method + +## Troubleshooting + +### Common Issues + +1. **Missing Variables**: Ensure all mapped variables exist in the OpenMDAO group +2. **Shape Mismatches**: Verify that specified shapes match the actual variable shapes +3. **Unit Inconsistencies**: Check that units are compatible between mapped variables +4. **Solver Convergence**: For cyclic groups, ensure appropriate solvers are configured + +### Debugging Tips + +- Use OpenMDAO's `list_inputs()` and `list_outputs()` to verify available variables +- Check solver convergence with `iprint` settings +- Validate partial derivatives using OpenMDAO's `check_partials()` method diff --git a/docs/docs/tutorials/explicit-disciplines.md b/docs/docs/tutorials/explicit-disciplines.md new file mode 100644 index 0000000..4daa245 --- /dev/null +++ b/docs/docs/tutorials/explicit-disciplines.md @@ -0,0 +1,111 @@ +--- +sidebar_position: 1 +title: "Explicit Disciplines" +--- + +# Creating Explicit Disciplines + +The [Quick Start](../getting-started/quickstart.md) guide introduces explicit disciplines without elaborating on how they work or how to create them. In this section, we will cover the basics of creating explicit disciplines. + +Philote-Python implements disciplines in a similar way to OpenMDAO. We create a class, inheriting from a base class and then specialize some methods to run the calculations we want. + +To illustrate this, let us take a look at a simple paraboloid problem (the same problem as in the OpenMDAO documentation, and the same one used in the quick start guide): + +$$ +f(x,y) = (x-3)^2 + x y + (y+4)^2 - 3 +$$ + +To create a discipline that executes this equation, we create a class and inherit from the `ExplicitDiscipline` class Philote-Python provides. Member functions of the inherited class are overloaded to implement the desired functionality. The most common functions that will need to be overloaded are `setup`, `setup_partials`, `compute`, and in the case of a discipline that offers derivatives, `compute_partials`. + +## Setup Functions + +First, let us look at the setup member function: + +```python +import philote_mdo.general as pmdo + +class Paraboloid(pmdo.ExplicitDiscipline): + + def setup(self): + self.add_input("x", shape=(1,), units="m") + self.add_input("y", shape=(1,), units="m") + + self.add_output("f_xy", shape=(1,), units="m**2") +``` + +The `setup` function exists to define the inputs and outputs of a discipline (this purpose is borrowed from the OpenMDAO workflow, which operates the same way). The `add_input` and `add_output` member functions are provided by `ExplicitDiscipline` to define both inputs and outputs. Here, two inputs (`x` and `y`) are defined, each with the shape of 1 (scalar) and with a unit of meters (no physical meaning, purely for demonstration). Furthermore, a scalar output (`f_xy`) was defined with the units of square meters. + +While the discipline gradients (or partials) can be defined within the `setup` function, it usually is good practice to separate these definitions into the `setup_partials` function. Behind the scenes, both functions (`setup` and `setup_partials`) are called back-to-back, so there is no actual difference where the gradients are defined. However, using both functions to define the variables and partials may have organizational benefits. + +To define the gradient of an output with respect to an input, the `declare_partials` function is invoked: + +```python + def setup_partials(self): + self.declare_partials("f_xy", "x") + self.declare_partials("f_xy", "y") +``` + +## Compute Function + +To implement the paraboloid function, the `compute` member function must be defined: + +```python + def compute(self, inputs, outputs): + x = inputs["x"] + y = inputs["y"] + + outputs["f_xy"] = (x - 3.0) ** 2 + x * y + (y + 4.0) ** 2 - 3.0 +``` + +The `inputs` and `outputs` variables for this function are later passed in by the server as dictionaries with the variable names as the keys. + +## Gradient Function + +To implement the paraboloid function derivatives, the `compute_partials` member function must be defined: + +```python + def compute_partials(self, inputs, partials): + x = inputs["x"] + y = inputs["y"] + + partials["f_xy", "x"] = 2.0 * x - 6.0 + y + partials["f_xy", "y"] = 2.0 * y + 8.0 + x +``` + +The `inputs` and `partials` variables for this function are later passed in by the server as dictionaries with the variable names as the keys. + +## Summary + +In this section we covered the basics of creating an explicit discipline. The entire code for the paraboloid discipline is listed here for completeness: + +```python +import philote_mdo.general as pmdo + +class Paraboloid(pmdo.ExplicitDiscipline): + """ + Basic two-dimensional paraboloid example (explicit) discipline. + """ + + def setup(self): + self.add_input("x", shape=(1,), units="m") + self.add_input("y", shape=(1,), units="m") + + self.add_output("f_xy", shape=(1,), units="m**2") + + def setup_partials(self): + self.declare_partials("f_xy", "x") + self.declare_partials("f_xy", "y") + + def compute(self, inputs, outputs): + x = inputs["x"] + y = inputs["y"] + + outputs["f_xy"] = (x - 3.0) ** 2 + x * y + (y + 4.0) ** 2 - 3.0 + + def compute_partials(self, inputs, partials): + x = inputs["x"] + y = inputs["y"] + + partials["f_xy", "x"] = 2.0 * x - 6.0 + y + partials["f_xy", "y"] = 2.0 * y + 8.0 + x +``` diff --git a/docs/docs/tutorials/implicit-disciplines.md b/docs/docs/tutorials/implicit-disciplines.md new file mode 100644 index 0000000..6a4ec07 --- /dev/null +++ b/docs/docs/tutorials/implicit-disciplines.md @@ -0,0 +1,407 @@ +--- +sidebar_position: 2 +title: "Implicit Disciplines" +--- + +# Creating Implicit Disciplines + +This guide explains how to create, serve, and use implicit disciplines in Philote. Implicit disciplines solve equations of the form $R(\text{inputs}, \text{outputs}) = 0$, where outputs are implicitly defined by the inputs through residual equations. Unlike explicit disciplines that compute outputs directly, implicit disciplines require solving nonlinear equations. + +## Overview + +Implicit disciplines are essential for modeling systems where: + +- Outputs cannot be computed directly from inputs (e.g., iterative problems) +- Equilibrium conditions must be satisfied + +### Key Concepts + +**Residual Equations**: Mathematical expressions $R(\text{inputs}, \text{outputs})$ that must equal zero at the solution. + +**Implicit Solving**: Finding output values that satisfy $R(\text{inputs}, \text{outputs}) = 0$ for given inputs. + +**Jacobian Matrix**: Partial derivatives $\partial R / \partial[\text{inputs},\text{outputs}]$ used for optimization and sensitivity analysis. + +### Mathematical Formulation + +For implicit disciplines, we solve: + +$$ +R(x, y) = 0 +$$ + +Where: + +- $x$ are the inputs (design variables, parameters) +- $y$ are the outputs (state variables, unknowns) +- $R$ is the residual function + +The goal is to find $y$ such that $R(x, y) = 0$ for given inputs $x$. + +## Creating Implicit Disciplines + +### Basic Structure + +All implicit disciplines inherit from `ImplicitDiscipline` and must implement four key methods: + +```python +import numpy as np +import philote_mdo.general as pmdo + +class MyImplicitDiscipline(pmdo.ImplicitDiscipline): + def setup(self): + # Define inputs and outputs + pass + + def setup_partials(self): + # Declare which partial derivatives will be computed + pass + + def compute_residuals(self, inputs, outputs, residuals): + # Compute R(inputs, outputs) + pass + + def solve_residuals(self, inputs, outputs): + # Solve R(inputs, outputs) = 0 for outputs + pass + + def residual_partials(self, inputs, outputs, partials): + # Compute dR/d[inputs,outputs] + pass +``` + +### Example 1: Quadratic Equation Solver + +Let's implement a solver for the quadratic equation $ax^2 + bx + c = 0$: + +```python +import numpy as np +import philote_mdo.general as pmdo + +class QuadraticSolver(pmdo.ImplicitDiscipline): + def setup(self): + # Define inputs: coefficients of quadratic equation + self.add_input('a', shape=(1,)) + self.add_input('b', shape=(1,)) + self.add_input('c', shape=(1,)) + + # Define output: solution to the equation + self.add_output('x', shape=(1,)) + + def setup_partials(self): + # Declare all partial derivatives that will be computed + self.declare_partials('x', 'a') # dR/da + self.declare_partials('x', 'b') # dR/db + self.declare_partials('x', 'c') # dR/dc + self.declare_partials('x', 'x') # dR/dx + + def compute_residuals(self, inputs, outputs, residuals): + """Compute residual: R = ax^2 + bx + c""" + a = inputs['a'] + b = inputs['b'] + c = inputs['c'] + x = outputs['x'] + + residuals['x'] = a * x**2 + b * x + c + + def solve_residuals(self, inputs, outputs): + """Solve quadratic equation analytically""" + a = inputs['a'] + b = inputs['b'] + c = inputs['c'] + + # Use quadratic formula: x = (-b + sqrt(b^2-4ac)) / 2a + discriminant = b**2 - 4*a*c + if discriminant < 0: + raise ValueError("No real solution exists") + + outputs['x'] = (-b + np.sqrt(discriminant)) / (2*a) + + def residual_partials(self, inputs, outputs, partials): + """Compute Jacobian of residual equation""" + a = inputs['a'] + b = inputs['b'] + c = inputs['c'] + x = outputs['x'] + + # Partial derivatives of R = ax^2 + bx + c + partials['x', 'a'] = x**2 # dR/da + partials['x', 'b'] = x # dR/db + partials['x', 'c'] = 1.0 # dR/dc + partials['x', 'x'] = 2*a*x + b # dR/dx +``` + +### Example 2: Coupled System of Equations + +For more complex systems with multiple residuals: + +```python +class CoupledSystem(pmdo.ImplicitDiscipline): + def setup(self): + # Inputs: parameters + self.add_input('p1', shape=(1,)) + self.add_input('p2', shape=(1,)) + + # Outputs: state variables + self.add_output('x1', shape=(1,)) + self.add_output('x2', shape=(1,)) + + def setup_partials(self): + # Declare partials for all residual-variable pairs + self.declare_partials('x1', ['p1', 'x1', 'x2']) + self.declare_partials('x2', ['p2', 'x1', 'x2']) + + def compute_residuals(self, inputs, outputs, residuals): + """ + Solve coupled system: + R1 = x1^2 + x2 - p1 = 0 + R2 = x1 + x2^2 - p2 = 0 + """ + p1, p2 = inputs['p1'], inputs['p2'] + x1, x2 = outputs['x1'], outputs['x2'] + + residuals['x1'] = x1**2 + x2 - p1 + residuals['x2'] = x1 + x2**2 - p2 + + def solve_residuals(self, inputs, outputs): + """Solve using Newton's method""" + p1, p2 = inputs['p1'], inputs['p2'] + x1, x2 = outputs['x1'], outputs['x2'] # Initial guess + + max_iter = 20 + tolerance = 1e-8 + + for i in range(max_iter): + # Compute residuals + residuals = {} + self.compute_residuals(inputs, outputs, residuals) + + # Check convergence + r1, r2 = residuals['x1'], residuals['x2'] + if np.abs(r1) < tolerance and np.abs(r2) < tolerance: + break + + # Compute Jacobian + partials = {} + self.residual_partials(inputs, outputs, partials) + + # Newton update: [x1; x2] -= J^(-1) * [r1; r2] + J11 = partials['x1', 'x1'] # dR1/dx1 + J12 = partials['x1', 'x2'] # dR1/dx2 + J21 = partials['x2', 'x1'] # dR2/dx1 + J22 = partials['x2', 'x2'] # dR2/dx2 + + det = J11*J22 - J12*J21 + dx1 = -(J22*r1 - J12*r2) / det + dx2 = -(-J21*r1 + J11*r2) / det + + outputs['x1'] += dx1 + outputs['x2'] += dx2 + else: + raise RuntimeError("Newton method failed to converge") + + def residual_partials(self, inputs, outputs, partials): + """Compute Jacobian matrix""" + x1, x2 = outputs['x1'], outputs['x2'] + + # Partials of R1 = x1^2 + x2 - p1 + partials['x1', 'p1'] = -1.0 + partials['x1', 'x1'] = 2*x1 + partials['x1', 'x2'] = 1.0 + + # Partials of R2 = x1 + x2^2 - p2 + partials['x2', 'p2'] = -1.0 + partials['x2', 'x1'] = 1.0 + partials['x2', 'x2'] = 2*x2 +``` + +## Serving Implicit Disciplines + +### Creating a Server + +To serve an implicit discipline over gRPC: + +```python +from concurrent import futures +import grpc +import philote_mdo.general as pmdo + +def run_server(): + # Create the discipline + discipline = QuadraticSolver() + + # Create gRPC server + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + # Create and attach implicit server + impl_server = pmdo.ImplicitServer(discipline=discipline) + impl_server.attach_to_server(server) + + # Start server + server.add_insecure_port('[::]:50051') + server.start() + print('Implicit discipline server started on port 50051') + server.wait_for_termination() + +if __name__ == '__main__': + run_server() +``` + +### Server Configuration + +For production servers, consider: + +```python +def run_production_server(): + discipline = QuadraticSolver() + + # Configure server with more workers for concurrent clients + server = grpc.server( + futures.ThreadPoolExecutor(max_workers=50), + options=[ + ('grpc.keepalive_time_ms', 30000), + ('grpc.keepalive_timeout_ms', 5000), + ('grpc.keepalive_permit_without_calls', True), + ('grpc.http2.max_pings_without_data', 0), + ('grpc.http2.min_time_between_pings_ms', 10000), + ('grpc.http2.min_ping_interval_without_data_ms', 300000) + ] + ) + + impl_server = pmdo.ImplicitServer(discipline=discipline) + impl_server.attach_to_server(server) + + # Use secure connection in production + private_key = open('server.key', 'rb').read() + certificate_chain = open('server.crt', 'rb').read() + credentials = grpc.ssl_server_credentials([(private_key, certificate_chain)]) + + server.add_secure_port('[::]:443', credentials) + server.start() + print('Secure implicit discipline server started on port 443') + server.wait_for_termination() +``` + +## Using Implicit Disciplines (Client Side) + +### Basic Client Usage + +```python +import grpc +import numpy as np +import philote_mdo.general as pmdo + +# Connect to server +channel = grpc.insecure_channel('localhost:50051') +client = pmdo.ImplicitClient(channel) + +# Define inputs for quadratic equation: x^2 - 5x + 6 = 0 +inputs = { + 'a': np.array([1.0]), + 'b': np.array([-5.0]), + 'c': np.array([6.0]) +} + +# Solve the implicit equations +solution = client.run_solve_residuals(inputs) +print(f"Solution: x = {solution['x'][0]}") # Should be 2.0 or 3.0 + +# Verify solution by computing residuals +outputs = {'x': solution['x']} +residuals = client.run_compute_residuals(inputs, outputs) +print(f"Residual: {residuals['x'][0]}") # Should be close to 0.0 + +# Compute Jacobian for sensitivity analysis +jacobian = client.run_residual_gradients(inputs, outputs) +print(f"dR/da = {jacobian[('x', 'a')][0]}") # x^2 = 4.0 +print(f"dR/db = {jacobian[('x', 'b')][0]}") # x = 2.0 +print(f"dR/dc = {jacobian[('x', 'c')][0]}") # 1.0 +print(f"dR/dx = {jacobian[('x', 'x')][0]}") # 2ax + b = -1.0 +``` + +### Advanced Client Operations + +```python +def analyze_quadratic_sensitivity(): + """Analyze how solution changes with coefficients""" + channel = grpc.insecure_channel('localhost:50051') + client = pmdo.ImplicitClient(channel) + + # Base case + base_inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([6.0])} + base_solution = client.run_solve_residuals(base_inputs) + base_x = base_solution['x'][0] + + print(f"Base solution: x = {base_x}") + + # Sensitivity to coefficient 'a' + perturbed_inputs = base_inputs.copy() + perturbed_inputs['a'] = np.array([1.1]) # 10% increase + + perturbed_solution = client.run_solve_residuals(perturbed_inputs) + perturbed_x = perturbed_solution['x'][0] + + sensitivity = (perturbed_x - base_x) / (0.1 * base_inputs['a'][0]) + print(f"Finite difference sensitivity dx/da ~ {sensitivity}") + + # Compare with analytical sensitivity from Jacobian + outputs = {'x': base_solution['x']} + jacobian = client.run_residual_gradients(base_inputs, outputs) + + # For implicit function theorem: dx/da = -(dR/da) / (dR/dx) + dR_da = jacobian[('x', 'a')][0] + dR_dx = jacobian[('x', 'x')][0] + analytical_sensitivity = -dR_da / dR_dx + + print(f"Analytical sensitivity dx/da = {analytical_sensitivity}") + +def solve_parameter_sweep(): + """Solve for multiple parameter values""" + channel = grpc.insecure_channel('localhost:50051') + client = pmdo.ImplicitClient(channel) + + # Sweep parameter 'c' while keeping 'a' and 'b' fixed + c_values = np.linspace(0, 10, 21) + solutions = [] + + for c in c_values: + inputs = {'a': np.array([1.0]), 'b': np.array([-5.0]), 'c': np.array([c])} + + try: + solution = client.run_solve_residuals(inputs) + x = solution['x'][0] + solutions.append((c, x)) + print(f"c = {c:4.1f}, x = {x:6.3f}") + except grpc.RpcError as e: + print(f"c = {c:4.1f}, Failed to solve: {e}") + + return solutions +``` + +### Error Handling + +```python +def robust_client_usage(): + """Demonstrate proper error handling""" + try: + channel = grpc.insecure_channel('localhost:50051') + client = pmdo.ImplicitClient(channel) + + # This should fail - no real solution + inputs = {'a': np.array([1.0]), 'b': np.array([1.0]), 'c': np.array([1.0])} + + solution = client.run_solve_residuals(inputs) + print(f"Unexpected solution: {solution}") + + except grpc.RpcError as e: + print(f"Server error: {e.code()}") + print(f"Details: {e.details()}") + + except Exception as e: + print(f"Other error: {e}") + + finally: + # Clean up connection + if 'channel' in locals(): + channel.close() +``` diff --git a/docs/docs/tutorials/units.md b/docs/docs/tutorials/units.md new file mode 100644 index 0000000..88af5cd --- /dev/null +++ b/docs/docs/tutorials/units.md @@ -0,0 +1,246 @@ +--- +sidebar_position: 3 +title: "Unit Definitions" +--- + +# Unit Definitions + +Philote's unit system overlaps with that of OpenMDAO. The main difference is that the lack of units cannot be specified using the `None` type in Python. Instead it should be listed as `unitless`. + +The currently defined units are listed here (taken from the OpenMDAO documentation, licensed under the Apache 2 license): + +``` +# Default unit library definitions. Much of this is based on +# http://www.bipm.org/utils/common/pdf/si_brochure_8_en.pdf + + +# ------------------------------------------------------------------------ +[prefixes] +# ------------------------------------------------------------------------ +# prefix-string: float-multiplier [, comment] + +# SI. +Y: 1.e24 +Z: 1.e21 +E: 1.e18 +P: 1.e15 +T: 1.e12 +G: 1.e9 +M: 1.e6 +k: 1.e3 +h: 1.e2 +da: 1.e1 +d: 1.e-1 +c: 1.e-2 +m: 1.e-3 +u: 1.e-6 +n: 1.e-9 +p: 1.e-12 +f: 1.e-15 +a: 1.e-18 +z: 1.e-21 +y: 1.e-24 + +# IEC +Ei: 1152921504606846976, (1 << 60) +Pi: 1125899906842624, (1 << 50) +Ti: 1099511627776, (1 << 40) +Gi: 1073741824, (1 << 30) +Mi: 1048576, (1 << 20) +Ki: 1024, (1 << 10) + + +# ------------------------------------------------------------------------ +[base_units] +# ------------------------------------------------------------------------ +# quantity-name: unit-name + +# SI. +length: m +mass: kg +time: s +current: A +temperature: K +amount: mol +luminous_intensity: cd + +# Derived SI (Table 3). +angle: rad +solid_angle: sr + +# Other. +money: USD +passengers: pax +digital_data: byte + +# Unitness +Unitless: unitless + + +# ------------------------------------------------------------------------ +[units] +# ------------------------------------------------------------------------ +# unit-name: unit-expression, comment +# OR +# unit-name: factor, base-unit, offset, comment +# +# Note that unit names cannot be Python reserved words (such as 'in' for inch). +# Unit names are matched before prefix names are checked. +# A unit-expression cannot use prefixes (except for 'kg'), however 'pi' is +# available as a predefined constant. + +g: kg/1000, gram + +# Derived SI (Table 3). +Hz: 1/s, hertz +N: m*kg/s**2, newton +Pa: N/m**2, pascal +J: N*m, joule +W: J/s, watt +C: A*s, coulomb +V: W/A, volt +F: C/V, farad +ohm: V/A, ohm +S: A/V, siemens +Wb: V*s, weber +T: Wb/m**2, tesla +H: Wb/A, henry +degC: 1, K, 273.15, degree celsius +lm: cd*sr, lumen +lx: lm/m**2, lux +Bq: 1/s, becquerel +Gy: J/kg, gray +Sv: J/kg, sievert +kat: mol/s, katal + +# Non-SI (Table 6). +min: 60*s, minute +h: 3600*s, hour +d: 86400*s, day +deg: (pi/180)*rad, degree +arc_minute: deg/60, arc minute +arc_second: arc_minute/60, arc second +ha: 1e4*m**2, hectare +L: 1e-3*m**3, liter +t: 1e3*kg, tonne (metric ton) + +# Non-SI (Table 7). +e: 1.60217653e-19*C, elementary charge +eV: e*V, electron volt +Da: 1.66053886e-27*kg, dalton +u: Da, unified atomic mass unit +ua: 1.49597870700e11*m, astronomical unit +AU: 1.49597870700e11*m, astronomical unit +c0: 299792458.*m/s, speed of light in vacuum +hbar: 1.05457168e-34*J*s, reduced Planck Constant +me: 9.1093826e-31*kg, electron mass +a0: 0.5291772108e-10*m, Bohr radius +Eh: 4.35974417e-18*J, Hartree energy + +# Non-SI (Table 8). +bar: 1.e5*Pa, pressure +Ang: 1.e-10*m, angstrom +NM: 1.852e3*m, nautical mile +b: 1.e-28*m**2, barn +kn: NM/h, knot + +# Non-SI (Table 9). +erg: 1.e-7*J, erg +dyn: 1.e-5*N, dyne +P: 0.1*Pa*s, poise (dynamic viscosity) +St: 1.e-4*m**2/s, stokes (kinematic viscosity) +sb: 1.e4*cd/m**2, stilb (luminance) +ph: 1.e4*lx, phot (illuminance) +# 'Gal' skipped due to likely confusion with volume unit. +Mx: 1.e-8*Wb, maxwell (magnetic flux) +# 'G' renamed 'gauss' to avoid confusion with acceleration unit. +gauss: 1e-4*T, gauss (magnetic flux density) +Oe: (1.e3/(4*pi))*A/m, oersted (magnetic field) + +# Backward compatibility. +degK: K, degree Kelvin +nmi: NM, nautical mile +knot: kn, knot + +# Other length. +inch: 2.54e-2*m, inch +ft: 0.3048*m, foot +mi: 1.609344e3*m, mile (U.S. statute) +ly: c0*365.25*d, light year +pc: 648000/pi*AU, parsec +# Other mass. +oz: 28.349523125*g, ounce +lb: 16*oz, pound mass +lbm: lb, pound mass +ton: 2000*lb, ton (short) +slug: 14.5939029*kg, slug + +# Other time. +wk: 7*d, week +week: 7*d, week (backward compatibility) +a: 365.242199*d, year +yr: a, year +year: a, year (backward compatibility) +mo: yr/12, month +month: yr/12, month (backward compatibility) + +# Other temperature. +degR: K*5./9., degree Rankine +degF: 1, degR, 459.67, degree Fahrenheit + +# Other volume. +tsp: 4.92892159375e-3*L, teaspoon +tbsp: 3*tsp, tablespoon +floz: 2*tbsp, fluid ounce +cup: 8*floz, cup +pt: 16*floz, pint +qt: 2*pt, quart +galUS: 3.785411*L, gallon (U.S.) +galUK: 4.54609*L, gallon (U.K.) + +# Other miscellaneous. +lbf: 4.44822162*N, pound force + +rev: 2*pi*rad, revolution +rpm: 2*pi*rad/min, RPM +rps: 2*pi*rad/s, RPS + +cal: 4.184*J, thermochemical calorie +cali: 4.1868*J, international calorie +Btu: 1055.05585262*J, British thermal unit + +acre: mi**2/640, acre +hp: 745.7*W, horsepower + +atm: 101325.*Pa, standard atmosphere +torr: atm/760, Torr (1 mm of mercury) +psi: 6894.75729317*Pa, pounds per square inch +psf: psi/144, pounds per square foot + +mu0: 4.e-7*pi*N/A**2, permeability of vacuum +eps0: 1/mu0/c0**2, permittivity of vacuum +Grav: 6.67259e-11*m**3/kg/s**2, universal gravitational constant +Nav: 6.0221367e23/mol, Avagadros number +R: 8.31424*J/(mol*K), gas constant +V0: 2.24136*m**3/(1000*mol), volume of ideal gas +planck: 2*pi*hbar, Planck constant +mp: 1.672614e-27*kg, proton rest mass +mn: 1.674920e-27*kg, neutron rest mass +sigma: 5.66961e-8*W/(m**2*K**-4), Stefan-Boltzmann constant +Ken: 1.380649e-23*J, kelvin as energy unit +Rinfinity: 1.09737312e7/m, Rydberg constant +re: 2.817939e-15*m, classic electron radius +lambdac: 2.4263096e-12*m, Compton wavelength of electron +lambdap: 1.3214409e-15*m, Compton wavelength of proton +lambdan: 1.3196217e-15*m, Compton wavelength of neutron +mue: 9.284851e-24*J/T, electron magnetic moment +mup: 1.4106203e-26*J/T, proton magnetic moment +mub: 9.274096e-24*J/T, bohr magneton +mun: 5.050951e-27*J/T, nuclear magneton +gammap: 2.6751270e8*rad/(s*T), gyromagnetic ratio of protons in H2O +gammapc: 2.6751965e8*rad/(s*T), gyromagnetic ratio of protons in H2O corrected for diamagnetism of H2O +phi0: 2.0678538e-15*Wb, magnetic flux quantum +hme: 7.273894e-4*J*s/kg, quantum of circulation + +percent: unitless/100, percentage +``` diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts new file mode 100644 index 0000000..7162679 --- /dev/null +++ b/docs/docusaurus.config.ts @@ -0,0 +1,138 @@ +import { themes as prismThemes } from "prism-react-renderer"; +import type { Config } from "@docusaurus/types"; +import type * as Preset from "@docusaurus/preset-classic"; +import remarkMath from "remark-math"; +import rehypeKatex from "rehype-katex"; + +const config: Config = { + title: "Philote-Python", + tagline: + "Python implementation of the Philote-MDO standard for distributed multidisciplinary analysis", + favicon: "img/favicon.ico", + + future: { + v4: true, + }, + + url: "https://mdo-standards.github.io", + baseUrl: "/Philote-Python/", + + organizationName: "MDO-Standards", + projectName: "Philote-Python", + + onBrokenLinks: "throw", + + i18n: { + defaultLocale: "en", + locales: ["en"], + }, + + stylesheets: [ + { + href: "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css", + type: "text/css", + integrity: + "sha384-nB0miv6/jRmo5RLHO8BIp/8hwC1slNFDuv3VUgI4A7O/lEKmpRgiuNOQI2bRpFB", + crossorigin: "anonymous", + }, + ], + + presets: [ + [ + "classic", + { + docs: { + sidebarPath: "./sidebars.ts", + remarkPlugins: [remarkMath], + rehypePlugins: [rehypeKatex], + editUrl: + "https://github.com/MDO-Standards/Philote-Python/tree/develop/docs/", + lastVersion: "current", + versions: { + current: { + label: "Next", + }, + }, + }, + blog: false, + theme: { + customCss: "./src/css/custom.css", + }, + } satisfies Preset.Options, + ], + ], + + themeConfig: { + colorMode: { + defaultMode: "dark", + respectPrefersColorScheme: true, + }, + navbar: { + title: "Philote-Python", + items: [ + { + type: "docSidebar", + sidebarId: "docsSidebar", + position: "left", + label: "Docs", + }, + { + type: "docsVersionDropdown", + position: "right", + }, + { + href: "https://github.com/MDO-Standards/Philote-Python", + label: "GitHub", + position: "right", + }, + ], + }, + footer: { + style: "dark", + links: [ + { + title: "Documentation", + items: [ + { + label: "Getting Started", + to: "/docs/getting-started/installation", + }, + { + label: "Quick Start", + to: "/docs/getting-started/quickstart", + }, + { + label: "Tutorials", + to: "/docs/tutorials/explicit-disciplines", + }, + ], + }, + { + title: "More", + items: [ + { + label: "GitHub", + href: "https://github.com/MDO-Standards/Philote-Python", + }, + { + label: "Changelog", + href: "https://github.com/MDO-Standards/Philote-Python/blob/main/CHANGELOG.md", + }, + { + label: "PyPI", + href: "https://pypi.org/project/philote-mdo/", + }, + ], + }, + ], + copyright: `Copyright \u00A9 2022-${new Date().getFullYear()} Christopher A. Lupp. Built with Docusaurus.`, + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + additionalLanguages: ["python", "bash"], + }, + } satisfies Preset.ThemeConfig, +}; + +export default config; diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 0000000..b633989 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,18717 @@ +{ + "name": "philote-python-docs", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "philote-python-docs", + "version": "0.0.0", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/preset-classic": "3.9.2", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "prism-react-renderer": "^2.3.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/tsconfig": "3.9.2", + "@docusaurus/types": "3.9.2", + "postcss": "^8.5.8", + "typescript": "~5.6.2" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@algolia/abtesting": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.16.1.tgz", + "integrity": "sha512-Xxk4l00pYI+jE0PNw8y0MvsQWh5278WRtZQav8/BMMi3HKi2xmeuqe11WJ3y8/6nuBHdv39w76OpJb09TMfAVQ==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz", + "integrity": "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", + "@algolia/autocomplete-shared": "1.19.2" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz", + "integrity": "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.19.2" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz", + "integrity": "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==", + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.50.1.tgz", + "integrity": "sha512-4peZlPXMwTOey9q1rQKMdCnwZb/E95/1e+7KujXpLLSh0FawJzg//U2NM+r4AiJy4+naT2MTBhj0K30yshnVTA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.50.1.tgz", + "integrity": "sha512-i+aWHHG8NZvGFHtPeMZkxL2Loc6Fm7iaRo15lYSMx8gFL+at9vgdWxhka7mD1fqxkrxXsQstUBCIsSY8FvkEOw==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.50.1.tgz", + "integrity": "sha512-Hw52Fwapyk/7hMSV/fI4+s3H9MGZEUcRh4VphyXLAk2oLYdndVUkc6KBi0zwHSzwPAr+ZBwFPe2x6naUt9mZGw==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.50.1.tgz", + "integrity": "sha512-Bn/wtwhJ7p1OD/6pY+Zzn+zlu2N/SJnH46md/PAbvqIzmjVuwjNwD4y0vV5Ov8naeukXdd7UU9v550+v8+mtlg==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.50.1.tgz", + "integrity": "sha512-0V4Tu0RWR8YxkgI9EPVOZHGE4K5pEIhkLNN0CTkP/rnPsqaaSQpNMYW3/mGWdiKOWbX0iVmwLB9QESk3H0jS5g==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.1.tgz", + "integrity": "sha512-jofcWNYMXJDDr87Z2eivlWY6o71Zn7F7aOvQCXSDAo9QTlyf7BhXEsZymLUvF0O1yU9Q9wvrjAWn8uVHYnAvgw==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.50.1.tgz", + "integrity": "sha512-OteRb8WubcmEvU0YlMJwCXs3Q6xrdkb0v50/qZBJP1TF0CvujFZQM++9BjEkTER/Jr9wbPHvjSFKnbMta0b4dQ==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", + "license": "MIT" + }, + "node_modules/@algolia/ingestion": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.50.1.tgz", + "integrity": "sha512-0GmfSgDQK6oiIVXnJvGxtNFOfosBspRTR7csCOYCTL1P8QtxX2vDCIKwTM7xdSAEbJaZ43QlWg25q0Qdsndz8Q==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.50.1.tgz", + "integrity": "sha512-ySuigKEe4YjYV3si8NVk9BHQpFj/1B+ON7DhhvTvbrZJseHQQloxzq0yHwKmznSdlO6C956fx4pcfOKkZClsyg==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.50.1.tgz", + "integrity": "sha512-Cp8T/B0gVmjFlzzp6eP47hwKh5FGyeqQp1N48/ANDdvdiQkPqLyFHQVDwLBH0LddfIPQE+yqmZIgmKc82haF4A==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.1.tgz", + "integrity": "sha512-XKdGGLikfrlK66ZSXh/vWcXZZ8Vg3byDFbJD8pwEvN1FoBRGxhxya476IY2ohoTymLa4qB5LBRlIa+2TLHx3Uw==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.50.1.tgz", + "integrity": "sha512-mBAU6WyVsDwhHyGM+nodt1/oebHxgvuLlOAoMGbj/1i6LygDHZWDgL1t5JEs37x9Aywv7ZGhqbM1GsfZ54sU6g==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.50.1.tgz", + "integrity": "sha512-qmo1LXrNKLHvJE6mdQbLnsZAoZvj7VyF2ft4xmbSGWI2WWm87fx/CjUX4kEExt4y0a6T6nEts6ofpUfH5TEE1A==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "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==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@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/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", + "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", + "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", + "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", + "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", + "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", + "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", + "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.0", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", + "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.28.0", + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/plugin-transform-react-jsx-development": "^7.27.1", + "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", + "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", + "license": "MIT", + "dependencies": { + "core-js-pure": "^3.48.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@csstools/cascade-layer-name-parser": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", + "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/postcss-alpha-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-alpha-function/-/postcss-alpha-function-1.0.1.tgz", + "integrity": "sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", + "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-cascade-layers/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.12.tgz", + "integrity": "sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-function-display-p3-linear": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function-display-p3-linear/-/postcss-color-function-display-p3-linear-1.0.1.tgz", + "integrity": "sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-mix-function": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.12.tgz", + "integrity": "sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.2.tgz", + "integrity": "sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-content-alt-text": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.8.tgz", + "integrity": "sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-contrast-color-function": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-contrast-color-function/-/postcss-contrast-color-function-2.0.12.tgz", + "integrity": "sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-exponential-functions": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", + "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz", + "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-gamut-mapping": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.11.tgz", + "integrity": "sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-gradients-interpolation-method": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.12.tgz", + "integrity": "sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.12.tgz", + "integrity": "sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.4.tgz", + "integrity": "sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-initial": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz", + "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", + "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@csstools/postcss-light-dark-function": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.11.tgz", + "integrity": "sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-float-and-clear": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz", + "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-overflow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz", + "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-overscroll-behavior": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz", + "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-resize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz", + "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-logical-viewport-units": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", + "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-media-minmax": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", + "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", + "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz", + "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.1.tgz", + "integrity": "sha512-TQUGBuRvxdc7TgNSTevYqrL8oItxiwPDixk20qCB5me/W8uF7BPbhRrAvFuhEoywQp/woRsUZ6SJ+sU5idZAIA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.12.tgz", + "integrity": "sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-position-area-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz", + "integrity": "sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz", + "integrity": "sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-property-rule-prelude-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-property-rule-prelude-list/-/postcss-property-rule-prelude-list-1.0.0.tgz", + "integrity": "sha512-IxuQjUXq19fobgmSSvUDO7fVwijDJaZMvWQugxfEUxmjBeDCVaDuMpsZ31MsTm5xbnhA+ElDi0+rQ7sQQGisFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-random-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", + "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-relative-color-syntax": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.12.tgz", + "integrity": "sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-scope-pseudo-class": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz", + "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@csstools/postcss-sign-functions": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", + "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", + "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-syntax-descriptor-syntax-production": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-syntax-descriptor-syntax-production/-/postcss-syntax-descriptor-syntax-production-1.0.1.tgz", + "integrity": "sha512-GneqQWefjM//f4hJ/Kbox0C6f2T7+pi4/fqTqOFGTL3EjnvOReTqO1qUQ30CaUjkwjYq9qZ41hzarrAxCc4gow==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-system-ui-font-family": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz", + "integrity": "sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz", + "integrity": "sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", + "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", + "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/utilities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", + "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@docsearch/core": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.2.tgz", + "integrity": "sha512-/S0e6Dj7Zcm8m9Rru49YEX49dhU11be68c+S/BCyN8zQsTTgkKzXlhRbVL5mV6lOLC2+ZRRryaTdcm070Ug2oA==", + "license": "MIT", + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@docsearch/css": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.2.tgz", + "integrity": "sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==", + "license": "MIT" + }, + "node_modules/@docsearch/react": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.2.tgz", + "integrity": "sha512-/BbtGFtqVOGwZx0dw/UfhN/0/DmMQYnulY4iv0tPRhC2JCXv0ka/+izwt3Jzo1ZxXS/2eMvv9zHsBJOK1I9f/w==", + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.19.2", + "@docsearch/core": "4.6.2", + "@docsearch/css": "4.6.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@docusaurus/babel": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.9.2.tgz", + "integrity": "sha512-GEANdi/SgER+L7Japs25YiGil/AUDnFFHaCGPBbundxoWtCkA2lmy7/tFmgED4y1htAy6Oi4wkJEQdGssnw9MA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/preset-env": "^7.25.9", + "@babel/preset-react": "^7.25.9", + "@babel/preset-typescript": "^7.25.9", + "@babel/runtime": "^7.25.9", + "@babel/runtime-corejs3": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", + "babel-plugin-dynamic-import-node": "^2.3.3", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/bundler": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.9.2.tgz", + "integrity": "sha512-ZOVi6GYgTcsZcUzjblpzk3wH1Fya2VNpd5jtHoCCFcJlMQ1EYXZetfAnRHLcyiFeBABaI1ltTYbOBtH/gahGVA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.9", + "@docusaurus/babel": "3.9.2", + "@docusaurus/cssnano-preset": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "babel-loader": "^9.2.1", + "clean-css": "^5.3.3", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.11.0", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", + "file-loader": "^6.2.0", + "html-minifier-terser": "^7.2.0", + "mini-css-extract-plugin": "^2.9.2", + "null-loader": "^4.0.1", + "postcss": "^8.5.4", + "postcss-loader": "^7.3.4", + "postcss-preset-env": "^10.2.1", + "terser-webpack-plugin": "^5.3.9", + "tslib": "^2.6.0", + "url-loader": "^4.1.1", + "webpack": "^5.95.0", + "webpackbar": "^6.0.1" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "@docusaurus/faster": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/faster": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.9.2.tgz", + "integrity": "sha512-HbjwKeC+pHUFBfLMNzuSjqFE/58+rLVKmOU3lxQrpsxLBOGosYco/Q0GduBb0/jEMRiyEqjNT/01rRdOMWq5pw==", + "license": "MIT", + "dependencies": { + "@docusaurus/babel": "3.9.2", + "@docusaurus/bundler": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cli-table3": "^0.6.3", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "core-js": "^3.31.1", + "detect-port": "^1.5.1", + "escape-html": "^1.0.3", + "eta": "^2.2.0", + "eval": "^0.1.8", + "execa": "5.1.1", + "fs-extra": "^11.1.1", + "html-tags": "^3.3.1", + "html-webpack-plugin": "^5.6.0", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "open": "^8.4.0", + "p-map": "^4.0.0", + "prompts": "^2.4.2", + "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.4", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.4", + "semver": "^7.5.4", + "serve-handler": "^6.1.6", + "tinypool": "^1.0.2", + "tslib": "^2.6.0", + "update-notifier": "^6.0.2", + "webpack": "^5.95.0", + "webpack-bundle-analyzer": "^4.10.2", + "webpack-dev-server": "^5.2.2", + "webpack-merge": "^6.0.1" + }, + "bin": { + "docusaurus": "bin/docusaurus.mjs" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "@mdx-js/react": "^3.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/cssnano-preset": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.9.2.tgz", + "integrity": "sha512-8gBKup94aGttRduABsj7bpPFTX7kbwu+xh3K9NMCF5K4bWBqTFYW+REKHF6iBVDHRJ4grZdIPbvkiHd/XNKRMQ==", + "license": "MIT", + "dependencies": { + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.5.4", + "postcss-sort-media-queries": "^5.2.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/logger": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.9.2.tgz", + "integrity": "sha512-/SVCc57ByARzGSU60c50rMyQlBuMIJCjcsJlkphxY6B0GV4UH3tcA1994N8fFfbJ9kX3jIBe/xg3XP5qBtGDbA==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/mdx-loader": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.9.2.tgz", + "integrity": "sha512-wiYoGwF9gdd6rev62xDU8AAM8JuLI/hlwOtCzMmYcspEkzecKrP8J8X+KpYnTlACBUUtXNJpSoCwFWJhLRevzQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "@mdx-js/mdx": "^3.0.0", + "@slorber/remark-comment": "^1.0.0", + "escape-html": "^1.0.3", + "estree-util-value-to-estree": "^3.0.1", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "image-size": "^2.0.2", + "mdast-util-mdx": "^3.0.0", + "mdast-util-to-string": "^4.0.0", + "rehype-raw": "^7.0.0", + "remark-directive": "^3.0.0", + "remark-emoji": "^4.0.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", + "stringify-object": "^3.3.0", + "tslib": "^2.6.0", + "unified": "^11.0.3", + "unist-util-visit": "^5.0.0", + "url-loader": "^4.1.1", + "vfile": "^6.0.1", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/module-type-aliases": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.9.2.tgz", + "integrity": "sha512-8qVe2QA9hVLzvnxP46ysuofJUIc/yYQ82tvA/rBTrnpXtCjNSFLxEZfd5U8cYZuJIVlkPxamsIgwd5tGZXfvew==", + "license": "MIT", + "dependencies": { + "@docusaurus/types": "3.9.2", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@docusaurus/plugin-content-blog": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.2.tgz", + "integrity": "sha512-3I2HXy3L1QcjLJLGAoTvoBnpOwa6DPUa3Q0dMK19UTY9mhPkKQg/DYhAGTiBUKcTR0f08iw7kLPqOhIgdV3eVQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "cheerio": "1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "schema-dts": "^1.1.2", + "srcset": "^4.0.0", + "tslib": "^2.6.0", + "unist-util-visit": "^5.0.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-docs": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", + "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "@types/react-router-config": "^5.0.7", + "combine-promises": "^1.1.0", + "fs-extra": "^11.1.1", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "schema-dts": "^1.1.2", + "tslib": "^2.6.0", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-pages": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.9.2.tgz", + "integrity": "sha512-s4849w/p4noXUrGpPUF0BPqIAfdAe76BLaRGAGKZ1gTDNiGxGcpsLcwJ9OTi1/V8A+AzvsmI9pkjie2zjIQZKA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "fs-extra": "^11.1.1", + "tslib": "^2.6.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-css-cascade-layers": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.9.2.tgz", + "integrity": "sha512-w1s3+Ss+eOQbscGM4cfIFBlVg/QKxyYgj26k5AnakuHkKxH6004ZtuLe5awMBotIYF2bbGDoDhpgQ4r/kcj4rQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/plugin-debug": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.9.2.tgz", + "integrity": "sha512-j7a5hWuAFxyQAkilZwhsQ/b3T7FfHZ+0dub6j/GxKNFJp2h9qk/P1Bp7vrGASnvA9KNQBBL1ZXTe7jlh4VdPdA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "fs-extra": "^11.1.1", + "react-json-view-lite": "^2.3.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-analytics": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.9.2.tgz", + "integrity": "sha512-mAwwQJ1Us9jL/lVjXtErXto4p4/iaLlweC54yDUK1a97WfkC6Z2k5/769JsFgwOwOP+n5mUQGACXOEQ0XDuVUw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-gtag": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.9.2.tgz", + "integrity": "sha512-YJ4lDCphabBtw19ooSlc1MnxtYGpjFV9rEdzjLsUnBCeis2djUyCozZaFhCg6NGEwOn7HDDyMh0yzcdRpnuIvA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "@types/gtag.js": "^0.0.12", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-tag-manager": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.9.2.tgz", + "integrity": "sha512-LJtIrkZN/tuHD8NqDAW1Tnw0ekOwRTfobWPsdO15YxcicBo2ykKF0/D6n0vVBfd3srwr9Z6rzrIWYrMzBGrvNw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-sitemap": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.9.2.tgz", + "integrity": "sha512-WLh7ymgDXjG8oPoM/T4/zUP7KcSuFYRZAUTl8vR6VzYkfc18GBM4xLhcT+AKOwun6kBivYKUJf+vlqYJkm+RHw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "fs-extra": "^11.1.1", + "sitemap": "^7.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/plugin-svgr": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.9.2.tgz", + "integrity": "sha512-n+1DE+5b3Lnf27TgVU5jM1d4x5tUh2oW5LTsBxJX4PsAPV0JGcmI6p3yLYtEY0LRVEIJh+8RsdQmRE66wSV8mw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "@svgr/core": "8.1.0", + "@svgr/webpack": "^8.1.0", + "tslib": "^2.6.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/preset-classic": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.9.2.tgz", + "integrity": "sha512-IgyYO2Gvaigi21LuDIe+nvmN/dfGXAiMcV/murFqcpjnZc7jxFAxW+9LEjdPt61uZLxG4ByW/oUmX/DDK9t/8w==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/plugin-content-blog": "3.9.2", + "@docusaurus/plugin-content-docs": "3.9.2", + "@docusaurus/plugin-content-pages": "3.9.2", + "@docusaurus/plugin-css-cascade-layers": "3.9.2", + "@docusaurus/plugin-debug": "3.9.2", + "@docusaurus/plugin-google-analytics": "3.9.2", + "@docusaurus/plugin-google-gtag": "3.9.2", + "@docusaurus/plugin-google-tag-manager": "3.9.2", + "@docusaurus/plugin-sitemap": "3.9.2", + "@docusaurus/plugin-svgr": "3.9.2", + "@docusaurus/theme-classic": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/theme-search-algolia": "3.9.2", + "@docusaurus/types": "3.9.2" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/theme-classic": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.9.2.tgz", + "integrity": "sha512-IGUsArG5hhekXd7RDb11v94ycpJpFdJPkLnt10fFQWOVxAtq5/D7hT6lzc2fhyQKaaCE62qVajOMKL7OiAFAIA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/plugin-content-blog": "3.9.2", + "@docusaurus/plugin-content-docs": "3.9.2", + "@docusaurus/plugin-content-pages": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/theme-translations": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "infima": "0.2.0-alpha.45", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.5.4", + "prism-react-renderer": "^2.3.0", + "prismjs": "^1.29.0", + "react-router-dom": "^5.3.4", + "rtlcss": "^4.1.0", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/theme-common": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz", + "integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==", + "license": "MIT", + "dependencies": { + "@docusaurus/mdx-loader": "3.9.2", + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^2.0.0", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^2.3.0", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/theme-search-algolia": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.2.tgz", + "integrity": "sha512-GBDSFNwjnh5/LdkxCKQHkgO2pIMX1447BxYUBG2wBiajS21uj64a+gH/qlbQjDLxmGrbrllBrtJkUHxIsiwRnw==", + "license": "MIT", + "dependencies": { + "@docsearch/react": "^3.9.0 || ^4.1.0", + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/plugin-content-docs": "3.9.2", + "@docusaurus/theme-common": "3.9.2", + "@docusaurus/theme-translations": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "algoliasearch": "^5.37.0", + "algoliasearch-helper": "^3.26.0", + "clsx": "^2.0.0", + "eta": "^2.2.0", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "tslib": "^2.6.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/theme-translations": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.9.2.tgz", + "integrity": "sha512-vIryvpP18ON9T9rjgMRFLr2xJVDpw1rtagEGf8Ccce4CkTrvM/fRB8N2nyWYOW5u3DdjkwKw5fBa+3tbn9P4PA==", + "license": "MIT", + "dependencies": { + "fs-extra": "^11.1.1", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/tsconfig": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.9.2.tgz", + "integrity": "sha512-j6/Fp4Rlpxsc632cnRnl5HpOWeb6ZKssDj6/XzzAzVGXXfm9Eptx3rxCC+fDzySn9fHTS+CWJjPineCR1bB5WQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docusaurus/types": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.9.2.tgz", + "integrity": "sha512-Ux1JUNswg+EfUEmajJjyhIohKceitY/yzjRUpu04WXgvVz+fbhVC0p+R0JhvEu4ytw8zIAys2hrdpQPBHRIa8Q==", + "license": "MIT", + "dependencies": { + "@mdx-js/mdx": "^3.0.0", + "@types/history": "^4.7.11", + "@types/mdast": "^4.0.2", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.9.2", + "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.95.0", + "webpack-merge": "^5.9.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@docusaurus/types/node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@docusaurus/utils": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.2.tgz", + "integrity": "sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "3.9.2", + "@docusaurus/types": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "escape-string-regexp": "^4.0.0", + "execa": "5.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^11.1.1", + "github-slugger": "^1.5.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "jiti": "^1.20.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "p-queue": "^6.6.2", + "prompts": "^2.4.2", + "resolve-pathname": "^3.0.0", + "tslib": "^2.6.0", + "url-loader": "^4.1.1", + "utility-types": "^3.10.0", + "webpack": "^5.88.1" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/utils-common": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.9.2.tgz", + "integrity": "sha512-I53UC1QctruA6SWLvbjbhCpAw7+X7PePoe5pYcwTOEXD/PxeP8LnECAhTHHwWCblyUX5bMi4QLRkxvyZ+IT8Aw==", + "license": "MIT", + "dependencies": { + "@docusaurus/types": "3.9.2", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@docusaurus/utils-validation": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.9.2.tgz", + "integrity": "sha512-l7yk3X5VnNmATbwijJkexdhulNsQaNDwoagiwujXoxFbWLcxHQqNQ+c/IAlzrfMMOfa/8xSBZ7KEKDesE/2J7A==", + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "fs-extra": "^11.2.0", + "joi": "^17.9.2", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-core": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.1.tgz", + "integrity": "sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.1", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.1.tgz", + "integrity": "sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.1", + "@jsonjoy.com/fs-node-builtins": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.1", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.1.tgz", + "integrity": "sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.1", + "@jsonjoy.com/fs-node-builtins": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/fs-print": "4.57.1", + "@jsonjoy.com/fs-snapshot": "4.57.1", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.1.tgz", + "integrity": "sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.1.tgz", + "integrity": "sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.57.1", + "@jsonjoy.com/fs-node-builtins": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.1" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.1.tgz", + "integrity": "sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.57.1" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-print": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.1.tgz", + "integrity": "sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.57.1", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.1.tgz", + "integrity": "sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", + "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", + "integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/asn1-x509-attr": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz", + "integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz", + "integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz", + "integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.1", + "@peculiar/asn1-pkcs8": "^2.6.1", + "@peculiar/asn1-rsa": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz", + "integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz", + "integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.1", + "@peculiar/asn1-pfx": "^2.6.1", + "@peculiar/asn1-pkcs8": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/asn1-x509-attr": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz", + "integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", + "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", + "license": "MIT", + "dependencies": { + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz", + "integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz", + "integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz", + "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==", + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "license": "MIT" + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@slorber/remark-comment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", + "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.1.0", + "micromark-util-symbol": "^1.0.1" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "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==", + "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==", + "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==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/gtag.js": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", + "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==", + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "license": "MIT" + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.17", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/prismjs": { + "version": "1.26.6", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", + "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-config": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.11.tgz", + "integrity": "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "^5.1.0" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "license": "MIT" + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "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==", + "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==", + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/algoliasearch": { + "version": "5.50.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.50.1.tgz", + "integrity": "sha512-/bwdue1/8LWELn/DBalGRfuLsXBLXULJo/yOeavJtDu8rBwxIzC6/Rz9Jg19S21VkJvRuZO1k8CZXBMS73mYbA==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.16.1", + "@algolia/client-abtesting": "5.50.1", + "@algolia/client-analytics": "5.50.1", + "@algolia/client-common": "5.50.1", + "@algolia/client-insights": "5.50.1", + "@algolia/client-personalization": "5.50.1", + "@algolia/client-query-suggestions": "5.50.1", + "@algolia/client-search": "5.50.1", + "@algolia/ingestion": "1.50.1", + "@algolia/monitoring": "1.50.1", + "@algolia/recommend": "5.50.1", + "@algolia/requester-browser-xhr": "5.50.1", + "@algolia/requester-fetch": "5.50.1", + "@algolia/requester-node-http": "5.50.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/algoliasearch-helper": { + "version": "3.28.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.28.1.tgz", + "integrity": "sha512-6iXpbkkrAI5HFpCWXlNmIDSBuoN/U1XnEvb2yJAoWfqrZ+DrybI7MQ5P5mthFaprmocq+zbi6HxnR28xnZAYBw==", + "license": "MIT", + "dependencies": { + "@algolia/events": "^4.0.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/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==", + "license": "MIT" + }, + "node_modules/ansi-align/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==", + "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/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1js": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", + "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "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/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "license": "MIT", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", + "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "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": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.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==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001786", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001786.tgz", + "integrity": "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==", + "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/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/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==", + "license": "MIT" + }, + "node_modules/cli-table3/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==", + "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/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==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "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==", + "license": "MIT" + }, + "node_modules/combine-promises": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", + "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compressible/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/configstore": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", + "license": "BSD-2-Clause", + "dependencies": { + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "license": "MIT", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz", + "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/css-blank-pseudo": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz", + "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz", + "integrity": "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==", + "license": "ISC", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.3.tgz", + "integrity": "sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-has-pseudo/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz", + "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssdb": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.8.0.tgz", + "integrity": "sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ], + "license": "MIT-0" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", + "license": "MIT", + "dependencies": { + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-advanced": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", + "license": "MIT", + "dependencies": { + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-default": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-utils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" + }, + "node_modules/detect-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", + "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", + "license": "MIT", + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.332", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.332.tgz", + "integrity": "sha512-7OOtytmh/rINMLwaFTbcMVvYXO3AUm029X0LcyfYk0B557RlPkdpTpnH9+htMlfu5dKwOmT0+Zs2Aw+lnn6TeQ==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/emoticon": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", + "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "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==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "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==", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-value-to-estree": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.5.0.tgz", + "integrity": "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/remcohaszing" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "dependencies": { + "@types/node": "*", + "require-like": ">= 0.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/express/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "license": "MIT", + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/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==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/file-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "license": "ISC" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "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==", + "license": "BSD-2-Clause" + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", + "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/html-webpack-plugin/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", + "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", + "license": "MIT", + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infima": { + "version": "0.2.0-alpha.45", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", + "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", + "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-network-error": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.1.tgz", + "integrity": "sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-npm": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz", + "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "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/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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==", + "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==", + "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==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/katex": { + "version": "0.16.45", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.45.tgz", + "integrity": "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "license": "MIT", + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/launch-editor": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.2.tgz", + "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.1.tgz", + "integrity": "sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.1", + "@jsonjoy.com/fs-fsa": "4.57.1", + "@jsonjoy.com/fs-node": "4.57.1", + "@jsonjoy.com/fs-node-builtins": "4.57.1", + "@jsonjoy.com/fs-node-to-fsa": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/fs-print": "4.57.1", + "@jsonjoy.com/fs-snapshot": "4.57.1", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-math/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-space/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/null-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", + "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/null-loader/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/null-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/null-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/null-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "license": "MIT", + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", + "license": "ISC" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz", + "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.12.tgz", + "integrity": "sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz", + "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz", + "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-colormin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-convert-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-custom-media": { + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", + "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-properties": { + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", + "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", + "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz", + "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-comments": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-empty": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-unused": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.4.tgz", + "integrity": "sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz", + "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-focus-within": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz", + "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz", + "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-image-set-function": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz", + "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/utilities": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-lab-function": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.12.tgz", + "integrity": "sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-loader": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", + "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^8.3.5", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz", + "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-merge-idents": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", + "license": "MIT", + "dependencies": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-rules": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "license": "MIT", + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-params": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", + "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/selector-resolve-nested": "^3.1.0", + "@csstools/selector-specificity": "^5.0.0", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-string": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz", + "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-ordered-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", + "license": "MIT", + "dependencies": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz", + "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz", + "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-preset-env": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.6.1.tgz", + "integrity": "sha512-yrk74d9EvY+W7+lO9Aj1QmjWY9q5NsKjK2V9drkOPZB/X6KZ0B3igKsHUYakb7oYVhnioWypQX3xGuePf89f3g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/postcss-alpha-function": "^1.0.1", + "@csstools/postcss-cascade-layers": "^5.0.2", + "@csstools/postcss-color-function": "^4.0.12", + "@csstools/postcss-color-function-display-p3-linear": "^1.0.1", + "@csstools/postcss-color-mix-function": "^3.0.12", + "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.2", + "@csstools/postcss-content-alt-text": "^2.0.8", + "@csstools/postcss-contrast-color-function": "^2.0.12", + "@csstools/postcss-exponential-functions": "^2.0.9", + "@csstools/postcss-font-format-keywords": "^4.0.0", + "@csstools/postcss-gamut-mapping": "^2.0.11", + "@csstools/postcss-gradients-interpolation-method": "^5.0.12", + "@csstools/postcss-hwb-function": "^4.0.12", + "@csstools/postcss-ic-unit": "^4.0.4", + "@csstools/postcss-initial": "^2.0.1", + "@csstools/postcss-is-pseudo-class": "^5.0.3", + "@csstools/postcss-light-dark-function": "^2.0.11", + "@csstools/postcss-logical-float-and-clear": "^3.0.0", + "@csstools/postcss-logical-overflow": "^2.0.0", + "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", + "@csstools/postcss-logical-resize": "^3.0.0", + "@csstools/postcss-logical-viewport-units": "^3.0.4", + "@csstools/postcss-media-minmax": "^2.0.9", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", + "@csstools/postcss-nested-calc": "^4.0.0", + "@csstools/postcss-normalize-display-values": "^4.0.1", + "@csstools/postcss-oklab-function": "^4.0.12", + "@csstools/postcss-position-area-property": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.2.1", + "@csstools/postcss-property-rule-prelude-list": "^1.0.0", + "@csstools/postcss-random-function": "^2.0.1", + "@csstools/postcss-relative-color-syntax": "^3.0.12", + "@csstools/postcss-scope-pseudo-class": "^4.0.1", + "@csstools/postcss-sign-functions": "^1.1.4", + "@csstools/postcss-stepped-value-functions": "^4.0.9", + "@csstools/postcss-syntax-descriptor-syntax-production": "^1.0.1", + "@csstools/postcss-system-ui-font-family": "^1.0.0", + "@csstools/postcss-text-decoration-shorthand": "^4.0.3", + "@csstools/postcss-trigonometric-functions": "^4.0.9", + "@csstools/postcss-unset-value": "^4.0.0", + "autoprefixer": "^10.4.23", + "browserslist": "^4.28.1", + "css-blank-pseudo": "^7.0.1", + "css-has-pseudo": "^7.0.3", + "css-prefers-color-scheme": "^10.0.0", + "cssdb": "^8.6.0", + "postcss-attribute-case-insensitive": "^7.0.1", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^7.0.12", + "postcss-color-hex-alpha": "^10.0.0", + "postcss-color-rebeccapurple": "^10.0.0", + "postcss-custom-media": "^11.0.6", + "postcss-custom-properties": "^14.0.6", + "postcss-custom-selectors": "^8.0.5", + "postcss-dir-pseudo-class": "^9.0.1", + "postcss-double-position-gradients": "^6.0.4", + "postcss-focus-visible": "^10.0.1", + "postcss-focus-within": "^9.0.1", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^6.0.0", + "postcss-image-set-function": "^7.0.0", + "postcss-lab-function": "^7.0.12", + "postcss-logical": "^8.1.0", + "postcss-nesting": "^13.0.2", + "postcss-opacity-percentage": "^3.0.0", + "postcss-overflow-shorthand": "^6.0.0", + "postcss-page-break": "^3.0.4", + "postcss-place": "^10.0.0", + "postcss-pseudo-class-any-link": "^10.0.1", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^8.0.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz", + "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz", + "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-sort-media-queries": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", + "license": "MIT", + "dependencies": { + "sort-css-media-queries": "2.2.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.23" + } + }, + "node_modules/postcss-svgo": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + }, + "engines": { + "node": "^14 || ^16 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/postcss-zindex": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prism-react-renderer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", + "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", + "license": "MIT", + "dependencies": { + "@types/prismjs": "^1.26.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "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/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz", + "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==", + "license": "MIT", + "dependencies": { + "escape-goat": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-helmet-async": { + "name": "@slorber/react-helmet-async", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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/react-json-view-lite": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.5.0.tgz", + "integrity": "sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.3.tgz", + "integrity": "sha512-GXfh9VLwB5ERaCsU6RULh7tkemeX15aNh6wuMEBtfdyMa7fFG8TXrhXlx1SoEK2Ty/l6XIkzzYIQmyaWW3JgdQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.3" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "react-loadable": "*", + "webpack": ">=4.41.1 || 5.x" + } + }, + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2" + }, + "peerDependencies": { + "react": ">=15", + "react-router": ">=5" + } + }, + "node_modules/react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz", + "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==", + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^3.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remark-directive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", + "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-emoji": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", + "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.2", + "emoticon": "^4.0.1", + "mdast-util-find-and-replace": "^3.0.1", + "node-emoji": "^2.1.0", + "unified": "^11.0.4" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/remark-frontmatter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", + "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-frontmatter": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "license": "MIT", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", + "engines": { + "node": "*" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", + "license": "MIT" + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rtlcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/schema-dts": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", + "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==", + "license": "Apache-2.0" + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "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/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "license": "MIT", + "peer": true + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", + "license": "MIT", + "dependencies": { + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-handler": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", + "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.5", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/serve-index": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.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", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.3.tgz", + "integrity": "sha512-tAjEd+wt/YwnEbfNB2ht51ybBJxbEWwe5ki/Z//Wh0rpBFTCUSj46GnxUKEWzhfuJTsee8x3lybHxFgUMig2hw==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=5.6.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sort-css-media-queries": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", + "license": "MIT", + "engines": { + "node": ">= 6.3.0" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "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==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/stylehacks": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "license": "MIT" + }, + "node_modules/svgo": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", + "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.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.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "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/terser-webpack-plugin/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==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/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==", + "license": "MIT" + }, + "node_modules/thingies": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", + "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", + "license": "MIT", + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "license": "0BSD" + }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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/update-notifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", + "is-installed-globally": "^0.4.0", + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/boxen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/url-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/url-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/url-loader/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webpack": { + "version": "5.105.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", + "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@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.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "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.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "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-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", + "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.43.1", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-middleware/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", + "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.25", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.8.1", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.22.1", + "graceful-fs": "^4.2.6", + "http-proxy-middleware": "^2.0.9", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^5.5.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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==", + "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.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpackbar": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", + "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "consola": "^3.2.3", + "figures": "^3.2.0", + "markdown-table": "^2.0.0", + "pretty-time": "^1.1.0", + "std-env": "^3.7.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "node_modules/webpackbar/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==", + "license": "MIT" + }, + "node_modules/webpackbar/node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webpackbar/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==", + "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/webpackbar/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==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..ffd2122 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,50 @@ +{ + "name": "philote-python-docs", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids", + "typecheck": "tsc" + }, + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/preset-classic": "3.9.2", + "@mdx-js/react": "^3.0.0", + "clsx": "^2.0.0", + "prism-react-renderer": "^2.3.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "3.9.2", + "@docusaurus/tsconfig": "3.9.2", + "@docusaurus/types": "3.9.2", + "postcss": "^8.5.8", + "typescript": "~5.6.2" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" + ] + }, + "engines": { + "node": ">=20.0" + } +} diff --git a/docs/sidebars.ts b/docs/sidebars.ts new file mode 100644 index 0000000..421f70f --- /dev/null +++ b/docs/sidebars.ts @@ -0,0 +1,38 @@ +import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; + +const sidebars: SidebarsConfig = { + docsSidebar: [ + { + type: "category", + label: "Getting Started", + items: [ + "getting-started/installation", + "getting-started/quickstart", + ], + }, + { + type: "category", + label: "Tutorials", + items: [ + "tutorials/explicit-disciplines", + "tutorials/implicit-disciplines", + "tutorials/units", + ], + }, + { + type: "category", + label: "Working with OpenMDAO", + items: [ + "openmdao/openmdao-clients", + "openmdao/openmdao-groups", + ], + }, + { + type: "category", + label: "About", + items: ["about/license"], + }, + ], +}; + +export default sidebars; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css new file mode 100644 index 0000000..2aa737a --- /dev/null +++ b/docs/src/css/custom.css @@ -0,0 +1,104 @@ +@import url("https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Crimson+Pro:ital,wght@0,400;0,600;1,400&display=swap"); + +/* ── CSS custom properties for use in modules ── */ +:root { + --font-display: "Instrument Sans", sans-serif; + --font-mono: "JetBrains Mono", monospace; + --font-body: "Crimson Pro", serif; + + --ifm-color-primary: #00829e; + --ifm-color-primary-dark: #00748e; + --ifm-color-primary-darker: #006d86; + --ifm-color-primary-darkest: #005a6e; + --ifm-color-primary-light: #0090ae; + --ifm-color-primary-lighter: #0097b6; + --ifm-color-primary-lightest: #00aed2; + --ifm-code-font-size: 95%; + --ifm-font-family-base: "Instrument Sans", sans-serif; + --ifm-font-family-monospace: "JetBrains Mono", monospace; + --ifm-heading-font-family: "Instrument Sans", sans-serif; + --docusaurus-highlighted-code-line-bg: rgba(0, 130, 158, 0.08); +} + +/* ── Dark mode ── */ +[data-theme="dark"] { + --ifm-color-primary: #00d4ff; + --ifm-color-primary-dark: #00bfe6; + --ifm-color-primary-darker: #00b4d9; + --ifm-color-primary-darkest: #0094b3; + --ifm-color-primary-light: #1ad9ff; + --ifm-color-primary-lighter: #26dbff; + --ifm-color-primary-lightest: #52e3ff; + --ifm-background-color: #08101d; + --ifm-background-surface-color: #0c1829; + --ifm-navbar-background-color: #04080fee; + --ifm-footer-background-color: #04080f; + --ifm-font-color-base: #c8d8e8; + --ifm-heading-color: #e8f0f8; + --docusaurus-highlighted-code-line-bg: rgba(0, 212, 255, 0.12); +} + +/* ── Dark navbar & footer ── */ +[data-theme="dark"] .navbar { + backdrop-filter: blur(16px) saturate(1.2); + border-bottom: 1px solid rgba(0, 212, 255, 0.08); +} + +[data-theme="dark"] .footer { + border-top: 1px solid rgba(0, 212, 255, 0.06); +} + +[data-theme="dark"] .footer .footer__title { + font-family: var(--font-mono); + text-transform: uppercase; + font-size: 0.7rem; + letter-spacing: 0.12em; + color: #5a7089; +} + +/* ── Markdown content ── */ +[data-theme="dark"] .markdown h1, +[data-theme="dark"] .markdown h2, +[data-theme="dark"] .markdown h3 { + font-family: var(--font-display); + font-weight: 600; + letter-spacing: -0.01em; +} + +[data-theme="dark"] .markdown p, +[data-theme="dark"] .markdown li { + font-family: var(--font-body); + font-size: 1.08rem; + line-height: 1.72; + color: #b0c4d8; +} + +[data-theme="dark"] .markdown code { + font-family: var(--font-mono); + background: #0c182966; + border: 1px solid rgba(0, 212, 255, 0.08); + padding: 0.1em 0.35em; + border-radius: 3px; +} + +[data-theme="dark"] pre.prism-code { + border: 1px solid rgba(0, 212, 255, 0.06); + border-radius: 6px; +} + +/* ── Sidebar ── */ +[data-theme="dark"] .menu__link { + font-family: var(--font-display); + font-size: 0.88rem; + font-weight: 500; +} + +[data-theme="dark"] .theme-doc-sidebar-container { + border-right: 1px solid rgba(0, 212, 255, 0.06) !important; +} + +/* ── Table of contents ── */ +[data-theme="dark"] .table-of-contents__link { + font-family: var(--font-mono); + font-size: 0.75rem; +} diff --git a/docs/src/pages/index.module.css b/docs/src/pages/index.module.css new file mode 100644 index 0000000..3b1df27 --- /dev/null +++ b/docs/src/pages/index.module.css @@ -0,0 +1,410 @@ +/* ── Hero ── */ +.hero { + position: relative; + overflow: hidden; + background: radial-gradient(ellipse at 50% 20%, #0c1829 0%, #04080f 70%); +} + +.heroGlow { + position: absolute; + top: -20%; + left: 30%; + width: 40%; + height: 60%; + background: radial-gradient( + ellipse, + rgba(0, 212, 255, 0.04) 0%, + transparent 70% + ); + animation: glowDrift 8s ease-in-out infinite; +} + +.heroInner { + position: relative; + max-width: 1000px; + margin: 0 auto; + padding: 96px 40px 48px; + text-align: center; +} + +.heroSuper { + font-family: var(--font-mono); + font-size: 0.65rem; + letter-spacing: 0.2em; + text-transform: uppercase; + color: #5a7089; + margin-bottom: 24px; + animation: fadeUp 0.6s ease-out both; +} + +.heroTitle { + font-family: var(--font-display); + font-size: clamp(3rem, 6vw, 4.5rem); + font-weight: 700; + color: #e8f0f8; + letter-spacing: -0.03em; + line-height: 1.05; + margin: 0 0 20px; + animation: fadeUp 0.6s ease-out 0.1s both; +} + +.heroTagline { + font-family: var(--font-body); + font-size: clamp(1.05rem, 2vw, 1.25rem); + color: #8fa4b8; + max-width: 560px; + margin: 0 auto 40px; + line-height: 1.65; + animation: fadeUp 0.6s ease-out 0.2s both; +} + +.heroCtas { + display: flex; + justify-content: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 64px; + animation: fadeUp 0.6s ease-out 0.3s both; +} + +.ctaPrimary { + font-family: var(--font-display); + font-size: 0.9rem; + font-weight: 600; + color: #04080f !important; + background: #00d4ff; + padding: 12px 32px; + border-radius: 6px; + text-decoration: none !important; + transition: all 0.2s; + border: 1px solid transparent; +} +.ctaPrimary:hover { + background: #1ad9ff; + box-shadow: 0 0 28px rgba(0, 212, 255, 0.3); + color: #04080f !important; +} + +.ctaSecondary { + font-family: var(--font-display); + font-size: 0.9rem; + font-weight: 600; + color: #00d4ff !important; + background: transparent; + padding: 12px 32px; + border-radius: 6px; + text-decoration: none !important; + border: 1px solid rgba(0, 212, 255, 0.25); + transition: all 0.2s; +} +.ctaSecondary:hover { + border-color: rgba(0, 212, 255, 0.6); + color: #e8f0f8 !important; +} + +/* ── Network SVG ── */ +.netWrap { + display: flex; + justify-content: center; + animation: fadeIn 1s ease-out 0.5s both; +} + +.netSvg { + width: 100%; + max-width: 640px; + height: auto; +} + +.netNode { + animation: nodePulse 4s ease-in-out infinite; +} + +.netPacket { + animation: packetFlow 3.5s ease-in-out infinite; +} + +.netPacketReverse { + animation: packetFlowReverse 3.5s ease-in-out infinite; +} + +.fadeInLate { + animation: fadeIn 0.8s ease-out 1.2s both; +} + +/* ── Stats bar ── */ +.statsBar { + background: #04080f; + border-top: 1px solid rgba(0, 212, 255, 0.06); + border-bottom: 1px solid rgba(0, 212, 255, 0.06); + padding: 36px 40px; +} + +.statsInner { + max-width: 720px; + margin: 0 auto; + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 32px; +} + +.stat { + text-align: center; + flex: 1; + min-width: 100px; +} + +.statValue { + font-family: var(--font-mono); + font-size: 1.6rem; + font-weight: 600; + color: #00d4ff; + letter-spacing: -0.02em; + line-height: 1; +} + +.statLabel { + font-family: var(--font-mono); + font-size: 0.6rem; + letter-spacing: 0.14em; + text-transform: uppercase; + color: #5a7089; + margin-top: 8px; +} + +/* ── Features section ── */ +.featuresSection { + background: radial-gradient( + ellipse at 50% 80%, + #0c1829 0%, + #04080f 70% + ); + padding: 96px 40px; +} + +.featuresInner { + max-width: 900px; + margin: 0 auto; +} + +.sectionHeader { + text-align: center; + margin-bottom: 56px; +} + +.sectionLabel { + font-family: var(--font-mono); + font-size: 0.6rem; + letter-spacing: 0.2em; + text-transform: uppercase; + color: #ff6b35; +} + +.sectionTitle { + font-family: var(--font-display); + font-size: 2rem; + font-weight: 600; + color: #e8f0f8; + letter-spacing: -0.02em; + margin: 10px 0 0; +} + +.featuresGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + margin-bottom: 80px; +} + +@media (max-width: 640px) { + .featuresGrid { + grid-template-columns: 1fr; + } + .statsInner { + justify-content: center; + } +} + +/* ── Feature card ── */ +.featureCard { + padding: 28px 28px 32px; + border-radius: 8px; + border: 1px solid rgba(0, 212, 255, 0.06); + background: rgba(12, 24, 41, 0.35); + transition: all 0.3s ease; +} +.featureCard:hover { + border-color: rgba(0, 212, 255, 0.18); + background: rgba(12, 24, 41, 0.65); + box-shadow: 0 4px 32px rgba(0, 212, 255, 0.04); +} + +.featureLabel { + font-family: var(--font-mono); + font-size: 0.58rem; + letter-spacing: 0.16em; + color: #ff6b35; + font-weight: 600; +} + +.featureTitle { + font-family: var(--font-display); + font-size: 1.15rem; + font-weight: 600; + color: #e8f0f8; + margin: 10px 0 8px; + letter-spacing: -0.01em; +} + +.featureDesc { + font-family: var(--font-body); + font-size: 1rem; + line-height: 1.65; + color: #8fa4b8; + margin: 0; +} + +/* ── Code preview ── */ +.codeSection { + text-align: center; +} + +.sectionLabelSmall { + font-family: var(--font-mono); + font-size: 0.6rem; + letter-spacing: 0.2em; + text-transform: uppercase; + color: #5a7089; + display: block; + margin-bottom: 24px; +} + +.codeBlock { + max-width: 560px; + margin: 0 auto; + border-radius: 8px; + overflow: hidden; + border: 1px solid rgba(0, 212, 255, 0.08); + background: rgba(4, 8, 15, 0.7); + text-align: left; +} + +.codeHeader { + padding: 10px 18px; + border-bottom: 1px solid rgba(0, 212, 255, 0.06); + display: flex; + align-items: center; + gap: 8px; +} + +.codeDotOrange { + width: 8px; + height: 8px; + border-radius: 50%; + background: #ff6b35; + opacity: 0.5; +} + +.codeDotCyan { + width: 8px; + height: 8px; + border-radius: 50%; + background: #00d4ff; + opacity: 0.35; +} + +.codeFilename { + font-family: var(--font-mono); + font-size: 0.6rem; + color: #5a7089; + letter-spacing: 0.08em; + margin-left: 8px; +} + +.codePre { + margin: 0; + padding: 24px 28px; + font-family: var(--font-mono); + font-size: 0.8rem; + line-height: 1.75; + color: #8fa4b8; + overflow-x: auto; + background: transparent; +} + +/* ── Animations ── */ +@keyframes fadeUp { + from { + opacity: 0; + transform: translateY(18px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes glowDrift { + 0%, + 100% { + opacity: 0.04; + } + 50% { + opacity: 0.08; + } +} + +@keyframes nodePulse { + 0%, + 100% { + r: 8; + opacity: 0.85; + } + 50% { + r: 10; + opacity: 1; + } +} + +@keyframes packetFlow { + 0% { + offset-distance: 0%; + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + offset-distance: 100%; + opacity: 0; + } +} + +@keyframes packetFlowReverse { + 0% { + offset-distance: 100%; + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + offset-distance: 0%; + opacity: 0; + } +} diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx new file mode 100644 index 0000000..2c21b42 --- /dev/null +++ b/docs/src/pages/index.tsx @@ -0,0 +1,391 @@ +import type { ReactNode } from "react"; +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Layout from "@theme/Layout"; +import styles from "./index.module.css"; + +/* ── Animated SVG: client / server gRPC exchange ── */ +function NetworkVisualization() { + return ( + + + + + + + + + + + + + + + + + + + + + + {/* Connection lines between client and the three servers */} + + + + + {/* Client node */} + + + CLIENT + + + openmdao + + + {/* Server nodes */} + {[ + { y: 80, label: "AERO", sub: "explicit" }, + { y: 150, label: "STRUCT", sub: "implicit" }, + { y: 220, label: "PROP", sub: "explicit" }, + ].map((s, i) => ( + + + + {s.label} + + + {s.sub} + + + ))} + + {/* Packets traveling along the wires */} + + + + + + + + {/* Footer label */} + + gRPC // PHILOTE-MDO + + + ); +} + +/* ── Feature card ── */ +type FeatureItem = { + label: string; + title: string; + description: string; +}; + +const features: FeatureItem[] = [ + { + label: "STANDARD", + title: "Philote-MDO Protocol", + description: + "Reference Python implementation of the Philote-MDO gRPC standard. Compatible with disciplines written in any supported language.", + }, + { + label: "DISCIPLINES", + title: "Explicit & Implicit", + description: + "First-class support for both explicit components and residual-based implicit disciplines, with analytic partial derivatives.", + }, + { + label: "INTEROP", + title: "OpenMDAO Bindings", + description: + "Drop-in RemoteExplicitComponent and RemoteImplicitComponent for OpenMDAO models, plus a wrapper to host OpenMDAO groups as Philote servers.", + }, + { + label: "DISTRIBUTED", + title: "Network Native", + description: + "Run expensive analyses on dedicated servers and call them from anywhere over gRPC. Same code, local or remote.", + }, +]; + +function FeatureCard({ label, title, description }: FeatureItem) { + return ( +
+ {label} +

{title}

+

{description}

+
+ ); +} + +/* ── Code preview ── */ +function CodePreview() { + const code = `import philote_mdo.general as pmdo + +class Paraboloid(pmdo.ExplicitDiscipline): + def setup(self): + self.add_input("x", shape=(1,), units="m") + self.add_input("y", shape=(1,), units="m") + self.add_output("f_xy", shape=(1,), units="m**2") + + def compute(self, inputs, outputs): + x, y = inputs["x"], inputs["y"] + outputs["f_xy"] = (x - 3)**2 + x*y + (y + 4)**2 - 3`; + + return ( +
+
+ + + paraboloid.py +
+
{code}
+
+ ); +} + +/* ── Stat ── */ +function Stat({ value, label }: { value: string; label: string }) { + return ( +
+
{value}
+
{label}
+
+ ); +} + +/* ── Hero ── */ +function Hero() { + const { siteConfig } = useDocusaurusContext(); + return ( +
+
+
+
+ Distributed Multidisciplinary Analysis +
+

{siteConfig.title}

+

+ Python implementation of the Philote-MDO standard for + language-agnostic, gRPC-based MDO discipline servers and + clients +

+
+ + Get Started + + + Quick Start + +
+
+ +
+
+
+ ); +} + +/* ── Page ── */ +export default function Home(): ReactNode { + return ( + + + +
+
+ + + + +
+
+ +
+
+
+ + Capabilities + +

+ Built for Multidisciplinary Workflows +

+
+ +
+ {features.map((f, i) => ( + + ))} +
+ +
+ + Define a Discipline + + +
+
+
+
+ ); +} diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0a84dd08d516f53e3c8393196de489e041274fd5 GIT binary patch literal 15086 zcmeHO`)^!F6&}}tloW6%qU9O8FNxnMP%0j!;a`9@X;O+rg<3)hO(;}}1X5<7c1v+m zL#sp})PQ(d^$V&%X%SAECV(Ol9)0iw#Zs}I#$J0%lk_HbynNr?IlFUb@4dcvcU^w* zO5>S3a~|KEIWu!+?zK`Ks#9Hmy+YojesP^rH!7uidd%~BrCvhadgN^1wnnKU2tEiJ zu&6I0vEKh(Tg_Jgu3BK#0`0Yc*7}MU|?WvbK0Bd0`4{WtiBf*rV`?f>(?Y?ZKurU z5hdsB&1OGZ%<8|j0e7HJ=)=i^)*nfZBt0c`+ymTR1a|twG5AszK7I?a^lE)I6U)V% zz7_mAv>yjul&^WkT;|K^k;QXVDyoBXF9P>G+VXu`Z-|#tZ_VfP-RMgJ@?H&(D!a{|88okQ?IgtHf=;PSd*QT8Qdoyb* z=GV@E9QY{4`8HQ)X;^&%c&C=Z9M-4F(vbSNE6?~+U_JoLc9$;hxxc^vE#R59@#k~; zM=no!NZkh9UK7}*KOX(fl?%O`)AcdP>Pd{jr7m66vmX-#`OiI|_4{plWA|3D`+a~f zz3z^KS(nOo9~f)c`s2#rJ7V)AUdv=@#rR_LqFpo!8LG{lOlCaT`Naxlkxv z2|nBZE@Hp=fK3nVg*IZBK4fei+~ZsvN#a({QK#QWZMmgG>g~XOw(a&s?8WYb|keRc)$KV$1M`&kVOaQ)eC4=aO) zXTBOwE|am)~03r2K=Ym*oP^`CAJRe%sUtk+wE~>#Hx1%c(Z!0x3~8lHcx3( zeG>Rvj-Mgp0`|n4ZP~E%<3)}B^ zbWf!@zfd0L{(X)No*x_HzqujxDfA(=FY)}qF>JujJK*5_s{qTrF5Pm^=E zL+JVa^q_hx@T5AwBM!tHqu|fPJ7)qqaehB=;J}-Ll*yj*d3_^foe#*vS(JLhB>3mD z>Y4Z_CeCB5#B`K#D}BYCry_^UH-xo8K+-JPn)l0e8clp>G>tZ|D83=r_LP&Ij2aF9OpuNSbFho_pl}oMrgi z=hy~(+#BtERrr~Eyl)kKE0O0hr}6&cAHJRHd%KOHxiZ!n$i87EFuO9jp21GvKp)&a zE;V+H=`_;LX4gm;TbgI?(6i3V`Ym=4yNI1CVmDeAcJP0VIlt5D zLHZ0*?K`c8$Q+HUztk3h-8;Ci@VvpqGV4+cO2NRhH{UyBulf!4Di6txDOh(1>2CK< zAcdL+Qs{j%VwxS08#woe=(q9AfoB)vhz5ZvDiEh$3q8*}x)$*KG-AcQeyoLf%P*r2 z^=z|oJcoPoYf`AJkruPrPeWG8k~a(6-xp_Fwwd_P<}2m%8mmz9pXs+PV+7-nc=q}@ zs^5xU_IoagKY(Yq z#WO0pD}$;Ndh*bB9x3Ym81KC@6|H={>Q|9Iqu3kP54i0vLM%J~bg$6RRiS6#%WJqa|1HbycvF+>h z3#pgf+;96f0_}ID9`|^EY*s(U?YPLn`D35aUxEIH`)YH~{CsC$oGV}XL)a}9cMM+L;wHOCj0k`e2%rq zm)!WDe}{tce@g0`ntOFW%!nMz|GY5&BOPq?q``OrH2&IyyUDB3|5oSUddR(CWFHbt zZjSikD`F#H*(vR?LF)H9w43llR_)F_?%O%%`<$7+zCQn3Fth%SrtpJd@=!nRh(67l ze$C@Ka{VPeeE(Zw1LVV?_ty%u|IiL`V{xAS7Tt^qA>)@K4?cj?L;Mf=)`9*7;laAm zZ^xFtKj591S(msrO!;Nhajh2V#0`CWYjO}rCE0(;k67V4@XcAgk3(MIJ(lPR%QivQF%d3U)PF%F; z%2KztXK~}=MsN3qZf|03_p9TbdtcKXe|cGV9Di9UKTRt&%_PrkBMoWELte_DEXu5m z82VkT;pO)iGe~!UuGU+Yad-X&)XzfZvtjcW^^cO5YZKRde4Fx9>;bQs zbnwiWy3(v$v&3PW(R@3xfcx;zo48^Id=e{4eu^>-js?Ec5*e@$-&vN36Z_oHLwH5T zLDGG?~FpEecz z^p7gHQdrA-fPWF`cFS03^ wcF@Ocns${D1F5AwxUWS-$VKM_-oC$xH_Rt5Q)(Y>nV48+9otAlTJn(hf1 + +AAAsdWp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAACxPanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46dXVpZDo2ZDgyNjhiYS1jNDVlLTQ0ZDgtYmRmNS05ZDZmNjFjYTkxMjYAAAABtWp1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAANdqdW1iAAAAJmp1bWRjYm9yABEAEIAAAKoAOJtxA2MycGEuYWN0aW9ucwAAAACpY2JvcqFnYWN0aW9uc4GjZmFjdGlvbmtjMnBhLmVkaXRlZG1zb2Z0d2FyZUFnZW50bUFkb2JlIEZpcmVmbHlxZGlnaXRhbFNvdXJjZVR5cGV4U2h0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvY29tcG9zaXRlV2l0aFRyYWluZWRBbGdvcml0aG1pY01lZGlhAAAArWp1bWIAAAAoanVtZGNib3IAEQAQgAAAqgA4m3EDYzJwYS5oYXNoLmRhdGEAAAAAfWNib3KlamV4Y2x1c2lvbnOBomVzdGFydBkBpWZsZW5ndGgZO0hkbmFtZW5qdW1iZiBtYW5pZmVzdGNhbGdmc2hhMjU2ZGhhc2hYIOXMi1nUATJgxs9D4TULsM7dj+61x79WiL1ZgCIek2/KY3BhZEkAAAAAAAAAAAAAAAILanVtYgAAACRqdW1kYzJjbAARABCAAACqADibcQNjMnBhLmNsYWltAAAAAd9jYm9yqGhkYzp0aXRsZW9HZW5lcmF0ZWQgSW1hZ2VpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDozOTBlM2MzOS0xYjkzLTRhMTEtYTE5Ny1jNGVhYzA4ZDI0YzJvY2xhaW1fZ2VuZXJhdG9yeDZBZG9iZV9JbGx1c3RyYXRvci8yOC4yIGFkb2JlX2MycGEvMC43LjYgYzJwYS1ycy8wLjI1LjJ0Y2xhaW1fZ2VuZXJhdG9yX2luZm+Bv2RuYW1lcUFkb2JlIElsbHVzdHJhdG9yZ3ZlcnNpb25kMjguMv9pc2lnbmF0dXJleBlzZWxmI2p1bWJmPWMycGEuc2lnbmF0dXJlamFzc2VydGlvbnOComN1cmx4J3NlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuYWN0aW9uc2RoYXNoWCBKacG9/6jeQTB4viTtzPgxOsHRZJU0VnGgDWsGszfUr6JjdXJseClzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmhhc2guZGF0YWRoYXNoWCATKjvIEW1nw/cLKrzyTTZ7ZIplwQFusGcr5h19Msu3zmNhbGdmc2hhMjU2AAAoQGp1bWIAAAAoanVtZGMyY3MAEQAQgAAAqgA4m3EDYzJwYS5zaWduYXR1cmUAAAAoEGNib3LShFkMwqIBOCQYIYJZBhAwggYMMIID9KADAgECAhB/8nQf0cbeQ7WUeo5lcJ6eMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNVBAYTAlVTMSMwIQYDVQQKExpBZG9iZSBTeXN0ZW1zIEluY29ycG9yYXRlZDEdMBsGA1UECxMUQWRvYmUgVHJ1c3QgU2VydmljZXMxIjAgBgNVBAMTGUFkb2JlIFByb2R1Y3QgU2VydmljZXMgRzMwHhcNMjQwMTExMDAwMDAwWhcNMjUwMTEwMjM1OTU5WjB/MREwDwYDVQQDDAhjYWktcHJvZDETMBEGA1UECgwKQWRvYmUgSW5jLjERMA8GA1UEBwwIU2FuIEpvc2UxEzARBgNVBAgMCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTMSAwHgYJKoZIhvcNAQkBFhFjYWktb3BzQGFkb2JlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO/TAKd9hj2WQcOzKStMbsViWdgcFzK5qW4Pm71QuPO/4WxNY4uDw2GvP1FPEj0R4Fu7dabt/i+o+xBh/GQSnTAhroNWYQ5mFsB5F9uHJtZD8Pha+9yTiYRlH5BmZOkifsdfrWBu0wUeUlTGRgFy0igC31MEAVvP13kiDZYbRuwff1vr/xRJecgiTgUHp20FFPXF4TBIO3A53VgGldc0EmZFYdc0llnzmh/a0FN6yD+Qy9Os4knFgNn3tTjXrBy9tWiaV56z41dRjf9kHjEf42xmKWzubVsva/aFKceIuipl5QqSVsdSZPhmmOtkJ224ixabmPY89cqvscpS4JtlJcECAwEABaOCAYwwggGIMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMB4GA1UdJQQXMBUGCSqGSIb3LwEBDAYIKwYBBQUHAwQwgY4GA1UdIASBhjCBgzCBgAYJKoZIhvcvAQIDMHMwcQYIKwYBBQUHAgIwZQxjWW91IGFyZSBub3QgcGVybWl0dGVkIHRvIHVzZSB0aGlzIExpY2Vuc2UgQ2VydGlmaWNhdGUgZXhjZXB0IGFzIHBlcm1pdHRlZCBieSB0aGUgbGljZW5zZSBhZ3JlZW1lbnQuMF0GA1UdHwRWMFQwUqBQoE6GTGh0dHA6Ly9wa2ktY3JsLnN5bWF1dGguY29tL2NhXzdhNWMzYTBjNzMxMTc0MDZhZGQxOTMxMmJjMWJjMjNmL0xhdGVzdENSTC5jcmwwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vcGtpLW9jc3Auc3ltYXV0aC5jb20wHwYDVR0jBBgwFoAUVyl6Mk3M/uQ1TsAfJHPOc1Or32owDQYJKoZIhvcNAQELBQADggIBACFj1fzbYu+jD34qVJ2QAj/3laxL+fVjmx7lgmR49QcvGxpuVJo04D0GJV8k4/FxP4mNu6YYKx4NKYg7Fg87ubPAekypGjJlL4LZPlILpMm233bQQuir7RoMuNHDuA5BFKDw4rQ8U9YoG0KnSqIAKFSqgxFapxfghUEM7WxnGYRSPVyk7AkMH/YcOy4SQqOowDQSAATckLUsFiKT3khCYT1YFi8inqYEMSTKi+rIGGcRqvaQkkJ9oEvKWn8kBSwcmcBAQ/w8crUWWS+m92V6hfn2WbBPX7AcaXFhYr/KRb4AUDrQs5Um3xvKO24ATyV5u5gAPI97d4QxHhMdBsfAf/WvPac03m1law90hHtLmZfRG1QvvheBKLESRXWMM7j9YiTtXeSN1cRmvRa1avxWARjkb7w2k8Gp3YswsGnuN6MwiPe0DxFLUvoyc7c9ZGqR3AbCwIey8ZxPNq5ZqtaOL33RbEFM+xxKTpDxZMiqIcj059RSKuJZCSl94wQOjWv7MclGaPbr7OAgPtAHjXE15XcroEVBfprKXWzC4iHbooHQy7viyd1HMb7y0xN9++MGg71A9b8611bJiawsZPQpyP0RpoiDUmaA21EHCj1/z15glJjB6dD4Z12mp2cEiOTrdD4reAJ2WlOz760kJnsNf3Kd2ZiXH3geBRkU9w2Ka6fpWQalMIIGoTCCBImgAwIBAgIQDKi2VHuJ5tIGiXXNi5uJ4jANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVUzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxHTAbBgNVBAsTFEFkb2JlIFRydXN0IFNlcnZpY2VzMRkwFwYDVQQDExBBZG9iZSBSb290IENBIEcyMB4XDTE2MTEyOTAwMDAwMFoXDTQxMTEyODIzNTk1OVowdTELMAkGA1UEBhMCVVMxIzAhBgNVBAoTGkFkb2JlIFN5c3RlbXMgSW5jb3Jwb3JhdGVkMR0wGwYDVQQLExRBZG9iZSBUcnVzdCBTZXJ2aWNlczEiMCAGA1UEAxMZQWRvYmUgUHJvZHVjdCBTZXJ2aWNlcyBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALcfLr29CbNcSGz+DIOubAHqUXglpIA+iaexsohk2vaJdoH5+R3zlfx4mI2Yjs/k7hxVPg1zWnfOsRKoFXhlTJbyBxnvxB3CgcbxA13ZU1wecyBJH5dP0hp+yer01/DDcm30oveXkA1DmfX4wmqvjwRY0uWX3jZs4v8kfjLANIyiqFmq0kQhRRQaVBUFnwIC8lzssTp10DkLnY8TY+lrtF9CAdd/iB9dVnCnFhFlzOI+I4eoS8tvQndxKFRt6MXFXpzBfxDIA9rV48eDVG0zQdf4PfjEejcOTIaeZP4N2rTRMQMYbboAvk90g0oUhCX7NqrookVB7V90YTnCtbNTiYE+bNrPcRsuf7sVaXACGitiogyV1t8cTfJ1z5pNTUlbv5sbX2qa+E70iW4a1O1AN6oUGPZ+Dp9rGx9V9U8Puy03pPCggOWQ4IThET4iKfybfPd6qL9WxOayZGoHFYNFqo4fPTYQmgQPFckbd6L5RsginTVdlC925+b3RbE5O6qpqfZmpM9f0rlV2MSH+i+vvEVzmrV1mj5JrnLixNUzznj+0tTeSU6BQrPNJdg9hLcaEFxgkePCv3E1Eec1f30PoXSDs6KNJxZ++2PGHXdpO/8fQRO/KZqHjJ8OlV2H1wrlhII+qe46Wy6MUDKFjAlc5YO9llTYSRZUsOGg/H3Ons3hAgMBAAGjggE0MIIBMDASBgNVHRMBAf8ECDAGAQH/AgEAMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuYWRvYmUuY29tL2Fkb2Jlcm9vdGcyLmNybDAOBgNVHQ8BAf8EBAMCAQYwFAYDVR0lBA0wCwYJKoZIhvcvAQEHMFcGA1UdIARQME4wTAYJKoZIhvcvAQIDMD8wPQYIKwYBBQUHAgEWMWh0dHBzOi8vd3d3LmFkb2JlLmNvbS9taXNjL3BraS9wcm9kX3N2Y2VfY3BzLmh0bWwwJAYDVR0RBB0wG6QZMBcxFTATBgNVBAMTDFNZTUMtNDA5Ni0zMzAdBgNVHQ4EFgQUVyl6Mk3M/uQ1TsAfJHPOc1Or32owHwYDVR0jBBgwFoAUphzhbVQkTKiPSHK/bqmM1eTsMdQwDQYJKoZIhvcNAQELBQADggIBAHHO5QeMptwt3MjgO2VeAJKBleuVICSvn2k4Xcl88bjapU0AZTslwRhcnr5Zt9wbBjtZgyX6M7si8k9vuyFcVhb1ucmDFfuUtTXgoTFyGZws1jV57oiEEnZjw/NkxFQpJ3kKRRE+DQ8EsaPP8pH8Oh8fH4bis9MI4Y5FjF5it3TWVyLmFXG8pxy8iTswPr1lN7B9k9Iz7RaexTd/RmZ3uGBtGlTJZx4bR4cWl1Qor9kVaEeMNULbyh0Kc3zzm0edwpe+Ii0rRlRSj8Ai2EUqWEReyer1Uv18VuC87zdm+lRCjnLyZjdy4acRUZd2GM1vncJ8LW7h1uliZZo332y5tTMSxRpRveWgs99V/MM6mDbL2/fuQF3L/C5evbS15jtTrbGP98CCzVBKeFS2UxN8Kpt5/ITJwpWYoismQkuy+BNJgpW8fgUUjB93laOo4L3uNf3ytxUDOEAjSJKRrOxY4y8vqbQvicslqnH7zkaxVfxjoAeYQ/huYISXCKXooA/5R7AkWLDmubBXakRIcCFi5klrTcHy2XSd3ZAnO8kaZt4GpeqkX05GKcUzccSsrym5GiQ6MUfb7Vqwt4ja0HfVb8Qt017bs6B26rpnqoHAKnn1hfburJ0OEPRZF83riQKzbkrzyIYAY1bYIB9MNL5v5ZgkGIgv2NdhngsX4GJS9927omZzaWdUc3ShaXRzdFRva2Vuc4GhY3ZhbFkXPzCCFzswAwIBADCCFzIGCSqGSIb3DQEHAqCCFyMwghcfAgEDMQ8wDQYJYIZIAWUDBAIBBQAwgYEGCyqGSIb3DQEJEAEEoHIEcDBuAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgZD6KiJEIfYglO+7b/7C90E/6MnDRfFFo/BSPVEg5rWsCEARKnPHR3O4gNesfOSAB7SAYDzIwMjQwMjI0MDAyNjMyWgIID+uH3XllwJOgghMJMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmmv9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwvSKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsvlmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4ZGIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RChG4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDaY7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S769SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQfqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7UjipmAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1Pt7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM604qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1Ott5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvPnPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7MmJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOGv6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2nyMpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0eaHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9nR2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI0MDIyNDAwMjYzMlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUZvArMsLCyQ+CXc6qisnGTxmcz0AwLwYJKoZIhvcNAQkEMSIEINssVYypA2kWuM0eD0IZJ1kF0J1o88almaMA4aYsimP0MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEINL25G3tdCLM0dRAV2hBNm+CitpVmq4zFq9NGprUDHgoMA0GCSqGSIb3DQEBAQUABIICAEBBpXFpm535wA7KgoHWx9m7/vWr7tARZUxzCqYWyZrChuAODIDqwnVyFaIlsXzzpIo96eCkQOWAggbCAQxOH0Ii6I2XDpM3bBWkKZY09GVWixbsKyTJFgXNq2XX6IKMdBqpTQLnzGABuwCoiRqB0D8k8wrbcnLh/0a+odHblSzlO2ASnliQPAdvm5eCds3nuc/ZOC8lJlYubpt2WHpLfNpzPFDfoCWE6DM4YTNasLLWEEWP65hm8VzcnIWPmlEx/sXnkVpw+9+fnf20D8nm+bC0f2uaGQEZpaNiH5xDnsBqnKwSfgrTXr6oXDqkXXFnsCbLvm1fO4pRvOj+tDbi1mVxfXYvE/tRwN8J9OBtCaDZtPUrRddtW55ILaPnYKjjlI8Q7x7ximMRVLSBVcQtUfo1R0EFt0+1RTlKrvtZl1bF/z/9qHjK/OdUAoK8s5/wuXh9pucgcfX2DpQzg3GgR3xb5wBm0qTGDynVAu4RHdC7CRLrbmYk2n6VMl6fIicawuxeSIU7q9flY6AiIVd+Cx0XxaG7LZS+V/DtwugdsrR0p2adE4fStfgCKKprUEzOp7jAi1298R5IDYfcOdp9np+/lsuyH9PoXBWbaENZorYtSHkKQ4LEc2H0OYDwbEkx3HNS3RuBESAukQ1gxTnB10sZ6S9FPhcFn1uiLiDK3JtEY3BhZFkC2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9lkBACl0na35qhUMoWsjA5XaEeca5OJ6AWbxxs7e3LiHPraMcpaSl52H2DJAqpsN/bapiuHVE5kInT2yky6zriibz8gI0WlCXLWkpA1cy7YjYLd6ph4tbS1eZBVrHzuSvUsw0bq3qc6ujRuknSa/Iom5qq7xxCK00oNg+j7KetCqImfy7v9g4eZ6lcwFCdI4DqADaPDhGaqp5zFEEZHcjTcSTApl3uF16dAytmhS6FcGJfAqNj734ZRTbJY+VsfhYJ4Yfmg/xWSq5OfZ3gUez0GVfyXBtO9glzFU6S9P7uMerTtkpQ2I/PnziXdO1JRycEbcp/iBNSjC45IqycMD9LeR1og= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..920d7a6 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,8 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": "." + }, + "exclude": [".docusaurus", "build"] +} diff --git a/philote_mdo/examples/sellar.py b/philote_mdo/examples/sellar.py index 3dde521..5a06bd3 100644 --- a/philote_mdo/examples/sellar.py +++ b/philote_mdo/examples/sellar.py @@ -66,13 +66,16 @@ def setup(self): "d2", SellarDis2(), promotes_inputs=["z", "y1"], promotes_outputs=["y2"] ) - cycle.set_input_defaults("x", 1.0) - cycle.set_input_defaults("z", np.array([5.0, 2.0])) - # Nonlinear Block Gauss Seidel is a gradient free solver cycle.nonlinear_solver = om.NonlinearBlockGS(iprint=0) cycle.linear_solver = om.LinearBlockGS(iprint=0) + # Set defaults at the top level so the promoted 'x' and 'z' values + # from `cycle` and `obj_cmp` agree; OpenMDAO otherwise flags the + # promoted inputs as ambiguous during final_setup. + self.set_input_defaults("x", 1.0) + self.set_input_defaults("z", np.array([5.0, 2.0])) + self.add_subsystem( "obj_cmp", om.ExecComp( diff --git a/tests/test_abstract_methods.py b/tests/test_abstract_methods.py index 4ae784f..4b6fed2 100644 --- a/tests/test_abstract_methods.py +++ b/tests/test_abstract_methods.py @@ -98,7 +98,7 @@ def test_implicit_discipline_apply_linear_not_implemented(self): discipline = ImplicitDiscipline() with self.assertRaises(NotImplementedError) as context: - discipline.apply_linear({}, {}, "fwd") + discipline.apply_linear({}, {}, {}, {}, {}, "fwd") self.assertEqual(str(context.exception), "apply_linear not implemented") From 225683bd987a5c5c9a471a29f6324a03a77a1854 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Wed, 8 Apr 2026 19:28:00 -0400 Subject: [PATCH 09/14] Add discrete variable support (#58) * feat: add discrete variable support Implement discrete variable handling across the full stack: - Discipline base class: add_discrete_input/add_discrete_output methods with VariableMetaData using kDiscreteInput/kDiscreteOutput types - Server: process_inputs demuxes VariableMessage oneof into continuous and discrete dicts; yields VariableMessage wrappers on output - Client: _assemble_input_messages wraps both Array and DiscreteVariable in VariableMessage; _recover_outputs handles discrete responses - OpenMDAO bindings: auto-discover discrete vars from server metadata, declare them via add_discrete_input/output, and forward through compute/apply_nonlinear/linearize calls Discrete values use google.protobuf.Value for language-interoperable serialization of scalars, lists, and nested structures. Updates proto submodule to feature/discrete_vars (DiscreteVariable message, VariableMessage wrapper, stream VariableMessage RPCs). Refs: MDO-Standards/Philote-Python#54 * fix: update tests for VariableMessage wrapper Wrap all raw data.Array messages in data.VariableMessage in test fixtures to match the new streaming protocol. Update OpenMDAO test assertions to include discrete_inputs parameters in expected calls. * test: add unit tests for discrete variable support Add comprehensive test suite covering all new discrete variable code paths: value conversion helpers, discipline base class, server/client discrete message handling, explicit/implicit server discrete dispatch, client discrete assembly/recovery, and OpenMDAO bindings. Coverage restored to 99% (remaining misses are import guards and an unreachable defensive branch). * ci: enforce 95% code coverage threshold Add codecov.yml requiring 95% coverage on both project total and patch (new/changed lines). Add fail_under = 95 to .coveragerc for local enforcement via coverage report. * chore: mark unreachable branches with pragma no cover Add coverage exclusion pragmas to three unreachable code paths: - _value_to_python else fallback (all protobuf Value kinds handled) - openmdao/__init__.py ImportError guard (requires uninstalling OpenMDAO) - examples/__init__.py ImportError guard (same) Also fixes bare except to except ImportError in examples/__init__.py. * test: add integration tests for discrete variable round-trip Add end-to-end integration tests using a ScaledParaboloid discipline that uses a discrete input (mode) to switch scaling behavior and a discrete output (label) to report the mode used. Tests cover: - Raw ExplicitClient compute with discrete inputs/outputs - Raw ExplicitClient compute_partials with discrete inputs - OpenMDAO RemoteExplicitComponent auto-discovery and forwarding of discrete variables through a real gRPC server * docs: update changelog with all discrete variable changes Add entries for coverage enforcement, pragma no cover annotations, and the bare except fix under the appropriate sections. --- .coveragerc | 3 + CHANGELOG.md | 15 + codecov.yml | 10 + philote_mdo/examples/__init__.py | 2 +- philote_mdo/general/discipline.py | 42 ++ philote_mdo/general/discipline_client.py | 158 ++-- philote_mdo/general/discipline_server.py | 140 +++- philote_mdo/general/explicit_client.py | 36 +- philote_mdo/general/explicit_server.py | 75 +- philote_mdo/general/implicit_client.py | 28 +- philote_mdo/general/implicit_server.py | 116 ++- philote_mdo/generated/data_pb2.py | 16 +- philote_mdo/generated/data_pb2.pyi | 22 + philote_mdo/generated/disciplines_pb2.py | 8 +- philote_mdo/generated/disciplines_pb2_grpc.py | 30 +- philote_mdo/openmdao/__init__.py | 2 +- philote_mdo/openmdao/explicit.py | 34 +- philote_mdo/openmdao/implicit.py | 81 +-- philote_mdo/openmdao/utils.py | 27 +- proto | 2 +- tests/test_discipline_client.py | 122 ++-- tests/test_discipline_server.py | 56 +- tests/test_discrete_integration.py | 254 +++++++ tests/test_discrete_variables.py | 687 ++++++++++++++++++ tests/test_edge_cases.py | 58 +- tests/test_explicit_client.py | 34 +- tests/test_explicit_server.py | 48 +- tests/test_implicit_client.py | 46 +- tests/test_implicit_server.py | 90 ++- tests/test_openmdao_explicit_client.py | 6 +- tests/test_openmdao_implicit_client.py | 10 +- tests/test_openmdao_utils.py | 1 + 32 files changed, 1841 insertions(+), 418 deletions(-) create mode 100644 codecov.yml create mode 100644 tests/test_discrete_integration.py create mode 100644 tests/test_discrete_variables.py diff --git a/.coveragerc b/.coveragerc index 49c8e0b..d5bf0ce 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,9 @@ source = philote_mdo [report] +# Fail if total coverage drops below 95% +fail_under = 95 + # Exclude generated protobuf files from coverage reports exclude_lines = pragma: no cover diff --git a/CHANGELOG.md b/CHANGELOG.md index 763a855..1885188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- Added discrete variable support throughout the stack. Disciplines can + now declare discrete inputs/outputs via `add_discrete_input` / + `add_discrete_output`. Discrete data is serialized as + `google.protobuf.Value` (supporting scalars, lists, and nested + structures) and multiplexed alongside continuous `Array` chunks in + the new `VariableMessage` wrapper. The OpenMDAO bindings + (`RemoteExplicitComponent`, `RemoteImplicitComponent`) automatically + discover and forward discrete variables. + ### Bug Fixes +- Fixed bare `except` to `except ImportError` in `examples/__init__.py`. - Fixed `SellarMDA` promoted-input ambiguity that newer OpenMDAO releases reject during `final_setup`. The `x` and `z` defaults were being set on the inner `cycle` subgroup, but `obj_cmp` promoted the same variables @@ -24,6 +34,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Documentation & Infrastructure +- Added Codecov configuration (`codecov.yml`) requiring 95% coverage on + both project total and patch (new/changed lines). +- Added `fail_under = 95` to `.coveragerc` for local coverage enforcement. +- Marked unreachable import guards and defensive branches with + `pragma: no cover`. - Updated installation instructions to reflect PyPI install option. - Added documentation for implicit disciplines. - Added documentation for OpenMDAO clients diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..e70d445 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,10 @@ +coverage: + status: + project: + default: + target: 95% + threshold: 0% + patch: + default: + target: 95% + threshold: 0% diff --git a/philote_mdo/examples/__init__.py b/philote_mdo/examples/__init__.py index 075fd5a..414b3e5 100644 --- a/philote_mdo/examples/__init__.py +++ b/philote_mdo/examples/__init__.py @@ -34,5 +34,5 @@ try: import openmdao.api from .sellar import SellarGroup -except: +except ImportError: # pragma: no cover pass diff --git a/philote_mdo/general/discipline.py b/philote_mdo/general/discipline.py index d00ef8b..f5930cf 100644 --- a/philote_mdo/general/discipline.py +++ b/philote_mdo/general/discipline.py @@ -44,6 +44,9 @@ def __init__(self): # variable metadata self._var_meta = [] + # discrete variable metadata (name → default value) + self._discrete_var_meta = [] + # partials metadata self._partials_meta = [] @@ -90,6 +93,44 @@ def add_input(self, name, shape=(1,), units=""): meta.units = units self._var_meta += [meta] + def add_discrete_input(self, name, default=None): + """ + Define a discrete input. + + Discrete inputs can hold any value that is representable as a + ``google.protobuf.Value`` (scalars, lists, or nested dicts). + + Parameters + ---------- + name : string + the name of the discrete input variable + default : object, optional + the default value for the discrete input + """ + meta = data.VariableMetaData() + meta.type = data.VariableType.kDiscreteInput + meta.name = name + self._discrete_var_meta += [meta] + + def add_discrete_output(self, name, default=None): + """ + Define a discrete output. + + Discrete outputs can hold any value that is representable as a + ``google.protobuf.Value`` (scalars, lists, or nested dicts). + + Parameters + ---------- + name : string + the name of the discrete output variable + default : object, optional + the default value for the discrete output + """ + meta = data.VariableMetaData() + meta.type = data.VariableType.kDiscreteOutput + meta.name = name + self._discrete_var_meta += [meta] + def add_output(self, name, shape=(1,), units=""): """ Defines a continuous output. @@ -179,4 +220,5 @@ def _clear_data(self): This function is invoked from the Setup function of the server. """ self._var_meta = [] + self._discrete_var_meta = [] self._partials_meta = [] diff --git a/philote_mdo/general/discipline_client.py b/philote_mdo/general/discipline_client.py index 4630cca..385d077 100644 --- a/philote_mdo/general/discipline_client.py +++ b/philote_mdo/general/discipline_client.py @@ -32,6 +32,7 @@ import philote_mdo.generated.data_pb2 as data import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.utils as utils +from philote_mdo.general.discipline_server import _python_to_value, _value_to_python class DisciplineClient: @@ -59,6 +60,7 @@ def __init__(self, channel): # variable and partials metadata self._var_meta = [] + self._discrete_var_meta = [] self._partials_meta = [] # list of available options @@ -123,9 +125,18 @@ def run_setup(self): def get_variable_definitions(self): """ Requests the input and output metadata from the server. + + Both continuous and discrete variable metadata are stored in their + respective lists. """ for message in self._disc_stub.GetVariableDefinitions(empty.Empty()): - self._var_meta += [message] + if message.type in ( + data.VariableType.kDiscreteInput, + data.VariableType.kDiscreteOutput, + ): + self._discrete_var_meta += [message] + else: + self._var_meta += [message] def get_partials_definitions(self): """ @@ -135,52 +146,92 @@ def get_partials_definitions(self): if message.name not in self._partials_meta: self._partials_meta += [message] - def _assemble_input_messages(self, inputs, outputs=None): + def _assemble_input_messages( + self, inputs, outputs=None, discrete_inputs=None, discrete_outputs=None + ): """ Assembles the messages for transmitting the input variables to the server. + + Both continuous and discrete inputs are wrapped in ``VariableMessage`` + envelopes. """ messages = [] + # Continuous inputs for input_name, value in inputs.items(): for b, e in utils.get_chunk_indices( value.size, self._stream_options.num_double ): messages += [ - data.Array( - name=input_name, - start=b, - end=e - 1, - type=data.VariableType.kInput, - data=value.ravel()[b:e], + data.VariableMessage( + continuous=data.Array( + name=input_name, + start=b, + end=e - 1, + type=data.VariableType.kInput, + data=value.ravel()[b:e], + ) ) ] + # Continuous outputs (for implicit disciplines) if outputs: for output_name, value in outputs.items(): for b, e in utils.get_chunk_indices( value.size, self._stream_options.num_double ): messages += [ - data.Array( - name=output_name, - start=b, - end=e - 1, - type=data.VariableType.kOutput, - data=value.ravel()[b:e], + data.VariableMessage( + continuous=data.Array( + name=output_name, + start=b, + end=e - 1, + type=data.VariableType.kOutput, + data=value.ravel()[b:e], + ) ) ] + # Discrete inputs + if discrete_inputs: + for name, value in discrete_inputs.items(): + messages += [ + data.VariableMessage( + discrete=data.DiscreteVariable( + name=name, + type=data.VariableType.kDiscreteInput, + value=_python_to_value(value), + ) + ) + ] + + # Discrete outputs (for implicit disciplines) + if discrete_outputs: + for name, value in discrete_outputs.items(): + messages += [ + data.VariableMessage( + discrete=data.DiscreteVariable( + name=name, + type=data.VariableType.kDiscreteOutput, + value=_python_to_value(value), + ) + ) + ] + return messages def _recover_outputs(self, responses): """ Recovers the outputs from the stream of responses. + + Returns both continuous outputs and discrete outputs. """ outputs = {} flat_outputs = {} + discrete_outputs = {} - # preallocate + # preallocate continuous outputs for out in self._var_meta: if out.type == data.kOutput: name = out.name @@ -188,16 +239,27 @@ def _recover_outputs(self, responses): flat_outputs[name] = utils.get_flattened_view(outputs[name]) for message in responses: - if message.type == data.kOutput: - b = message.start - e = message.end + 1 - if len(message.data) > 0: - flat_outputs[message.name][b:e] = message.data - else: - raise ValueError( - "Expected continuous variables, but array is empty." - ) + variant = message.WhichOneof("payload") + + if variant == "continuous": + arr = message.continuous + if arr.type == data.kOutput: + b = arr.start + e = arr.end + 1 + if len(arr.data) > 0: + flat_outputs[arr.name][b:e] = arr.data + else: + raise ValueError( + "Expected continuous variables, but array is empty." + ) + elif variant == "discrete": + dv = message.discrete + if dv.type == data.VariableType.kDiscreteOutput: + discrete_outputs[dv.name] = _value_to_python(dv.value) + + if discrete_outputs: + return outputs, discrete_outputs return outputs def _recover_residuals(self, responses): @@ -215,15 +277,19 @@ def _recover_residuals(self, responses): flat_residuals[name] = utils.get_flattened_view(residuals[name]) for message in responses: - if message.type == data.kResidual: - b = message.start - e = message.end + 1 - if len(message.data) > 0: - flat_residuals[message.name][b:e] = message.data - else: - raise ValueError( - "Expected continuous variables, but array is empty." - ) + variant = message.WhichOneof("payload") + + if variant == "continuous": + arr = message.continuous + if arr.type == data.kResidual: + b = arr.start + e = arr.end + 1 + if len(arr.data) > 0: + flat_residuals[arr.name][b:e] = arr.data + else: + raise ValueError( + "Expected continuous variables, but array is empty." + ) return residuals @@ -257,16 +323,20 @@ def _recover_partials(self, responses): ) for message in responses: - b = message.start - e = message.end + 1 - - if message.type == data.kPartial: - if len(message.data) > 0: - flat_p[(message.name, message.subname)][b:e] = message.data - else: - raise ValueError( - "Expected continuous outputs for the " - "partials, but array was empty." - ) + variant = message.WhichOneof("payload") + + if variant == "continuous": + arr = message.continuous + b = arr.start + e = arr.end + 1 + + if arr.type == data.kPartial: + if len(arr.data) > 0: + flat_p[(arr.name, arr.subname)][b:e] = arr.data + else: + raise ValueError( + "Expected continuous outputs for the " + "partials, but array was empty." + ) return partials diff --git a/philote_mdo/general/discipline_server.py b/philote_mdo/general/discipline_server.py index 5af98df..bde20a4 100644 --- a/philote_mdo/general/discipline_server.py +++ b/philote_mdo/general/discipline_server.py @@ -32,6 +32,7 @@ import philote_mdo.generated.data_pb2 as data import philote_mdo.generated.disciplines_pb2_grpc as disc from google.protobuf.empty_pb2 import Empty +from google.protobuf import struct_pb2 from philote_mdo.utils import PairDict, get_flattened_view @@ -128,10 +129,15 @@ def Setup(self, request, context): def GetVariableDefinitions(self, request, context): """ Transmits variable metadata about the analysis discipline to the client. + + Both continuous and discrete variable metadata are streamed. """ for var in self._discipline._var_meta: yield var + for var in self._discipline._discrete_var_meta: + yield var + def GetPartialDefinitions(self, request, context): """ Transmits partials metadata about the analysis discipline to the client. @@ -193,27 +199,127 @@ def preallocate_partials(self): return jac - def process_inputs(self, request_iterator, flat_inputs, flat_outputs=None): + def process_inputs( + self, + request_iterator, + flat_inputs, + flat_outputs=None, + discrete_inputs=None, + discrete_outputs=None, + ): """ Processes the message inputs from a gRPC stream. + The stream consists of ``VariableMessage`` wrappers, each of which + contains either a continuous ``Array`` or a ``DiscreteVariable``. + Note, for implicit disciplines, the function values are considered inputs to evaluate the residuals and the partials of the residuals. """ - # process inputs + if discrete_inputs is None: + discrete_inputs = {} + if discrete_outputs is None: + discrete_outputs = {} + for message in request_iterator: - # start and end indices for the array chunk - b = message.start - e = message.end - - # assign either continuous or discrete data - if len(message.data) > 0: - if message.type == data.VariableType.kInput: - flat_inputs[message.name][b : e + 1] = message.data - elif message.type == data.VariableType.kOutput: - flat_outputs[message.name][b : e + 1] = message.data - else: - raise ValueError( - "Expected continuous variables but arrays were" - " empty for variable %s." % (message.name) - ) + variant = message.WhichOneof("payload") + + if variant == "continuous": + arr = message.continuous + b = arr.start + e = arr.end + + if len(arr.data) > 0: + if arr.type == data.VariableType.kInput: + flat_inputs[arr.name][b : e + 1] = arr.data + elif arr.type == data.VariableType.kOutput: + flat_outputs[arr.name][b : e + 1] = arr.data + else: + raise ValueError( + "Expected continuous variables but arrays were" + " empty for variable %s." % (arr.name) + ) + + elif variant == "discrete": + dv = message.discrete + # Convert protobuf Value to native Python type + native_value = _value_to_python(dv.value) + + if dv.type == data.VariableType.kDiscreteInput: + discrete_inputs[dv.name] = native_value + elif dv.type == data.VariableType.kDiscreteOutput: + discrete_outputs[dv.name] = native_value + + return discrete_inputs, discrete_outputs + + +def _value_to_python(value): + """ + Converts a ``google.protobuf.Value`` to a native Python object. + + Parameters + ---------- + value : google.protobuf.Value + protobuf Value message + + Returns + ------- + object + Native Python equivalent (None, bool, int/float, str, list, or dict) + """ + kind = value.WhichOneof("kind") + + if kind == "null_value": + return None + elif kind == "bool_value": + return value.bool_value + elif kind == "number_value": + # protobuf stores all numbers as doubles; return int if lossless + num = value.number_value + if num == int(num): + return int(num) + return num + elif kind == "string_value": + return value.string_value + elif kind == "list_value": + return [_value_to_python(v) for v in value.list_value.values] + elif kind == "struct_value": + return {k: _value_to_python(v) for k, v in value.struct_value.fields.items()} + else: # pragma: no cover – all protobuf Value kinds are handled above + return None + + +def _python_to_value(obj): + """ + Converts a native Python object to a ``google.protobuf.Value``. + + Parameters + ---------- + obj : object + A Python scalar, list, or dict + + Returns + ------- + google.protobuf.Value + protobuf Value message + """ + val = struct_pb2.Value() + + if obj is None: + val.null_value = 0 + elif isinstance(obj, bool): + val.bool_value = obj + elif isinstance(obj, (int, float)): + val.number_value = float(obj) + elif isinstance(obj, str): + val.string_value = obj + elif isinstance(obj, (list, tuple)): + for item in obj: + val.list_value.values.append(_python_to_value(item)) + elif isinstance(obj, dict): + for k, v in obj.items(): + val.struct_value.fields[str(k)].CopyFrom(_python_to_value(v)) + else: + val.string_value = str(obj) + + return val diff --git a/philote_mdo/general/explicit_client.py b/philote_mdo/general/explicit_client.py index d0f9ef9..5b278cb 100644 --- a/philote_mdo/general/explicit_client.py +++ b/philote_mdo/general/explicit_client.py @@ -41,23 +41,45 @@ def __init__(self, channel): super().__init__(channel) self._expl_stub = disc.ExplicitServiceStub(channel) - def run_compute(self, inputs): + def run_compute(self, inputs, discrete_inputs=None): """ Requests and receives the function evaluation from the analysis server for a set of inputs (sent to the server). + + Parameters + ---------- + inputs : dict + Continuous input values. + discrete_inputs : dict, optional + Discrete input values. + + Returns + ------- + dict or tuple(dict, dict) + Continuous outputs, or (continuous outputs, discrete outputs) when + the server returns discrete output data. """ - messages = self._assemble_input_messages(inputs) + messages = self._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) responses = self._expl_stub.ComputeFunction(iter(messages)) - outputs = self._recover_outputs(responses) + return self._recover_outputs(responses) - return outputs - - def run_compute_partials(self, inputs): + def run_compute_partials(self, inputs, discrete_inputs=None): """ Requests and receives the gradient evaluation from the analysis server for a set of inputs (sent to the server). + + Parameters + ---------- + inputs : dict + Continuous input values. + discrete_inputs : dict, optional + Discrete input values. """ - messages = self._assemble_input_messages(inputs) + messages = self._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) responses = self._expl_stub.ComputeGradient(iter(messages)) partials = self._recover_partials(responses) diff --git a/philote_mdo/general/explicit_server.py b/philote_mdo/general/explicit_server.py index 6f6f329..049e0a2 100644 --- a/philote_mdo/general/explicit_server.py +++ b/philote_mdo/general/explicit_server.py @@ -29,7 +29,10 @@ # control over the information you may find at these locations. import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.generated.data_pb2 as data -from philote_mdo.general.discipline_server import DisciplineServer +from philote_mdo.general.discipline_server import ( + DisciplineServer, + _python_to_value, +) from philote_mdo.utils import get_chunk_indices @@ -55,21 +58,44 @@ def ComputeFunction(self, request_iterator, context): inputs = {} flat_inputs = {} outputs = {} + discrete_inputs = {} + discrete_outputs = {} self.preallocate_inputs(inputs, flat_inputs) - self.process_inputs(request_iterator, flat_inputs) - self._discipline.compute(inputs, outputs) + discrete_inputs, _ = self.process_inputs( + request_iterator, flat_inputs, discrete_inputs=discrete_inputs + ) + # Call compute with discrete data when discrete variables are present + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.compute( + inputs, outputs, discrete_inputs, discrete_outputs + ) + else: + self._discipline.compute(inputs, outputs) + + # Stream continuous outputs for output_name, value in outputs.items(): - # iterate through all chunks needed for the current output for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.Array( - name=output_name, - type=data.kOutput, - start=b, - end=e - 1, - data=value.ravel()[b:e], + yield data.VariableMessage( + continuous=data.Array( + name=output_name, + type=data.kOutput, + start=b, + end=e - 1, + data=value.ravel()[b:e], + ) + ) + + # Stream discrete outputs + for name, value in discrete_outputs.items(): + yield data.VariableMessage( + discrete=data.DiscreteVariable( + name=name, + type=data.VariableType.kDiscreteOutput, + value=_python_to_value(value), ) + ) def ComputeGradient(self, request_iterator, context): """ @@ -77,19 +103,28 @@ def ComputeGradient(self, request_iterator, context): """ inputs = {} flat_inputs = {} + discrete_inputs = {} + self.preallocate_inputs(inputs, flat_inputs) jac = self.preallocate_partials() - self.process_inputs(request_iterator, flat_inputs) - self._discipline.compute_partials(inputs, jac) + discrete_inputs, _ = self.process_inputs( + request_iterator, flat_inputs, discrete_inputs=discrete_inputs + ) + + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.compute_partials(inputs, jac, discrete_inputs) + else: + self._discipline.compute_partials(inputs, jac) for jac, value in jac.items(): - # iterate through all chunks needed for the current partials for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.Array( - name=jac[0], - subname=jac[1], - type=data.kPartial, - start=b, - end=e - 1, - data=value.ravel()[b:e], + yield data.VariableMessage( + continuous=data.Array( + name=jac[0], + subname=jac[1], + type=data.kPartial, + start=b, + end=e - 1, + data=value.ravel()[b:e], + ) ) diff --git a/philote_mdo/general/implicit_client.py b/philote_mdo/general/implicit_client.py index d62c2b1..81fb6f3 100644 --- a/philote_mdo/general/implicit_client.py +++ b/philote_mdo/general/implicit_client.py @@ -119,7 +119,9 @@ def __init__(self, channel): super().__init__(channel=channel) self._impl_stub = disc.ImplicitServiceStub(channel) - def run_compute_residuals(self, inputs, outputs): + def run_compute_residuals( + self, inputs, outputs, discrete_inputs=None, discrete_outputs=None + ): """ Compute residuals R(inputs, outputs) by calling the remote server. @@ -167,13 +169,18 @@ def run_compute_residuals(self, inputs, outputs): - This is typically used for residual evaluation during Newton iterations """ # Assemble input messages and call server - messages = self._assemble_input_messages(inputs, outputs) + messages = self._assemble_input_messages( + inputs, + outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) responses = self._impl_stub.ComputeResiduals(iter(messages)) residuals = self._recover_residuals(responses) return residuals - def run_solve_residuals(self, inputs): + def run_solve_residuals(self, inputs, discrete_inputs=None): """ Solve implicit equations R(inputs, outputs) = 0 by calling the remote server. @@ -225,12 +232,16 @@ def run_solve_residuals(self, inputs): - Solution quality depends on the server's implementation and input conditioning """ # Assemble input messages and call server - messages = self._assemble_input_messages(inputs) + messages = self._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) responses = self._impl_stub.SolveResiduals(iter(messages)) outputs = self._recover_outputs(responses) return outputs - def run_residual_gradients(self, inputs, outputs): + def run_residual_gradients( + self, inputs, outputs, discrete_inputs=None, discrete_outputs=None + ): """ Compute Jacobian of residuals dR/d[inputs,outputs] by calling the remote server. @@ -289,7 +300,12 @@ def run_residual_gradients(self, inputs, outputs): - For large problems, consider matrix-free methods if available """ # Assemble input messages and call server - messages = self._assemble_input_messages(inputs, outputs) + messages = self._assemble_input_messages( + inputs, + outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) responses = self._impl_stub.ComputeResidualGradients(iter(messages)) partials = self._recover_partials(responses) return partials diff --git a/philote_mdo/general/implicit_server.py b/philote_mdo/general/implicit_server.py index 21d45e3..973cfc8 100644 --- a/philote_mdo/general/implicit_server.py +++ b/philote_mdo/general/implicit_server.py @@ -30,6 +30,7 @@ import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.generated.data_pb2 as data import philote_mdo.general as pmdo +from philote_mdo.general.discipline_server import _python_to_value from philote_mdo.utils import get_chunk_indices @@ -136,15 +137,15 @@ def ComputeResiduals(self, request_iterator, context): Parameters ---------- - request_iterator : Iterator[data.Array] - Stream of input and output arrays from the client + request_iterator : Iterator[data.VariableMessage] + Stream of input and output variable messages from the client context : grpc.ServicerContext gRPC context for the request Yields ------ - data.Array - Stream of residual arrays back to the client + data.VariableMessage + Stream of residual variable messages back to the client Notes ----- @@ -159,21 +160,36 @@ def ComputeResiduals(self, request_iterator, context): outputs = {} flat_outputs = {} residuals = {} + discrete_inputs = {} + discrete_outputs = {} self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) - self.process_inputs(request_iterator, flat_inputs, flat_outputs) + discrete_inputs, discrete_outputs = self.process_inputs( + request_iterator, + flat_inputs, + flat_outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) # Call the user-defined compute_residuals function - self._discipline.compute_residuals(inputs, outputs, residuals) + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.compute_residuals( + inputs, outputs, residuals, discrete_inputs, discrete_outputs + ) + else: + self._discipline.compute_residuals(inputs, outputs, residuals) for res_name, value in residuals.items(): for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.Array( - name=res_name, - start=b, - end=e, - type=data.kResidual, - data=value.ravel()[b:e], + yield data.VariableMessage( + continuous=data.Array( + name=res_name, + start=b, + end=e, + type=data.kResidual, + data=value.ravel()[b:e], + ) ) def SolveResiduals(self, request_iterator, context): @@ -186,15 +202,15 @@ def SolveResiduals(self, request_iterator, context): Parameters ---------- - request_iterator : Iterator[data.Array] - Stream of input arrays from the client + request_iterator : Iterator[data.VariableMessage] + Stream of input variable messages from the client context : grpc.ServicerContext gRPC context for the request Yields ------ - data.Array - Stream of solved output arrays back to the client + data.VariableMessage + Stream of solved output variable messages back to the client Notes ----- @@ -208,21 +224,32 @@ def SolveResiduals(self, request_iterator, context): flat_inputs = {} outputs = {} flat_outputs = {} + discrete_inputs = {} self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) - self.process_inputs(request_iterator, flat_inputs, flat_outputs) + discrete_inputs, _ = self.process_inputs( + request_iterator, + flat_inputs, + flat_outputs, + discrete_inputs=discrete_inputs, + ) # Call the user-defined solve function - self._discipline.solve_residuals(inputs, outputs) + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.solve_residuals(inputs, outputs, discrete_inputs) + else: + self._discipline.solve_residuals(inputs, outputs) for output_name, value in outputs.items(): for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.Array( - name=output_name, - start=b, - end=e, - type=data.kOutput, - data=value.ravel()[b:e], + yield data.VariableMessage( + continuous=data.Array( + name=output_name, + start=b, + end=e, + type=data.kOutput, + data=value.ravel()[b:e], + ) ) def ComputeResidualGradients(self, request_iterator, context): @@ -235,15 +262,15 @@ def ComputeResidualGradients(self, request_iterator, context): Parameters ---------- - request_iterator : Iterator[data.Array] - Stream of input and output arrays from the client + request_iterator : Iterator[data.VariableMessage] + Stream of input and output variable messages from the client context : grpc.ServicerContext gRPC context for the request Yields ------ - data.Array - Stream of partial derivative arrays back to the client + data.VariableMessage + Stream of partial derivative variable messages back to the client Notes ----- @@ -257,23 +284,38 @@ def ComputeResidualGradients(self, request_iterator, context): flat_inputs = {} outputs = {} flat_outputs = {} + discrete_inputs = {} + discrete_outputs = {} self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) jac = self.preallocate_partials() - self.process_inputs(request_iterator, flat_inputs, flat_outputs) + discrete_inputs, discrete_outputs = self.process_inputs( + request_iterator, + flat_inputs, + flat_outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) # Call the user-defined residual partials function - self._discipline.residual_partials(inputs, outputs, jac) + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.residual_partials( + inputs, outputs, jac, discrete_inputs, discrete_outputs + ) + else: + self._discipline.residual_partials(inputs, outputs, jac) for jac, value in jac.items(): for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.Array( - name=jac[0], - subname=jac[1], - type=data.kPartial, - start=b, - end=e, - data=value.ravel()[b:e], + yield data.VariableMessage( + continuous=data.Array( + name=jac[0], + subname=jac[1], + type=data.kPartial, + start=b, + end=e, + data=value.ravel()[b:e], + ) ) # def MatrixFreeGradients(self, request_iterator, context): diff --git a/philote_mdo/generated/data_pb2.py b/philote_mdo/generated/data_pb2.py index 64720fe..8334dc1 100644 --- a/philote_mdo/generated/data_pb2.py +++ b/philote_mdo/generated/data_pb2.py @@ -7,17 +7,17 @@ _runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 27, 2, '', 'data.proto') _sym_db = _symbol_database.Default() from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x07philote\x1a\x1cgoogle/protobuf/struct.proto"}\n\x14DisciplineProperties\x12\x12\n\ncontinuous\x18\x01 \x01(\x08\x12\x16\n\x0edifferentiable\x18\x02 \x01(\x08\x12\x1a\n\x12provides_gradients\x18\x03 \x01(\x08\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"#\n\rStreamOptions\x12\x12\n\nnum_double\x18\x01 \x01(\x03"?\n\x0bOptionsList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x1f\n\x04type\x18\x02 \x03(\x0e2\x11.philote.DataType"=\n\x11DisciplineOptions\x12(\n\x07options\x18\x01 \x01(\x0b2\x17.google.protobuf.Struct"c\n\x10VariableMetaData\x12#\n\x04type\x18\x01 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05shape\x18\x04 \x03(\x03\x12\r\n\x05units\x18\x05 \x01(\t"@\n\x10PartialsMetaData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x03"u\n\x05Array\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05start\x18\x03 \x01(\x03\x12\x0b\n\x03end\x18\x04 \x01(\x03\x12#\n\x04type\x18\x05 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04data\x18\x06 \x03(\x01*9\n\x08DataType\x12\t\n\x05kBool\x10\x00\x12\x08\n\x04kInt\x10\x01\x12\x0b\n\x07kDouble\x10\x02\x12\x0b\n\x07kString\x10\x03*m\n\x0cVariableType\x12\n\n\x06kInput\x10\x00\x12\x12\n\x0ekDiscreteInput\x10\x01\x12\r\n\tkResidual\x10\x02\x12\x0b\n\x07kOutput\x10\x03\x12\x13\n\x0fkDiscreteOutput\x10\x04\x12\x0c\n\x08kPartial\x10\x05B\x11\n\x0forg.philote.mdob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x07philote\x1a\x1cgoogle/protobuf/struct.proto"}\n\x14DisciplineProperties\x12\x12\n\ncontinuous\x18\x01 \x01(\x08\x12\x16\n\x0edifferentiable\x18\x02 \x01(\x08\x12\x1a\n\x12provides_gradients\x18\x03 \x01(\x08\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"#\n\rStreamOptions\x12\x12\n\nnum_double\x18\x01 \x01(\x03"?\n\x0bOptionsList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x1f\n\x04type\x18\x02 \x03(\x0e2\x11.philote.DataType"=\n\x11DisciplineOptions\x12(\n\x07options\x18\x01 \x01(\x0b2\x17.google.protobuf.Struct"c\n\x10VariableMetaData\x12#\n\x04type\x18\x01 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05shape\x18\x04 \x03(\x03\x12\r\n\x05units\x18\x05 \x01(\t"@\n\x10PartialsMetaData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x03"u\n\x05Array\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05start\x18\x03 \x01(\x03\x12\x0b\n\x03end\x18\x04 \x01(\x03\x12#\n\x04type\x18\x05 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04data\x18\x06 \x03(\x01"l\n\x10DiscreteVariable\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x04type\x18\x02 \x01(\x0e2\x15.philote.VariableType\x12%\n\x05value\x18\x03 \x01(\x0b2\x16.google.protobuf.Value"q\n\x0fVariableMessage\x12$\n\ncontinuous\x18\x01 \x01(\x0b2\x0e.philote.ArrayH\x00\x12-\n\x08discrete\x18\x02 \x01(\x0b2\x19.philote.DiscreteVariableH\x00B\t\n\x07payload*9\n\x08DataType\x12\t\n\x05kBool\x10\x00\x12\x08\n\x04kInt\x10\x01\x12\x0b\n\x07kDouble\x10\x02\x12\x0b\n\x07kString\x10\x03*m\n\x0cVariableType\x12\n\n\x06kInput\x10\x00\x12\x12\n\x0ekDiscreteInput\x10\x01\x12\r\n\tkResidual\x10\x02\x12\x0b\n\x07kOutput\x10\x03\x12\x13\n\x0fkDiscreteOutput\x10\x04\x12\x0c\n\x08kPartial\x10\x05B\x11\n\x0forg.philote.mdob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'data_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\x0forg.philote.mdo' - _globals['_DATATYPE']._serialized_start = 631 - _globals['_DATATYPE']._serialized_end = 688 - _globals['_VARIABLETYPE']._serialized_start = 690 - _globals['_VARIABLETYPE']._serialized_end = 799 + _globals['_DATATYPE']._serialized_start = 856 + _globals['_DATATYPE']._serialized_end = 913 + _globals['_VARIABLETYPE']._serialized_start = 915 + _globals['_VARIABLETYPE']._serialized_end = 1024 _globals['_DISCIPLINEPROPERTIES']._serialized_start = 53 _globals['_DISCIPLINEPROPERTIES']._serialized_end = 178 _globals['_STREAMOPTIONS']._serialized_start = 180 @@ -31,4 +31,8 @@ _globals['_PARTIALSMETADATA']._serialized_start = 446 _globals['_PARTIALSMETADATA']._serialized_end = 510 _globals['_ARRAY']._serialized_start = 512 - _globals['_ARRAY']._serialized_end = 629 \ No newline at end of file + _globals['_ARRAY']._serialized_end = 629 + _globals['_DISCRETEVARIABLE']._serialized_start = 631 + _globals['_DISCRETEVARIABLE']._serialized_end = 739 + _globals['_VARIABLEMESSAGE']._serialized_start = 741 + _globals['_VARIABLEMESSAGE']._serialized_end = 854 \ No newline at end of file diff --git a/philote_mdo/generated/data_pb2.pyi b/philote_mdo/generated/data_pb2.pyi index 6d9cdd1..a699396 100644 --- a/philote_mdo/generated/data_pb2.pyi +++ b/philote_mdo/generated/data_pb2.pyi @@ -116,4 +116,26 @@ class Array(_message.Message): data: _containers.RepeatedScalarFieldContainer[float] def __init__(self, name: _Optional[str]=..., subname: _Optional[str]=..., start: _Optional[int]=..., end: _Optional[int]=..., type: _Optional[_Union[VariableType, str]]=..., data: _Optional[_Iterable[float]]=...) -> None: + ... + +class DiscreteVariable(_message.Message): + __slots__ = ('name', 'type', 'value') + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + name: str + type: VariableType + value: _struct_pb2.Value + + def __init__(self, name: _Optional[str]=..., type: _Optional[_Union[VariableType, str]]=..., value: _Optional[_Union[_struct_pb2.Value, _Mapping]]=...) -> None: + ... + +class VariableMessage(_message.Message): + __slots__ = ('continuous', 'discrete') + CONTINUOUS_FIELD_NUMBER: _ClassVar[int] + DISCRETE_FIELD_NUMBER: _ClassVar[int] + continuous: Array + discrete: DiscreteVariable + + def __init__(self, continuous: _Optional[_Union[Array, _Mapping]]=..., discrete: _Optional[_Union[DiscreteVariable, _Mapping]]=...) -> None: ... \ No newline at end of file diff --git a/philote_mdo/generated/disciplines_pb2.py b/philote_mdo/generated/disciplines_pb2.py index aa06069..a78cc88 100644 --- a/philote_mdo/generated/disciplines_pb2.py +++ b/philote_mdo/generated/disciplines_pb2.py @@ -8,7 +8,7 @@ _sym_db = _symbol_database.Default() from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 from . import data_pb2 as data__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11disciplines.proto\x12\x07philote\x1a\x1bgoogle/protobuf/empty.proto\x1a\ndata.proto2\x84\x04\n\x11DisciplineService\x12B\n\x07GetInfo\x12\x16.google.protobuf.Empty\x1a\x1d.philote.DisciplineProperties"\x00\x12D\n\x10SetStreamOptions\x12\x16.philote.StreamOptions\x1a\x16.google.protobuf.Empty"\x00\x12E\n\x13GetAvailableOptions\x12\x16.google.protobuf.Empty\x1a\x14.philote.OptionsList"\x00\x12B\n\nSetOptions\x12\x1a.philote.DisciplineOptions\x1a\x16.google.protobuf.Empty"\x00\x129\n\x05Setup\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty"\x00\x12O\n\x16GetVariableDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.VariableMetaData"\x000\x01\x12N\n\x15GetPartialDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.PartialsMetaData"\x000\x012\x83\x01\n\x0fExplicitService\x127\n\x0fComputeFunction\x12\x0e.philote.Array\x1a\x0e.philote.Array"\x00(\x010\x01\x127\n\x0fComputeGradient\x12\x0e.philote.Array\x1a\x0e.philote.Array"\x00(\x010\x012\xc5\x01\n\x0fImplicitService\x128\n\x10ComputeResiduals\x12\x0e.philote.Array\x1a\x0e.philote.Array"\x00(\x010\x01\x126\n\x0eSolveResiduals\x12\x0e.philote.Array\x1a\x0e.philote.Array"\x00(\x010\x01\x12@\n\x18ComputeResidualGradients\x12\x0e.philote.Array\x1a\x0e.philote.Array"\x00(\x010\x01B\x11\n\x0forg.philote.mdob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11disciplines.proto\x12\x07philote\x1a\x1bgoogle/protobuf/empty.proto\x1a\ndata.proto2\x84\x04\n\x11DisciplineService\x12B\n\x07GetInfo\x12\x16.google.protobuf.Empty\x1a\x1d.philote.DisciplineProperties"\x00\x12D\n\x10SetStreamOptions\x12\x16.philote.StreamOptions\x1a\x16.google.protobuf.Empty"\x00\x12E\n\x13GetAvailableOptions\x12\x16.google.protobuf.Empty\x1a\x14.philote.OptionsList"\x00\x12B\n\nSetOptions\x12\x1a.philote.DisciplineOptions\x1a\x16.google.protobuf.Empty"\x00\x129\n\x05Setup\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty"\x00\x12O\n\x16GetVariableDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.VariableMetaData"\x000\x01\x12N\n\x15GetPartialDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.PartialsMetaData"\x000\x012\xab\x01\n\x0fExplicitService\x12K\n\x0fComputeFunction\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12K\n\x0fComputeGradient\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x012\x81\x02\n\x0fImplicitService\x12L\n\x10ComputeResiduals\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12J\n\x0eSolveResiduals\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12T\n\x18ComputeResidualGradients\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01B\x11\n\x0forg.philote.mdob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'disciplines_pb2', _globals) @@ -18,6 +18,6 @@ _globals['_DISCIPLINESERVICE']._serialized_start = 72 _globals['_DISCIPLINESERVICE']._serialized_end = 588 _globals['_EXPLICITSERVICE']._serialized_start = 591 - _globals['_EXPLICITSERVICE']._serialized_end = 722 - _globals['_IMPLICITSERVICE']._serialized_start = 725 - _globals['_IMPLICITSERVICE']._serialized_end = 922 \ No newline at end of file + _globals['_EXPLICITSERVICE']._serialized_end = 762 + _globals['_IMPLICITSERVICE']._serialized_start = 765 + _globals['_IMPLICITSERVICE']._serialized_end = 1022 \ No newline at end of file diff --git a/philote_mdo/generated/disciplines_pb2_grpc.py b/philote_mdo/generated/disciplines_pb2_grpc.py index 443d5da..1b23738 100644 --- a/philote_mdo/generated/disciplines_pb2_grpc.py +++ b/philote_mdo/generated/disciplines_pb2_grpc.py @@ -142,8 +142,8 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.ComputeFunction = channel.stream_stream('/philote.ExplicitService/ComputeFunction', request_serializer=data__pb2.Array.SerializeToString, response_deserializer=data__pb2.Array.FromString, _registered_method=True) - self.ComputeGradient = channel.stream_stream('/philote.ExplicitService/ComputeGradient', request_serializer=data__pb2.Array.SerializeToString, response_deserializer=data__pb2.Array.FromString, _registered_method=True) + self.ComputeFunction = channel.stream_stream('/philote.ExplicitService/ComputeFunction', request_serializer=data__pb2.VariableMessage.SerializeToString, response_deserializer=data__pb2.VariableMessage.FromString, _registered_method=True) + self.ComputeGradient = channel.stream_stream('/philote.ExplicitService/ComputeGradient', request_serializer=data__pb2.VariableMessage.SerializeToString, response_deserializer=data__pb2.VariableMessage.FromString, _registered_method=True) class ExplicitServiceServicer(object): """Definition of the generic Explicit Component RPC @@ -164,7 +164,7 @@ def ComputeGradient(self, request_iterator, context): raise NotImplementedError('Method not implemented!') def add_ExplicitServiceServicer_to_server(servicer, server): - rpc_method_handlers = {'ComputeFunction': grpc.stream_stream_rpc_method_handler(servicer.ComputeFunction, request_deserializer=data__pb2.Array.FromString, response_serializer=data__pb2.Array.SerializeToString), 'ComputeGradient': grpc.stream_stream_rpc_method_handler(servicer.ComputeGradient, request_deserializer=data__pb2.Array.FromString, response_serializer=data__pb2.Array.SerializeToString)} + rpc_method_handlers = {'ComputeFunction': grpc.stream_stream_rpc_method_handler(servicer.ComputeFunction, request_deserializer=data__pb2.VariableMessage.FromString, response_serializer=data__pb2.VariableMessage.SerializeToString), 'ComputeGradient': grpc.stream_stream_rpc_method_handler(servicer.ComputeGradient, request_deserializer=data__pb2.VariableMessage.FromString, response_serializer=data__pb2.VariableMessage.SerializeToString)} generic_handler = grpc.method_handlers_generic_handler('philote.ExplicitService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) server.add_registered_method_handlers('philote.ExplicitService', rpc_method_handlers) @@ -175,14 +175,14 @@ class ExplicitService(object): @staticmethod def ComputeFunction(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/philote.ExplicitService/ComputeFunction', data__pb2.Array.SerializeToString, data__pb2.Array.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) + return grpc.experimental.stream_stream(request_iterator, target, '/philote.ExplicitService/ComputeFunction', data__pb2.VariableMessage.SerializeToString, data__pb2.VariableMessage.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) @staticmethod def ComputeGradient(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/philote.ExplicitService/ComputeGradient', data__pb2.Array.SerializeToString, data__pb2.Array.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) + return grpc.experimental.stream_stream(request_iterator, target, '/philote.ExplicitService/ComputeGradient', data__pb2.VariableMessage.SerializeToString, data__pb2.VariableMessage.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) class ImplicitServiceStub(object): - """Definition of the generic Explicit Discipline RPC + """Definition of the generic Implicit Discipline RPC """ def __init__(self, channel): @@ -191,12 +191,12 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.ComputeResiduals = channel.stream_stream('/philote.ImplicitService/ComputeResiduals', request_serializer=data__pb2.Array.SerializeToString, response_deserializer=data__pb2.Array.FromString, _registered_method=True) - self.SolveResiduals = channel.stream_stream('/philote.ImplicitService/SolveResiduals', request_serializer=data__pb2.Array.SerializeToString, response_deserializer=data__pb2.Array.FromString, _registered_method=True) - self.ComputeResidualGradients = channel.stream_stream('/philote.ImplicitService/ComputeResidualGradients', request_serializer=data__pb2.Array.SerializeToString, response_deserializer=data__pb2.Array.FromString, _registered_method=True) + self.ComputeResiduals = channel.stream_stream('/philote.ImplicitService/ComputeResiduals', request_serializer=data__pb2.VariableMessage.SerializeToString, response_deserializer=data__pb2.VariableMessage.FromString, _registered_method=True) + self.SolveResiduals = channel.stream_stream('/philote.ImplicitService/SolveResiduals', request_serializer=data__pb2.VariableMessage.SerializeToString, response_deserializer=data__pb2.VariableMessage.FromString, _registered_method=True) + self.ComputeResidualGradients = channel.stream_stream('/philote.ImplicitService/ComputeResidualGradients', request_serializer=data__pb2.VariableMessage.SerializeToString, response_deserializer=data__pb2.VariableMessage.FromString, _registered_method=True) class ImplicitServiceServicer(object): - """Definition of the generic Explicit Discipline RPC + """Definition of the generic Implicit Discipline RPC """ def ComputeResiduals(self, request_iterator, context): @@ -221,23 +221,23 @@ def ComputeResidualGradients(self, request_iterator, context): raise NotImplementedError('Method not implemented!') def add_ImplicitServiceServicer_to_server(servicer, server): - rpc_method_handlers = {'ComputeResiduals': grpc.stream_stream_rpc_method_handler(servicer.ComputeResiduals, request_deserializer=data__pb2.Array.FromString, response_serializer=data__pb2.Array.SerializeToString), 'SolveResiduals': grpc.stream_stream_rpc_method_handler(servicer.SolveResiduals, request_deserializer=data__pb2.Array.FromString, response_serializer=data__pb2.Array.SerializeToString), 'ComputeResidualGradients': grpc.stream_stream_rpc_method_handler(servicer.ComputeResidualGradients, request_deserializer=data__pb2.Array.FromString, response_serializer=data__pb2.Array.SerializeToString)} + rpc_method_handlers = {'ComputeResiduals': grpc.stream_stream_rpc_method_handler(servicer.ComputeResiduals, request_deserializer=data__pb2.VariableMessage.FromString, response_serializer=data__pb2.VariableMessage.SerializeToString), 'SolveResiduals': grpc.stream_stream_rpc_method_handler(servicer.SolveResiduals, request_deserializer=data__pb2.VariableMessage.FromString, response_serializer=data__pb2.VariableMessage.SerializeToString), 'ComputeResidualGradients': grpc.stream_stream_rpc_method_handler(servicer.ComputeResidualGradients, request_deserializer=data__pb2.VariableMessage.FromString, response_serializer=data__pb2.VariableMessage.SerializeToString)} generic_handler = grpc.method_handlers_generic_handler('philote.ImplicitService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) server.add_registered_method_handlers('philote.ImplicitService', rpc_method_handlers) class ImplicitService(object): - """Definition of the generic Explicit Discipline RPC + """Definition of the generic Implicit Discipline RPC """ @staticmethod def ComputeResiduals(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/philote.ImplicitService/ComputeResiduals', data__pb2.Array.SerializeToString, data__pb2.Array.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) + return grpc.experimental.stream_stream(request_iterator, target, '/philote.ImplicitService/ComputeResiduals', data__pb2.VariableMessage.SerializeToString, data__pb2.VariableMessage.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) @staticmethod def SolveResiduals(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/philote.ImplicitService/SolveResiduals', data__pb2.Array.SerializeToString, data__pb2.Array.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) + return grpc.experimental.stream_stream(request_iterator, target, '/philote.ImplicitService/SolveResiduals', data__pb2.VariableMessage.SerializeToString, data__pb2.VariableMessage.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) @staticmethod def ComputeResidualGradients(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/philote.ImplicitService/ComputeResidualGradients', data__pb2.Array.SerializeToString, data__pb2.Array.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) \ No newline at end of file + return grpc.experimental.stream_stream(request_iterator, target, '/philote.ImplicitService/ComputeResidualGradients', data__pb2.VariableMessage.SerializeToString, data__pb2.VariableMessage.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) \ No newline at end of file diff --git a/philote_mdo/openmdao/__init__.py b/philote_mdo/openmdao/__init__.py index 13cbbb4..34919f4 100644 --- a/philote_mdo/openmdao/__init__.py +++ b/philote_mdo/openmdao/__init__.py @@ -31,7 +31,7 @@ import openmdao.api as om omdao_installed = True -except ImportError: +except ImportError: # pragma: no cover omdao_installed = False om = None diff --git a/philote_mdo/openmdao/explicit.py b/philote_mdo/openmdao/explicit.py index b22c045..458b7e6 100644 --- a/philote_mdo/openmdao/explicit.py +++ b/philote_mdo/openmdao/explicit.py @@ -219,12 +219,12 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): ---------- inputs : dict Dictionary of input values with variable names as keys - outputs : dict + outputs : dict Dictionary to store computed output values with variable names as keys discrete_inputs : dict, optional - Dictionary of discrete input values (currently unused), by default None + Dictionary of discrete input values, by default None discrete_outputs : dict, optional - Dictionary of discrete output values (currently unused), by default None + Dictionary of discrete output values, by default None Notes ----- @@ -234,8 +234,19 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): - This method is called automatically by OpenMDAO during model execution """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) - out = self._client.run_compute(local_inputs) - utils.assign_global_outputs(out, outputs) + local_discrete = utils.create_local_discrete_inputs( + discrete_inputs, self._client._discrete_var_meta + ) + result = self._client.run_compute(local_inputs, discrete_inputs=local_discrete) + + # run_compute returns (outputs, discrete_outputs) when discrete data exists + if isinstance(result, tuple): + out, d_out = result + utils.assign_global_outputs(out, outputs) + if discrete_outputs is not None: + utils.assign_global_outputs(d_out, discrete_outputs) + else: + utils.assign_global_outputs(result, outputs) def compute_partials(self, inputs, partials, discrete_inputs=None, discrete_outputs=None): """ @@ -250,12 +261,12 @@ def compute_partials(self, inputs, partials, discrete_inputs=None, discrete_outp inputs : dict Dictionary of input values with variable names as keys partials : dict - Dictionary to store computed partial derivatives with (output, input) + Dictionary to store computed partial derivatives with (output, input) tuples as keys discrete_inputs : dict, optional - Dictionary of discrete input values (currently unused), by default None + Dictionary of discrete input values, by default None discrete_outputs : dict, optional - Dictionary of discrete output values (currently unused), by default None + Dictionary of discrete output values, by default None Notes ----- @@ -266,5 +277,10 @@ def compute_partials(self, inputs, partials, discrete_inputs=None, discrete_outp - Sparsity patterns from the server are preserved in the OpenMDAO component """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) - jac = self._client.run_compute_partials(local_inputs) + local_discrete = utils.create_local_discrete_inputs( + discrete_inputs, self._client._discrete_var_meta + ) + jac = self._client.run_compute_partials( + local_inputs, discrete_inputs=local_discrete + ) utils.assign_global_outputs(jac, partials) diff --git a/philote_mdo/openmdao/implicit.py b/philote_mdo/openmdao/implicit.py index e4a862f..1aa8843 100644 --- a/philote_mdo/openmdao/implicit.py +++ b/philote_mdo/openmdao/implicit.py @@ -220,11 +220,6 @@ def apply_nonlinear(self, inputs, outputs, residuals, discrete_inputs=None, disc """ Compute residual evaluation by calling the remote Philote server. - This method transfers both input and output values to the server, requests a - residual evaluation, and transfers the computed residuals back to the OpenMDAO - component. The residuals represent R(inputs, outputs) where the goal is to - find outputs such that R = 0. - Parameters ---------- inputs : dict @@ -234,33 +229,32 @@ def apply_nonlinear(self, inputs, outputs, residuals, discrete_inputs=None, disc residuals : dict Dictionary to store computed residual values with variable names as keys discrete_inputs : dict, optional - Dictionary of discrete input values (currently unused), by default None + Dictionary of discrete input values, by default None discrete_outputs : dict, optional - Dictionary of discrete output values (currently unused), by default None - - Notes - ----- - - Both inputs and outputs are sent to the server for residual computation - - Residuals are computed as R(inputs, outputs) at the current point - - The goal is to find outputs where residuals are zero - - This method is called automatically by OpenMDAO during residual evaluation + Dictionary of discrete output values, by default None """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) local_outputs = utils.create_local_inputs( outputs, self._client._var_meta, data.kOutput ) + local_di = utils.create_local_discrete_inputs( + discrete_inputs, self._client._discrete_var_meta + ) + local_do = utils.create_local_discrete_inputs( + discrete_outputs, + self._client._discrete_var_meta, + data.VariableType.kDiscreteOutput, + ) - res = self._client.run_compute_residuals(local_inputs, local_outputs) + res = self._client.run_compute_residuals( + local_inputs, local_outputs, local_di, local_do + ) utils.assign_global_outputs(res, residuals) def solve_nonlinear(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): """ Solve the implicit equations by calling the remote Philote server. - This method transfers input values to the server, requests the server to solve - the implicit equations R(inputs, outputs) = 0 for the outputs, and transfers - the solved output values back to the OpenMDAO component. - Parameters ---------- inputs : dict @@ -268,31 +262,21 @@ def solve_nonlinear(self, inputs, outputs, discrete_inputs=None, discrete_output outputs : dict Dictionary to store solved output values with variable names as keys discrete_inputs : dict, optional - Dictionary of discrete input values (currently unused), by default None + Dictionary of discrete input values, by default None discrete_outputs : dict, optional - Dictionary of discrete output values (currently unused), by default None - - Notes - ----- - - The server performs the nonlinear solve internally - - Server may use Newton's method, fixed-point iteration, or other solvers - - Convergence criteria are controlled by server options - - This method is called by OpenMDAO's nonlinear solvers - - Output values are updated with the converged solution + Dictionary of discrete output values, by default None """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) - out = self._client.run_solve_residuals(local_inputs) + local_di = utils.create_local_discrete_inputs( + discrete_inputs, self._client._discrete_var_meta + ) + out = self._client.run_solve_residuals(local_inputs, local_di) utils.assign_global_outputs(out, outputs) def linearize(self, inputs, outputs, partials, discrete_inputs=None, discrete_outputs=None): """ Compute partial derivatives of residuals by calling the remote Philote server. - This method transfers both input and output values to the server, requests - computation of the residual Jacobian (dR/dinputs and dR/doutputs), and transfers - the computed partial derivatives back to the OpenMDAO component. These derivatives - are used by OpenMDAO's linear solvers and optimization algorithms. - Parameters ---------- inputs : dict @@ -300,24 +284,25 @@ def linearize(self, inputs, outputs, partials, discrete_inputs=None, discrete_ou outputs : dict Dictionary of output values with variable names as keys partials : dict - Dictionary to store computed partial derivatives with (residual, variable) - tuples as keys + Dictionary to store computed partial derivatives discrete_inputs : dict, optional - Dictionary of discrete input values (currently unused), by default None + Dictionary of discrete input values, by default None discrete_outputs : dict, optional - Dictionary of discrete output values (currently unused), by default None - - Notes - ----- - - Computes both dR/dinputs and dR/doutputs partial derivatives - - Derivatives are computed at the current (inputs, outputs) point - - Server determines whether to use analytic or finite difference derivatives - - This method is called automatically by OpenMDAO when derivatives are needed - - Results are used by linear solvers and optimization algorithms + Dictionary of discrete output values, by default None """ local_inputs = utils.create_local_inputs(inputs, self._client._var_meta) local_outputs = utils.create_local_inputs( outputs, self._client._var_meta, data.kOutput ) - jac = self._client.run_residual_gradients(local_inputs, local_outputs) + local_di = utils.create_local_discrete_inputs( + discrete_inputs, self._client._discrete_var_meta + ) + local_do = utils.create_local_discrete_inputs( + discrete_outputs, + self._client._discrete_var_meta, + data.VariableType.kDiscreteOutput, + ) + jac = self._client.run_residual_gradients( + local_inputs, local_outputs, local_di, local_do + ) utils.assign_global_outputs(jac, partials) diff --git a/philote_mdo/openmdao/utils.py b/philote_mdo/openmdao/utils.py index ed626a9..c11f881 100644 --- a/philote_mdo/openmdao/utils.py +++ b/philote_mdo/openmdao/utils.py @@ -53,13 +53,14 @@ def client_setup(comp): Sets up the OpenMDAO component with all required inputs and outputs. This function will call the required RPCs to obtain the variables - from the remote discipline server. + from the remote discipline server. Both continuous and discrete + variables are declared. """ # set up the remote discipline and get the variable definitions comp._client.run_setup() comp._client.get_variable_definitions() - # define inputs and outputs based on the discipline metadata + # define continuous inputs and outputs based on the discipline metadata for var in comp._client._var_meta: if not var.units: units = None @@ -72,6 +73,14 @@ def client_setup(comp): if var.type == data.kOutput: comp.add_output(var.name, shape=tuple(var.shape), units=units) + # define discrete inputs and outputs + for var in comp._client._discrete_var_meta: + if var.type == data.VariableType.kDiscreteInput: + comp.add_discrete_input(var.name, val=None) + + if var.type == data.VariableType.kDiscreteOutput: + comp.add_discrete_output(var.name, val=None) + def client_setup_partials(comp): """ @@ -88,6 +97,20 @@ def client_setup_partials(comp): comp.declare_partials(partial.name, partial.subname) +def create_local_discrete_inputs(discrete_inputs, discrete_var_meta, type=data.VariableType.kDiscreteInput): + """ + Creates a Philote-Python local discrete inputs dictionary from OpenMDAO + discrete inputs. + """ + if discrete_inputs is None: + return None + local = {} + for var in discrete_var_meta: + if var.type == type: + local[var.name] = discrete_inputs[var.name] + return local if local else None + + def create_local_inputs(inputs, var_meta, type=data.kInput): """ Creates a Philote-Python local inputs dictionary from OpenMDAO inputs. diff --git a/proto b/proto index 5979d87..a5a21b8 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 5979d8713a4528348ebd3672a1740a6e33e7742a +Subproject commit a5a21b8c151c34f9f7b6f8fb717da76bb7e18011 diff --git a/tests/test_discipline_client.py b/tests/test_discipline_client.py index 75cc6a4..1e24d9b 100644 --- a/tests/test_discipline_client.py +++ b/tests/test_discipline_client.py @@ -257,21 +257,29 @@ def test_assemble_input_messages(self): } expected_messages = [ - data.Array( - name="x", start=0, end=1, type=data.VariableType.kInput, data=[1.0, 2.0] + data.VariableMessage( + continuous=data.Array( + name="x", start=0, end=1, type=data.VariableType.kInput, data=[1.0, 2.0] + ) ), - data.Array( - name="x", start=2, end=3, type=data.VariableType.kInput, data=[3.0, 4.0] + data.VariableMessage( + continuous=data.Array( + name="x", start=2, end=3, type=data.VariableType.kInput, data=[3.0, 4.0] + ) ), - data.Array( - name="f", - start=0, - end=1, - type=data.VariableType.kOutput, - data=[5.0, 6.0], + data.VariableMessage( + continuous=data.Array( + name="f", + start=0, + end=1, + type=data.VariableType.kOutput, + data=[5.0, 6.0], + ) ), - data.Array( - name="f", start=2, end=2, type=data.VariableType.kOutput, data=[7.0] + data.VariableMessage( + continuous=data.Array( + name="f", start=2, end=2, type=data.VariableType.kOutput, data=[7.0] + ) ), ] @@ -294,14 +302,20 @@ def test_recover_outputs(self): data.VariableMetaData(name="g", type=data.kOutput, shape=(3,)), ] - response1 = data.Array( - name="f", start=0, end=1, type=data.kOutput, data=[1.0, 2.0] + response1 = data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=1, type=data.kOutput, data=[1.0, 2.0] + ) ) - response2 = data.Array( - name="f", start=2, end=3, type=data.kOutput, data=[3.0, 4.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="f", start=2, end=3, type=data.kOutput, data=[3.0, 4.0] + ) ) - response3 = data.Array( - name="g", start=0, end=2, type=data.kOutput, data=[4.0, 5.0, 6.0] + response3 = data.VariableMessage( + continuous=data.Array( + name="g", start=0, end=2, type=data.kOutput, data=[4.0, 5.0, 6.0] + ) ) mock_responses = [response1, response2, response3] @@ -330,14 +344,20 @@ def test_recover_residuals(self): data.VariableMetaData(name="g", type=data.kResidual, shape=(3,)), ] - response1 = data.Array( - name="f", start=0, end=1, type=data.kResidual, data=[1.0, 2.0] + response1 = data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=1, type=data.kResidual, data=[1.0, 2.0] + ) ) - response2 = data.Array( - name="f", start=2, end=3, type=data.kResidual, data=[3.0, 4.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="f", start=2, end=3, type=data.kResidual, data=[3.0, 4.0] + ) ) - response3 = data.Array( - name="g", start=0, end=2, type=data.kResidual, data=[4.0, 5.0, 6.0] + response3 = data.VariableMessage( + continuous=data.Array( + name="g", start=0, end=2, type=data.kResidual, data=[4.0, 5.0, 6.0] + ) ) mock_responses = [response1, response2, response3] @@ -373,19 +393,25 @@ def test_recover_partials(self): client._partials_meta = [partial_metadata1, partial_metadata2] # Define mock responses - response1 = data.Array( - name="f", subname="x", type=data.kPartial, start=0, end=1, data=[1.0, 2.0] + response1 = data.VariableMessage( + continuous=data.Array( + name="f", subname="x", type=data.kPartial, start=0, end=1, data=[1.0, 2.0] + ) ) - response2 = data.Array( - name="f", subname="x", type=data.kPartial, start=2, end=3, data=[3.0, 4.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="f", subname="x", type=data.kPartial, start=2, end=3, data=[3.0, 4.0] + ) ) - response3 = data.Array( - name="g", - subname="y", - type=data.kPartial, - start=0, - end=2, - data=[4.0, 5.0, 6.0], + response3 = data.VariableMessage( + continuous=data.Array( + name="g", + subname="y", + type=data.kPartial, + start=0, + end=2, + data=[4.0, 5.0, 6.0], + ) ) mock_responses = [response1, response2, response3] @@ -420,14 +446,16 @@ def test_recover_outputs_empty_array_raises_error(self): ] # Create response with empty data array - response_empty = data.Array( - name="f", start=0, end=1, type=data.kOutput, data=[] + response_empty = data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=1, type=data.kOutput, data=[] + ) ) mock_responses = [response_empty] with self.assertRaises(ValueError) as context: client._recover_outputs(mock_responses) - + self.assertIn("Expected continuous variables, but array is empty", str(context.exception)) def test_recover_residuals_empty_array_raises_error(self): @@ -442,14 +470,16 @@ def test_recover_residuals_empty_array_raises_error(self): ] # Create response with empty data array - response_empty = data.Array( - name="f", start=0, end=1, type=data.kResidual, data=[] + response_empty = data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=1, type=data.kResidual, data=[] + ) ) mock_responses = [response_empty] with self.assertRaises(ValueError) as context: client._recover_residuals(mock_responses) - + self.assertIn("Expected continuous variables, but array is empty", str(context.exception)) def test_recover_partials_empty_array_raises_error(self): @@ -463,20 +493,22 @@ def test_recover_partials_empty_array_raises_error(self): data.VariableMetaData(name="f", type=data.kOutput, shape=(2,)), data.VariableMetaData(name="x", type=data.kInput, shape=(2,)), ] - + client._partials_meta = [ data.PartialsMetaData(name="f", subname="x"), ] # Create response with empty data array - response_empty = data.Array( - name="f", subname="x", start=0, end=1, type=data.kPartial, data=[] + response_empty = data.VariableMessage( + continuous=data.Array( + name="f", subname="x", start=0, end=1, type=data.kPartial, data=[] + ) ) mock_responses = [response_empty] with self.assertRaises(ValueError) as context: client._recover_partials(mock_responses) - + self.assertIn("Expected continuous outputs for the partials, but array was empty", str(context.exception)) diff --git a/tests/test_discipline_server.py b/tests/test_discipline_server.py index db89c98..f9b2550 100644 --- a/tests/test_discipline_server.py +++ b/tests/test_discipline_server.py @@ -325,22 +325,28 @@ def test_preallocate_partials(self): def test_process_inputs(self): # create a mock request_iterator request_iterator = [ - data.Array( - start=0, - end=2, - data=[1.0, 2.0, 3.0], - type=data.VariableType.kInput, - name="x", + data.VariableMessage( + continuous=data.Array( + start=0, + end=2, + data=[1.0, 2.0, 3.0], + type=data.VariableType.kInput, + name="x", + ) ), - data.Array( - start=3, end=4, data=[4.0, 5.0], type=data.VariableType.kInput, name="x" + data.VariableMessage( + continuous=data.Array( + start=3, end=4, data=[4.0, 5.0], type=data.VariableType.kInput, name="x" + ) ), - data.Array( - start=0, - end=1, - data=[0.1, 0.2], - type=data.VariableType.kOutput, - name="f", + data.VariableMessage( + continuous=data.Array( + start=0, + end=1, + data=[0.1, 0.2], + type=data.VariableType.kOutput, + name="f", + ) ), ] @@ -379,24 +385,26 @@ def test_process_inputs_empty_array_raises_error(self): Tests that process_inputs raises ValueError when array data is empty. """ server = DisciplineServer() - + # Create request with empty data array request_iterator = [ - data.Array( - start=0, - end=2, - data=[], # Empty data array - type=data.VariableType.kInput, - name="x", + data.VariableMessage( + continuous=data.Array( + start=0, + end=2, + data=[], # Empty data array + type=data.VariableType.kInput, + name="x", + ) ), ] - + flat_inputs = {"x": np.zeros(3)} flat_outputs = {} - + with self.assertRaises(ValueError) as context: server.process_inputs(request_iterator, flat_inputs, flat_outputs) - + self.assertIn("Expected continuous variables but arrays were empty for variable x", str(context.exception)) diff --git a/tests/test_discrete_integration.py b/tests/test_discrete_integration.py new file mode 100644 index 0000000..79e1316 --- /dev/null +++ b/tests/test_discrete_integration.py @@ -0,0 +1,254 @@ +# Philote-Python +# +# Copyright 2022-2025 Christopher A. Lupp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# This work has been cleared for public release, distribution unlimited, case +# number: AFRL-2023-5713. +# +# The views expressed are those of the authors and do not reflect the +# official guidance or position of the United States Government, the +# Department of Defense or of the United States Air Force. +# +# Statement from DoD: The Appearance of external hyperlinks does not +# constitute endorsement by the United States Department of Defense (DoD) of +# the linked websites, of the information, products, or services contained +# therein. The DoD does not exercise any editorial, security, or other +# control over the information you may find at these locations. +""" +Integration tests for disciplines with discrete variables. + +These tests spin up a real gRPC server and client and exercise the full +discrete variable round-trip: declaration, metadata discovery, message +serialization, discipline evaluation, and result recovery. +""" +from concurrent import futures +import unittest + +import grpc +import numpy as np +import openmdao.api as om + +import philote_mdo.general as pmdo +import philote_mdo.openmdao as pmdo_om + + +# --------------------------------------------------------------------------- +# Example discipline with discrete inputs/outputs +# --------------------------------------------------------------------------- +class ScaledParaboloid(pmdo.ExplicitDiscipline): + """ + Paraboloid whose output is scaled by a discrete mode flag. + + Continuous inputs: x, y (scalars) + Discrete input: mode ("double" or "half") + Continuous output: f_xy (scalar) + Discrete output: label (string describing the mode used) + + f_xy = scale * ((x - 3)^2 + x*y + (y + 4)^2 - 3) + + where scale = 2.0 for "double" and 0.5 for "half". + """ + + def setup(self): + self.add_input("x", shape=(1,), units="m") + self.add_input("y", shape=(1,), units="m") + self.add_output("f_xy", shape=(1,), units="m**2") + + self.add_discrete_input("mode") + self.add_discrete_output("label") + + def setup_partials(self): + self.declare_partials("f_xy", "x") + self.declare_partials("f_xy", "y") + + def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): + x = inputs["x"] + y = inputs["y"] + base = (x - 3.0) ** 2 + x * y + (y + 4.0) ** 2 - 3.0 + + mode = (discrete_inputs or {}).get("mode", None) or "double" + scale = 2.0 if mode == "double" else 0.5 + + outputs["f_xy"] = scale * base + + if discrete_outputs is not None: + discrete_outputs["label"] = f"scaled_{mode}" + + def compute_partials(self, inputs, partials, discrete_inputs=None): + x = inputs["x"] + y = inputs["y"] + + mode = (discrete_inputs or {}).get("mode", None) or "double" + scale = 2.0 if mode == "double" else 0.5 + + partials["f_xy", "x"] = scale * (2.0 * x - 6.0 + y) + partials["f_xy", "y"] = scale * (2.0 * y + 8.0 + x) + + +# --------------------------------------------------------------------------- +# Integration tests +# --------------------------------------------------------------------------- +class TestDiscreteIntegration(unittest.TestCase): + """ + End-to-end integration tests for disciplines with discrete variables. + """ + + def _start_server(self, discipline, port): + """Helper to start a gRPC server with the given discipline.""" + server = grpc.server(futures.ThreadPoolExecutor(max_workers=4)) + explicit_server = pmdo.ExplicitServer(discipline=discipline) + explicit_server.attach_to_server(server) + server.add_insecure_port(f"[::]:{port}") + server.start() + return server + + # ------------------------------------------------------------------ + # Raw client tests (no OpenMDAO) + # ------------------------------------------------------------------ + def test_client_compute_with_discrete_inputs(self): + """ + Test that a raw ExplicitClient can send discrete inputs and + receive both continuous and discrete outputs. + """ + port = 50061 + server = self._start_server(ScaledParaboloid(), port) + + try: + channel = grpc.insecure_channel(f"localhost:{port}") + client = pmdo.ExplicitClient(channel) + + # setup handshake + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + client.get_partials_definitions() + + # verify discrete metadata was discovered + self.assertTrue(len(client._discrete_var_meta) > 0) + discrete_names = {v.name for v in client._discrete_var_meta} + self.assertIn("mode", discrete_names) + self.assertIn("label", discrete_names) + + # run compute with mode="double" + inputs = {"x": np.array([1.0]), "y": np.array([2.0])} + result = client.run_compute(inputs, discrete_inputs={"mode": "double"}) + + # Should return (outputs, discrete_outputs) tuple + self.assertIsInstance(result, tuple) + outputs, discrete_outputs = result + + # base paraboloid value at (1, 2) = (1-3)^2 + 1*2 + (2+4)^2 - 3 = 39 + # scaled by 2.0 => 78.0 + np.testing.assert_almost_equal(outputs["f_xy"][0], 78.0) + self.assertEqual(discrete_outputs["label"], "scaled_double") + + # run compute with mode="half" + result = client.run_compute(inputs, discrete_inputs={"mode": "half"}) + outputs, discrete_outputs = result + + # 39 * 0.5 = 19.5 + np.testing.assert_almost_equal(outputs["f_xy"][0], 19.5) + self.assertEqual(discrete_outputs["label"], "scaled_half") + + finally: + server.stop(0) + + def test_client_compute_partials_with_discrete_inputs(self): + """ + Test that a raw ExplicitClient can send discrete inputs for + gradient evaluation. + """ + port = 50062 + server = self._start_server(ScaledParaboloid(), port) + + try: + channel = grpc.insecure_channel(f"localhost:{port}") + client = pmdo.ExplicitClient(channel) + + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + client.get_partials_definitions() + + inputs = {"x": np.array([1.0]), "y": np.array([2.0])} + + # mode="double", scale=2.0 + # df/dx = 2*(2*1 - 6 + 2) = 2*(-2) = -4.0 + # df/dy = 2*(2*2 + 8 + 1) = 2*(13) = 26.0 + partials = client.run_compute_partials( + inputs, discrete_inputs={"mode": "double"} + ) + + np.testing.assert_almost_equal(partials[("f_xy", "x")][0], -4.0) + np.testing.assert_almost_equal(partials[("f_xy", "y")][0], 26.0) + + # mode="half", scale=0.5 + # df/dx = 0.5*(-2) = -1.0 + # df/dy = 0.5*(13) = 6.5 + partials = client.run_compute_partials( + inputs, discrete_inputs={"mode": "half"} + ) + + np.testing.assert_almost_equal(partials[("f_xy", "x")][0], -1.0) + np.testing.assert_almost_equal(partials[("f_xy", "y")][0], 6.5) + + finally: + server.stop(0) + + # ------------------------------------------------------------------ + # OpenMDAO integration test + # ------------------------------------------------------------------ + def test_openmdao_compute_with_discrete(self): + """ + Test the full OpenMDAO integration: RemoteExplicitComponent + auto-discovers discrete variables from the server and returns + correct results. + """ + port = 50063 + server = self._start_server(ScaledParaboloid(), port) + + try: + channel = grpc.insecure_channel(f"localhost:{port}") + + prob = om.Problem() + comp = pmdo_om.RemoteExplicitComponent(channel=channel) + prob.model.add_subsystem("scaled", comp) + + prob.setup() + + # verify discrete variables were discovered + discrete_names = {v.name for v in comp._client._discrete_var_meta} + self.assertIn("mode", discrete_names) + self.assertIn("label", discrete_names) + + # set continuous inputs + prob.set_val("scaled.x", 1.0) + prob.set_val("scaled.y", 2.0) + + # run model – mode defaults to "double" when not set (scale=2.0) + prob.run_model() + + # base value at (1,2) = 39.0, scale = 2.0 => 78.0 + np.testing.assert_almost_equal( + prob.get_val("scaled.f_xy")[0], 78.0 + ) + + finally: + server.stop(0) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_discrete_variables.py b/tests/test_discrete_variables.py new file mode 100644 index 0000000..6b3811a --- /dev/null +++ b/tests/test_discrete_variables.py @@ -0,0 +1,687 @@ +# Philote-Python +# +# Copyright 2022-2025 Christopher A. Lupp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# This work has been cleared for public release, distribution unlimited, case +# number: AFRL-2023-5713. +# +# The views expressed are those of the authors and do not reflect the +# official guidance or position of the United States Government, the +# Department of Defense or of the United States Air Force. +# +# Statement from DoD: The Appearance of external hyperlinks does not +# constitute endorsement by the United States Department of Defense (DoD) of +# the linked websites, of the information, products, or services contained +# therein. The DoD does not exercise any editorial, security, or other +# control over the information you may find at these locations. +""" +Unit tests for discrete variable support across the Philote stack. +""" +import unittest +from unittest.mock import Mock, MagicMock, patch + +import numpy as np +from google.protobuf import struct_pb2 + +import philote_mdo.generated.data_pb2 as data +from philote_mdo.general import ( + Discipline, + DisciplineClient, + DisciplineServer, + ExplicitDiscipline, + ExplicitServer, + ExplicitClient, + ImplicitDiscipline, + ImplicitServer, + ImplicitClient, +) +from philote_mdo.general.discipline_server import _value_to_python, _python_to_value +import philote_mdo.openmdao.utils as om_utils + + +# --------------------------------------------------------------------------- +# Value conversion helpers +# --------------------------------------------------------------------------- +class TestValueConversion(unittest.TestCase): + """Tests for _value_to_python and _python_to_value round-trip conversion.""" + + def test_none(self): + val = _python_to_value(None) + self.assertIsNone(_value_to_python(val)) + + def test_bool_true(self): + val = _python_to_value(True) + self.assertIs(_value_to_python(val), True) + + def test_bool_false(self): + val = _python_to_value(False) + self.assertIs(_value_to_python(val), False) + + def test_int(self): + val = _python_to_value(42) + result = _value_to_python(val) + self.assertEqual(result, 42) + self.assertIsInstance(result, int) + + def test_float(self): + val = _python_to_value(3.14) + result = _value_to_python(val) + self.assertAlmostEqual(result, 3.14) + self.assertIsInstance(result, float) + + def test_string(self): + val = _python_to_value("hello") + self.assertEqual(_value_to_python(val), "hello") + + def test_list(self): + original = [1, "two", 3.0, True] + val = _python_to_value(original) + result = _value_to_python(val) + self.assertEqual(result, [1, "two", 3, True]) + + def test_tuple_converts_to_list(self): + val = _python_to_value((1, 2)) + result = _value_to_python(val) + self.assertEqual(result, [1, 2]) + + def test_dict(self): + original = {"key": "value", "n": 5} + val = _python_to_value(original) + result = _value_to_python(val) + self.assertEqual(result, original) + + def test_nested_structure(self): + original = {"mesh": "coarse", "params": [1, 2, 3], "opts": {"tol": 1e-6}} + val = _python_to_value(original) + result = _value_to_python(val) + self.assertEqual(result["mesh"], "coarse") + self.assertEqual(result["params"], [1, 2, 3]) + + def test_unsupported_type_becomes_string(self): + """Unsupported types are serialized via str().""" + val = _python_to_value(object) + result = _value_to_python(val) + self.assertIsInstance(result, str) + + +# --------------------------------------------------------------------------- +# Discipline base class +# --------------------------------------------------------------------------- +class TestDisciplineDiscreteVars(unittest.TestCase): + """Tests for add_discrete_input / add_discrete_output on Discipline.""" + + def test_add_discrete_input(self): + d = Discipline() + d.add_discrete_input("mode") + self.assertEqual(len(d._discrete_var_meta), 1) + meta = d._discrete_var_meta[0] + self.assertEqual(meta.name, "mode") + self.assertEqual(meta.type, data.VariableType.kDiscreteInput) + + def test_add_discrete_output(self): + d = Discipline() + d.add_discrete_output("status") + self.assertEqual(len(d._discrete_var_meta), 1) + meta = d._discrete_var_meta[0] + self.assertEqual(meta.name, "status") + self.assertEqual(meta.type, data.VariableType.kDiscreteOutput) + + def test_clear_data_resets_discrete_meta(self): + d = Discipline() + d.add_discrete_input("x") + d.add_discrete_output("y") + d._clear_data() + self.assertEqual(len(d._discrete_var_meta), 0) + + +# --------------------------------------------------------------------------- +# DisciplineServer – discrete metadata streaming and process_inputs +# --------------------------------------------------------------------------- +class TestDisciplineServerDiscrete(unittest.TestCase): + """Tests for discrete variable handling in DisciplineServer.""" + + def test_get_variable_definitions_includes_discrete(self): + """GetVariableDefinitions should stream both continuous and discrete metadata.""" + server = DisciplineServer() + server._discipline = Discipline() + server._discipline.add_input("x", shape=(1,)) + server._discipline.add_discrete_input("mode") + + responses = list(server.GetVariableDefinitions(None, None)) + self.assertEqual(len(responses), 2) + types = [r.type for r in responses] + self.assertIn(data.VariableType.kInput, types) + self.assertIn(data.VariableType.kDiscreteInput, types) + + def test_process_inputs_with_discrete(self): + """process_inputs should demux continuous and discrete messages.""" + server = DisciplineServer() + + flat_inputs = {"x": np.zeros(2)} + discrete_inputs = {} + discrete_outputs = {} + + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + name="x", start=0, end=1, + type=data.VariableType.kInput, data=[1.0, 2.0], + ) + ), + data.VariableMessage( + discrete=data.DiscreteVariable( + name="mode", + type=data.VariableType.kDiscreteInput, + value=_python_to_value("forward"), + ) + ), + data.VariableMessage( + discrete=data.DiscreteVariable( + name="status", + type=data.VariableType.kDiscreteOutput, + value=_python_to_value(0), + ) + ), + ] + + di, do = server.process_inputs( + request_iterator, flat_inputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) + + np.testing.assert_array_equal(flat_inputs["x"], [1.0, 2.0]) + self.assertEqual(di["mode"], "forward") + self.assertEqual(do["status"], 0) + + +# --------------------------------------------------------------------------- +# DisciplineClient – discrete variable definitions, assembly, recovery +# --------------------------------------------------------------------------- +class TestDisciplineClientDiscrete(unittest.TestCase): + """Tests for discrete variable handling in DisciplineClient.""" + + @patch("philote_mdo.generated.disciplines_pb2_grpc.DisciplineServiceStub") + def test_get_variable_definitions_separates_discrete(self, mock_stub_cls): + mock_channel = Mock() + mock_stub = mock_stub_cls.return_value + mock_stub.GetVariableDefinitions.return_value = [ + data.VariableMetaData(name="x", type=data.kInput, shape=[1]), + data.VariableMetaData( + name="mode", type=data.VariableType.kDiscreteInput + ), + data.VariableMetaData(name="f", type=data.kOutput, shape=[1]), + data.VariableMetaData( + name="status", type=data.VariableType.kDiscreteOutput + ), + ] + + client = DisciplineClient(mock_channel) + client.get_variable_definitions() + + self.assertEqual(len(client._var_meta), 2) + self.assertEqual(len(client._discrete_var_meta), 2) + + def test_assemble_input_messages_with_discrete(self): + """_assemble_input_messages should include discrete messages.""" + mock_channel = Mock() + client = DisciplineClient(mock_channel) + client._stream_options.num_double = 10 + + inputs = {"x": np.array([1.0])} + discrete_inputs = {"mode": "forward", "order": 3} + + messages = client._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) + + # 1 continuous + 2 discrete = 3 messages + self.assertEqual(len(messages), 3) + + # Check that discrete messages are present + discrete_msgs = [ + m for m in messages if m.WhichOneof("payload") == "discrete" + ] + self.assertEqual(len(discrete_msgs), 2) + + names = {m.discrete.name for m in discrete_msgs} + self.assertEqual(names, {"mode", "order"}) + + def test_assemble_input_messages_with_discrete_outputs(self): + """_assemble_input_messages should include discrete output messages.""" + mock_channel = Mock() + client = DisciplineClient(mock_channel) + client._stream_options.num_double = 10 + + inputs = {"x": np.array([1.0])} + discrete_outputs = {"status": 0} + + messages = client._assemble_input_messages( + inputs, discrete_outputs=discrete_outputs + ) + + # 1 continuous + 1 discrete output + self.assertEqual(len(messages), 2) + discrete_msgs = [ + m for m in messages if m.WhichOneof("payload") == "discrete" + ] + self.assertEqual(len(discrete_msgs), 1) + self.assertEqual(discrete_msgs[0].discrete.name, "status") + self.assertEqual( + discrete_msgs[0].discrete.type, data.VariableType.kDiscreteOutput + ) + + def test_recover_outputs_with_discrete(self): + """_recover_outputs should return (outputs, discrete_outputs) tuple.""" + mock_channel = Mock() + client = DisciplineClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + ] + + responses = [ + data.VariableMessage( + continuous=data.Array( + name="f", type=data.kOutput, start=0, end=0, data=[42.0] + ) + ), + data.VariableMessage( + discrete=data.DiscreteVariable( + name="status", + type=data.VariableType.kDiscreteOutput, + value=_python_to_value("converged"), + ) + ), + ] + + result = client._recover_outputs(responses) + + # Should be a tuple (continuous_outputs, discrete_outputs) + self.assertIsInstance(result, tuple) + outputs, d_outputs = result + np.testing.assert_array_equal(outputs["f"], [42.0]) + self.assertEqual(d_outputs["status"], "converged") + + +# --------------------------------------------------------------------------- +# ExplicitServer – discrete data flow +# --------------------------------------------------------------------------- +class TestExplicitServerDiscrete(unittest.TestCase): + """Tests for discrete variable handling in ExplicitServer.""" + + def test_compute_function_with_discrete(self): + """ComputeFunction should pass discrete data to discipline.compute.""" + server = ExplicitServer() + discipline = ExplicitDiscipline() + discipline.add_input("x", shape=(1,)) + discipline.add_output("f", shape=(1,)) + discipline.add_discrete_input("mode") + discipline.add_discrete_output("status") + server._discipline = discipline + server._stream_opts.num_double = 10 + + captured = {} + + def compute(inputs, outputs, discrete_inputs, discrete_outputs): + captured["discrete_inputs"] = dict(discrete_inputs) + outputs["f"] = inputs["x"] * 2 + discrete_outputs["status"] = "ok" + + discipline.compute = compute + + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + name="x", start=0, end=0, + type=data.VariableType.kInput, data=[3.0], + ) + ), + data.VariableMessage( + discrete=data.DiscreteVariable( + name="mode", + type=data.VariableType.kDiscreteInput, + value=_python_to_value("fast"), + ) + ), + ] + + responses = list(server.ComputeFunction(request_iterator, None)) + + # Should have received the discrete input + self.assertEqual(captured["discrete_inputs"]["mode"], "fast") + + # Should have continuous and discrete output responses + continuous_responses = [ + r for r in responses if r.WhichOneof("payload") == "continuous" + ] + discrete_responses = [ + r for r in responses if r.WhichOneof("payload") == "discrete" + ] + + self.assertEqual(len(continuous_responses), 1) + self.assertEqual(continuous_responses[0].continuous.data[0], 6.0) + + self.assertEqual(len(discrete_responses), 1) + self.assertEqual(discrete_responses[0].discrete.name, "status") + + def test_compute_gradient_with_discrete(self): + """ComputeGradient should pass discrete data to compute_partials.""" + server = ExplicitServer() + discipline = ExplicitDiscipline() + discipline.add_input("x", shape=(1,)) + discipline.add_output("f", shape=(1,)) + discipline.add_discrete_input("mode") + discipline.declare_partials("f", "x") + server._discipline = discipline + server._stream_opts.num_double = 10 + + captured = {} + + def compute_partials(inputs, jac, discrete_inputs): + captured["discrete_inputs"] = dict(discrete_inputs) + jac["f", "x"] = np.array([2.0]) + + discipline.compute_partials = compute_partials + + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + name="x", start=0, end=0, + type=data.VariableType.kInput, data=[3.0], + ) + ), + data.VariableMessage( + discrete=data.DiscreteVariable( + name="mode", + type=data.VariableType.kDiscreteInput, + value=_python_to_value("fast"), + ) + ), + ] + + responses = list(server.ComputeGradient(request_iterator, None)) + + self.assertEqual(captured["discrete_inputs"]["mode"], "fast") + self.assertEqual(len(responses), 1) + self.assertEqual(responses[0].continuous.data[0], 2.0) + + +# --------------------------------------------------------------------------- +# ImplicitServer – discrete data flow +# --------------------------------------------------------------------------- +class TestImplicitServerDiscrete(unittest.TestCase): + """Tests for discrete variable handling in ImplicitServer.""" + + def _make_discipline(self): + discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,)) + discipline.add_output("f", shape=(1,)) + discipline.add_discrete_input("mode") + discipline.declare_partials("f", "x") + return discipline + + def _make_request(self, x_val=1.0, f_val=0.0, mode_val="fast"): + return [ + data.VariableMessage( + continuous=data.Array( + name="x", start=0, end=0, + type=data.VariableType.kInput, data=[x_val], + ) + ), + data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=0, + type=data.VariableType.kOutput, data=[f_val], + ) + ), + data.VariableMessage( + discrete=data.DiscreteVariable( + name="mode", + type=data.VariableType.kDiscreteInput, + value=_python_to_value(mode_val), + ) + ), + ] + + def test_compute_residuals_with_discrete(self): + server = ImplicitServer() + discipline = self._make_discipline() + server._discipline = discipline + server._stream_opts.num_double = 10 + + captured = {} + + def compute_residuals(inputs, outputs, residuals, di, do): + captured["mode"] = di["mode"] + residuals["f"] = outputs["f"] - inputs["x"] + + discipline.compute_residuals = compute_residuals + + responses = list( + server.ComputeResiduals(self._make_request(), None) + ) + + self.assertEqual(captured["mode"], "fast") + self.assertGreater(len(responses), 0) + + def test_solve_residuals_with_discrete(self): + server = ImplicitServer() + discipline = self._make_discipline() + server._discipline = discipline + server._stream_opts.num_double = 10 + + captured = {} + + def solve_residuals(inputs, outputs, di): + captured["mode"] = di["mode"] + outputs["f"] = inputs["x"] + + discipline.solve_residuals = solve_residuals + + responses = list( + server.SolveResiduals(self._make_request(), None) + ) + + self.assertEqual(captured["mode"], "fast") + self.assertGreater(len(responses), 0) + + def test_compute_residual_gradients_with_discrete(self): + server = ImplicitServer() + discipline = self._make_discipline() + server._discipline = discipline + server._stream_opts.num_double = 10 + + captured = {} + + def residual_partials(inputs, outputs, jac, di, do): + captured["mode"] = di["mode"] + jac["f", "x"] = np.array([1.0]) + + discipline.residual_partials = residual_partials + + responses = list( + server.ComputeResidualGradients(self._make_request(), None) + ) + + self.assertEqual(captured["mode"], "fast") + self.assertGreater(len(responses), 0) + + +# --------------------------------------------------------------------------- +# ExplicitClient – discrete round-trip +# --------------------------------------------------------------------------- +class TestExplicitClientDiscrete(unittest.TestCase): + """Tests for discrete inputs through ExplicitClient.""" + + @patch("philote_mdo.generated.disciplines_pb2_grpc.ExplicitServiceStub") + def test_run_compute_with_discrete(self, mock_stub_cls): + mock_channel = Mock() + mock_stub = mock_stub_cls.return_value + client = ExplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + ] + + mock_stub.ComputeFunction.return_value = [ + data.VariableMessage( + continuous=data.Array( + name="f", type=data.kOutput, start=0, end=0, data=[6.0] + ) + ), + ] + + result = client.run_compute( + {"x": np.array([3.0])}, + discrete_inputs={"mode": "fast"}, + ) + + # Should have been called with VariableMessage including discrete + call_args = mock_stub.ComputeFunction.call_args + messages = list(call_args[0][0]) + discrete_msgs = [ + m for m in messages if m.WhichOneof("payload") == "discrete" + ] + self.assertEqual(len(discrete_msgs), 1) + + @patch("philote_mdo.generated.disciplines_pb2_grpc.ExplicitServiceStub") + def test_run_compute_partials_with_discrete(self, mock_stub_cls): + mock_channel = Mock() + mock_stub = mock_stub_cls.return_value + client = ExplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + data.VariableMetaData(name="x", type=data.kInput, shape=(1,)), + ] + client._partials_meta = [ + data.PartialsMetaData(name="f", subname="x"), + ] + + mock_stub.ComputeGradient.return_value = [ + data.VariableMessage( + continuous=data.Array( + name="f", subname="x", type=data.kPartial, + start=0, end=0, data=[2.0], + ) + ), + ] + + partials = client.run_compute_partials( + {"x": np.array([3.0])}, + discrete_inputs={"mode": "fast"}, + ) + + np.testing.assert_array_equal(partials[("f", "x")], [2.0]) + + +# --------------------------------------------------------------------------- +# OpenMDAO utils – discrete variable setup and extraction +# --------------------------------------------------------------------------- +class TestOpenMdaoUtilsDiscrete(unittest.TestCase): + """Tests for discrete variable support in OpenMDAO utility functions.""" + + def test_client_setup_declares_discrete_vars(self): + comp = MagicMock() + comp._client._var_meta = [ + data.VariableMetaData(name="x", type=data.kInput, shape=[1], units="m"), + ] + comp._client._discrete_var_meta = [ + data.VariableMetaData( + name="mode", type=data.VariableType.kDiscreteInput + ), + data.VariableMetaData( + name="status", type=data.VariableType.kDiscreteOutput + ), + ] + + om_utils.client_setup(comp) + + comp.add_discrete_input.assert_called_once_with("mode", val=None) + comp.add_discrete_output.assert_called_once_with("status", val=None) + + def test_create_local_discrete_inputs(self): + discrete_inputs = {"mode": "forward", "extra": 99} + meta = [ + data.VariableMetaData( + name="mode", type=data.VariableType.kDiscreteInput + ), + ] + + result = om_utils.create_local_discrete_inputs(discrete_inputs, meta) + self.assertEqual(result, {"mode": "forward"}) + + def test_create_local_discrete_inputs_none(self): + result = om_utils.create_local_discrete_inputs(None, []) + self.assertIsNone(result) + + def test_create_local_discrete_inputs_empty_returns_none(self): + """When no matching vars, should return None.""" + discrete_inputs = {"other": 1} + meta = [ + data.VariableMetaData( + name="mode", type=data.VariableType.kDiscreteOutput + ), + ] + result = om_utils.create_local_discrete_inputs(discrete_inputs, meta) + self.assertIsNone(result) + + +# --------------------------------------------------------------------------- +# OpenMDAO Explicit – discrete tuple return handling +# --------------------------------------------------------------------------- +@patch("openmdao.api.ExplicitComponent.__init__") +class TestOpenMdaoExplicitDiscrete(unittest.TestCase): + """Tests for discrete data flow through RemoteExplicitComponent.""" + + def test_compute_with_discrete_tuple_result(self, om_patch): + from philote_mdo.openmdao import RemoteExplicitComponent + + mock_channel = Mock() + comp = RemoteExplicitComponent(channel=mock_channel) + + client_mock = MagicMock() + client_mock._var_meta = [ + data.VariableMetaData(name="x", type=data.kInput, shape=[1]), + data.VariableMetaData(name="f", type=data.kOutput, shape=[1]), + ] + client_mock._discrete_var_meta = [ + data.VariableMetaData( + name="mode", type=data.VariableType.kDiscreteInput + ), + data.VariableMetaData( + name="status", type=data.VariableType.kDiscreteOutput + ), + ] + # Simulate server returning tuple (outputs, discrete_outputs) + client_mock.run_compute.return_value = ( + {"f": np.array([6.0])}, + {"status": "ok"}, + ) + comp._client = client_mock + comp.name = "test" + + inputs = {"x": np.array([3.0])} + outputs = {"f": np.zeros(1)} + discrete_inputs = {"mode": "fast"} + discrete_outputs = {"status": None} + + comp.compute(inputs, outputs, discrete_inputs, discrete_outputs) + + np.testing.assert_array_equal(outputs["f"], [6.0]) + self.assertEqual(discrete_outputs["status"], "ok") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index ecda48a..72b5e40 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -98,36 +98,39 @@ def test_get_available_options_with_invalid_type(self): def test_process_inputs_with_empty_continuous_data(self): """ - Test process_inputs with empty continuous data arrays (line 216). + Test process_inputs with empty continuous data arrays. """ server = DisciplineServer() discipline = Mock() - + # Set up discipline with continuous variables discipline._is_continuous = True discipline._var_meta = [Mock()] discipline._var_meta[0].name = "test_var" discipline._var_meta[0].shape = [2] discipline._var_meta[0].type = data.kInput - + server.attach_discipline(discipline) - - # Create a message with empty data - message = Mock() - message.name = "test_var" - message.type = data.VariableType.kInput - message.start = 0 - message.end = 1 - message.data = [] # Empty data array - + + # Create a VariableMessage wrapping an Array with empty data + message = data.VariableMessage( + continuous=data.Array( + name="test_var", + type=data.VariableType.kInput, + start=0, + end=1, + data=[], + ) + ) + # Create mock for flat_inputs and flat_outputs flat_inputs = {"test_var": [0.0, 0.0]} flat_outputs = {} - + # This should raise a ValueError with self.assertRaises(ValueError) as context: server.process_inputs([message], flat_inputs, flat_outputs) - + self.assertIn("Expected continuous variables but arrays were empty", str(context.exception)) @@ -138,30 +141,33 @@ class TestDisciplineClientEdgeCases(unittest.TestCase): def test_recover_outputs_with_empty_data(self): """ - Test _recover_outputs with empty data arrays (line 197). + Test _recover_outputs with empty data arrays. """ # Create a mock channel channel = Mock() client = DisciplineClient(channel) - + # Set up outputs structure client._var_meta = [Mock()] client._var_meta[0].name = "test_output" client._var_meta[0].shape = [2] client._var_meta[0].type = data.kOutput - - # Create a response message with empty data - message = Mock() - message.name = "test_output" - message.type = data.kOutput - message.start = 0 - message.end = 1 - message.data = [] # Empty data array - + + # Create a VariableMessage wrapping an Array with empty data + message = data.VariableMessage( + continuous=data.Array( + name="test_output", + type=data.kOutput, + start=0, + end=1, + data=[], + ) + ) + # This should raise a ValueError with self.assertRaises(ValueError) as context: client._recover_outputs([message]) - + self.assertIn("Expected continuous variables, but array is empty", str(context.exception)) # NOTE: Other client edge case tests are more complex to set up properly diff --git a/tests/test_explicit_client.py b/tests/test_explicit_client.py index 4a4be6c..59bd35b 100644 --- a/tests/test_explicit_client.py +++ b/tests/test_explicit_client.py @@ -60,11 +60,15 @@ def test_compute(self, mock_explicit_stub): "x": np.array([1.0, 2.0, 3.0, 4.0]).reshape(2, 2), } - response1 = data.Array( - name="f", type=data.kOutput, start=0, end=2, data=[5.0, 6.0, 7.0] + response1 = data.VariableMessage( + continuous=data.Array( + name="f", type=data.kOutput, start=0, end=2, data=[5.0, 6.0, 7.0] + ) ) - response2 = data.Array( - name="g", type=data.kOutput, start=0, end=2, data=[8.0, 9.0, 10.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="g", type=data.kOutput, start=0, end=2, data=[8.0, 9.0, 10.0] + ) ) mock_responses = [response1, response2] @@ -101,16 +105,20 @@ def test_compute_partials(self, mock_explicit_stub): "x": np.array([1.0, 2.0, 3.0, 4.0]).reshape(2, 2), } - response1 = data.Array( - name="f", - subname="x", - type=data.kPartial, - start=0, - end=2, - data=[5.0, 6.0, 7.0], + response1 = data.VariableMessage( + continuous=data.Array( + name="f", + subname="x", + type=data.kPartial, + start=0, + end=2, + data=[5.0, 6.0, 7.0], + ) ) - response2 = data.Array( - name="f", subname="x", type=data.kPartial, start=3, end=3, data=[4.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="f", subname="x", type=data.kPartial, start=3, end=3, data=[4.0] + ) ) mock_responses = [response1, response2] diff --git a/tests/test_explicit_server.py b/tests/test_explicit_server.py index 8ff60bf..0416ec0 100644 --- a/tests/test_explicit_server.py +++ b/tests/test_explicit_server.py @@ -56,15 +56,17 @@ def test_compute_function(self): context = Mock() request_iterator = [ - data.Array( - start=0, - end=2, - data=[0.5, 1.5, 3.5], - type=data.VariableType.kInput, - name="x", + data.VariableMessage( + continuous=data.Array( + start=0, end=2, data=[0.5, 1.5, 3.5], + type=data.VariableType.kInput, name="x", + ) ), - data.Array( - start=3, end=4, data=[4.5, 5.5], type=data.VariableType.kInput, name="x" + data.VariableMessage( + continuous=data.Array( + start=3, end=4, data=[4.5, 5.5], + type=data.VariableType.kInput, name="x", + ) ), ] @@ -81,8 +83,8 @@ def compute(inputs, outputs): # check that there is only one response self.assertEqual(len(responses), 1) - # check the function value - response = responses[0] + # check the function value (unwrap VariableMessage) + response = responses[0].continuous self.assertEqual(response.name, "f") self.assertEqual(response.start, 0) self.assertEqual(response.end, 1) @@ -102,15 +104,17 @@ def test_compute_gradient(self): context = Mock() request_iterator = [ - data.Array( - start=0, - end=2, - data=[0.5, 1.5, 3.5], - type=data.VariableType.kInput, - name="x", + data.VariableMessage( + continuous=data.Array( + start=0, end=2, data=[0.5, 1.5, 3.5], + type=data.VariableType.kInput, name="x", + ) ), - data.Array( - start=3, end=4, data=[4.5, 5.5], type=data.VariableType.kInput, name="x" + data.VariableMessage( + continuous=data.Array( + start=3, end=4, data=[4.5, 5.5], + type=data.VariableType.kInput, name="x", + ) ), ] @@ -124,18 +128,18 @@ def compute_partials(inputs, jac): response_generator = server.ComputeGradient(request_iterator, context) responses = list(response_generator) - # check that there is only one response + # check that there are two responses self.assertEqual(len(responses), 2) - # check the function value - response = responses[0] + # check the function value (unwrap VariableMessage) + response = responses[0].continuous self.assertEqual(response.name, "f") self.assertEqual(response.subname, "x") self.assertEqual(response.start, 0) self.assertEqual(response.end, 2) grad = np.array(response.data) - response = responses[1] + response = responses[1].continuous grad = np.append(grad, np.array(response.data)) self.assertTrue( np.array_equal(grad, np.array([-251.0, -499.0, 11105.0, 25007.0, -2950.0])) diff --git a/tests/test_implicit_client.py b/tests/test_implicit_client.py index 72e267f..3ec34a9 100644 --- a/tests/test_implicit_client.py +++ b/tests/test_implicit_client.py @@ -67,11 +67,15 @@ def test_compute_residuals(self, mock_implicit_stub): "g": np.array([7.0, 6.0, 5.0]), } - response1 = data.Array( - name="f", type=data.kResidual, start=0, end=2, data=[5.0, 6.0, 7.0] + response1 = data.VariableMessage( + continuous=data.Array( + name="f", type=data.kResidual, start=0, end=2, data=[5.0, 6.0, 7.0] + ) ) - response2 = data.Array( - name="g", type=data.kResidual, start=0, end=2, data=[8.0, 9.0, 10.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="g", type=data.kResidual, start=0, end=2, data=[8.0, 9.0, 10.0] + ) ) mock_responses = [response1, response2] @@ -114,11 +118,15 @@ def test_solve_residuals(self, mock_implicit_stub): "f": np.array([5.0, 6.0, 7.0]), } - response1 = data.Array( - name="f", type=data.kOutput, start=0, end=2, data=[5.0, 6.0, 7.0] + response1 = data.VariableMessage( + continuous=data.Array( + name="f", type=data.kOutput, start=0, end=2, data=[5.0, 6.0, 7.0] + ) ) - response2 = data.Array( - name="g", type=data.kOutput, start=0, end=2, data=[8.0, 9.0, 10.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="g", type=data.kOutput, start=0, end=2, data=[8.0, 9.0, 10.0] + ) ) mock_responses = [response1, response2] @@ -162,16 +170,20 @@ def test_residual_partials(self, mock_implicit_stub): "f": np.array([5.0, 6.0]), } - response1 = data.Array( - name="f", - subname="x", - type=data.kPartial, - start=0, - end=2, - data=[5.0, 6.0, 7.0], + response1 = data.VariableMessage( + continuous=data.Array( + name="f", + subname="x", + type=data.kPartial, + start=0, + end=2, + data=[5.0, 6.0, 7.0], + ) ) - response2 = data.Array( - name="f", subname="x", type=data.kPartial, start=3, end=3, data=[4.0] + response2 = data.VariableMessage( + continuous=data.Array( + name="f", subname="x", type=data.kPartial, start=3, end=3, data=[4.0] + ) ) mock_responses = [response1, response2] diff --git a/tests/test_implicit_server.py b/tests/test_implicit_server.py index 9a2e341..21e7c82 100644 --- a/tests/test_implicit_server.py +++ b/tests/test_implicit_server.py @@ -58,9 +58,9 @@ def test_compute_residuals(self): # mock request iterator mock_request_iterator = [ - data.Array(name="x", start=0, end=2, type=data.kInput, data=[1.0, 2.0]), - data.Array(name="y", start=0, end=2, type=data.kInput, data=[3.0, 4.0]), - data.Array(name="f", start=0, end=2, type=data.kOutput, data=[5.0, 6.0]), + data.VariableMessage(continuous=data.Array(name="x", start=0, end=2, type=data.kInput, data=[1.0, 2.0])), + data.VariableMessage(continuous=data.Array(name="y", start=0, end=2, type=data.kInput, data=[3.0, 4.0])), + data.VariableMessage(continuous=data.Array(name="f", start=0, end=2, type=data.kOutput, data=[5.0, 6.0])), ] # mock inputs, outputs, and residuals @@ -81,19 +81,17 @@ def compute_residuals(inputs, outputs, residuals): # assert that the expected residual messages were yielded expected_result = [ - data.Array( - name="f", - start=0, - end=1, - type=data.VariableType.kResidual, - data=[7.0], + data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=1, + type=data.VariableType.kResidual, data=[7.0], + ) ), - data.Array( - name="f", - start=1, - end=2, - type=data.VariableType.kResidual, - data=[8.0], + data.VariableMessage( + continuous=data.Array( + name="f", start=1, end=2, + type=data.VariableType.kResidual, data=[8.0], + ) ), ] self.assertEqual(result, expected_result) @@ -115,9 +113,9 @@ def test_solve_residuals(self): # mock request iterator mock_request_iterator = [ - data.Array(name="x", start=0, end=2, type=data.kInput, data=[1.0, 2.0]), - data.Array(name="y", start=0, end=2, type=data.kInput, data=[3.0, 4.0]), - data.Array(name="f", start=0, end=2, type=data.kOutput, data=[5.0, 6.0]), + data.VariableMessage(continuous=data.Array(name="x", start=0, end=2, type=data.kInput, data=[1.0, 2.0])), + data.VariableMessage(continuous=data.Array(name="y", start=0, end=2, type=data.kInput, data=[3.0, 4.0])), + data.VariableMessage(continuous=data.Array(name="f", start=0, end=2, type=data.kOutput, data=[5.0, 6.0])), ] # mock inputs, outputs, and residuals @@ -132,25 +130,23 @@ def solve_residuals(inputs, outputs): server._discipline.solve_residuals = solve_residuals - # call the ComputeResiduals method + # call the SolveResiduals method response_generator = server.SolveResiduals(mock_request_iterator, None) result = list(response_generator) - # assert that the expected residual messages were yielded + # assert that the expected output messages were yielded expected_result = [ - data.Array( - name="f", - start=0, - end=1, - type=data.VariableType.kOutput, - data=[7.0], + data.VariableMessage( + continuous=data.Array( + name="f", start=0, end=1, + type=data.VariableType.kOutput, data=[7.0], + ) ), - data.Array( - name="f", - start=1, - end=2, - type=data.VariableType.kOutput, - data=[8.0], + data.VariableMessage( + continuous=data.Array( + name="f", start=1, end=2, + type=data.VariableType.kOutput, data=[8.0], + ) ), ] self.assertEqual(result, expected_result) @@ -168,19 +164,17 @@ def test_residual_gradients(self): context = Mock() request_iterator = [ - data.Array( - start=0, - end=2, - data=[0.5, 1.5, 3.5], - type=data.VariableType.kInput, - name="x", + data.VariableMessage( + continuous=data.Array( + start=0, end=2, data=[0.5, 1.5, 3.5], + type=data.VariableType.kInput, name="x", + ) ), - data.Array( - start=3, - end=4, - data=[4.5, 5.5], - type=data.VariableType.kInput, - name="x", + data.VariableMessage( + continuous=data.Array( + start=3, end=4, data=[4.5, 5.5], + type=data.VariableType.kInput, name="x", + ) ), ] @@ -194,18 +188,18 @@ def residual_partials(inputs, residuals, jac): response_generator = server.ComputeResidualGradients(request_iterator, context) responses = list(response_generator) - # check that there is only one response + # check that there are two responses self.assertEqual(len(responses), 2) - # check the function value - response = responses[0] + # check the function value (unwrap VariableMessage) + response = responses[0].continuous self.assertEqual(response.name, "f") self.assertEqual(response.subname, "x") self.assertEqual(response.start, 0) self.assertEqual(response.end, 3) grad = np.array(response.data) - response = responses[1] + response = responses[1].continuous grad = np.append(grad, np.array(response.data)) self.assertTrue( np.array_equal(grad, np.array([-251.0, -499.0, 11105.0, 25007.0, -2950.0])) diff --git a/tests/test_openmdao_explicit_client.py b/tests/test_openmdao_explicit_client.py index bc02160..c8339e5 100644 --- a/tests/test_openmdao_explicit_client.py +++ b/tests/test_openmdao_explicit_client.py @@ -224,7 +224,9 @@ def test_compute(self, om_explicit_component_patch): instance.compute(inputs, outputs, discrete_inputs, discrete_outputs) # Asserting that the method calls are made correctly - client_mock.run_compute.assert_called_once_with({"input1": 10, "input2": 20}) + client_mock.run_compute.assert_called_once_with( + {"input1": 10, "input2": 20}, discrete_inputs=None + ) self.assertEqual(outputs["output1"], 30) self.assertEqual(outputs["output2"], 40) @@ -281,7 +283,7 @@ def test_compute_partials(self, om_explicit_component_patch): # Asserting that the method calls are made correctly client_mock.run_compute_partials.assert_called_once_with( - {"input1": 10, "input2": 20} + {"input1": 10, "input2": 20}, discrete_inputs=None ) self.assertEqual(partials["output1"]["input1"], 1) self.assertEqual(partials["output1"]["input2"], 2) diff --git a/tests/test_openmdao_implicit_client.py b/tests/test_openmdao_implicit_client.py index 96cfc4e..2895e7f 100644 --- a/tests/test_openmdao_implicit_client.py +++ b/tests/test_openmdao_implicit_client.py @@ -228,7 +228,9 @@ def test_apply_nonlinear(self, mock): ) # asserting that the method calls are made correctly - client_mock.run_compute_residuals.assert_called_once_with(inputs, outputs) + client_mock.run_compute_residuals.assert_called_once_with( + inputs, outputs, None, None + ) for res_name, expected_data in expected_residuals.items(): self.assertTrue(res_name in residuals) np.testing.assert_array_equal(residuals[res_name], expected_data) @@ -277,7 +279,7 @@ def test_solve_nonlinear(self, mock): comp.solve_nonlinear(inputs, outputs, discrete_inputs, discrete_outputs) # asserting that the method calls are made correctly - client_mock.run_solve_residuals.assert_called_once_with(inputs) + client_mock.run_solve_residuals.assert_called_once_with(inputs, None) for output_name, expected_data in expected_outputs.items(): self.assertTrue(output_name in outputs) np.testing.assert_array_equal(outputs[output_name], expected_data) @@ -343,7 +345,9 @@ def test_linearize(self, om_explicit_component_patch): comp.linearize(inputs, outputs, partials, discrete_inputs, discrete_outputs) # asserting that the method calls are made correctly - client_mock.run_residual_gradients.assert_called_once_with(inputs, outputs) + client_mock.run_residual_gradients.assert_called_once_with( + inputs, outputs, None, None + ) for jac_key, expected_data in expected_jac.items(): self.assertTrue(jac_key in partials) np.testing.assert_array_equal(partials[jac_key], expected_data) diff --git a/tests/test_openmdao_utils.py b/tests/test_openmdao_utils.py index 8011ffb..d928faa 100644 --- a/tests/test_openmdao_utils.py +++ b/tests/test_openmdao_utils.py @@ -55,6 +55,7 @@ def test_openmdao_client_setup(self): var2.shape = [1] comp._client._var_meta = [var1, var2] + comp._client._discrete_var_meta = [] utils.client_setup(comp) From a8cf437a8e0604ee9a4fff3a0305dee1a7712815 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Wed, 8 Apr 2026 21:45:21 -0400 Subject: [PATCH 10/14] Add support for struct (dict) discipline options (#60) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add support for struct (dict) discipline options (#49) Add kStruct DataType to the protocol and wire it through the Python stack so disciplines can declare and receive complex nested data as options. Changes: - Update proto submodule (kStruct = 4 in DataType enum) - Regenerate gRPC stubs - Map "dict" ↔ kStruct in server and client type mappings - Support dict type in OpenMDAO declare_options utility - Add tests for struct option round-trip - Update CHANGELOG * test: cover dict type in declare_options for 100% coverage * test: add gRPC integration test for struct option round-trip Adds a full end-to-end test that spins up a discipline with a dict option, discovers it via GetAvailableOptions, sends a nested dict, and verifies compute/partials use the struct values correctly. --- CHANGELOG.md | 3 + philote_mdo/general/discipline.py | 2 +- philote_mdo/general/discipline_client.py | 2 + philote_mdo/general/discipline_server.py | 2 + philote_mdo/generated/data_pb2.py | 8 +-- philote_mdo/generated/data_pb2.pyi | 2 + philote_mdo/openmdao/utils.py | 2 + proto | 2 +- tests/test_discipline_client.py | 45 ++++++++++++++ tests/test_discipline_server.py | 49 +++++++++++++++ tests/test_edge_cases.py | 20 +++++++ tests/test_integration.py | 76 ++++++++++++++++++++++++ tests/test_openmdao_utils.py | 14 +++-- 13 files changed, 215 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1885188..e160b23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- Added support for struct (dict) options via the new `kStruct` DataType enum + value, enabling complex nested data to be declared and passed as discipline + options (#49). - Added discrete variable support throughout the stack. Disciplines can now declare discrete inputs/outputs via `add_discrete_input` / `add_discrete_output`. Discrete data is serialized as diff --git a/philote_mdo/general/discipline.py b/philote_mdo/general/discipline.py index f5930cf..ae1f020 100644 --- a/philote_mdo/general/discipline.py +++ b/philote_mdo/general/discipline.py @@ -69,7 +69,7 @@ def add_option(self, name, type): the name of the option being added type : string the data type of the option. acceptable types are 'bool', 'int', - 'float' + 'float', 'str', 'dict' """ self.options_list[name] = type diff --git a/philote_mdo/general/discipline_client.py b/philote_mdo/general/discipline_client.py index 385d077..13b206f 100644 --- a/philote_mdo/general/discipline_client.py +++ b/philote_mdo/general/discipline_client.py @@ -97,6 +97,8 @@ def get_available_options(self): type_str = "float" if val == data.kString: type_str = "str" + if val == data.kStruct: + type_str = "dict" self.options_list[name] = type_str def send_options(self, options): diff --git a/philote_mdo/general/discipline_server.py b/philote_mdo/general/discipline_server.py index bde20a4..d784b18 100644 --- a/philote_mdo/general/discipline_server.py +++ b/philote_mdo/general/discipline_server.py @@ -100,6 +100,8 @@ def GetAvailableOptions(self, request, context): type = data.kDouble elif val == "str": type = data.kString + elif val == "dict": + type = data.kStruct else: raise ValueError( "Invalid value for discipline option '{}'".format(name) diff --git a/philote_mdo/generated/data_pb2.py b/philote_mdo/generated/data_pb2.py index 8334dc1..b6029b2 100644 --- a/philote_mdo/generated/data_pb2.py +++ b/philote_mdo/generated/data_pb2.py @@ -7,7 +7,7 @@ _runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 27, 2, '', 'data.proto') _sym_db = _symbol_database.Default() from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x07philote\x1a\x1cgoogle/protobuf/struct.proto"}\n\x14DisciplineProperties\x12\x12\n\ncontinuous\x18\x01 \x01(\x08\x12\x16\n\x0edifferentiable\x18\x02 \x01(\x08\x12\x1a\n\x12provides_gradients\x18\x03 \x01(\x08\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"#\n\rStreamOptions\x12\x12\n\nnum_double\x18\x01 \x01(\x03"?\n\x0bOptionsList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x1f\n\x04type\x18\x02 \x03(\x0e2\x11.philote.DataType"=\n\x11DisciplineOptions\x12(\n\x07options\x18\x01 \x01(\x0b2\x17.google.protobuf.Struct"c\n\x10VariableMetaData\x12#\n\x04type\x18\x01 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05shape\x18\x04 \x03(\x03\x12\r\n\x05units\x18\x05 \x01(\t"@\n\x10PartialsMetaData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x03"u\n\x05Array\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05start\x18\x03 \x01(\x03\x12\x0b\n\x03end\x18\x04 \x01(\x03\x12#\n\x04type\x18\x05 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04data\x18\x06 \x03(\x01"l\n\x10DiscreteVariable\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x04type\x18\x02 \x01(\x0e2\x15.philote.VariableType\x12%\n\x05value\x18\x03 \x01(\x0b2\x16.google.protobuf.Value"q\n\x0fVariableMessage\x12$\n\ncontinuous\x18\x01 \x01(\x0b2\x0e.philote.ArrayH\x00\x12-\n\x08discrete\x18\x02 \x01(\x0b2\x19.philote.DiscreteVariableH\x00B\t\n\x07payload*9\n\x08DataType\x12\t\n\x05kBool\x10\x00\x12\x08\n\x04kInt\x10\x01\x12\x0b\n\x07kDouble\x10\x02\x12\x0b\n\x07kString\x10\x03*m\n\x0cVariableType\x12\n\n\x06kInput\x10\x00\x12\x12\n\x0ekDiscreteInput\x10\x01\x12\r\n\tkResidual\x10\x02\x12\x0b\n\x07kOutput\x10\x03\x12\x13\n\x0fkDiscreteOutput\x10\x04\x12\x0c\n\x08kPartial\x10\x05B\x11\n\x0forg.philote.mdob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x07philote\x1a\x1cgoogle/protobuf/struct.proto"}\n\x14DisciplineProperties\x12\x12\n\ncontinuous\x18\x01 \x01(\x08\x12\x16\n\x0edifferentiable\x18\x02 \x01(\x08\x12\x1a\n\x12provides_gradients\x18\x03 \x01(\x08\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"#\n\rStreamOptions\x12\x12\n\nnum_double\x18\x01 \x01(\x03"?\n\x0bOptionsList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x1f\n\x04type\x18\x02 \x03(\x0e2\x11.philote.DataType"=\n\x11DisciplineOptions\x12(\n\x07options\x18\x01 \x01(\x0b2\x17.google.protobuf.Struct"c\n\x10VariableMetaData\x12#\n\x04type\x18\x01 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05shape\x18\x04 \x03(\x03\x12\r\n\x05units\x18\x05 \x01(\t"@\n\x10PartialsMetaData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x03"u\n\x05Array\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05start\x18\x03 \x01(\x03\x12\x0b\n\x03end\x18\x04 \x01(\x03\x12#\n\x04type\x18\x05 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04data\x18\x06 \x03(\x01"l\n\x10DiscreteVariable\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x04type\x18\x02 \x01(\x0e2\x15.philote.VariableType\x12%\n\x05value\x18\x03 \x01(\x0b2\x16.google.protobuf.Value"q\n\x0fVariableMessage\x12$\n\ncontinuous\x18\x01 \x01(\x0b2\x0e.philote.ArrayH\x00\x12-\n\x08discrete\x18\x02 \x01(\x0b2\x19.philote.DiscreteVariableH\x00B\t\n\x07payload*F\n\x08DataType\x12\t\n\x05kBool\x10\x00\x12\x08\n\x04kInt\x10\x01\x12\x0b\n\x07kDouble\x10\x02\x12\x0b\n\x07kString\x10\x03\x12\x0b\n\x07kStruct\x10\x04*m\n\x0cVariableType\x12\n\n\x06kInput\x10\x00\x12\x12\n\x0ekDiscreteInput\x10\x01\x12\r\n\tkResidual\x10\x02\x12\x0b\n\x07kOutput\x10\x03\x12\x13\n\x0fkDiscreteOutput\x10\x04\x12\x0c\n\x08kPartial\x10\x05B\x11\n\x0forg.philote.mdob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'data_pb2', _globals) @@ -15,9 +15,9 @@ _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\x0forg.philote.mdo' _globals['_DATATYPE']._serialized_start = 856 - _globals['_DATATYPE']._serialized_end = 913 - _globals['_VARIABLETYPE']._serialized_start = 915 - _globals['_VARIABLETYPE']._serialized_end = 1024 + _globals['_DATATYPE']._serialized_end = 926 + _globals['_VARIABLETYPE']._serialized_start = 928 + _globals['_VARIABLETYPE']._serialized_end = 1037 _globals['_DISCIPLINEPROPERTIES']._serialized_start = 53 _globals['_DISCIPLINEPROPERTIES']._serialized_end = 178 _globals['_STREAMOPTIONS']._serialized_start = 180 diff --git a/philote_mdo/generated/data_pb2.pyi b/philote_mdo/generated/data_pb2.pyi index a699396..13c800f 100644 --- a/philote_mdo/generated/data_pb2.pyi +++ b/philote_mdo/generated/data_pb2.pyi @@ -12,6 +12,7 @@ class DataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): kInt: _ClassVar[DataType] kDouble: _ClassVar[DataType] kString: _ClassVar[DataType] + kStruct: _ClassVar[DataType] class VariableType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -25,6 +26,7 @@ kBool: DataType kInt: DataType kDouble: DataType kString: DataType +kStruct: DataType kInput: VariableType kDiscreteInput: VariableType kResidual: VariableType diff --git a/philote_mdo/openmdao/utils.py b/philote_mdo/openmdao/utils.py index c11f881..3b95870 100644 --- a/philote_mdo/openmdao/utils.py +++ b/philote_mdo/openmdao/utils.py @@ -44,6 +44,8 @@ def declare_options(opt_list, options): opt_type = float elif type_str == "str": opt_type = str + elif type_str == "dict": + opt_type = dict options.declare(name, types=opt_type) diff --git a/proto b/proto index a5a21b8..300d686 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit a5a21b8c151c34f9f7b6f8fb717da76bb7e18011 +Subproject commit 300d6865d4d5b394853d06604b73c4f8e3a0c4a1 diff --git a/tests/test_discipline_client.py b/tests/test_discipline_client.py index 1e24d9b..d7be559 100644 --- a/tests/test_discipline_client.py +++ b/tests/test_discipline_client.py @@ -130,6 +130,51 @@ def test_get_available_options(self, mock_discipline_stub): } self.assertEqual(instance.options_list, expected_options_list) + @patch("philote_mdo.generated.disciplines_pb2_grpc.DisciplineServiceStub") + def test_get_available_options_with_dict_type(self, mock_discipline_stub): + """ + Tests that get_available_options correctly maps kStruct to 'dict'. + """ + mock_channel = Mock() + mock_stub = mock_discipline_stub.return_value + + instance = DisciplineClient(channel=mock_channel) + + mock_options = MagicMock() + mock_options.options = ["config", "flag"] + mock_options.type = [data.kStruct, data.kBool] + instance._disc_stub.GetAvailableOptions.return_value = mock_options + + instance.get_available_options() + + expected_options_list = { + "config": "dict", + "flag": "bool", + } + self.assertEqual(instance.options_list, expected_options_list) + + def test_send_options_with_nested_dict(self): + """ + Tests that send_options correctly serializes nested dict values via Struct. + """ + mock_stub = Mock() + mock_channel = Mock() + mock_channel.stub.return_value = mock_stub + + client = DisciplineClient(channel=mock_channel) + client._disc_stub = mock_stub + + options = { + "config": {"solver": "newton", "tol": 1e-6}, + "name": "test", + } + + client.send_options(options) + + expected_proto_options = data.DisciplineOptions() + expected_proto_options.options.update(options) + mock_stub.SetOptions.assert_called_once_with(expected_proto_options) + def test_send_options(self): # mock gRPC stub and channel mock_stub = Mock() diff --git a/tests/test_discipline_server.py b/tests/test_discipline_server.py index f9b2550..2f30542 100644 --- a/tests/test_discipline_server.py +++ b/tests/test_discipline_server.py @@ -362,6 +362,55 @@ def test_process_inputs(self): self.assertEqual(flat_inputs["x"].tolist(), [1.0, 2.0, 3.0, 4.0, 5.0, 0.0]) self.assertEqual(flat_outputs["f"].tolist(), [0.1, 0.2, 0.0]) + def test_get_available_options_with_dict_type(self): + """ + Tests that GetAvailableOptions correctly maps dict options to kStruct. + """ + server = DisciplineServer() + + request_mock = Mock() + context_mock = None + + server._discipline = Discipline() + server._discipline.options_list = { + "config": "dict", + "flag": "bool", + } + + results = server.GetAvailableOptions(request_mock, context_mock) + + expected_options = ["config", "flag"] + expected_types = [data.kStruct, data.kBool] + + self.assertEqual(results.options, expected_options) + self.assertEqual(results.type, expected_types) + + def test_set_options_with_nested_dict(self): + """ + Tests that SetOptions correctly passes nested dict values through. + """ + server = DisciplineServer() + + request_mock = Mock() + context_mock = Mock() + + request_mock.options = { + "config": {"solver": "newton", "tol": 1e-6, "nested": {"a": 1}}, + "name": "test", + } + + discipline_mock = Mock() + server._discipline = discipline_mock + + server.SetOptions(request_mock, context_mock) + + server._discipline.set_options.assert_called_once_with( + { + "config": {"solver": "newton", "tol": 1e-6, "nested": {"a": 1}}, + "name": "test", + } + ) + def test_get_available_options_invalid_type_raises_error(self): """ Tests that GetAvailableOptions raises ValueError for invalid option types. diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index 72b5e40..0afc37b 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -73,6 +73,26 @@ def test_get_available_options_with_str_type(self): # The method should complete without error and return options self.assertIsNotNone(result) + def test_get_available_options_with_dict_type(self): + """ + Test GetAvailableOptions with dict option type (covers kStruct mapping). + """ + server = DisciplineServer() + discipline = Mock() + + discipline.options_list = {"config": "dict"} + + server.attach_discipline(discipline) + + request = Mock() + context = Mock() + + result = server.GetAvailableOptions(request, context) + + self.assertIsNotNone(result) + self.assertEqual(list(result.options), ["config"]) + self.assertEqual(list(result.type), [data.kStruct]) + def test_get_available_options_with_invalid_type(self): """ Test GetAvailableOptions with invalid option type (covers lines 100-103). diff --git a/tests/test_integration.py b/tests/test_integration.py index 4229d46..12be39f 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -222,5 +222,81 @@ def test_quadratic_residual_gradients(self): server.stop(0) +class StructOptionDiscipline(pmdo.ExplicitDiscipline): + """ + Minimal discipline that declares a dict option and uses it in compute. + """ + + def initialize(self): + self.add_option("config", "dict") + + def set_options(self, options): + self.config = dict(options["config"]) + + def setup(self): + self.add_input("x", shape=(1,), units="") + self.add_output("f", shape=(1,), units="") + + def setup_partials(self): + self.declare_partials("f", "x") + + def compute(self, inputs, outputs): + scale = self.config.get("scale", 1.0) + offset = self.config.get("offset", 0.0) + outputs["f"] = scale * inputs["x"] + offset + + def compute_partials(self, inputs, partials): + scale = self.config.get("scale", 1.0) + partials["f", "x"] = np.array([scale]) + + +class StructOptionIntegrationTests(unittest.TestCase): + """ + Integration tests for struct (dict) options round-trip over gRPC. + """ + + def test_struct_option_round_trip(self): + """ + Tests that a discipline with a dict option can be discovered, + set with a nested dict, and used for compute over gRPC. + """ + # server + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + discipline = pmdo.ExplicitServer(discipline=StructOptionDiscipline()) + discipline.attach_to_server(server) + server.add_insecure_port("[::]:50051") + server.start() + + try: + # client + client = pmdo.ExplicitClient( + channel=grpc.insecure_channel("localhost:50051") + ) + + # discover options and verify dict type + client.get_available_options() + self.assertEqual(client.options_list["config"], "dict") + + # send nested dict option + client.send_options({"config": {"scale": 3.0, "offset": 5.0}}) + + # standard setup + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + client.get_partials_definitions() + + # compute: f = 3.0 * 2.0 + 5.0 = 11.0 + inputs = {"x": np.array([2.0])} + outputs = client.run_compute(inputs) + self.assertAlmostEqual(outputs["f"][0], 11.0) + + # partials: df/dx = 3.0 + jac = client.run_compute_partials(inputs) + self.assertAlmostEqual(jac["f", "x"][0], 3.0) + finally: + server.stop(0) + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_openmdao_utils.py b/tests/test_openmdao_utils.py index d928faa..95a5acd 100644 --- a/tests/test_openmdao_utils.py +++ b/tests/test_openmdao_utils.py @@ -188,21 +188,23 @@ def test_declare_options(self): ("max_iter", "int"), ("tolerance", "float"), ("method", "str"), - ("verbose", "bool") + ("verbose", "bool"), + ("config", "dict"), ] declare_options(opt_list, options_mock) - + expected_calls = [ ("max_iter", int), ("tolerance", float), ("method", str), - ("verbose", bool) + ("verbose", bool), + ("config", dict), ] - + for name, opt_type in expected_calls: options_mock.declare.assert_any_call(name, types=opt_type) - - self.assertEqual(options_mock.declare.call_count, 4) + + self.assertEqual(options_mock.declare.call_count, 5) # Test case 3: Unknown type (should result in None) options_mock.reset_mock() From ebb03fff82ab68b135216971f35f45c26f55940c Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Wed, 8 Apr 2026 22:34:07 -0400 Subject: [PATCH 11/14] Add comprehensive input validation and error handling (#61) * Add comprehensive input validation and error handling (#46) Introduce custom exception classes (PhiloteValidationError, PhiloteServerError), parameter validation in discipline base classes, gRPC error propagation via context.abort() in all server RPC methods, and client-side input validation with gRPC error wrapping. Fix missing space in RemoteImplicitComponent error message. * Add tests to restore 100% coverage Cover all previously uncovered error handling paths: PhiloteValidationError and general Exception branches in server RPC methods, gRPC error wrapping in all client methods, and num_par_fd validation in OpenMDAO components. --- CHANGELOG.md | 9 + philote_mdo/general/discipline.py | 45 +++++ philote_mdo/general/discipline_client.py | 20 +- philote_mdo/general/discipline_server.py | 85 ++++++--- philote_mdo/general/explicit_client.py | 35 ++-- philote_mdo/general/explicit_server.py | 130 +++++++------ philote_mdo/general/implicit_client.py | 77 +++++--- philote_mdo/general/implicit_server.py | 228 +++++++++++++---------- philote_mdo/openmdao/explicit.py | 4 + philote_mdo/openmdao/group.py | 39 ++++ philote_mdo/openmdao/implicit.py | 6 +- philote_mdo/openmdao/utils.py | 27 +-- philote_mdo/utils/__init__.py | 11 ++ philote_mdo/utils/helper.py | 18 ++ philote_mdo/utils/pair_dict.py | 12 ++ philote_mdo/utils/validation.py | 210 +++++++++++++++++++++ tests/test_discipline.py | 95 ++++++++++ tests/test_discipline_client.py | 19 ++ tests/test_discipline_server.py | 138 ++++++++++++-- tests/test_edge_cases.py | 27 +-- tests/test_explicit_client.py | 52 ++++++ tests/test_explicit_server.py | 134 +++++++++++++ tests/test_implicit_client.py | 91 +++++++++ tests/test_implicit_server.py | 222 ++++++++++++++++++++++ tests/test_openmdao_explicit_client.py | 9 + tests/test_openmdao_group.py | 41 ++++ tests/test_openmdao_implicit_client.py | 9 + tests/test_openmdao_utils.py | 7 +- tests/test_utils.py | 36 +++- tests/test_validation.py | 184 ++++++++++++++++++ 30 files changed, 1748 insertions(+), 272 deletions(-) create mode 100644 philote_mdo/utils/validation.py create mode 100644 tests/test_validation.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e160b23..0426c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,10 +20,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 the new `VariableMessage` wrapper. The OpenMDAO bindings (`RemoteExplicitComponent`, `RemoteImplicitComponent`) automatically discover and forward discrete variables. +- Added comprehensive input validation and error handling across the + framework. Introduces custom exception classes (`PhiloteValidationError`, + `PhiloteServerError`), parameter validation in discipline base classes + (`add_input`, `add_output`, `add_option`, `declare_partials`), proper + gRPC error propagation via `context.abort()` with appropriate status + codes in all server RPC methods, and client-side input validation with + gRPC error wrapping (#46). ### Bug Fixes - Fixed bare `except` to `except ImportError` in `examples/__init__.py`. +- Fixed missing space in `RemoteImplicitComponent` error message + ("will notbe" -> "will not be"). - Fixed `SellarMDA` promoted-input ambiguity that newer OpenMDAO releases reject during `final_setup`. The `x` and `z` defaults were being set on the inner `cycle` subgroup, but `obj_cmp` promoted the same variables diff --git a/philote_mdo/general/discipline.py b/philote_mdo/general/discipline.py index ae1f020..ed8414d 100644 --- a/philote_mdo/general/discipline.py +++ b/philote_mdo/general/discipline.py @@ -28,6 +28,13 @@ # therein. The DoD does not exercise any editorial, security, or other # control over the information you may find at these locations. import philote_mdo.generated.data_pb2 as data +from philote_mdo.utils.validation import ( + validate_name, + validate_shape, + validate_units, + validate_option_type, + PhiloteValidationError, +) class Discipline: @@ -71,6 +78,12 @@ def add_option(self, name, type): the data type of the option. acceptable types are 'bool', 'int', 'float', 'str', 'dict' """ + validate_name(name, "add_option") + validate_option_type(type, name) + if name in self.options_list: + raise PhiloteValidationError( + f"add_option: option '{name}' is already defined." + ) self.options_list[name] = type def add_input(self, name, shape=(1,), units=""): @@ -86,6 +99,13 @@ def add_input(self, name, shape=(1,), units=""): units : string the unit definition for the input variable """ + validate_name(name, "add_input") + validate_shape(shape, "add_input") + validate_units(units, "add_input") + if any(v.name == name and v.type == data.VariableType.kInput for v in self._var_meta): + raise PhiloteValidationError( + f"add_input: input '{name}' is already defined." + ) meta = data.VariableMetaData() meta.type = data.VariableType.kInput meta.name = name @@ -107,6 +127,14 @@ def add_discrete_input(self, name, default=None): default : object, optional the default value for the discrete input """ + validate_name(name, "add_discrete_input") + if any( + v.name == name and v.type == data.VariableType.kDiscreteInput + for v in self._discrete_var_meta + ): + raise PhiloteValidationError( + f"add_discrete_input: discrete input '{name}' is already defined." + ) meta = data.VariableMetaData() meta.type = data.VariableType.kDiscreteInput meta.name = name @@ -126,6 +154,14 @@ def add_discrete_output(self, name, default=None): default : object, optional the default value for the discrete output """ + validate_name(name, "add_discrete_output") + if any( + v.name == name and v.type == data.VariableType.kDiscreteOutput + for v in self._discrete_var_meta + ): + raise PhiloteValidationError( + f"add_discrete_output: discrete output '{name}' is already defined." + ) meta = data.VariableMetaData() meta.type = data.VariableType.kDiscreteOutput meta.name = name @@ -144,6 +180,13 @@ def add_output(self, name, shape=(1,), units=""): units : string the unit definition for the output variable """ + validate_name(name, "add_output") + validate_shape(shape, "add_output") + validate_units(units, "add_output") + if any(v.name == name and v.type == data.VariableType.kOutput for v in self._var_meta): + raise PhiloteValidationError( + f"add_output: output '{name}' is already defined." + ) out_meta = data.VariableMetaData() out_meta.type = data.VariableType.kOutput out_meta.name = name @@ -164,6 +207,8 @@ def declare_partials(self, func, var): """ Defines partials that will be determined using the analysis server. """ + validate_name(func, "declare_partials (func)") + validate_name(var, "declare_partials (var)") self._partials_meta += [data.PartialsMetaData(name=func, subname=var)] def initialize(self): diff --git a/philote_mdo/general/discipline_client.py b/philote_mdo/general/discipline_client.py index 13b206f..dfbed15 100644 --- a/philote_mdo/general/discipline_client.py +++ b/philote_mdo/general/discipline_client.py @@ -33,6 +33,11 @@ import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.utils as utils from philote_mdo.general.discipline_server import _python_to_value, _value_to_python +from philote_mdo.utils.validation import ( + PhiloteValidationError, + validate_is_dict, + validate_numpy_array, +) class DisciplineClient: @@ -114,6 +119,7 @@ def send_options(self, options): ------- None """ + validate_is_dict(options, "send_options") proto_options = data.DisciplineOptions() proto_options.options.update(options) self._disc_stub.SetOptions(proto_options) @@ -158,6 +164,14 @@ def _assemble_input_messages( Both continuous and discrete inputs are wrapped in ``VariableMessage`` envelopes. """ + validate_is_dict(inputs, "_assemble_input_messages (inputs)") + for input_name, value in inputs.items(): + validate_numpy_array(value, input_name) + if outputs is not None: + validate_is_dict(outputs, "_assemble_input_messages (outputs)") + for output_name, value in outputs.items(): + validate_numpy_array(value, output_name) + messages = [] # Continuous inputs @@ -251,7 +265,7 @@ def _recover_outputs(self, responses): if len(arr.data) > 0: flat_outputs[arr.name][b:e] = arr.data else: - raise ValueError( + raise PhiloteValidationError( "Expected continuous variables, but array is empty." ) @@ -289,7 +303,7 @@ def _recover_residuals(self, responses): if len(arr.data) > 0: flat_residuals[arr.name][b:e] = arr.data else: - raise ValueError( + raise PhiloteValidationError( "Expected continuous variables, but array is empty." ) @@ -336,7 +350,7 @@ def _recover_partials(self, responses): if len(arr.data) > 0: flat_p[(arr.name, arr.subname)][b:e] = arr.data else: - raise ValueError( + raise PhiloteValidationError( "Expected continuous outputs for the " "partials, but array was empty." ) diff --git a/philote_mdo/general/discipline_server.py b/philote_mdo/general/discipline_server.py index d784b18..a7baee6 100644 --- a/philote_mdo/general/discipline_server.py +++ b/philote_mdo/general/discipline_server.py @@ -27,6 +27,7 @@ # the linked websites, of the information, products, or services contained # therein. The DoD does not exercise any editorial, security, or other # control over the information you may find at these locations. +import grpc import numpy as np import philote_mdo.generated.data_pb2 as data @@ -34,6 +35,7 @@ from google.protobuf.empty_pb2 import Empty from google.protobuf import struct_pb2 from philote_mdo.utils import PairDict, get_flattened_view +from philote_mdo.utils.validation import PhiloteValidationError class DisciplineServer(disc.DisciplineService): @@ -85,48 +87,69 @@ def GetAvailableOptions(self, request, context): """ RPC that gets the names and types of all available discipline options. """ - opts_dict = self._discipline.options_list - opts = data.OptionsList() - - for name, val in opts_dict.items(): - opts.options.append(name) - - # assign the correct data type - if val == "bool": - type = data.kBool - elif val == "int": - type = data.kInt - elif val == "float": - type = data.kDouble - elif val == "str": - type = data.kString - elif val == "dict": - type = data.kStruct - else: - raise ValueError( - "Invalid value for discipline option '{}'".format(name) - ) + try: + opts_dict = self._discipline.options_list + opts = data.OptionsList() + + for name, val in opts_dict.items(): + opts.options.append(name) + + # assign the correct data type + if val == "bool": + type = data.kBool + elif val == "int": + type = data.kInt + elif val == "float": + type = data.kDouble + elif val == "str": + type = data.kString + elif val == "dict": + type = data.kStruct + else: + raise PhiloteValidationError( + "Invalid value for discipline option '{}'".format(name) + ) - opts.type.append(type) + opts.type.append(type) - return opts + return opts + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"GetAvailableOptions failed: {e}" + ) def SetOptions(self, request, context): """ RPC that sets the discipline options. """ - options = request.options - self._discipline.set_options(options) - return Empty() + try: + options = request.options + self._discipline.set_options(options) + return Empty() + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"SetOptions failed: {e}" + ) def Setup(self, request, context): """ RPC that runs the setup function """ - self._discipline._clear_data() - self._discipline.setup() - self._discipline.setup_partials() - return Empty() + try: + self._discipline._clear_data() + self._discipline.setup() + self._discipline.setup_partials() + return Empty() + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"Setup failed: {e}" + ) def GetVariableDefinitions(self, request, context): """ @@ -237,7 +260,7 @@ def process_inputs( elif arr.type == data.VariableType.kOutput: flat_outputs[arr.name][b : e + 1] = arr.data else: - raise ValueError( + raise PhiloteValidationError( "Expected continuous variables but arrays were" " empty for variable %s." % (arr.name) ) diff --git a/philote_mdo/general/explicit_client.py b/philote_mdo/general/explicit_client.py index 5b278cb..b42f8e7 100644 --- a/philote_mdo/general/explicit_client.py +++ b/philote_mdo/general/explicit_client.py @@ -29,6 +29,7 @@ # control over the information you may find at these locations. import grpc from philote_mdo.general.discipline_client import DisciplineClient +from philote_mdo.utils.validation import PhiloteServerError, validate_is_dict import philote_mdo.generated.disciplines_pb2_grpc as disc @@ -59,11 +60,17 @@ def run_compute(self, inputs, discrete_inputs=None): Continuous outputs, or (continuous outputs, discrete outputs) when the server returns discrete output data. """ - messages = self._assemble_input_messages( - inputs, discrete_inputs=discrete_inputs - ) - responses = self._expl_stub.ComputeFunction(iter(messages)) - return self._recover_outputs(responses) + validate_is_dict(inputs, "run_compute (inputs)") + try: + messages = self._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) + responses = self._expl_stub.ComputeFunction(iter(messages)) + return self._recover_outputs(responses) + except grpc.RpcError as e: + raise PhiloteServerError( + f"Server error during run_compute: {e.details()}" + ) from e def run_compute_partials(self, inputs, discrete_inputs=None): """ @@ -77,10 +84,16 @@ def run_compute_partials(self, inputs, discrete_inputs=None): discrete_inputs : dict, optional Discrete input values. """ - messages = self._assemble_input_messages( - inputs, discrete_inputs=discrete_inputs - ) - responses = self._expl_stub.ComputeGradient(iter(messages)) - partials = self._recover_partials(responses) + validate_is_dict(inputs, "run_compute_partials (inputs)") + try: + messages = self._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) + responses = self._expl_stub.ComputeGradient(iter(messages)) + partials = self._recover_partials(responses) - return partials + return partials + except grpc.RpcError as e: + raise PhiloteServerError( + f"Server error during run_compute_partials: {e.details()}" + ) from e diff --git a/philote_mdo/general/explicit_server.py b/philote_mdo/general/explicit_server.py index 049e0a2..e8b8fff 100644 --- a/philote_mdo/general/explicit_server.py +++ b/philote_mdo/general/explicit_server.py @@ -27,6 +27,7 @@ # the linked websites, of the information, products, or services contained # therein. The DoD does not exercise any editorial, security, or other # control over the information you may find at these locations. +import grpc import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.generated.data_pb2 as data from philote_mdo.general.discipline_server import ( @@ -34,6 +35,7 @@ _python_to_value, ) from philote_mdo.utils import get_chunk_indices +from philote_mdo.utils.validation import PhiloteValidationError class ExplicitServer(DisciplineServer, disc.ExplicitServiceServicer): @@ -55,76 +57,90 @@ def ComputeFunction(self, request_iterator, context): """ Computes the function evaluation and sends the result to the client. """ - inputs = {} - flat_inputs = {} - outputs = {} - discrete_inputs = {} - discrete_outputs = {} + try: + inputs = {} + flat_inputs = {} + outputs = {} + discrete_inputs = {} + discrete_outputs = {} - self.preallocate_inputs(inputs, flat_inputs) - discrete_inputs, _ = self.process_inputs( - request_iterator, flat_inputs, discrete_inputs=discrete_inputs - ) - - # Call compute with discrete data when discrete variables are present - if discrete_inputs or self._discipline._discrete_var_meta: - self._discipline.compute( - inputs, outputs, discrete_inputs, discrete_outputs + self.preallocate_inputs(inputs, flat_inputs) + discrete_inputs, _ = self.process_inputs( + request_iterator, flat_inputs, discrete_inputs=discrete_inputs ) - else: - self._discipline.compute(inputs, outputs) - # Stream continuous outputs - for output_name, value in outputs.items(): - for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.VariableMessage( - continuous=data.Array( - name=output_name, - type=data.kOutput, - start=b, - end=e - 1, - data=value.ravel()[b:e], - ) + # Call compute with discrete data when discrete variables are present + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.compute( + inputs, outputs, discrete_inputs, discrete_outputs ) + else: + self._discipline.compute(inputs, outputs) - # Stream discrete outputs - for name, value in discrete_outputs.items(): - yield data.VariableMessage( - discrete=data.DiscreteVariable( - name=name, - type=data.VariableType.kDiscreteOutput, - value=_python_to_value(value), + # Stream continuous outputs + for output_name, value in outputs.items(): + for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): + yield data.VariableMessage( + continuous=data.Array( + name=output_name, + type=data.kOutput, + start=b, + end=e - 1, + data=value.ravel()[b:e], + ) + ) + + # Stream discrete outputs + for name, value in discrete_outputs.items(): + yield data.VariableMessage( + discrete=data.DiscreteVariable( + name=name, + type=data.VariableType.kDiscreteOutput, + value=_python_to_value(value), + ) ) + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"ComputeFunction failed: {e}" ) def ComputeGradient(self, request_iterator, context): """ Computes the gradient evaluation and sends the result to the client. """ - inputs = {} - flat_inputs = {} - discrete_inputs = {} + try: + inputs = {} + flat_inputs = {} + discrete_inputs = {} - self.preallocate_inputs(inputs, flat_inputs) - jac = self.preallocate_partials() - discrete_inputs, _ = self.process_inputs( - request_iterator, flat_inputs, discrete_inputs=discrete_inputs - ) + self.preallocate_inputs(inputs, flat_inputs) + jac = self.preallocate_partials() + discrete_inputs, _ = self.process_inputs( + request_iterator, flat_inputs, discrete_inputs=discrete_inputs + ) - if discrete_inputs or self._discipline._discrete_var_meta: - self._discipline.compute_partials(inputs, jac, discrete_inputs) - else: - self._discipline.compute_partials(inputs, jac) + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.compute_partials(inputs, jac, discrete_inputs) + else: + self._discipline.compute_partials(inputs, jac) - for jac, value in jac.items(): - for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.VariableMessage( - continuous=data.Array( - name=jac[0], - subname=jac[1], - type=data.kPartial, - start=b, - end=e - 1, - data=value.ravel()[b:e], + for jac, value in jac.items(): + for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): + yield data.VariableMessage( + continuous=data.Array( + name=jac[0], + subname=jac[1], + type=data.kPartial, + start=b, + end=e - 1, + data=value.ravel()[b:e], + ) ) - ) + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"ComputeGradient failed: {e}" + ) diff --git a/philote_mdo/general/implicit_client.py b/philote_mdo/general/implicit_client.py index 81fb6f3..9ef8156 100644 --- a/philote_mdo/general/implicit_client.py +++ b/philote_mdo/general/implicit_client.py @@ -29,6 +29,7 @@ # control over the information you may find at these locations. import grpc from philote_mdo.general.discipline_client import DisciplineClient +from philote_mdo.utils.validation import PhiloteServerError, validate_is_dict import philote_mdo.generated.data_pb2 as data import philote_mdo.generated.disciplines_pb2_grpc as disc @@ -168,17 +169,24 @@ def run_compute_residuals( - Large arrays are automatically streamed for efficiency - This is typically used for residual evaluation during Newton iterations """ - # Assemble input messages and call server - messages = self._assemble_input_messages( - inputs, - outputs, - discrete_inputs=discrete_inputs, - discrete_outputs=discrete_outputs, - ) - responses = self._impl_stub.ComputeResiduals(iter(messages)) - residuals = self._recover_residuals(responses) - - return residuals + validate_is_dict(inputs, "run_compute_residuals (inputs)") + validate_is_dict(outputs, "run_compute_residuals (outputs)") + try: + # Assemble input messages and call server + messages = self._assemble_input_messages( + inputs, + outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) + responses = self._impl_stub.ComputeResiduals(iter(messages)) + residuals = self._recover_residuals(responses) + + return residuals + except grpc.RpcError as e: + raise PhiloteServerError( + f"Server error during run_compute_residuals: {e.details()}" + ) from e def run_solve_residuals(self, inputs, discrete_inputs=None): """ @@ -231,13 +239,19 @@ def run_solve_residuals(self, inputs, discrete_inputs=None): - May raise exceptions for ill-conditioned or non-convergent problems - Solution quality depends on the server's implementation and input conditioning """ - # Assemble input messages and call server - messages = self._assemble_input_messages( - inputs, discrete_inputs=discrete_inputs - ) - responses = self._impl_stub.SolveResiduals(iter(messages)) - outputs = self._recover_outputs(responses) - return outputs + validate_is_dict(inputs, "run_solve_residuals (inputs)") + try: + # Assemble input messages and call server + messages = self._assemble_input_messages( + inputs, discrete_inputs=discrete_inputs + ) + responses = self._impl_stub.SolveResiduals(iter(messages)) + outputs = self._recover_outputs(responses) + return outputs + except grpc.RpcError as e: + raise PhiloteServerError( + f"Server error during run_solve_residuals: {e.details()}" + ) from e def run_residual_gradients( self, inputs, outputs, discrete_inputs=None, discrete_outputs=None @@ -299,13 +313,20 @@ def run_residual_gradients( - Used by optimization algorithms and sensitivity analysis tools - For large problems, consider matrix-free methods if available """ - # Assemble input messages and call server - messages = self._assemble_input_messages( - inputs, - outputs, - discrete_inputs=discrete_inputs, - discrete_outputs=discrete_outputs, - ) - responses = self._impl_stub.ComputeResidualGradients(iter(messages)) - partials = self._recover_partials(responses) - return partials + validate_is_dict(inputs, "run_residual_gradients (inputs)") + validate_is_dict(outputs, "run_residual_gradients (outputs)") + try: + # Assemble input messages and call server + messages = self._assemble_input_messages( + inputs, + outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, + ) + responses = self._impl_stub.ComputeResidualGradients(iter(messages)) + partials = self._recover_partials(responses) + return partials + except grpc.RpcError as e: + raise PhiloteServerError( + f"Server error during run_residual_gradients: {e.details()}" + ) from e diff --git a/philote_mdo/general/implicit_server.py b/philote_mdo/general/implicit_server.py index 973cfc8..ac7c1f7 100644 --- a/philote_mdo/general/implicit_server.py +++ b/philote_mdo/general/implicit_server.py @@ -27,11 +27,13 @@ # the linked websites, of the information, products, or services contained # therein. The DoD does not exercise any editorial, security, or other # control over the information you may find at these locations. +import grpc import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.generated.data_pb2 as data import philote_mdo.general as pmdo from philote_mdo.general.discipline_server import _python_to_value from philote_mdo.utils import get_chunk_indices +from philote_mdo.utils.validation import PhiloteValidationError class ImplicitServer(pmdo.DisciplineServer, disc.ImplicitServiceServicer): @@ -154,43 +156,50 @@ def ComputeResiduals(self, request_iterator, context): - Streams results back in chunks for efficiency - This method is called automatically by the gRPC framework """ - # inputs and outputs - inputs = {} - flat_inputs = {} - outputs = {} - flat_outputs = {} - residuals = {} - discrete_inputs = {} - discrete_outputs = {} - - self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) - discrete_inputs, discrete_outputs = self.process_inputs( - request_iterator, - flat_inputs, - flat_outputs, - discrete_inputs=discrete_inputs, - discrete_outputs=discrete_outputs, - ) - - # Call the user-defined compute_residuals function - if discrete_inputs or self._discipline._discrete_var_meta: - self._discipline.compute_residuals( - inputs, outputs, residuals, discrete_inputs, discrete_outputs + try: + # inputs and outputs + inputs = {} + flat_inputs = {} + outputs = {} + flat_outputs = {} + residuals = {} + discrete_inputs = {} + discrete_outputs = {} + + self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) + discrete_inputs, discrete_outputs = self.process_inputs( + request_iterator, + flat_inputs, + flat_outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, ) - else: - self._discipline.compute_residuals(inputs, outputs, residuals) - - for res_name, value in residuals.items(): - for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.VariableMessage( - continuous=data.Array( - name=res_name, - start=b, - end=e, - type=data.kResidual, - data=value.ravel()[b:e], - ) + + # Call the user-defined compute_residuals function + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.compute_residuals( + inputs, outputs, residuals, discrete_inputs, discrete_outputs ) + else: + self._discipline.compute_residuals(inputs, outputs, residuals) + + for res_name, value in residuals.items(): + for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): + yield data.VariableMessage( + continuous=data.Array( + name=res_name, + start=b, + end=e, + type=data.kResidual, + data=value.ravel()[b:e], + ) + ) + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"ComputeResiduals failed: {e}" + ) def SolveResiduals(self, request_iterator, context): """ @@ -219,38 +228,45 @@ def SolveResiduals(self, request_iterator, context): - Outputs are streamed back in chunks for large arrays - This method is called automatically by the gRPC framework """ - # inputs and outputs - inputs = {} - flat_inputs = {} - outputs = {} - flat_outputs = {} - discrete_inputs = {} - - self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) - discrete_inputs, _ = self.process_inputs( - request_iterator, - flat_inputs, - flat_outputs, - discrete_inputs=discrete_inputs, - ) - - # Call the user-defined solve function - if discrete_inputs or self._discipline._discrete_var_meta: - self._discipline.solve_residuals(inputs, outputs, discrete_inputs) - else: - self._discipline.solve_residuals(inputs, outputs) - - for output_name, value in outputs.items(): - for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.VariableMessage( - continuous=data.Array( - name=output_name, - start=b, - end=e, - type=data.kOutput, - data=value.ravel()[b:e], + try: + # inputs and outputs + inputs = {} + flat_inputs = {} + outputs = {} + flat_outputs = {} + discrete_inputs = {} + + self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) + discrete_inputs, _ = self.process_inputs( + request_iterator, + flat_inputs, + flat_outputs, + discrete_inputs=discrete_inputs, + ) + + # Call the user-defined solve function + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.solve_residuals(inputs, outputs, discrete_inputs) + else: + self._discipline.solve_residuals(inputs, outputs) + + for output_name, value in outputs.items(): + for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): + yield data.VariableMessage( + continuous=data.Array( + name=output_name, + start=b, + end=e, + type=data.kOutput, + data=value.ravel()[b:e], + ) ) - ) + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"SolveResiduals failed: {e}" + ) def ComputeResidualGradients(self, request_iterator, context): """ @@ -279,44 +295,52 @@ def ComputeResidualGradients(self, request_iterator, context): - Used for gradient-based optimization and sensitivity analysis - This method is called automatically by the gRPC framework """ - # inputs and outputs - inputs = {} - flat_inputs = {} - outputs = {} - flat_outputs = {} - discrete_inputs = {} - discrete_outputs = {} - - self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) - jac = self.preallocate_partials() - discrete_inputs, discrete_outputs = self.process_inputs( - request_iterator, - flat_inputs, - flat_outputs, - discrete_inputs=discrete_inputs, - discrete_outputs=discrete_outputs, - ) - - # Call the user-defined residual partials function - if discrete_inputs or self._discipline._discrete_var_meta: - self._discipline.residual_partials( - inputs, outputs, jac, discrete_inputs, discrete_outputs + try: + # inputs and outputs + inputs = {} + flat_inputs = {} + outputs = {} + flat_outputs = {} + discrete_inputs = {} + discrete_outputs = {} + + self.preallocate_inputs(inputs, flat_inputs, outputs, flat_outputs) + jac = self.preallocate_partials() + discrete_inputs, discrete_outputs = self.process_inputs( + request_iterator, + flat_inputs, + flat_outputs, + discrete_inputs=discrete_inputs, + discrete_outputs=discrete_outputs, ) - else: - self._discipline.residual_partials(inputs, outputs, jac) - - for jac, value in jac.items(): - for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): - yield data.VariableMessage( - continuous=data.Array( - name=jac[0], - subname=jac[1], - type=data.kPartial, - start=b, - end=e, - data=value.ravel()[b:e], - ) + + # Call the user-defined residual partials function + if discrete_inputs or self._discipline._discrete_var_meta: + self._discipline.residual_partials( + inputs, outputs, jac, discrete_inputs, discrete_outputs ) + else: + self._discipline.residual_partials(inputs, outputs, jac) + + for jac, value in jac.items(): + for b, e in get_chunk_indices(value.size, self._stream_opts.num_double): + yield data.VariableMessage( + continuous=data.Array( + name=jac[0], + subname=jac[1], + type=data.kPartial, + start=b, + end=e, + data=value.ravel()[b:e], + ) + ) + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, + f"ComputeResidualGradients failed: {e}", + ) # def MatrixFreeGradients(self, request_iterator, context): # """ diff --git a/philote_mdo/openmdao/explicit.py b/philote_mdo/openmdao/explicit.py index 458b7e6..f80c9ef 100644 --- a/philote_mdo/openmdao/explicit.py +++ b/philote_mdo/openmdao/explicit.py @@ -128,6 +128,10 @@ def __init__(self, channel=None, num_par_fd=1, **kwargs): raise ValueError( "No channel provided, the Philote client will not be able to connect." ) + if not isinstance(num_par_fd, int) or num_par_fd < 1: + raise ValueError( + f"num_par_fd must be a positive integer, got {num_par_fd!r}." + ) # generic Philote client # The setting of OpenMDAO options requires the list of available diff --git a/philote_mdo/openmdao/group.py b/philote_mdo/openmdao/group.py index 4ca6c54..f27f9d9 100644 --- a/philote_mdo/openmdao/group.py +++ b/philote_mdo/openmdao/group.py @@ -29,6 +29,12 @@ # control over the information you may find at these locations. import openmdao.api as om import philote_mdo.general as pm +from philote_mdo.utils.validation import ( + PhiloteValidationError, + validate_name, + validate_shape, + validate_units, +) class OpenMdaoSubProblem(pm.ExplicitDiscipline): @@ -115,6 +121,11 @@ def add_group(self, group): >>> subprob = OpenMdaoSubProblem() >>> subprob.add_group(MyGroup()) """ + if not isinstance(group, om.Group): + raise PhiloteValidationError( + f"add_group: expected an om.Group instance, " + f"got {type(group).__name__}." + ) self._prob = om.Problem(model=group) self._model = self._prob.model @@ -142,6 +153,14 @@ def add_mapped_input(self, local_var, subprob_var, shape=(1,), units=""): >>> subprob.add_mapped_input('x_local', 'x', shape=(1,), units='m') >>> subprob.add_mapped_input('design_vars', 'z', shape=(2,), units='') """ + validate_name(local_var, "add_mapped_input (local_var)") + validate_name(subprob_var, "add_mapped_input (subprob_var)") + validate_shape(shape, "add_mapped_input") + validate_units(units, "add_mapped_input") + if local_var in self._input_map: + raise PhiloteValidationError( + f"add_mapped_input: '{local_var}' is already mapped." + ) self._input_map[local_var] = { "sub_prob_name": subprob_var, "shape": shape, @@ -172,6 +191,14 @@ def add_mapped_output(self, local_var, subprob_var, shape=(1,), units=""): >>> subprob.add_mapped_output('objective', 'obj', shape=(1,), units='') >>> subprob.add_mapped_output('constraint1', 'con1', shape=(1,), units='N') """ + validate_name(local_var, "add_mapped_output (local_var)") + validate_name(subprob_var, "add_mapped_output (subprob_var)") + validate_shape(shape, "add_mapped_output") + validate_units(units, "add_mapped_output") + if local_var in self._output_map: + raise PhiloteValidationError( + f"add_mapped_output: '{local_var}' is already mapped." + ) self._output_map[local_var] = { "sub_prob_name": subprob_var, "shape": shape, @@ -222,6 +249,18 @@ def declare_subproblem_partial(self, local_func, local_var): >>> subprob.add_mapped_output('y_local', 'y') >>> subprob.declare_subproblem_partial('y_local', 'x_local') """ + validate_name(local_func, "declare_subproblem_partial (local_func)") + validate_name(local_var, "declare_subproblem_partial (local_var)") + if local_func not in self._output_map: + raise PhiloteValidationError( + f"declare_subproblem_partial: output '{local_func}' has not " + f"been mapped. Call add_mapped_output first." + ) + if local_var not in self._input_map: + raise PhiloteValidationError( + f"declare_subproblem_partial: input '{local_var}' has not " + f"been mapped. Call add_mapped_input first." + ) self._partials_map[(local_func, local_var)] = ( self._output_map[local_func]["sub_prob_name"], self._input_map[local_var]["sub_prob_name"], diff --git a/philote_mdo/openmdao/implicit.py b/philote_mdo/openmdao/implicit.py index 1aa8843..2176bdc 100644 --- a/philote_mdo/openmdao/implicit.py +++ b/philote_mdo/openmdao/implicit.py @@ -132,9 +132,13 @@ def __init__(self, channel=None, num_par_fd=1, **kwargs): """ if not channel: raise ValueError( - "No channel provided, the Philote client will not" + "No channel provided, the Philote client will not " "be able to connect." ) + if not isinstance(num_par_fd, int) or num_par_fd < 1: + raise ValueError( + f"num_par_fd must be a positive integer, got {num_par_fd!r}." + ) # generic Philote client # The setting of OpenMDAO options requires the list of available diff --git a/philote_mdo/openmdao/utils.py b/philote_mdo/openmdao/utils.py index 3b95870..ea11503 100644 --- a/philote_mdo/openmdao/utils.py +++ b/philote_mdo/openmdao/utils.py @@ -28,6 +28,16 @@ # therein. The DoD does not exercise any editorial, security, or other # control over the information you may find at these locations. import philote_mdo.generated.data_pb2 as data +from philote_mdo.utils.validation import PhiloteValidationError + + +_TYPE_MAP = { + "bool": bool, + "int": int, + "float": float, + "str": str, + "dict": dict, +} def declare_options(opt_list, options): @@ -35,17 +45,12 @@ def declare_options(opt_list, options): Declares the options from the client options list. """ for name, type_str in opt_list: - opt_type = None - if type_str == "bool": - opt_type = bool - elif type_str == "int": - opt_type = int - elif type_str == "float": - opt_type = float - elif type_str == "str": - opt_type = str - elif type_str == "dict": - opt_type = dict + opt_type = _TYPE_MAP.get(type_str) + if opt_type is None: + raise PhiloteValidationError( + f"declare_options: unknown option type '{type_str}' " + f"for option '{name}'." + ) options.declare(name, types=opt_type) diff --git a/philote_mdo/utils/__init__.py b/philote_mdo/utils/__init__.py index 2e99b77..db801d8 100644 --- a/philote_mdo/utils/__init__.py +++ b/philote_mdo/utils/__init__.py @@ -29,3 +29,14 @@ # control over the information you may find at these locations. from .pair_dict import PairDict from .helper import get_chunk_indices, get_flattened_view +from .validation import ( + PhiloteError, + PhiloteValidationError, + PhiloteServerError, + validate_name, + validate_shape, + validate_units, + validate_option_type, + validate_is_dict, + validate_numpy_array, +) diff --git a/philote_mdo/utils/helper.py b/philote_mdo/utils/helper.py index 67bb39f..eb2a147 100644 --- a/philote_mdo/utils/helper.py +++ b/philote_mdo/utils/helper.py @@ -29,8 +29,21 @@ # control over the information you may find at these locations. import numpy as np +from philote_mdo.utils.validation import PhiloteValidationError + def get_chunk_indices(num_values, chunk_size): + if not isinstance(num_values, (int, np.integer)) or num_values < 0: + raise PhiloteValidationError( + f"get_chunk_indices: num_values must be a non-negative integer, " + f"got {num_values!r}." + ) + if not isinstance(chunk_size, (int, np.integer)) or chunk_size < 1: + raise PhiloteValidationError( + f"get_chunk_indices: chunk_size must be a positive integer, " + f"got {chunk_size!r}." + ) + beg_i = np.arange(0, num_values, chunk_size) if beg_i.size == 1: @@ -48,6 +61,11 @@ def get_flattened_view(arr): :param arr: Array to get a flattened view :return: A view of the input array, guaranteed to not be a copy """ + if not isinstance(arr, np.ndarray): + raise PhiloteValidationError( + f"get_flattened_view: expected a numpy ndarray, " + f"got {type(arr).__name__}." + ) flat_view = arr.view() flat_view.shape = -1 return flat_view diff --git a/philote_mdo/utils/pair_dict.py b/philote_mdo/utils/pair_dict.py index d72fdfc..bae5b9d 100644 --- a/philote_mdo/utils/pair_dict.py +++ b/philote_mdo/utils/pair_dict.py @@ -29,15 +29,27 @@ # control over the information you may find at these locations. +from philote_mdo.utils.validation import PhiloteValidationError + + class PairDict(dict): """ Jacobian dictionary for storing values with respect to two keys. """ + @staticmethod + def _validate_key(keys): + if not isinstance(keys, tuple) or len(keys) != 2: + raise PhiloteValidationError( + f"PairDict keys must be a 2-tuple, got {keys!r}." + ) + def __setitem__(self, keys, value): + self._validate_key(keys) key1, key2 = keys super().__setitem__((key1, key2), value) def __getitem__(self, keys): + self._validate_key(keys) key1, key2 = keys return super().__getitem__((key1, key2)) diff --git a/philote_mdo/utils/validation.py b/philote_mdo/utils/validation.py new file mode 100644 index 0000000..75395f2 --- /dev/null +++ b/philote_mdo/utils/validation.py @@ -0,0 +1,210 @@ +# Philote-Python +# +# Copyright 2022-2025 Christopher A. Lupp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# This work has been cleared for public release, distribution unlimited, case +# number: AFRL-2023-5713. +# +# The views expressed are those of the authors and do not reflect the +# official guidance or position of the United States Government, the +# Department of Defense or of the United States Air Force. +# +# Statement from DoD: The Appearance of external hyperlinks does not +# constitute endorsement by the United States Department of Defense (DoD) of +# the linked websites, of the information, products, or services contained +# therein. The DoD does not exercise any editorial, security, or other +# control over the information you may find at these locations. +import numpy as np + + +# --------------------------------------------------------------------------- +# Custom exception hierarchy +# --------------------------------------------------------------------------- + + +class PhiloteError(Exception): + """Base class for all Philote-specific errors.""" + + pass + + +class PhiloteValidationError(PhiloteError, ValueError): + """Raised when an input fails validation. + + Inherits from ``ValueError`` so that existing ``except ValueError`` + handlers in user code continue to work. + """ + + pass + + +class PhiloteServerError(PhiloteError, RuntimeError): + """Raised on the client side when a gRPC server call fails. + + Wraps the gRPC error details into a framework-specific exception so + that users do not need to catch ``grpc.RpcError`` directly. + """ + + pass + + +# --------------------------------------------------------------------------- +# Validation helpers +# --------------------------------------------------------------------------- + +VALID_OPTION_TYPES = {"bool", "int", "float", "str", "dict"} + + +def validate_name(name, context): + """Validate that *name* is a non-empty string. + + Parameters + ---------- + name : object + The name to validate. + context : str + Human-readable context for error messages (e.g. ``"add_input"``). + + Raises + ------ + PhiloteValidationError + If *name* is not a string or is empty. + """ + if not isinstance(name, str): + raise PhiloteValidationError( + f"{context}: 'name' must be a string, got {type(name).__name__}." + ) + if not name: + raise PhiloteValidationError(f"{context}: 'name' must not be empty.") + + +def validate_shape(shape, context): + """Validate that *shape* is a tuple of positive integers. + + Parameters + ---------- + shape : object + The shape to validate. + context : str + Human-readable context for error messages. + + Raises + ------ + PhiloteValidationError + If *shape* is not a tuple, or contains non-positive / non-integer + elements. + """ + if not isinstance(shape, tuple): + raise PhiloteValidationError( + f"{context}: 'shape' must be a tuple, got {type(shape).__name__}." + ) + for i, dim in enumerate(shape): + if not isinstance(dim, int): + raise PhiloteValidationError( + f"{context}: all elements of 'shape' must be integers, " + f"but element {i} is {type(dim).__name__}." + ) + if dim <= 0: + raise PhiloteValidationError( + f"{context}: all elements of 'shape' must be positive, " + f"but element {i} is {dim}." + ) + + +def validate_units(units, context): + """Validate that *units* is a string. + + Parameters + ---------- + units : object + The units string to validate. + context : str + Human-readable context for error messages. + + Raises + ------ + PhiloteValidationError + If *units* is not a string. + """ + if not isinstance(units, str): + raise PhiloteValidationError( + f"{context}: 'units' must be a string, got {type(units).__name__}." + ) + + +def validate_option_type(type_str, name): + """Validate that *type_str* is one of the allowed option types. + + Parameters + ---------- + type_str : object + The option type string to validate. + name : str + The option name (for error messages). + + Raises + ------ + PhiloteValidationError + If *type_str* is not in the allowed set. + """ + if type_str not in VALID_OPTION_TYPES: + raise PhiloteValidationError( + f"Invalid type '{type_str}' for option '{name}'. " + f"Allowed types are: {sorted(VALID_OPTION_TYPES)}." + ) + + +def validate_is_dict(obj, context): + """Validate that *obj* is a dictionary. + + Parameters + ---------- + obj : object + The object to validate. + context : str + Human-readable context for error messages. + + Raises + ------ + PhiloteValidationError + If *obj* is not a ``dict``. + """ + if not isinstance(obj, dict): + raise PhiloteValidationError( + f"{context}: expected a dict, got {type(obj).__name__}." + ) + + +def validate_numpy_array(value, name): + """Validate that *value* is a NumPy ndarray. + + Parameters + ---------- + value : object + The value to validate. + name : str + Variable name (for error messages). + + Raises + ------ + PhiloteValidationError + If *value* is not a ``numpy.ndarray``. + """ + if not isinstance(value, np.ndarray): + raise PhiloteValidationError( + f"Variable '{name}' must be a numpy ndarray, " + f"got {type(value).__name__}." + ) diff --git a/tests/test_discipline.py b/tests/test_discipline.py index 6dbf5a6..a1b0e8e 100644 --- a/tests/test_discipline.py +++ b/tests/test_discipline.py @@ -31,6 +31,7 @@ from unittest.mock import Mock from philote_mdo.general import Discipline +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.generated.data_pb2 as data @@ -172,6 +173,100 @@ def test_clear_data(self): self.assertEqual(len(disc._var_meta), 0) self.assertEqual(len(disc._partials_meta), 0) + # ------------------------------------------------------------------ + # Validation tests + # ------------------------------------------------------------------ + + def test_add_input_invalid_name_type(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_input(123) + + def test_add_input_empty_name(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_input("") + + def test_add_input_invalid_shape(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_input("x", shape=[2, 3]) + + def test_add_input_invalid_units(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_input("x", units=42) + + def test_add_input_duplicate(self): + disc = Discipline() + disc.add_input("x", shape=(2,)) + with self.assertRaises(PhiloteValidationError): + disc.add_input("x", shape=(3,)) + + def test_add_output_invalid_name(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_output(None) + + def test_add_output_invalid_shape(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_output("y", shape=(-1,)) + + def test_add_output_duplicate(self): + disc = Discipline() + disc.add_output("y") + with self.assertRaises(PhiloteValidationError): + disc.add_output("y") + + def test_add_option_invalid_name(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_option(42, "int") + + def test_add_option_invalid_type(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_option("opt", "unknown") + + def test_add_option_duplicate(self): + disc = Discipline() + disc.add_option("opt", "int") + with self.assertRaises(PhiloteValidationError): + disc.add_option("opt", "float") + + def test_add_discrete_input_invalid_name(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_discrete_input("") + + def test_add_discrete_input_duplicate(self): + disc = Discipline() + disc.add_discrete_input("d") + with self.assertRaises(PhiloteValidationError): + disc.add_discrete_input("d") + + def test_add_discrete_output_invalid_name(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.add_discrete_output(123) + + def test_add_discrete_output_duplicate(self): + disc = Discipline() + disc.add_discrete_output("d") + with self.assertRaises(PhiloteValidationError): + disc.add_discrete_output("d") + + def test_declare_partials_invalid_func(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.declare_partials("", "x") + + def test_declare_partials_invalid_var(self): + disc = Discipline() + with self.assertRaises(PhiloteValidationError): + disc.declare_partials("f", 123) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_discipline_client.py b/tests/test_discipline_client.py index d7be559..62d2bae 100644 --- a/tests/test_discipline_client.py +++ b/tests/test_discipline_client.py @@ -33,6 +33,7 @@ from google.protobuf.empty_pb2 import Empty from google.protobuf.struct_pb2 import Struct from philote_mdo.general import DisciplineClient +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.generated.data_pb2 as data import philote_mdo.generated.disciplines_pb2_grpc as disc import philote_mdo.utils as utils @@ -556,6 +557,24 @@ def test_recover_partials_empty_array_raises_error(self): self.assertIn("Expected continuous outputs for the partials, but array was empty", str(context.exception)) + def test_send_options_non_dict_raises(self): + mock_channel = Mock() + client = DisciplineClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.send_options("not a dict") + + def test_assemble_input_messages_non_dict_raises(self): + mock_channel = Mock() + client = DisciplineClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client._assemble_input_messages("not a dict") + + def test_assemble_input_messages_non_array_value_raises(self): + mock_channel = Mock() + client = DisciplineClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client._assemble_input_messages({"x": [1.0, 2.0]}) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_discipline_server.py b/tests/test_discipline_server.py index 2f30542..9b5c930 100644 --- a/tests/test_discipline_server.py +++ b/tests/test_discipline_server.py @@ -30,11 +30,13 @@ import unittest from unittest.mock import Mock +import grpc import numpy as np from google.protobuf.empty_pb2 import Empty from philote_mdo.general import Discipline, DisciplineServer +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.generated.data_pb2 as data @@ -411,27 +413,32 @@ def test_set_options_with_nested_dict(self): } ) - def test_get_available_options_invalid_type_raises_error(self): + def test_get_available_options_invalid_type_aborts(self): """ - Tests that GetAvailableOptions raises ValueError for invalid option types. + Tests that GetAvailableOptions calls context.abort for invalid option + types. """ server = DisciplineServer() discipline = server._discipline = Discipline() - - # Add option with invalid type - discipline.add_option("invalid_option", "unknown_type") - + + # Add option with invalid type (bypasses add_option validation by + # writing directly to options_list) + discipline.options_list["invalid_option"] = "unknown_type" + request = Empty() context = Mock() - - with self.assertRaises(ValueError) as error_context: - server.GetAvailableOptions(request, context) - - self.assertIn("Invalid value for discipline option 'invalid_option'", str(error_context.exception)) + + server.GetAvailableOptions(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("Invalid value for discipline option 'invalid_option'", args[0][1]) def test_process_inputs_empty_array_raises_error(self): """ - Tests that process_inputs raises ValueError when array data is empty. + Tests that process_inputs raises PhiloteValidationError when array + data is empty. """ server = DisciplineServer() @@ -451,11 +458,116 @@ def test_process_inputs_empty_array_raises_error(self): flat_inputs = {"x": np.zeros(3)} flat_outputs = {} - with self.assertRaises(ValueError) as context: + with self.assertRaises(PhiloteValidationError) as context: server.process_inputs(request_iterator, flat_inputs, flat_outputs) self.assertIn("Expected continuous variables but arrays were empty for variable x", str(context.exception)) + def test_get_available_options_general_exception_aborts(self): + """ + Tests that GetAvailableOptions calls context.abort with INTERNAL + for unexpected exceptions. + """ + server = DisciplineServer() + discipline = Mock() + # options_list property raises an unexpected error + type(discipline).options_list = property( + lambda self: (_ for _ in ()).throw(RuntimeError("unexpected")) + ) + server._discipline = discipline + + request = Mock() + context = Mock() + + server.GetAvailableOptions(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("GetAvailableOptions failed", args[0][1]) + + def test_set_options_validation_error_aborts(self): + """ + Tests that SetOptions calls context.abort with INVALID_ARGUMENT + for PhiloteValidationError. + """ + server = DisciplineServer() + discipline = Mock() + discipline.set_options.side_effect = PhiloteValidationError("bad option") + server._discipline = discipline + + request = Mock() + request.options = {} + context = Mock() + + server.SetOptions(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("bad option", args[0][1]) + + def test_set_options_general_exception_aborts(self): + """ + Tests that SetOptions calls context.abort with INTERNAL for + unexpected exceptions. + """ + server = DisciplineServer() + discipline = Mock() + discipline.set_options.side_effect = RuntimeError("boom") + server._discipline = discipline + + request = Mock() + request.options = {} + context = Mock() + + server.SetOptions(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("SetOptions failed", args[0][1]) + + def test_setup_validation_error_aborts(self): + """ + Tests that Setup calls context.abort with INVALID_ARGUMENT + for PhiloteValidationError. + """ + server = DisciplineServer() + discipline = Mock() + discipline.setup.side_effect = PhiloteValidationError("bad setup") + server._discipline = discipline + + request = Mock() + context = Mock() + + server.Setup(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("bad setup", args[0][1]) + + def test_setup_general_exception_aborts(self): + """ + Tests that Setup calls context.abort with INTERNAL for + unexpected exceptions. + """ + server = DisciplineServer() + discipline = Mock() + discipline._clear_data.side_effect = RuntimeError("crash") + server._discipline = discipline + + request = Mock() + context = Mock() + + server.Setup(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("Setup failed", args[0][1]) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index 0afc37b..87d70d7 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -29,7 +29,11 @@ # control over the information you may find at these locations. import unittest from unittest.mock import Mock, MagicMock + +import grpc + from philote_mdo.general import DisciplineServer, DisciplineClient, ExplicitDiscipline +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.generated.data_pb2 as data @@ -95,26 +99,27 @@ def test_get_available_options_with_dict_type(self): def test_get_available_options_with_invalid_type(self): """ - Test GetAvailableOptions with invalid option type (covers lines 100-103). + Test GetAvailableOptions with invalid option type aborts with + INVALID_ARGUMENT. """ server = DisciplineServer() discipline = Mock() - + # Mock the options_list attribute to return a dict with invalid type discipline.options_list = {"invalid_option": "invalid_type"} - + server.attach_discipline(discipline) - + # Create a mock request and context request = Mock() context = Mock() - - # This should raise a ValueError - with self.assertRaises(ValueError) as context_err: - server.GetAvailableOptions(request, context) - - self.assertIn("Invalid value for discipline option", str(context_err.exception)) - self.assertIn("invalid_option", str(context_err.exception)) + + server.GetAvailableOptions(request, context) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("Invalid value for discipline option", args[0][1]) def test_process_inputs_with_empty_continuous_data(self): """ diff --git a/tests/test_explicit_client.py b/tests/test_explicit_client.py index 59bd35b..558ff01 100644 --- a/tests/test_explicit_client.py +++ b/tests/test_explicit_client.py @@ -30,9 +30,11 @@ import unittest from unittest.mock import Mock, patch +import grpc import numpy as np from philote_mdo.general import ExplicitClient +from philote_mdo.utils.validation import PhiloteValidationError, PhiloteServerError import philote_mdo.generated.data_pb2 as data import philote_mdo.utils as utils @@ -135,3 +137,53 @@ def test_compute_partials(self, mock_explicit_stub): for output_name, expected_data in expected_outputs.items(): self.assertTrue(output_name in outputs) np.testing.assert_array_equal(outputs[output_name], expected_data) + + def test_run_compute_non_dict_raises(self): + mock_channel = Mock() + client = ExplicitClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.run_compute("not a dict") + + @patch("philote_mdo.generated.disciplines_pb2_grpc.ExplicitServiceStub") + def test_run_compute_grpc_error_wraps(self, mock_explicit_stub): + mock_channel = Mock() + mock_stub = mock_explicit_stub.return_value + client = ExplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="x", type=data.kInput, shape=(1,)), + ] + + rpc_error = grpc.RpcError() + rpc_error.details = lambda: "server crashed" + rpc_error.code = lambda: grpc.StatusCode.INTERNAL + mock_stub.ComputeFunction.side_effect = rpc_error + + with self.assertRaises(PhiloteServerError) as ctx: + client.run_compute({"x": np.array([1.0])}) + self.assertIn("server crashed", str(ctx.exception)) + + @patch("philote_mdo.generated.disciplines_pb2_grpc.ExplicitServiceStub") + def test_run_compute_partials_grpc_error_wraps(self, mock_explicit_stub): + mock_channel = Mock() + mock_stub = mock_explicit_stub.return_value + client = ExplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + data.VariableMetaData(name="x", type=data.kInput, shape=(1,)), + ] + client._partials_meta = [data.PartialsMetaData(name="f", subname="x")] + + rpc_error = grpc.RpcError() + rpc_error.details = lambda: "gradient computation failed" + rpc_error.code = lambda: grpc.StatusCode.INTERNAL + mock_stub.ComputeGradient.side_effect = rpc_error + + with self.assertRaises(PhiloteServerError) as ctx: + client.run_compute_partials({"x": np.array([1.0])}) + self.assertIn("gradient computation failed", str(ctx.exception)) + + def test_run_compute_partials_non_dict_raises(self): + mock_channel = Mock() + client = ExplicitClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.run_compute_partials(42) diff --git a/tests/test_explicit_server.py b/tests/test_explicit_server.py index 0416ec0..f4110b7 100644 --- a/tests/test_explicit_server.py +++ b/tests/test_explicit_server.py @@ -30,12 +30,14 @@ import unittest from unittest.mock import Mock +import grpc import numpy as np from scipy.optimize import rosen, rosen_der from google.protobuf.empty_pb2 import Empty from philote_mdo.general import ExplicitDiscipline, ExplicitServer +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.generated.data_pb2 as data @@ -146,5 +148,137 @@ def compute_partials(inputs, jac): ) + def test_compute_function_aborts_on_validation_error(self): + """ + Tests that ComputeFunction calls context.abort with INVALID_ARGUMENT + when a PhiloteValidationError is raised. + """ + server = ExplicitServer() + discipline = server._discipline = ExplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + ] + + def bad_compute(inputs, outputs): + raise PhiloteValidationError("bad input data") + + server._discipline.compute = bad_compute + + list(server.ComputeFunction(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("bad input data", args[0][1]) + + def test_compute_gradient_aborts_on_validation_error(self): + """ + Tests that ComputeGradient calls context.abort with INVALID_ARGUMENT + when a PhiloteValidationError is raised. + """ + server = ExplicitServer() + discipline = server._discipline = ExplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + discipline.declare_partials("f", "x") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + ] + + def bad_partials(inputs, jac): + raise PhiloteValidationError("invalid partials") + + server._discipline.compute_partials = bad_partials + + list(server.ComputeGradient(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("invalid partials", args[0][1]) + + def test_compute_function_aborts_on_discipline_error(self): + """ + Tests that ComputeFunction calls context.abort when the discipline's + compute raises an exception. + """ + server = ExplicitServer() + discipline = server._discipline = ExplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + ] + + def bad_compute(inputs, outputs): + raise RuntimeError("division by zero in compute") + + server._discipline.compute = bad_compute + + # Exhaust the generator + list(server.ComputeFunction(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("ComputeFunction failed", args[0][1]) + + def test_compute_gradient_aborts_on_discipline_error(self): + """ + Tests that ComputeGradient calls context.abort when the discipline's + compute_partials raises an exception. + """ + server = ExplicitServer() + discipline = server._discipline = ExplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + discipline.declare_partials("f", "x") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + ] + + def bad_partials(inputs, jac): + raise RuntimeError("singular matrix") + + server._discipline.compute_partials = bad_partials + + list(server.ComputeGradient(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("ComputeGradient failed", args[0][1]) + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_implicit_client.py b/tests/test_implicit_client.py index 3ec34a9..d9333c9 100644 --- a/tests/test_implicit_client.py +++ b/tests/test_implicit_client.py @@ -30,9 +30,11 @@ import unittest from unittest.mock import Mock, patch +import grpc import numpy as np from philote_mdo.general import ImplicitClient +from philote_mdo.utils.validation import PhiloteValidationError, PhiloteServerError import philote_mdo.generated.data_pb2 as data import philote_mdo.utils as utils @@ -202,5 +204,94 @@ def test_residual_partials(self, mock_implicit_stub): np.testing.assert_array_equal(partials[key], expected_data) + @patch("philote_mdo.generated.disciplines_pb2_grpc.ImplicitServiceStub") + def test_run_compute_residuals_grpc_error_wraps(self, mock_implicit_stub): + mock_channel = Mock() + mock_stub = mock_implicit_stub.return_value + client = ImplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="x", type=data.kInput, shape=(1,)), + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + data.VariableMetaData(name="f", type=data.kResidual, shape=(1,)), + ] + + rpc_error = grpc.RpcError() + rpc_error.details = lambda: "residual failed" + rpc_error.code = lambda: grpc.StatusCode.INTERNAL + mock_stub.ComputeResiduals.side_effect = rpc_error + + with self.assertRaises(PhiloteServerError) as ctx: + client.run_compute_residuals( + {"x": np.array([1.0])}, {"f": np.array([1.0])} + ) + self.assertIn("residual failed", str(ctx.exception)) + + @patch("philote_mdo.generated.disciplines_pb2_grpc.ImplicitServiceStub") + def test_run_solve_residuals_grpc_error_wraps(self, mock_implicit_stub): + mock_channel = Mock() + mock_stub = mock_implicit_stub.return_value + client = ImplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="x", type=data.kInput, shape=(1,)), + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + ] + + rpc_error = grpc.RpcError() + rpc_error.details = lambda: "solve failed" + rpc_error.code = lambda: grpc.StatusCode.INTERNAL + mock_stub.SolveResiduals.side_effect = rpc_error + + with self.assertRaises(PhiloteServerError) as ctx: + client.run_solve_residuals({"x": np.array([1.0])}) + self.assertIn("solve failed", str(ctx.exception)) + + @patch("philote_mdo.generated.disciplines_pb2_grpc.ImplicitServiceStub") + def test_run_residual_gradients_grpc_error_wraps(self, mock_implicit_stub): + mock_channel = Mock() + mock_stub = mock_implicit_stub.return_value + client = ImplicitClient(mock_channel) + client._var_meta = [ + data.VariableMetaData(name="x", type=data.kInput, shape=(1,)), + data.VariableMetaData(name="f", type=data.kOutput, shape=(1,)), + data.VariableMetaData(name="f", type=data.kResidual, shape=(1,)), + ] + client._partials_meta = [data.PartialsMetaData(name="f", subname="x")] + + rpc_error = grpc.RpcError() + rpc_error.details = lambda: "gradient failed" + rpc_error.code = lambda: grpc.StatusCode.INTERNAL + mock_stub.ComputeResidualGradients.side_effect = rpc_error + + with self.assertRaises(PhiloteServerError) as ctx: + client.run_residual_gradients( + {"x": np.array([1.0])}, {"f": np.array([1.0])} + ) + self.assertIn("gradient failed", str(ctx.exception)) + + def test_run_compute_residuals_non_dict_inputs_raises(self): + mock_channel = Mock() + client = ImplicitClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.run_compute_residuals("not a dict", {"f": np.array([1.0])}) + + def test_run_compute_residuals_non_dict_outputs_raises(self): + mock_channel = Mock() + client = ImplicitClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.run_compute_residuals({"x": np.array([1.0])}, "not a dict") + + def test_run_solve_residuals_non_dict_raises(self): + mock_channel = Mock() + client = ImplicitClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.run_solve_residuals(42) + + def test_run_residual_gradients_non_dict_raises(self): + mock_channel = Mock() + client = ImplicitClient(mock_channel) + with self.assertRaises(PhiloteValidationError): + client.run_residual_gradients("bad", {"f": np.array([1.0])}) + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_implicit_server.py b/tests/test_implicit_server.py index 21e7c82..6f21d68 100644 --- a/tests/test_implicit_server.py +++ b/tests/test_implicit_server.py @@ -29,10 +29,13 @@ # control over the information you may find at these locations. import unittest from unittest.mock import Mock + +import grpc import numpy as np import numpy.testing as np_testing from google.protobuf.empty_pb2 import Empty from philote_mdo.general import ImplicitDiscipline, ImplicitServer +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.generated.data_pb2 as data @@ -206,5 +209,224 @@ def residual_partials(inputs, residuals, jac): ) + def test_compute_residuals_aborts_on_validation_error(self): + """ + Tests that ComputeResiduals calls context.abort with INVALID_ARGUMENT + when a PhiloteValidationError is raised. + """ + server = ImplicitServer() + discipline = server._discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kOutput, name="f", + ) + ), + ] + + def bad_residuals(inputs, outputs, residuals): + raise PhiloteValidationError("bad residual input") + + server._discipline.compute_residuals = bad_residuals + + list(server.ComputeResiduals(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("bad residual input", args[0][1]) + + def test_solve_residuals_aborts_on_validation_error(self): + """ + Tests that SolveResiduals calls context.abort with INVALID_ARGUMENT + when a PhiloteValidationError is raised. + """ + server = ImplicitServer() + discipline = server._discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kOutput, name="f", + ) + ), + ] + + def bad_solve(inputs, outputs): + raise PhiloteValidationError("bad solve input") + + server._discipline.solve_residuals = bad_solve + + list(server.SolveResiduals(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("bad solve input", args[0][1]) + + def test_compute_residual_gradients_aborts_on_validation_error(self): + """ + Tests that ComputeResidualGradients calls context.abort with + INVALID_ARGUMENT when a PhiloteValidationError is raised. + """ + server = ImplicitServer() + discipline = server._discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + discipline.declare_partials("f", "x") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + ] + + def bad_partials(inputs, outputs, jac): + raise PhiloteValidationError("bad partials input") + + server._discipline.residual_partials = bad_partials + + list(server.ComputeResidualGradients(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INVALID_ARGUMENT) + self.assertIn("bad partials input", args[0][1]) + + def test_compute_residual_gradients_aborts_on_discipline_error(self): + """ + Tests that ComputeResidualGradients calls context.abort with INTERNAL + when an unexpected exception is raised. + """ + server = ImplicitServer() + discipline = server._discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + discipline.declare_partials("f", "x") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + ] + + def bad_partials(inputs, outputs, jac): + raise RuntimeError("unexpected crash") + + server._discipline.residual_partials = bad_partials + + list(server.ComputeResidualGradients(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("ComputeResidualGradients failed", args[0][1]) + + def test_compute_residuals_aborts_on_discipline_error(self): + """ + Tests that ComputeResiduals calls context.abort when the discipline's + compute_residuals raises an exception. + """ + server = ImplicitServer() + discipline = server._discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kOutput, name="f", + ) + ), + ] + + def bad_residuals(inputs, outputs, residuals): + raise RuntimeError("residual computation failed") + + server._discipline.compute_residuals = bad_residuals + + list(server.ComputeResiduals(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("ComputeResiduals failed", args[0][1]) + + def test_solve_residuals_aborts_on_discipline_error(self): + """ + Tests that SolveResiduals calls context.abort when the discipline's + solve_residuals raises an exception. + """ + server = ImplicitServer() + discipline = server._discipline = ImplicitDiscipline() + discipline.add_input("x", shape=(1,), units="") + discipline.add_output("f", shape=(1,), units="") + + context = Mock() + request_iterator = [ + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kInput, name="x", + ) + ), + data.VariableMessage( + continuous=data.Array( + start=0, end=0, data=[1.0], + type=data.VariableType.kOutput, name="f", + ) + ), + ] + + def bad_solve(inputs, outputs): + raise RuntimeError("solver did not converge") + + server._discipline.solve_residuals = bad_solve + + list(server.SolveResiduals(request_iterator, context)) + + context.abort.assert_called_once() + args = context.abort.call_args + self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL) + self.assertIn("SolveResiduals failed", args[0][1]) + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_openmdao_explicit_client.py b/tests/test_openmdao_explicit_client.py index c8339e5..b97551f 100644 --- a/tests/test_openmdao_explicit_client.py +++ b/tests/test_openmdao_explicit_client.py @@ -305,6 +305,15 @@ def test_constructor_no_channel_raises_error(self, om_explicit_component_patch): self.assertIn("No channel provided", str(context.exception)) + def test_invalid_num_par_fd_raises(self, mock_explicit_component): + """ + Tests that an invalid num_par_fd raises a ValueError. + """ + mock_channel = Mock() + with self.assertRaises(ValueError) as context: + RemoteExplicitComponent(channel=mock_channel, num_par_fd=0) + self.assertIn("num_par_fd must be a positive integer", str(context.exception)) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_openmdao_group.py b/tests/test_openmdao_group.py index 016831a..c78b0fa 100644 --- a/tests/test_openmdao_group.py +++ b/tests/test_openmdao_group.py @@ -31,6 +31,7 @@ import numpy as np import openmdao.api as om from philote_mdo.openmdao.group import OpenMdaoSubProblem +from philote_mdo.utils.validation import PhiloteValidationError class SimpleGroup(om.Group): @@ -257,5 +258,45 @@ def test_units_and_shapes(self): self.assertEqual(subprob._output_map['vector_out']['units'], 'kg') + def test_add_group_non_group_raises(self): + subprob = OpenMdaoSubProblem() + with self.assertRaises(PhiloteValidationError): + subprob.add_group("not a group") + + def test_add_mapped_input_invalid_name_raises(self): + subprob = OpenMdaoSubProblem() + with self.assertRaises(PhiloteValidationError): + subprob.add_mapped_input("", "x") + + def test_add_mapped_input_invalid_shape_raises(self): + subprob = OpenMdaoSubProblem() + with self.assertRaises(PhiloteValidationError): + subprob.add_mapped_input("x", "sub_x", shape=[2]) + + def test_add_mapped_input_duplicate_raises(self): + subprob = OpenMdaoSubProblem() + subprob.add_mapped_input("x", "sub_x") + with self.assertRaises(PhiloteValidationError): + subprob.add_mapped_input("x", "sub_x2") + + def test_add_mapped_output_duplicate_raises(self): + subprob = OpenMdaoSubProblem() + subprob.add_mapped_output("y", "sub_y") + with self.assertRaises(PhiloteValidationError): + subprob.add_mapped_output("y", "sub_y2") + + def test_declare_subproblem_partial_unmapped_output_raises(self): + subprob = OpenMdaoSubProblem() + subprob.add_mapped_input("x", "sub_x") + with self.assertRaises(PhiloteValidationError): + subprob.declare_subproblem_partial("unmapped_y", "x") + + def test_declare_subproblem_partial_unmapped_input_raises(self): + subprob = OpenMdaoSubProblem() + subprob.add_mapped_output("y", "sub_y") + with self.assertRaises(PhiloteValidationError): + subprob.declare_subproblem_partial("y", "unmapped_x") + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_openmdao_implicit_client.py b/tests/test_openmdao_implicit_client.py index 2895e7f..045ddcb 100644 --- a/tests/test_openmdao_implicit_client.py +++ b/tests/test_openmdao_implicit_client.py @@ -367,6 +367,15 @@ def test_constructor_no_channel_raises_error(self, om_implicit_component_patch): self.assertIn("No channel provided", str(context.exception)) + def test_invalid_num_par_fd_raises(self, mock_implicit_component): + """ + Tests that an invalid num_par_fd raises a ValueError. + """ + mock_channel = Mock() + with self.assertRaises(ValueError) as context: + RemoteImplicitComponent(channel=mock_channel, num_par_fd=-1) + self.assertIn("num_par_fd must be a positive integer", str(context.exception)) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_openmdao_utils.py b/tests/test_openmdao_utils.py index 95a5acd..763504e 100644 --- a/tests/test_openmdao_utils.py +++ b/tests/test_openmdao_utils.py @@ -32,6 +32,7 @@ import numpy as np from philote_mdo.generated.data_pb2 import kInput, kOutput +from philote_mdo.utils.validation import PhiloteValidationError import philote_mdo.openmdao.utils as utils @@ -206,11 +207,11 @@ def test_declare_options(self): self.assertEqual(options_mock.declare.call_count, 5) - # Test case 3: Unknown type (should result in None) + # Test case 3: Unknown type now raises PhiloteValidationError options_mock.reset_mock() opt_list = [("unknown_param", "unknown_type")] - declare_options(opt_list, options_mock) - options_mock.declare.assert_called_once_with("unknown_param", types=None) + with self.assertRaises(PhiloteValidationError): + declare_options(opt_list, options_mock) if __name__ == "__main__": diff --git a/tests/test_utils.py b/tests/test_utils.py index ab65ff2..d262bb8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -29,7 +29,8 @@ # control over the information you may find at these locations. import unittest import numpy as np -from philote_mdo.utils import get_chunk_indices, get_flattened_view +from philote_mdo.utils import get_chunk_indices, get_flattened_view, PairDict +from philote_mdo.utils.validation import PhiloteValidationError class TestUtils(unittest.TestCase): @@ -76,5 +77,38 @@ def test_get_flattened_view(self): self.assertEqual(result_empty.shape, (0,)) + def test_get_chunk_indices_negative_num_values_raises(self): + with self.assertRaises(PhiloteValidationError): + list(get_chunk_indices(-1, 3)) + + def test_get_chunk_indices_zero_chunk_size_raises(self): + with self.assertRaises(PhiloteValidationError): + list(get_chunk_indices(10, 0)) + + def test_get_chunk_indices_non_int_raises(self): + with self.assertRaises(PhiloteValidationError): + list(get_chunk_indices(10.5, 3)) + + def test_get_flattened_view_non_array_raises(self): + with self.assertRaises(PhiloteValidationError): + get_flattened_view([1, 2, 3]) + + def test_pair_dict_invalid_key_raises(self): + pd = PairDict() + with self.assertRaises(PhiloteValidationError): + pd["single_key"] = 1.0 + + def test_pair_dict_three_tuple_raises(self): + pd = PairDict() + with self.assertRaises(PhiloteValidationError): + pd[("a", "b", "c")] = 1.0 + + def test_pair_dict_get_invalid_key_raises(self): + pd = PairDict() + pd[("a", "b")] = 1.0 + with self.assertRaises(PhiloteValidationError): + _ = pd["single_key"] + + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/tests/test_validation.py b/tests/test_validation.py new file mode 100644 index 0000000..8d1d09c --- /dev/null +++ b/tests/test_validation.py @@ -0,0 +1,184 @@ +# Philote-Python +# +# Copyright 2022-2025 Christopher A. Lupp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# This work has been cleared for public release, distribution unlimited, case +# number: AFRL-2023-5713. +# +# The views expressed are those of the authors and do not reflect the +# official guidance or position of the United States Government, the +# Department of Defense or of the United States Air Force. +# +# Statement from DoD: The Appearance of external hyperlinks does not +# constitute endorsement by the United States Department of Defense (DoD) of +# the linked websites, of the information, products, or services contained +# therein. The DoD does not exercise any editorial, security, or other +# control over the information you may find at these locations. +import unittest + +import numpy as np + +from philote_mdo.utils.validation import ( + PhiloteError, + PhiloteValidationError, + PhiloteServerError, + validate_name, + validate_shape, + validate_units, + validate_option_type, + validate_is_dict, + validate_numpy_array, +) + + +class TestExceptionHierarchy(unittest.TestCase): + """Tests for the custom exception class hierarchy.""" + + def test_philote_validation_error_is_value_error(self): + with self.assertRaises(ValueError): + raise PhiloteValidationError("test") + + def test_philote_validation_error_is_philote_error(self): + with self.assertRaises(PhiloteError): + raise PhiloteValidationError("test") + + def test_philote_server_error_is_runtime_error(self): + with self.assertRaises(RuntimeError): + raise PhiloteServerError("test") + + def test_philote_server_error_is_philote_error(self): + with self.assertRaises(PhiloteError): + raise PhiloteServerError("test") + + def test_philote_error_is_exception(self): + with self.assertRaises(Exception): + raise PhiloteError("test") + + +class TestValidateName(unittest.TestCase): + """Tests for validate_name.""" + + def test_valid_name(self): + validate_name("x", "test") + + def test_non_string_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_name(123, "add_input") + self.assertIn("must be a string", str(ctx.exception)) + self.assertIn("add_input", str(ctx.exception)) + + def test_empty_string_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_name("", "add_output") + self.assertIn("must not be empty", str(ctx.exception)) + + def test_none_raises(self): + with self.assertRaises(PhiloteValidationError): + validate_name(None, "test") + + +class TestValidateShape(unittest.TestCase): + """Tests for validate_shape.""" + + def test_valid_shape_1d(self): + validate_shape((3,), "test") + + def test_valid_shape_2d(self): + validate_shape((2, 4), "test") + + def test_non_tuple_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_shape([2, 3], "add_input") + self.assertIn("must be a tuple", str(ctx.exception)) + + def test_int_raises(self): + with self.assertRaises(PhiloteValidationError): + validate_shape(3, "test") + + def test_non_integer_element_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_shape((2.0,), "test") + self.assertIn("must be integers", str(ctx.exception)) + + def test_zero_element_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_shape((0,), "test") + self.assertIn("must be positive", str(ctx.exception)) + + def test_negative_element_raises(self): + with self.assertRaises(PhiloteValidationError): + validate_shape((-1, 3), "test") + + +class TestValidateUnits(unittest.TestCase): + """Tests for validate_units.""" + + def test_valid_units(self): + validate_units("m**2", "test") + + def test_empty_string_is_valid(self): + validate_units("", "test") + + def test_non_string_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_units(42, "add_input") + self.assertIn("must be a string", str(ctx.exception)) + + +class TestValidateOptionType(unittest.TestCase): + """Tests for validate_option_type.""" + + def test_valid_types(self): + for t in ("bool", "int", "float", "str", "dict"): + validate_option_type(t, "opt") + + def test_invalid_type_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_option_type("unknown", "my_opt") + self.assertIn("Invalid type", str(ctx.exception)) + self.assertIn("my_opt", str(ctx.exception)) + + +class TestValidateIsDict(unittest.TestCase): + """Tests for validate_is_dict.""" + + def test_valid_dict(self): + validate_is_dict({"a": 1}, "test") + + def test_non_dict_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_is_dict([1, 2], "send_options") + self.assertIn("expected a dict", str(ctx.exception)) + + +class TestValidateNumpyArray(unittest.TestCase): + """Tests for validate_numpy_array.""" + + def test_valid_array(self): + validate_numpy_array(np.array([1.0, 2.0]), "x") + + def test_list_raises(self): + with self.assertRaises(PhiloteValidationError) as ctx: + validate_numpy_array([1.0, 2.0], "x") + self.assertIn("must be a numpy ndarray", str(ctx.exception)) + + def test_scalar_raises(self): + with self.assertRaises(PhiloteValidationError): + validate_numpy_array(1.0, "x") + + +if __name__ == "__main__": + unittest.main(verbosity=2) From 2225686c41610eba0ad63f10d6a88f88aad759a8 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Thu, 9 Apr 2026 18:24:30 -0400 Subject: [PATCH 12/14] Add dynamic shapes for inputs and outputs (#62) * Add comprehensive input validation and error handling (#46) Introduce custom exception classes (PhiloteValidationError, PhiloteServerError), parameter validation in discipline base classes, gRPC error propagation via context.abort() in all server RPC methods, and client-side input validation with gRPC error wrapping. Fix missing space in RemoteImplicitComponent error message. * Add tests to restore 100% coverage Cover all previously uncovered error handling paths: PhiloteValidationError and general Exception branches in server RPC methods, gRPC error wrapping in all client methods, and num_par_fd validation in OpenMDAO components. * Add dynamic shapes for inputs and outputs (MDO-Standards/Philote-MDO#6) Disciplines can now declare variables with `dynamic_shape=True` in `add_input`/`add_output`, indicating that the client is allowed to set the variable's shape at runtime. A new `SetVariableShapes` gRPC RPC lets clients send resolved shapes after querying variable definitions. Protocol changes: - Added `bool dynamic_shape` field to `VariableMetaData` in data.proto - Added `SetVariableShapes` streaming RPC to DisciplineService Server: - `add_input`/`add_output` accept `dynamic_shape` parameter - `SetVariableShapes` handler validates and updates shapes, including implicit residual entries - `preallocate_inputs` raises if a dynamic variable has no shape set Client: - `set_variable_shape`, `send_variable_shapes`, `get_dynamic_variables` helper methods OpenMDAO bindings: - Dynamic-shape variables map to `shape_by_conn=True` automatically - Resolved shapes are sent to the server before partials setup Includes FlexibleDiscipline example and 22 new tests (100% coverage). --- CHANGELOG.md | 8 + philote_mdo/examples/__init__.py | 1 + philote_mdo/examples/flexible.py | 55 ++ philote_mdo/general/discipline.py | 32 +- philote_mdo/general/discipline_client.py | 65 +++ philote_mdo/general/discipline_server.py | 61 ++- philote_mdo/generated/data_pb2.py | 28 +- philote_mdo/generated/data_pb2.pyi | 6 +- philote_mdo/generated/disciplines_pb2.py | 12 +- philote_mdo/generated/disciplines_pb2_grpc.py | 15 +- philote_mdo/openmdao/explicit.py | 5 + philote_mdo/openmdao/implicit.py | 5 + philote_mdo/openmdao/utils.py | 40 +- tests/test_dynamic_shapes.py | 509 ++++++++++++++++++ tests/test_openmdao_explicit_client.py | 1 + tests/test_openmdao_implicit_client.py | 1 + tests/test_openmdao_utils.py | 2 + 17 files changed, 809 insertions(+), 37 deletions(-) create mode 100644 philote_mdo/examples/flexible.py create mode 100644 tests/test_dynamic_shapes.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0426c32..f86ac6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- Added dynamic shapes for inputs and outputs. Disciplines can now + declare variables with `dynamic_shape=True` in `add_input` / + `add_output`, indicating that the client is allowed to set the + variable's shape at runtime. A new `SetVariableShapes` gRPC RPC + lets clients send resolved shapes after querying variable definitions. + The OpenMDAO bindings automatically map dynamic-shape variables to + `shape_by_conn=True` and send resolved shapes back to the server + (MDO-Standards/Philote-MDO#6). - Added support for struct (dict) options via the new `kStruct` DataType enum value, enabling complex nested data to be declared and passed as discipline options (#49). diff --git a/philote_mdo/examples/__init__.py b/philote_mdo/examples/__init__.py index 414b3e5..ae2f477 100644 --- a/philote_mdo/examples/__init__.py +++ b/philote_mdo/examples/__init__.py @@ -27,6 +27,7 @@ # the linked websites, of the information, products, or services contained # therein. The DoD does not exercise any editorial, security, or other # control over the information you may find at these locations. +from .flexible import FlexibleDiscipline from .paraboloid import Paraboloid from .quadratic import QuadradicImplicit from .rosenbrock import Rosenbrock diff --git a/philote_mdo/examples/flexible.py b/philote_mdo/examples/flexible.py new file mode 100644 index 0000000..a58c59e --- /dev/null +++ b/philote_mdo/examples/flexible.py @@ -0,0 +1,55 @@ +# Philote-Python +# +# Copyright 2022-2025 Christopher A. Lupp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# This work has been cleared for public release, distribution unlimited, case +# number: AFRL-2023-5713. +# +# The views expressed are those of the authors and do not reflect the +# official guidance or position of the United States Government, the +# Department of Defense or of the United States Air Force. +# +# Statement from DoD: The Appearance of external hyperlinks does not +# constitute endorsement by the United States Department of Defense (DoD) of +# the linked websites, of the information, products, or services contained +# therein. The DoD does not exercise any editorial, security, or other +# control over the information you may find at these locations. +import numpy as np +import philote_mdo.general as pmdo + + +class FlexibleDiscipline(pmdo.ExplicitDiscipline): + """ + Example explicit discipline with dynamic shapes. + + This discipline doubles every element of the input vector. The input + and output shapes are not fixed by the server — the client is + expected to set them via ``SetVariableShapes`` before computation. + """ + + def setup(self): + self.add_input("x", dynamic_shape=True, units="m") + self.add_output("y", dynamic_shape=True, units="m") + + def setup_partials(self): + self.declare_partials("y", "x") + + def compute(self, inputs, outputs): + outputs["y"] = 2.0 * inputs["x"] + + def compute_partials(self, inputs, partials): + n = inputs["x"].size + partials["y", "x"] = 2.0 * np.eye(n) diff --git a/philote_mdo/general/discipline.py b/philote_mdo/general/discipline.py index ed8414d..010975f 100644 --- a/philote_mdo/general/discipline.py +++ b/philote_mdo/general/discipline.py @@ -86,7 +86,7 @@ def add_option(self, name, type): ) self.options_list[name] = type - def add_input(self, name, shape=(1,), units=""): + def add_input(self, name, shape=(1,), units="", dynamic_shape=False): """ Define a continuous input. @@ -95,12 +95,16 @@ def add_input(self, name, shape=(1,), units=""): name : string the name of the input variable shape : tuple - the shape of the input variable + the shape of the input variable (ignored when dynamic_shape + is True) units : string the unit definition for the input variable + dynamic_shape : bool + when True, the client is allowed to set this variable's shape """ validate_name(name, "add_input") - validate_shape(shape, "add_input") + if not dynamic_shape: + validate_shape(shape, "add_input") validate_units(units, "add_input") if any(v.name == name and v.type == data.VariableType.kInput for v in self._var_meta): raise PhiloteValidationError( @@ -109,8 +113,10 @@ def add_input(self, name, shape=(1,), units=""): meta = data.VariableMetaData() meta.type = data.VariableType.kInput meta.name = name - meta.shape.extend(shape) + if not dynamic_shape: + meta.shape.extend(shape) meta.units = units + meta.dynamic_shape = dynamic_shape self._var_meta += [meta] def add_discrete_input(self, name, default=None): @@ -167,7 +173,7 @@ def add_discrete_output(self, name, default=None): meta.name = name self._discrete_var_meta += [meta] - def add_output(self, name, shape=(1,), units=""): + def add_output(self, name, shape=(1,), units="", dynamic_shape=False): """ Defines a continuous output. @@ -176,12 +182,16 @@ def add_output(self, name, shape=(1,), units=""): name : string the name of the output variable shape : tuple - the shape of the output variable + the shape of the output variable (ignored when dynamic_shape + is True) units : string the unit definition for the output variable + dynamic_shape : bool + when True, the client is allowed to set this variable's shape """ validate_name(name, "add_output") - validate_shape(shape, "add_output") + if not dynamic_shape: + validate_shape(shape, "add_output") validate_units(units, "add_output") if any(v.name == name and v.type == data.VariableType.kOutput for v in self._var_meta): raise PhiloteValidationError( @@ -190,17 +200,21 @@ def add_output(self, name, shape=(1,), units=""): out_meta = data.VariableMetaData() out_meta.type = data.VariableType.kOutput out_meta.name = name - out_meta.shape.extend(shape) + if not dynamic_shape: + out_meta.shape.extend(shape) out_meta.units = units + out_meta.dynamic_shape = dynamic_shape self._var_meta += [out_meta] if self._is_implicit: res_meta = data.VariableMetaData() res_meta.type = data.VariableType.kOutput res_meta.name = name - res_meta.shape.extend(shape) + if not dynamic_shape: + res_meta.shape.extend(shape) res_meta.units = units res_meta.type = data.VariableType.kResidual + res_meta.dynamic_shape = dynamic_shape self._var_meta += [res_meta] def declare_partials(self, func, var): diff --git a/philote_mdo/general/discipline_client.py b/philote_mdo/general/discipline_client.py index dfbed15..684f5e2 100644 --- a/philote_mdo/general/discipline_client.py +++ b/philote_mdo/general/discipline_client.py @@ -154,6 +154,71 @@ def get_partials_definitions(self): if message.name not in self._partials_meta: self._partials_meta += [message] + def get_dynamic_variables(self): + """ + Returns a list of variable metadata entries that have + ``dynamic_shape`` set to ``True``. + """ + return [v for v in self._var_meta if v.dynamic_shape] + + def set_variable_shape(self, name, shape, var_type=data.VariableType.kInput): + """ + Creates a ``VariableMetaData`` message for setting a dynamic + variable's shape. + + Parameters + ---------- + name : str + the name of the variable + shape : tuple + the desired shape + var_type : VariableType + the variable type (kInput or kOutput) + + Returns + ------- + VariableMetaData + protobuf message ready for ``send_variable_shapes`` + """ + meta = data.VariableMetaData() + meta.type = var_type + meta.name = name + meta.shape.extend(shape) + return meta + + def send_variable_shapes(self, variable_metadata): + """ + Sends shapes for variables flagged as ``dynamic_shape``. + + Call after ``get_variable_definitions()`` and before compute + calls. + + Parameters + ---------- + variable_metadata : list of VariableMetaData + shapes for dynamic variables + """ + self._disc_stub.SetVariableShapes(iter(variable_metadata)) + + # update local metadata to reflect the new shapes + for meta in variable_metadata: + for var in self._var_meta: + if var.name == meta.name and var.type == meta.type: + var.shape[:] = [] + var.shape.extend(meta.shape) + break + + # for implicit outputs, also update the matching residual + if meta.type == data.VariableType.kOutput: + for var in self._var_meta: + if ( + var.name == meta.name + and var.type == data.VariableType.kResidual + ): + var.shape[:] = [] + var.shape.extend(meta.shape) + break + def _assemble_input_messages( self, inputs, outputs=None, discrete_inputs=None, discrete_outputs=None ): diff --git a/philote_mdo/general/discipline_server.py b/philote_mdo/general/discipline_server.py index a7baee6..2a0d956 100644 --- a/philote_mdo/general/discipline_server.py +++ b/philote_mdo/general/discipline_server.py @@ -35,7 +35,7 @@ from google.protobuf.empty_pb2 import Empty from google.protobuf import struct_pb2 from philote_mdo.utils import PairDict, get_flattened_view -from philote_mdo.utils.validation import PhiloteValidationError +from philote_mdo.utils.validation import PhiloteValidationError, validate_shape class DisciplineServer(disc.DisciplineService): @@ -170,6 +170,57 @@ def GetPartialDefinitions(self, request, context): for jac in self._discipline._partials_meta: yield jac + def SetVariableShapes(self, request_iterator, context): + """ + Receives client-defined shapes for variables flagged as + dynamic_shape. + + The client must call this RPC after GetVariableDefinitions and + before any compute RPCs for disciplines that contain variables + with dynamic shapes. + """ + try: + for meta in request_iterator: + validate_shape(tuple(meta.shape), "SetVariableShapes") + + # find the matching variable and update its shape + for var in self._discipline._var_meta: + if var.name == meta.name and var.type == meta.type: + if not var.dynamic_shape: + raise PhiloteValidationError( + f"Variable '{meta.name}' does not allow " + f"dynamic shapes." + ) + var.shape[:] = [] + var.shape.extend(meta.shape) + break + else: + raise PhiloteValidationError( + f"SetVariableShapes: variable '{meta.name}' " + f"not found." + ) + + # if the variable is an output on an implicit discipline, + # also update the matching residual entry + if meta.type == data.VariableType.kOutput: + for var in self._discipline._var_meta: + if ( + var.name == meta.name + and var.type == data.VariableType.kResidual + and var.dynamic_shape + ): + var.shape[:] = [] + var.shape.extend(meta.shape) + break + + return Empty() + except PhiloteValidationError as e: + context.abort(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + except Exception as e: + context.abort( + grpc.StatusCode.INTERNAL, f"SetVariableShapes failed: {e}" + ) + def preallocate_inputs(self, inputs, flat_inputs, outputs=None, flat_outputs=None): """ Preallocates the inputs before receiving data from the client. @@ -178,6 +229,14 @@ def preallocate_inputs(self, inputs, flat_inputs, outputs=None, flat_outputs=Non inputs to evaluate the residuals and the partials of the residuals. """ for var in self._discipline._var_meta: + # validate that dynamic-shape variables have been resolved + if var.dynamic_shape and len(var.shape) == 0: + raise PhiloteValidationError( + f"Variable '{var.name}' has dynamic_shape=True but " + f"no shape has been set. Call SetVariableShapes " + f"before computing." + ) + if var.type == data.kInput: inputs[var.name] = np.zeros(var.shape) flat_inputs[var.name] = get_flattened_view(inputs[var.name]) diff --git a/philote_mdo/generated/data_pb2.py b/philote_mdo/generated/data_pb2.py index b6029b2..668e1ab 100644 --- a/philote_mdo/generated/data_pb2.py +++ b/philote_mdo/generated/data_pb2.py @@ -7,17 +7,17 @@ _runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 27, 2, '', 'data.proto') _sym_db = _symbol_database.Default() from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x07philote\x1a\x1cgoogle/protobuf/struct.proto"}\n\x14DisciplineProperties\x12\x12\n\ncontinuous\x18\x01 \x01(\x08\x12\x16\n\x0edifferentiable\x18\x02 \x01(\x08\x12\x1a\n\x12provides_gradients\x18\x03 \x01(\x08\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"#\n\rStreamOptions\x12\x12\n\nnum_double\x18\x01 \x01(\x03"?\n\x0bOptionsList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x1f\n\x04type\x18\x02 \x03(\x0e2\x11.philote.DataType"=\n\x11DisciplineOptions\x12(\n\x07options\x18\x01 \x01(\x0b2\x17.google.protobuf.Struct"c\n\x10VariableMetaData\x12#\n\x04type\x18\x01 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05shape\x18\x04 \x03(\x03\x12\r\n\x05units\x18\x05 \x01(\t"@\n\x10PartialsMetaData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x03"u\n\x05Array\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05start\x18\x03 \x01(\x03\x12\x0b\n\x03end\x18\x04 \x01(\x03\x12#\n\x04type\x18\x05 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04data\x18\x06 \x03(\x01"l\n\x10DiscreteVariable\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x04type\x18\x02 \x01(\x0e2\x15.philote.VariableType\x12%\n\x05value\x18\x03 \x01(\x0b2\x16.google.protobuf.Value"q\n\x0fVariableMessage\x12$\n\ncontinuous\x18\x01 \x01(\x0b2\x0e.philote.ArrayH\x00\x12-\n\x08discrete\x18\x02 \x01(\x0b2\x19.philote.DiscreteVariableH\x00B\t\n\x07payload*F\n\x08DataType\x12\t\n\x05kBool\x10\x00\x12\x08\n\x04kInt\x10\x01\x12\x0b\n\x07kDouble\x10\x02\x12\x0b\n\x07kString\x10\x03\x12\x0b\n\x07kStruct\x10\x04*m\n\x0cVariableType\x12\n\n\x06kInput\x10\x00\x12\x12\n\x0ekDiscreteInput\x10\x01\x12\r\n\tkResidual\x10\x02\x12\x0b\n\x07kOutput\x10\x03\x12\x13\n\x0fkDiscreteOutput\x10\x04\x12\x0c\n\x08kPartial\x10\x05B\x11\n\x0forg.philote.mdob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x07philote\x1a\x1cgoogle/protobuf/struct.proto"}\n\x14DisciplineProperties\x12\x12\n\ncontinuous\x18\x01 \x01(\x08\x12\x16\n\x0edifferentiable\x18\x02 \x01(\x08\x12\x1a\n\x12provides_gradients\x18\x03 \x01(\x08\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"#\n\rStreamOptions\x12\x12\n\nnum_double\x18\x01 \x01(\x03"?\n\x0bOptionsList\x12\x0f\n\x07options\x18\x01 \x03(\t\x12\x1f\n\x04type\x18\x02 \x03(\x0e2\x11.philote.DataType"=\n\x11DisciplineOptions\x12(\n\x07options\x18\x01 \x01(\x0b2\x17.google.protobuf.Struct"z\n\x10VariableMetaData\x12#\n\x04type\x18\x01 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05shape\x18\x04 \x03(\x03\x12\r\n\x05units\x18\x05 \x01(\t\x12\x15\n\rdynamic_shape\x18\x06 \x01(\x08"@\n\x10PartialsMetaData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x03"u\n\x05Array\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07subname\x18\x02 \x01(\t\x12\r\n\x05start\x18\x03 \x01(\x03\x12\x0b\n\x03end\x18\x04 \x01(\x03\x12#\n\x04type\x18\x05 \x01(\x0e2\x15.philote.VariableType\x12\x0c\n\x04data\x18\x06 \x03(\x01"l\n\x10DiscreteVariable\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x04type\x18\x02 \x01(\x0e2\x15.philote.VariableType\x12%\n\x05value\x18\x03 \x01(\x0b2\x16.google.protobuf.Value"q\n\x0fVariableMessage\x12$\n\ncontinuous\x18\x01 \x01(\x0b2\x0e.philote.ArrayH\x00\x12-\n\x08discrete\x18\x02 \x01(\x0b2\x19.philote.DiscreteVariableH\x00B\t\n\x07payload*F\n\x08DataType\x12\t\n\x05kBool\x10\x00\x12\x08\n\x04kInt\x10\x01\x12\x0b\n\x07kDouble\x10\x02\x12\x0b\n\x07kString\x10\x03\x12\x0b\n\x07kStruct\x10\x04*m\n\x0cVariableType\x12\n\n\x06kInput\x10\x00\x12\x12\n\x0ekDiscreteInput\x10\x01\x12\r\n\tkResidual\x10\x02\x12\x0b\n\x07kOutput\x10\x03\x12\x13\n\x0fkDiscreteOutput\x10\x04\x12\x0c\n\x08kPartial\x10\x05B\x11\n\x0forg.philote.mdob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'data_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\x0forg.philote.mdo' - _globals['_DATATYPE']._serialized_start = 856 - _globals['_DATATYPE']._serialized_end = 926 - _globals['_VARIABLETYPE']._serialized_start = 928 - _globals['_VARIABLETYPE']._serialized_end = 1037 + _globals['_DATATYPE']._serialized_start = 879 + _globals['_DATATYPE']._serialized_end = 949 + _globals['_VARIABLETYPE']._serialized_start = 951 + _globals['_VARIABLETYPE']._serialized_end = 1060 _globals['_DISCIPLINEPROPERTIES']._serialized_start = 53 _globals['_DISCIPLINEPROPERTIES']._serialized_end = 178 _globals['_STREAMOPTIONS']._serialized_start = 180 @@ -27,12 +27,12 @@ _globals['_DISCIPLINEOPTIONS']._serialized_start = 282 _globals['_DISCIPLINEOPTIONS']._serialized_end = 343 _globals['_VARIABLEMETADATA']._serialized_start = 345 - _globals['_VARIABLEMETADATA']._serialized_end = 444 - _globals['_PARTIALSMETADATA']._serialized_start = 446 - _globals['_PARTIALSMETADATA']._serialized_end = 510 - _globals['_ARRAY']._serialized_start = 512 - _globals['_ARRAY']._serialized_end = 629 - _globals['_DISCRETEVARIABLE']._serialized_start = 631 - _globals['_DISCRETEVARIABLE']._serialized_end = 739 - _globals['_VARIABLEMESSAGE']._serialized_start = 741 - _globals['_VARIABLEMESSAGE']._serialized_end = 854 \ No newline at end of file + _globals['_VARIABLEMETADATA']._serialized_end = 467 + _globals['_PARTIALSMETADATA']._serialized_start = 469 + _globals['_PARTIALSMETADATA']._serialized_end = 533 + _globals['_ARRAY']._serialized_start = 535 + _globals['_ARRAY']._serialized_end = 652 + _globals['_DISCRETEVARIABLE']._serialized_start = 654 + _globals['_DISCRETEVARIABLE']._serialized_end = 762 + _globals['_VARIABLEMESSAGE']._serialized_start = 764 + _globals['_VARIABLEMESSAGE']._serialized_end = 877 \ No newline at end of file diff --git a/philote_mdo/generated/data_pb2.pyi b/philote_mdo/generated/data_pb2.pyi index 13c800f..500c23f 100644 --- a/philote_mdo/generated/data_pb2.pyi +++ b/philote_mdo/generated/data_pb2.pyi @@ -77,17 +77,19 @@ class DisciplineOptions(_message.Message): ... class VariableMetaData(_message.Message): - __slots__ = ('type', 'name', 'shape', 'units') + __slots__ = ('type', 'name', 'shape', 'units', 'dynamic_shape') TYPE_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] SHAPE_FIELD_NUMBER: _ClassVar[int] UNITS_FIELD_NUMBER: _ClassVar[int] + DYNAMIC_SHAPE_FIELD_NUMBER: _ClassVar[int] type: VariableType name: str shape: _containers.RepeatedScalarFieldContainer[int] units: str + dynamic_shape: bool - def __init__(self, type: _Optional[_Union[VariableType, str]]=..., name: _Optional[str]=..., shape: _Optional[_Iterable[int]]=..., units: _Optional[str]=...) -> None: + def __init__(self, type: _Optional[_Union[VariableType, str]]=..., name: _Optional[str]=..., shape: _Optional[_Iterable[int]]=..., units: _Optional[str]=..., dynamic_shape: bool=...) -> None: ... class PartialsMetaData(_message.Message): diff --git a/philote_mdo/generated/disciplines_pb2.py b/philote_mdo/generated/disciplines_pb2.py index a78cc88..1425ba1 100644 --- a/philote_mdo/generated/disciplines_pb2.py +++ b/philote_mdo/generated/disciplines_pb2.py @@ -8,7 +8,7 @@ _sym_db = _symbol_database.Default() from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 from . import data_pb2 as data__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11disciplines.proto\x12\x07philote\x1a\x1bgoogle/protobuf/empty.proto\x1a\ndata.proto2\x84\x04\n\x11DisciplineService\x12B\n\x07GetInfo\x12\x16.google.protobuf.Empty\x1a\x1d.philote.DisciplineProperties"\x00\x12D\n\x10SetStreamOptions\x12\x16.philote.StreamOptions\x1a\x16.google.protobuf.Empty"\x00\x12E\n\x13GetAvailableOptions\x12\x16.google.protobuf.Empty\x1a\x14.philote.OptionsList"\x00\x12B\n\nSetOptions\x12\x1a.philote.DisciplineOptions\x1a\x16.google.protobuf.Empty"\x00\x129\n\x05Setup\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty"\x00\x12O\n\x16GetVariableDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.VariableMetaData"\x000\x01\x12N\n\x15GetPartialDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.PartialsMetaData"\x000\x012\xab\x01\n\x0fExplicitService\x12K\n\x0fComputeFunction\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12K\n\x0fComputeGradient\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x012\x81\x02\n\x0fImplicitService\x12L\n\x10ComputeResiduals\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12J\n\x0eSolveResiduals\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12T\n\x18ComputeResidualGradients\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01B\x11\n\x0forg.philote.mdob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11disciplines.proto\x12\x07philote\x1a\x1bgoogle/protobuf/empty.proto\x1a\ndata.proto2\xd0\x04\n\x11DisciplineService\x12B\n\x07GetInfo\x12\x16.google.protobuf.Empty\x1a\x1d.philote.DisciplineProperties"\x00\x12D\n\x10SetStreamOptions\x12\x16.philote.StreamOptions\x1a\x16.google.protobuf.Empty"\x00\x12E\n\x13GetAvailableOptions\x12\x16.google.protobuf.Empty\x1a\x14.philote.OptionsList"\x00\x12B\n\nSetOptions\x12\x1a.philote.DisciplineOptions\x1a\x16.google.protobuf.Empty"\x00\x129\n\x05Setup\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty"\x00\x12O\n\x16GetVariableDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.VariableMetaData"\x000\x01\x12N\n\x15GetPartialDefinitions\x12\x16.google.protobuf.Empty\x1a\x19.philote.PartialsMetaData"\x000\x01\x12J\n\x11SetVariableShapes\x12\x19.philote.VariableMetaData\x1a\x16.google.protobuf.Empty"\x00(\x012\xab\x01\n\x0fExplicitService\x12K\n\x0fComputeFunction\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12K\n\x0fComputeGradient\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x012\x81\x02\n\x0fImplicitService\x12L\n\x10ComputeResiduals\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12J\n\x0eSolveResiduals\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01\x12T\n\x18ComputeResidualGradients\x12\x18.philote.VariableMessage\x1a\x18.philote.VariableMessage"\x00(\x010\x01B\x11\n\x0forg.philote.mdob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'disciplines_pb2', _globals) @@ -16,8 +16,8 @@ _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\x0forg.philote.mdo' _globals['_DISCIPLINESERVICE']._serialized_start = 72 - _globals['_DISCIPLINESERVICE']._serialized_end = 588 - _globals['_EXPLICITSERVICE']._serialized_start = 591 - _globals['_EXPLICITSERVICE']._serialized_end = 762 - _globals['_IMPLICITSERVICE']._serialized_start = 765 - _globals['_IMPLICITSERVICE']._serialized_end = 1022 \ No newline at end of file + _globals['_DISCIPLINESERVICE']._serialized_end = 664 + _globals['_EXPLICITSERVICE']._serialized_start = 667 + _globals['_EXPLICITSERVICE']._serialized_end = 838 + _globals['_IMPLICITSERVICE']._serialized_start = 841 + _globals['_IMPLICITSERVICE']._serialized_end = 1098 \ No newline at end of file diff --git a/philote_mdo/generated/disciplines_pb2_grpc.py b/philote_mdo/generated/disciplines_pb2_grpc.py index 1b23738..638bd69 100644 --- a/philote_mdo/generated/disciplines_pb2_grpc.py +++ b/philote_mdo/generated/disciplines_pb2_grpc.py @@ -34,6 +34,7 @@ def __init__(self, channel): self.Setup = channel.unary_unary('/philote.DisciplineService/Setup', request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, _registered_method=True) self.GetVariableDefinitions = channel.unary_stream('/philote.DisciplineService/GetVariableDefinitions', request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, response_deserializer=data__pb2.VariableMetaData.FromString, _registered_method=True) self.GetPartialDefinitions = channel.unary_stream('/philote.DisciplineService/GetPartialDefinitions', request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, response_deserializer=data__pb2.PartialsMetaData.FromString, _registered_method=True) + self.SetVariableShapes = channel.stream_unary('/philote.DisciplineService/SetVariableShapes', request_serializer=data__pb2.VariableMetaData.SerializeToString, response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, _registered_method=True) class DisciplineServiceServicer(object): """Generic Discipline Definition @@ -91,8 +92,16 @@ def GetPartialDefinitions(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def SetVariableShapes(self, request_iterator, context): + """Sets shapes for variables flagged as dynamic_shape. + Must be called after GetVariableDefinitions and before compute RPCs. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_DisciplineServiceServicer_to_server(servicer, server): - rpc_method_handlers = {'GetInfo': grpc.unary_unary_rpc_method_handler(servicer.GetInfo, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.DisciplineProperties.SerializeToString), 'SetStreamOptions': grpc.unary_unary_rpc_method_handler(servicer.SetStreamOptions, request_deserializer=data__pb2.StreamOptions.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString), 'GetAvailableOptions': grpc.unary_unary_rpc_method_handler(servicer.GetAvailableOptions, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.OptionsList.SerializeToString), 'SetOptions': grpc.unary_unary_rpc_method_handler(servicer.SetOptions, request_deserializer=data__pb2.DisciplineOptions.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString), 'Setup': grpc.unary_unary_rpc_method_handler(servicer.Setup, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString), 'GetVariableDefinitions': grpc.unary_stream_rpc_method_handler(servicer.GetVariableDefinitions, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.VariableMetaData.SerializeToString), 'GetPartialDefinitions': grpc.unary_stream_rpc_method_handler(servicer.GetPartialDefinitions, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.PartialsMetaData.SerializeToString)} + rpc_method_handlers = {'GetInfo': grpc.unary_unary_rpc_method_handler(servicer.GetInfo, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.DisciplineProperties.SerializeToString), 'SetStreamOptions': grpc.unary_unary_rpc_method_handler(servicer.SetStreamOptions, request_deserializer=data__pb2.StreamOptions.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString), 'GetAvailableOptions': grpc.unary_unary_rpc_method_handler(servicer.GetAvailableOptions, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.OptionsList.SerializeToString), 'SetOptions': grpc.unary_unary_rpc_method_handler(servicer.SetOptions, request_deserializer=data__pb2.DisciplineOptions.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString), 'Setup': grpc.unary_unary_rpc_method_handler(servicer.Setup, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString), 'GetVariableDefinitions': grpc.unary_stream_rpc_method_handler(servicer.GetVariableDefinitions, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.VariableMetaData.SerializeToString), 'GetPartialDefinitions': grpc.unary_stream_rpc_method_handler(servicer.GetPartialDefinitions, request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, response_serializer=data__pb2.PartialsMetaData.SerializeToString), 'SetVariableShapes': grpc.stream_unary_rpc_method_handler(servicer.SetVariableShapes, request_deserializer=data__pb2.VariableMetaData.FromString, response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString)} generic_handler = grpc.method_handlers_generic_handler('philote.DisciplineService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) server.add_registered_method_handlers('philote.DisciplineService', rpc_method_handlers) @@ -132,6 +141,10 @@ def GetVariableDefinitions(request, target, options=(), channel_credentials=None def GetPartialDefinitions(request, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): return grpc.experimental.unary_stream(request, target, '/philote.DisciplineService/GetPartialDefinitions', google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, data__pb2.PartialsMetaData.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) + @staticmethod + def SetVariableShapes(request_iterator, target, options=(), channel_credentials=None, call_credentials=None, insecure=False, compression=None, wait_for_ready=None, timeout=None, metadata=None): + return grpc.experimental.stream_unary(request_iterator, target, '/philote.DisciplineService/SetVariableShapes', data__pb2.VariableMetaData.SerializeToString, google_dot_protobuf_dot_empty__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata, _registered_method=True) + class ExplicitServiceStub(object): """Definition of the generic Explicit Component RPC """ diff --git a/philote_mdo/openmdao/explicit.py b/philote_mdo/openmdao/explicit.py index f80c9ef..2e02b9f 100644 --- a/philote_mdo/openmdao/explicit.py +++ b/philote_mdo/openmdao/explicit.py @@ -200,6 +200,10 @@ def setup_partials(self): the server's partial derivative metadata. The component can compute partials either analytically (if supported by the server) or via finite differencing. + If any variables were declared with ``dynamic_shape=True`` on the server, + the shapes resolved by OpenMDAO (e.g. via ``shape_by_conn``) are sent + back to the server before querying partial definitions. + The method is called automatically by OpenMDAO during component setup and should not be called directly by users. @@ -209,6 +213,7 @@ def setup_partials(self): - Both analytic and finite difference methods are supported - Sparsity patterns are preserved when available from server metadata """ + utils.send_resolved_shapes(self) utils.client_setup_partials(self) def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): diff --git a/philote_mdo/openmdao/implicit.py b/philote_mdo/openmdao/implicit.py index 2176bdc..70c8c17 100644 --- a/philote_mdo/openmdao/implicit.py +++ b/philote_mdo/openmdao/implicit.py @@ -209,6 +209,10 @@ def setup_partials(self): pairs based on the server's partial derivative metadata. For implicit components, this includes both dR/dinputs and dR/doutputs terms needed for Newton-type solvers. + If any variables were declared with ``dynamic_shape=True`` on the server, + the shapes resolved by OpenMDAO (e.g. via ``shape_by_conn``) are sent + back to the server before querying partial definitions. + The method is called automatically by OpenMDAO during component setup and should not be called directly by users. @@ -218,6 +222,7 @@ def setup_partials(self): - Both dR/dinputs and dR/doutputs partials are declared - Sparsity patterns are preserved when available from server metadata """ + utils.send_resolved_shapes(self) utils.client_setup_partials(self) def apply_nonlinear(self, inputs, outputs, residuals, discrete_inputs=None, discrete_outputs=None): diff --git a/philote_mdo/openmdao/utils.py b/philote_mdo/openmdao/utils.py index ea11503..d7a599e 100644 --- a/philote_mdo/openmdao/utils.py +++ b/philote_mdo/openmdao/utils.py @@ -74,11 +74,19 @@ def client_setup(comp): else: units = var.units - if var.type == data.kInput: - comp.add_input(var.name, shape=tuple(var.shape), units=units) + if var.dynamic_shape: + # let OpenMDAO resolve the shape from connections + if var.type == data.kInput: + comp.add_input(var.name, shape_by_conn=True, units=units) - if var.type == data.kOutput: - comp.add_output(var.name, shape=tuple(var.shape), units=units) + if var.type == data.kOutput: + comp.add_output(var.name, shape_by_conn=True, units=units) + else: + if var.type == data.kInput: + comp.add_input(var.name, shape=tuple(var.shape), units=units) + + if var.type == data.kOutput: + comp.add_output(var.name, shape=tuple(var.shape), units=units) # define discrete inputs and outputs for var in comp._client._discrete_var_meta: @@ -89,6 +97,30 @@ def client_setup(comp): comp.add_discrete_output(var.name, val=None) +def send_resolved_shapes(comp): + """ + Sends resolved shapes for dynamic-shape variables back to the server. + + After OpenMDAO resolves shapes (e.g. via ``shape_by_conn``), this + function reads the resolved metadata from the component and transmits + the shapes to the remote discipline server. + """ + dynamic_shapes = [] + for var in comp._client._var_meta: + if not var.dynamic_shape: + continue + + resolved_meta = comp._var_rel2meta[var.name] + dynamic_shapes.append( + comp._client.set_variable_shape( + var.name, resolved_meta["shape"], var.type + ) + ) + + if dynamic_shapes: + comp._client.send_variable_shapes(dynamic_shapes) + + def client_setup_partials(comp): """ Sets up the partials for the OpenMDAO component. diff --git a/tests/test_dynamic_shapes.py b/tests/test_dynamic_shapes.py new file mode 100644 index 0000000..6ec15f5 --- /dev/null +++ b/tests/test_dynamic_shapes.py @@ -0,0 +1,509 @@ +# Philote-Python +# +# Copyright 2022-2025 Christopher A. Lupp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# This work has been cleared for public release, distribution unlimited, case +# number: AFRL-2023-5713. +# +# The views expressed are those of the authors and do not reflect the +# official guidance or position of the United States Government, the +# Department of Defense or of the United States Air Force. +# +# Statement from DoD: The Appearance of external hyperlinks does not +# constitute endorsement by the United States Department of Defense (DoD) of +# the linked websites, of the information, products, or services contained +# therein. The DoD does not exercise any editorial, security, or other +# control over the information you may find at these locations. +from concurrent import futures +import unittest +from unittest.mock import Mock + +import grpc +import numpy as np +import numpy.testing as npt + +from philote_mdo.general import ( + Discipline, + DisciplineServer, + ExplicitDiscipline, + ExplicitServer, + ExplicitClient, + ImplicitDiscipline, + ImplicitServer, + ImplicitClient, +) +from philote_mdo.utils.validation import PhiloteValidationError +from philote_mdo.examples import FlexibleDiscipline +import philote_mdo.generated.data_pb2 as data + + +# --------------------------------------------------------------- +# Unit tests: Discipline base class +# --------------------------------------------------------------- +class TestDynamicShapeDiscipline(unittest.TestCase): + """Unit tests for dynamic_shape flag on the Discipline base class.""" + + def test_add_input_dynamic_shape(self): + """add_input with dynamic_shape=True stores the flag and omits shape.""" + disc = Discipline() + disc.add_input("x", dynamic_shape=True) + + self.assertEqual(len(disc._var_meta), 1) + meta = disc._var_meta[0] + self.assertEqual(meta.name, "x") + self.assertTrue(meta.dynamic_shape) + self.assertEqual(list(meta.shape), []) + + def test_add_output_dynamic_shape(self): + """add_output with dynamic_shape=True stores the flag and omits shape.""" + disc = Discipline() + disc.add_output("y", dynamic_shape=True) + + self.assertEqual(len(disc._var_meta), 1) + meta = disc._var_meta[0] + self.assertEqual(meta.name, "y") + self.assertTrue(meta.dynamic_shape) + self.assertEqual(list(meta.shape), []) + + def test_add_input_static_shape_unchanged(self): + """add_input without dynamic_shape behaves as before.""" + disc = Discipline() + disc.add_input("x", shape=(3, 2), units="m") + + meta = disc._var_meta[0] + self.assertFalse(meta.dynamic_shape) + self.assertEqual(list(meta.shape), [3, 2]) + + def test_add_output_static_shape_unchanged(self): + """add_output without dynamic_shape behaves as before.""" + disc = Discipline() + disc.add_output("y", shape=(4,)) + + meta = disc._var_meta[0] + self.assertFalse(meta.dynamic_shape) + self.assertEqual(list(meta.shape), [4]) + + def test_dynamic_shape_skips_shape_validation(self): + """dynamic_shape=True should not validate the default shape arg.""" + disc = Discipline() + # Should not raise even though we pass no valid shape + disc.add_input("x", dynamic_shape=True) + disc.add_output("y", dynamic_shape=True) + self.assertEqual(len(disc._var_meta), 2) + + def test_implicit_output_dynamic_shape_creates_residual(self): + """For implicit disciplines, dynamic output also creates a residual entry.""" + disc = Discipline() + disc._is_implicit = True + disc.add_output("y", dynamic_shape=True, units="m") + + # Should have output and residual + self.assertEqual(len(disc._var_meta), 2) + out = disc._var_meta[0] + res = disc._var_meta[1] + self.assertEqual(out.type, data.VariableType.kOutput) + self.assertTrue(out.dynamic_shape) + self.assertEqual(res.type, data.VariableType.kResidual) + self.assertTrue(res.dynamic_shape) + + +# --------------------------------------------------------------- +# Unit tests: DisciplineServer.SetVariableShapes +# --------------------------------------------------------------- +class TestSetVariableShapesRPC(unittest.TestCase): + """Unit tests for the SetVariableShapes RPC handler.""" + + def _make_server_with_dynamic_disc(self): + server = DisciplineServer() + disc = Discipline() + disc.add_input("x", dynamic_shape=True) + disc.add_output("y", dynamic_shape=True) + disc.add_input("z", shape=(2,)) # static + server._discipline = disc + return server + + def test_set_shapes_for_dynamic_variables(self): + """SetVariableShapes updates shapes on dynamic variables.""" + server = self._make_server_with_dynamic_disc() + context = Mock() + + x_meta = data.VariableMetaData( + name="x", type=data.VariableType.kInput, shape=[5] + ) + y_meta = data.VariableMetaData( + name="y", type=data.VariableType.kOutput, shape=[5] + ) + server.SetVariableShapes(iter([x_meta, y_meta]), context) + + # verify shapes were updated + for var in server._discipline._var_meta: + if var.name == "x": + self.assertEqual(list(var.shape), [5]) + if var.name == "y" and var.type == data.VariableType.kOutput: + self.assertEqual(list(var.shape), [5]) + + def test_reject_shape_for_static_variable(self): + """SetVariableShapes aborts when targeting a non-dynamic variable.""" + server = self._make_server_with_dynamic_disc() + context = Mock() + + z_meta = data.VariableMetaData( + name="z", type=data.VariableType.kInput, shape=[10] + ) + server.SetVariableShapes(iter([z_meta]), context) + context.abort.assert_called_once() + + def test_reject_unknown_variable(self): + """SetVariableShapes aborts when the variable name is not found.""" + server = self._make_server_with_dynamic_disc() + context = Mock() + + meta = data.VariableMetaData( + name="nope", type=data.VariableType.kInput, shape=[3] + ) + server.SetVariableShapes(iter([meta]), context) + context.abort.assert_called_once() + + def test_reject_invalid_shape(self): + """SetVariableShapes aborts on invalid (non-positive) shape.""" + server = self._make_server_with_dynamic_disc() + context = Mock() + + meta = data.VariableMetaData( + name="x", type=data.VariableType.kInput, shape=[-1] + ) + server.SetVariableShapes(iter([meta]), context) + context.abort.assert_called_once() + + def test_preallocate_raises_when_shape_unset(self): + """preallocate_inputs raises if a dynamic variable has no shape.""" + server = self._make_server_with_dynamic_disc() + + with self.assertRaises(PhiloteValidationError): + server.preallocate_inputs({}, {}) + + +# --------------------------------------------------------------- +# Unit tests: DisciplineClient helpers +# --------------------------------------------------------------- +class TestDynamicShapeClient(unittest.TestCase): + """Unit tests for client-side dynamic shape helpers.""" + + def test_set_variable_shape(self): + """set_variable_shape creates correct VariableMetaData.""" + client = ExplicitClient.__new__(ExplicitClient) + client._var_meta = [] + + meta = client.set_variable_shape("x", (3, 2), data.VariableType.kInput) + + self.assertEqual(meta.name, "x") + self.assertEqual(list(meta.shape), [3, 2]) + self.assertEqual(meta.type, data.VariableType.kInput) + + def test_get_dynamic_variables(self): + """get_dynamic_variables filters correctly.""" + client = ExplicitClient.__new__(ExplicitClient) + + static_var = data.VariableMetaData( + name="a", type=data.kInput, shape=[1], dynamic_shape=False + ) + dynamic_var = data.VariableMetaData( + name="b", type=data.kInput, dynamic_shape=True + ) + client._var_meta = [static_var, dynamic_var] + + result = client.get_dynamic_variables() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].name, "b") + + +# --------------------------------------------------------------- +# Integration test: FlexibleDiscipline round-trip +# --------------------------------------------------------------- +class TestFlexibleDisciplineIntegration(unittest.TestCase): + """End-to-end test: dynamic shapes over gRPC.""" + + def test_flexible_compute(self): + """Client sets shapes, then computes with the FlexibleDiscipline.""" + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + discipline = ExplicitServer(discipline=FlexibleDiscipline()) + discipline.attach_to_server(server) + + server.add_insecure_port("[::]:50051") + server.start() + + try: + client = ExplicitClient( + channel=grpc.insecure_channel("localhost:50051") + ) + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + + # verify dynamic_shape flags came through + dynamic = client.get_dynamic_variables() + self.assertEqual(len(dynamic), 2) + + # set shapes + shapes = [ + client.set_variable_shape("x", (4,), data.VariableType.kInput), + client.set_variable_shape( + "y", (4,), data.VariableType.kOutput + ), + ] + client.send_variable_shapes(shapes) + client.get_partials_definitions() + + # compute + inputs = {"x": np.array([1.0, 2.0, 3.0, 4.0])} + outputs = client.run_compute(inputs) + + npt.assert_array_almost_equal( + outputs["y"], [2.0, 4.0, 6.0, 8.0] + ) + finally: + server.stop(0) + + def test_flexible_compute_partials(self): + """Client sets shapes, then computes partials.""" + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + discipline = ExplicitServer(discipline=FlexibleDiscipline()) + discipline.attach_to_server(server) + + server.add_insecure_port("[::]:50051") + server.start() + + try: + client = ExplicitClient( + channel=grpc.insecure_channel("localhost:50051") + ) + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + + shapes = [ + client.set_variable_shape("x", (3,), data.VariableType.kInput), + client.set_variable_shape( + "y", (3,), data.VariableType.kOutput + ), + ] + client.send_variable_shapes(shapes) + client.get_partials_definitions() + + inputs = {"x": np.array([1.0, 2.0, 3.0])} + jac = client.run_compute_partials(inputs) + + expected = 2.0 * np.eye(3) + npt.assert_array_almost_equal(jac["y", "x"], expected) + finally: + server.stop(0) + + def test_backward_compat_static_shapes(self): + """Existing static-shape disciplines work without calling SetVariableShapes.""" + from philote_mdo.examples import Paraboloid + + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + discipline = ExplicitServer(discipline=Paraboloid()) + discipline.attach_to_server(server) + + server.add_insecure_port("[::]:50051") + server.start() + + try: + client = ExplicitClient( + channel=grpc.insecure_channel("localhost:50051") + ) + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + client.get_partials_definitions() + + # no dynamic variables + self.assertEqual(len(client.get_dynamic_variables()), 0) + + inputs = {"x": np.array([1.0]), "y": np.array([2.0])} + outputs = client.run_compute(inputs) + self.assertAlmostEqual(outputs["f_xy"][0], 39.0) + finally: + server.stop(0) + + +# --------------------------------------------------------------- +# Integration test: implicit discipline with dynamic shapes +# --------------------------------------------------------------- +class DynamicImplicit(ImplicitDiscipline): + """Implicit discipline with dynamic shapes for residual testing.""" + + def setup(self): + self.add_input("a", shape=(1,)) + self.add_output("x", dynamic_shape=True) + + def setup_partials(self): + self.declare_partials("x", "a") + self.declare_partials("x", "x") + + def compute_residuals(self, inputs, outputs, residuals): + residuals["x"] = outputs["x"] ** 2 - inputs["a"] + + def solve_residuals(self, inputs, outputs): + outputs["x"] = np.sqrt(np.abs(inputs["a"])) + + def compute_residual_partials(self, inputs, outputs, partials): + partials["x", "a"] = -np.ones(inputs["a"].shape) + partials["x", "x"] = 2.0 * np.diag(outputs["x"].ravel()) + + +class TestDynamicImplicitIntegration(unittest.TestCase): + """Integration test for implicit discipline with dynamic shapes.""" + + def test_implicit_dynamic_shape_residual(self): + """SetVariableShapes updates residual entries for implicit disciplines.""" + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + discipline = ImplicitServer(discipline=DynamicImplicit()) + discipline.attach_to_server(server) + + server.add_insecure_port("[::]:50051") + server.start() + + try: + client = ImplicitClient( + channel=grpc.insecure_channel("localhost:50051") + ) + client.send_stream_options() + client.run_setup() + client.get_variable_definitions() + + # set shape for the dynamic output (and its residual) + shapes = [ + client.set_variable_shape( + "x", (2,), data.VariableType.kOutput + ), + ] + client.send_variable_shapes(shapes) + client.get_partials_definitions() + + inputs = {"a": np.array([4.0])} + outputs = {"x": np.array([3.0, 1.0])} + residuals = client.run_compute_residuals(inputs, outputs) + + # x**2 - a = [9-4, 1-4] = [5, -3] + npt.assert_array_almost_equal(residuals["x"], [5.0, -3.0]) + finally: + server.stop(0) + + +# --------------------------------------------------------------- +# Unit test: SetVariableShapes generic exception path +# --------------------------------------------------------------- +class TestSetVariableShapesGenericException(unittest.TestCase): + """Tests the generic exception handler in SetVariableShapes.""" + + def test_generic_exception_aborts(self): + server = DisciplineServer() + disc = Discipline() + disc.add_input("x", dynamic_shape=True) + server._discipline = disc + + context = Mock() + + # Craft an iterator that raises a non-validation exception + def bad_iterator(): + raise RuntimeError("unexpected error") + yield # pragma: no cover + + server.SetVariableShapes(bad_iterator(), context) + context.abort.assert_called_once() + self.assertEqual( + context.abort.call_args[0][0], grpc.StatusCode.INTERNAL + ) + + +# --------------------------------------------------------------- +# Unit test: OpenMDAO utils dynamic shape paths +# --------------------------------------------------------------- +class TestOpenMdaoUtilsDynamicShapes(unittest.TestCase): + """Tests the OpenMDAO utils functions with dynamic-shape variables.""" + + def test_client_setup_with_dynamic_input(self): + """client_setup passes shape_by_conn=True for dynamic inputs.""" + comp = Mock() + var = Mock() + var.name = "x" + var.units = "m" + var.type = data.kInput + var.shape = [] + var.dynamic_shape = True + + comp._client._var_meta = [var] + comp._client._discrete_var_meta = [] + + from philote_mdo.openmdao.utils import client_setup + + client_setup(comp) + + comp.add_input.assert_called_once_with( + "x", shape_by_conn=True, units="m" + ) + + def test_client_setup_with_dynamic_output(self): + """client_setup passes shape_by_conn=True for dynamic outputs.""" + comp = Mock() + var = Mock() + var.name = "y" + var.units = "" + var.type = data.kOutput + var.shape = [] + var.dynamic_shape = True + + comp._client._var_meta = [var] + comp._client._discrete_var_meta = [] + + from philote_mdo.openmdao.utils import client_setup + + client_setup(comp) + + comp.add_output.assert_called_once_with( + "y", shape_by_conn=True, units=None + ) + + def test_send_resolved_shapes_with_dynamic_vars(self): + """send_resolved_shapes reads OpenMDAO metadata and sends shapes.""" + comp = Mock() + var = data.VariableMetaData( + name="x", type=data.kInput, dynamic_shape=True + ) + comp._client._var_meta = [var] + comp._var_rel2meta = {"x": {"shape": (5,)}} + comp._client.set_variable_shape.return_value = data.VariableMetaData( + name="x", type=data.kInput, shape=[5] + ) + + from philote_mdo.openmdao.utils import send_resolved_shapes + + send_resolved_shapes(comp) + + comp._client.set_variable_shape.assert_called_once_with( + "x", (5,), data.kInput + ) + comp._client.send_variable_shapes.assert_called_once() + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_openmdao_explicit_client.py b/tests/test_openmdao_explicit_client.py index b97551f..b50bc59 100644 --- a/tests/test_openmdao_explicit_client.py +++ b/tests/test_openmdao_explicit_client.py @@ -167,6 +167,7 @@ def test_setup_partials( component = RemoteExplicitComponent(channel=mock_channel) component._client = Mock() component._client._partials_meta = [par1, par2] + component._client._var_meta = [] # call the function component.setup_partials() diff --git a/tests/test_openmdao_implicit_client.py b/tests/test_openmdao_implicit_client.py index 045ddcb..82255c1 100644 --- a/tests/test_openmdao_implicit_client.py +++ b/tests/test_openmdao_implicit_client.py @@ -171,6 +171,7 @@ def test_setup_partials( component = RemoteImplicitComponent(channel=mock_channel) component._client = Mock() component._client._partials_meta = [par1, par2] + component._client._var_meta = [] # call the function component.setup_partials() diff --git a/tests/test_openmdao_utils.py b/tests/test_openmdao_utils.py index 763504e..bc65eba 100644 --- a/tests/test_openmdao_utils.py +++ b/tests/test_openmdao_utils.py @@ -48,12 +48,14 @@ def test_openmdao_client_setup(self): var1.units = "m" var1.type = kInput var1.shape = [2] + var1.dynamic_shape = False var2 = Mock() var2.name = "var2" var2.units = None var2.type = kOutput var2.shape = [1] + var2.dynamic_shape = False comp._client._var_meta = [var1, var2] comp._client._discrete_var_meta = [] From cc9299a608cf8e59977ebf678f674f1358157280 Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Sat, 16 May 2026 10:54:38 -0400 Subject: [PATCH 13/14] Add 'double' and 'string' type aliases to OpenMDAO type map --- philote_mdo/openmdao/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/philote_mdo/openmdao/utils.py b/philote_mdo/openmdao/utils.py index d7a599e..4eb666d 100644 --- a/philote_mdo/openmdao/utils.py +++ b/philote_mdo/openmdao/utils.py @@ -35,7 +35,9 @@ "bool": bool, "int": int, "float": float, + "double": float, "str": str, + "string": str, "dict": dict, } From af971ecaa2cd563f1cc6f89ecefd7a5cb0252bbd Mon Sep 17 00:00:00 2001 From: Christopher Lupp Date: Sat, 16 May 2026 11:05:17 -0400 Subject: [PATCH 14/14] Update proto submodule to v0.8.0 and update changelog --- CHANGELOG.md | 3 +++ proto | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f86ac6f..6559532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- Added `"double"` and `"string"` as type aliases in the OpenMDAO type map, + mapping them to `float` and `str` respectively, to match common proto type + name conventions. - Added dynamic shapes for inputs and outputs. Disciplines can now declare variables with `dynamic_shape=True` in `add_input` / `add_output`, indicating that the client is allowed to set the diff --git a/proto b/proto index 300d686..6f2d8a1 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 300d6865d4d5b394853d06604b73c4f8e3a0c4a1 +Subproject commit 6f2d8a1f107a4be23304bf50193d1a580b379c5d