diff --git a/docs/docs/Guides/_category_.json b/docs/docs/Guides/_category_.json new file mode 100644 index 00000000..5707fc1c --- /dev/null +++ b/docs/docs/Guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 7 +} diff --git a/docs/docs/Upgrading/_category_.json b/docs/docs/Upgrading/_category_.json new file mode 100644 index 00000000..a2283e68 --- /dev/null +++ b/docs/docs/Upgrading/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Upgrading", + "position": 8 +} diff --git a/docs/docs/authorization-service.md b/docs/docs/authorization-service.md index 93db3587..731d110f 100644 --- a/docs/docs/authorization-service.md +++ b/docs/docs/authorization-service.md @@ -6,7 +6,7 @@ title: Authorization Service ### Usage -The Authorization service can be retrieved from the service manager using the name +The Authorization service can be retrieved from the container using the name `Lmc\Rbac\Service\AuthorizationServiceInterface` and injected into your code: ```php @@ -163,4 +163,12 @@ class Module } ``` +## Authorization Exception + +LmcRbac provides a `Lmc\Rbac\Exception\UnauthorizedException` exception class that can be thrown by a service +when authorization is denied. + +This is for convenience as LmcRbac does not provide any handler for this exception +and it is expected that applications will implement the exception handler. + diff --git a/docs/docs/configuration.md b/docs/docs/configuration.md index 7d940880..bd7af213 100644 --- a/docs/docs/configuration.md +++ b/docs/docs/configuration.md @@ -1,6 +1,6 @@ --- sidebar_label: Configuration -sidebar_position: 7 +sidebar_position: 5 title: Configuring LmcRbac --- @@ -14,6 +14,6 @@ a `config/autoload/lmcrbac.global.php` file. A sample configuration file is prov | Key | Description | |--|------------------------------------------------------------------------------------------------------------------------------------------------| | `guest_role` | Defines the name of the `guest` role when no identity exists.
Defaults to `'guest'`. | -| `role_provider` | Defines the role provider.
Defaults to `[]`
See the [Role Providers](role-providers) section. | +| `role_provider` | Defines the role provider.
Defaults to `[]`
See the [Role Providers](roles) section. | | `assertion_map` | Defines the dynamic assertions that are associated to permissions.
Defaults to `[]`.
See the [Dynamic Assertions](assertions) section. | -| `assertion_manager` | Provides a configuration for the Assertion Plugin Manager.
Defaults to `[]`.
See the [Dynamic Assertion](assertions.md) section. | +| `assertion_manager` | Provides a configuration for the Assertion Plugin Manager.
Defaults to `[]`.
See the [Dynamic Assertion](assertions.md) section. | diff --git a/docs/docs/quick-start.md b/docs/docs/quick-start.md index 05c9ef9c..d1bc5889 100644 --- a/docs/docs/quick-start.md +++ b/docs/docs/quick-start.md @@ -9,7 +9,7 @@ LmcRbac extends the components provided by [laminas-permissions-rbac](https://gi LmcRbac can be used in Laminas MVC and in Mezzio applications. :::tip -If you are upgrading from LmcRbac v1 or from zfc-rbac v3, please read the [Upgrading section](Upgrading/to-v2.md) +If you are upgrading from LmcRbac v1 or from zfc-rbac v3, please read the [Upgrading section](upgrading/to-v2) ::: ## Concepts @@ -63,7 +63,8 @@ Install the module: $ composer require lm-commons/lmc-rbac ``` -You will be prompted by the Laminas Component Installer plugin to inject LM-Commons\LmcRbac. +You will be prompted by the Laminas Component Installer plugin to inject the package +into the application's configuration. :::note **Manual installation:** @@ -82,7 +83,7 @@ By default, no roles and no permissions are defined. Roles and permissions are defined by a Role Provider. `LmcRbac` ships with two roles providers: - a simple `InMemoryRoleProvider` that uses an associative array to define roles and their permission. This is the default. -- a `ObjectRepositoyRoleProvider` that is based on Doctrine ORM. +- a `ObjectRepositoyRoleProvider` that is based on Doctrine ORM. (*Deprecated since v2.3*) To quickly get started, let's use the `InMemoryRoleProvider` role provider. @@ -118,7 +119,7 @@ a child and with its own permission. If the hierarchy is flattened: ## Basic authorization -The authorization service can get retrieved from the service manager container and used to check if a permission +The authorization service can get retrieved from the container and used to check if a permission is granted to an identity: ```php diff --git a/docs/docs/roles/custom-provider.md b/docs/docs/roles/custom-provider.md new file mode 100644 index 00000000..06ada961 --- /dev/null +++ b/docs/docs/roles/custom-provider.md @@ -0,0 +1,87 @@ +--- +title: Building Custom Providers +sidebar_position: 4 +--- + +To create a custom role provider, you first need to create a class that implements the +`Lmc\Rbac\Role\RoleProviderInterface` interface. + +Then, you need to add it to the role provider manager: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + MyCustomRoleProvider::class => [ + // Options + ], + ], + ], +]; +``` +And the role provider is created using the service manager: +```php +return [ + 'service_manager' => [ + 'factories' => [ + MyCustomRoleProvider::class => MyCustomRoleProviderFactory::class, + ], + ], +]; +``` +### Role Factory + +A Role Factory is a factory that can be used to create basic role instances for a role returned by the role provider. + +LmcRbac provide a convenience interface `RoleFactoryInterface` that can be used by a role provider to create roles. + +```php +namespace Lmc\Rbac\Role; + +use Laminas\Permissions\Rbac\RoleInterface; + +interface RoleFactoryInterface +{ + public function createRole(string $roleName): RoleInterface; +} +``` + +LmcRbac does not provide an implementation for this interface and it is left to the custom role provider to implement +the factory. + +Therefore, in your application, you need to define a factory that returns a factory +in your container config. For example: + +```php title="src/MyRoleFactory.php" + [ + 'factories' => [ + Lmc\Rbac\Role\RoleProviderInterface::class => function (ContainerInterface $container) { + return new MyApp\MyRoleFactory(); + } + ], + ], +]; +``` + + + + diff --git a/docs/docs/roles/in-memory-provider.md b/docs/docs/roles/in-memory-provider.md new file mode 100644 index 00000000..b8898f71 --- /dev/null +++ b/docs/docs/roles/in-memory-provider.md @@ -0,0 +1,75 @@ +--- +title: Lmc\Rbac\Role\InMemoryRoleProvider +sidebar_position: 1 +--- + +### `Lmc\Rbac\Role\InMemoryRoleProvider` (built-in) + +This built-in provider is ideal for small/medium sites with few roles/permissions. All the data is specified in a simple associative array in a +PHP file. + +Here is an example of the format you need to use: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\InMemoryRoleProvider::class => [ + 'admin' => [ + 'children' => ['member'], + 'permissions' => ['article.delete'] + ], + 'member' => [ + 'children' => ['guest'], + 'permissions' => ['article.edit', 'article.archive'] + ], + 'guest' => [ + 'permissions' => ['article.read'] + ], + ], + ], + ], +]; +``` + +The `children` and `permissions` subkeys are entirely optional. Internally, the `Lmc\Rbac\Role\InMemoryRoleProvider` creates +`Laminas\Permissions\Rbac\Role` objects with children, if any. + +If you are more confident with flat RBAC, the previous config can be re-written to remove any inheritence between roles: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\InMemoryRoleProvider::class => [ + 'admin' => [ + 'permissions' => [ + 'article.delete', + 'article.edit', + 'article.archive', + 'article.read' + ] + ], + 'member' => [ + 'permissions' => [ + 'article.edit', + 'article.archive', + 'article.read' + ] + ], + 'guest' => [ + 'permissions' => ['article.read'] + ] + ] + ] + ] +]; +``` + +### Handling non existent roles + +If the list of roles names to retrieve using this provider includes roles that are +not defined in the provider's configuration, the role provider will create `Laminas\Permissions\Role\Role` objects +for them with no permissions. + + diff --git a/docs/docs/roles/index.md b/docs/docs/roles/index.md new file mode 100644 index 00000000..1e9e5f7d --- /dev/null +++ b/docs/docs/roles/index.md @@ -0,0 +1,106 @@ +--- +sidebar_label: Roles, permissions and Role providers +title: Roles, Permissions and Role providers +sidebar_position: 4 +--- + +## Roles + +A role is an object that returns a list of permissions that the role has. + +LmcRbac uses the Role class defined by [laminas-permissions-rbac](https://github.com/laminas/laminas-permissions-rbac). Roles +are defined using the `\Laminas\Permissions\Rbac\Role` class or by a class +implementing `\Laminas\Permissions\Rbac\RoleInterface`. + +Roles can have child roles and therefore provides a hierarchy of roles where a role inherit the permissions of all its +child roles. + +For example, a 'user' role may have the 'read' and 'write' permissions, and a 'admin' role +may inherit the permissions of the 'user' role plus an additional 'delete' role. In this structure, +the 'admin' role will have 'user' as its child role. + + +:::tip[Flat roles] +Previous version of LmcRbac used to make a distinction between flat roles and hierarchical roles. +A flat role is just a simplification of a hierarchical role, i.e. a hierarchical role without children. + +In `laminas-permissions-rbac`, roles are hierarchical. +::: + +## Permissions + +A permission in `laminas-permissions-rbac` is simply a string that represents the permission such as 'read', 'write' or 'delete'. +But it can also be more precise like 'article.read' or 'article.write'. + +A permission can also be an object as long as it can be cast to a string. This could be the +case, for example, when permissions are stored in a database where they could +also have a identifier and a description. + +:::tip +An object can be cast to a string by implementing the `__toString()` method. +::: + +## Role Service + +LmcRbac provides a role service that will use a role providers to fetch the +roles and their permissions associated with a given identity. + +It can be retrieved from the container be requesting the `Lmc\Rbac\Service\RoleServiceInterface` service. + +`Lmc\Rbac\Service\RoleServiceInterface` defines the following method: + +- `getIdentityRoles(object|null $identity = null): iterable` + +| Parameter | Description | +|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `$identity` | The identity whose roles to retrieve.
If `$identity` is null, then the `guest` is used.
The `guest` role is definable via configuration and defaults to `'guest'`. | + +:::warning +The `$identity` parameter must be an object that either implements the `Lmc\Rbac\Identity\IdentityInterface` +interface or has a `getRoles()` method that returns an array of strings or an array of objects +implementing the `Laminas\Permissions\Rbac\RoleInterface` interface. + + +::: + + +## Role Providers +A role provider is an object that returns a list of roles. A role provider must implement the +`Lmc\Rbac\Role\RoleProviderInterface` interface. The only required method is `getRoles`, and must return an array +of `Laminas\Permissions\Rbac\RoleInterface` objects. + +```php +use Laminas\Permissions\Rbac\RoleInterface; + +interface RoleProviderInterface +{ + /** + * Get the roles from the provider + * + * @param string[] $roleNames + * @return RoleInterface[] + */ + public function getRoles(iterable $roleNames): iterable; +} +``` + +Roles can come from one of many sources: in memory, from a file, from a database, etc. However, you can specify only one role provider per application. + +LmcRbac comes with two built-in role providers: +- [`Lmc\Rbac\Role\InMemoryRoleProvider`](/docs/roles/in-memory-provider) +- [`Lmc\Rbac\Role\ObjectRepositoryRoleProvider`](/docs/roles/object-repository-role-provider). **(deprecated as of v2.3)** + +A role provider must be added to the `role_provider` subkey in the +configuration file. For example: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\InMemoryRoleProvider::class => [ + // configuration + ], + ] + ] +]; +``` diff --git a/docs/docs/roles/object-repository-role-provider-doctrine.md b/docs/docs/roles/object-repository-role-provider-doctrine.md new file mode 100644 index 00000000..53df9198 --- /dev/null +++ b/docs/docs/roles/object-repository-role-provider-doctrine.md @@ -0,0 +1,68 @@ +--- +title: Lmc\Rbac\Role\Doctrine\ObjectRepositoryRoleProvider +sidebar_position: 3 +--- + +:::info +New since v2.3 +::: + +### `Lmc\Rbac\Role\Doctrine\ObjectRepositoryRoleProvider` + +This provider fetches roles from a database using a Doctrine ORM Object Repository. + +### Installation + +```shell +$ composer require lm-commons/lmc-rbac-role-provider-doctrine-orm +``` + +You can configure this provider by giving an object repository service name that is fetched from the container +using the `object_repository` key: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\Doctrine\ObjectRepositoryRoleProvider::class => [ + 'object_repository' => 'App\Repository\RoleRepository', + 'role_name_property' => 'name', + 'factory_interface' => MyRoleFactory::class, + ], + ], + ], +]; +``` + +Or you can specify the `object_manager` and `class_name` options: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\ObjectRepositoryRoleProvider::class => [ + 'object_manager' => 'doctrine.entitymanager.orm_default', + 'class_name' => 'App\Entity\Role', + 'role_name_property' => 'name', + 'factory_interface' => MyRoleFactory::class, + ], + ], + ], +]; +``` + +In both cases, you need to define the `role_name_property` value, which is the name of the entity's property +that holds the actual role name. This is used internally to only load the identity roles, instead of loading +the whole table every time. + +Please note that your entity fetched from the table MUST implement the +`Laminas\Permissions\Rbac\RoleInterface` interface. + +Sample ORM entity models are provided in the `/data` folder for flat role, hierarchical role and permission. + +### Handling non existent roles + +You also need to define the `factory_interface` value to specify a Role Factory that implements the +`Lmc\Rbac\Role\RoleFactoryInterface`. The Role Factory can be used to create a +Role as a fallback when a requested role is not found in the database. + diff --git a/docs/docs/roles/object-repository-role-provider.md b/docs/docs/roles/object-repository-role-provider.md new file mode 100644 index 00000000..da3fb0ef --- /dev/null +++ b/docs/docs/roles/object-repository-role-provider.md @@ -0,0 +1,64 @@ +--- +title: Lmc\Rbac\Role\ObjectRepositoryRoleProvider +sidebar_position: 2 +--- + +:::warning +Deprecated since v2.3 +You should use `Lmc\Rbac\Role\Doctrine\ObjectRepositoryRoleProvider` instead +::: + +### `Lmc\Rbac\Role\ObjectRepositoryRoleProvider` (built-in) + +This builtin provider fetches roles from a database using +`Doctrine\Common\Persistence\ObjectRepository` interface. + +You can configure this provider by giving an object repository service name that is fetched from the service manager +using the `object_repository` key: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\ObjectRepositoryRoleProvider::class => [ + 'object_repository' => 'App\Repository\RoleRepository', + 'role_name_property' => 'name' + ], + ], + ], +]; +``` + +Or you can specify the `object_manager` and `class_name` options: + +```php +return [ + 'lmc_rbac' => [ + 'role_provider' => [ + Lmc\Rbac\Role\ObjectRepositoryRoleProvider::class => [ + 'object_manager' => 'doctrine.entitymanager.orm_default', + 'class_name' => 'App\Entity\Role', + 'role_name_property' => 'name' + ], + ], + ], +]; +``` + +In both cases, you need to specify the `role_name_property` value, which is the name of the entity's property +that holds the actual role name. This is used internally to only load the identity roles, instead of loading +the whole table every time. + +Please note that your entity fetched from the table MUST implement the `Laminas\Permissions\Rbac\RoleInterface` interface. + +Sample ORM entity models are provided in the `/data` folder for flat role, hierarchical role and permission. + +### Handling non existent roles + +This provider assumes that all required roles are available in the database. + +If the list of roles names to retrieve using this provider includes roles that are +not defined in the database, the role provider will throw an exception. + +Therefore, extra care must be taken to ensure that the roles of an identity have +corresponsing roles defined in the database. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 3526559c..5b983d53 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -24,7 +24,7 @@ const config = { projectName: 'LmcRbac', trailingSlash: false, - onBrokenLinks: 'throw', + onBrokenLinks: 'warn', //onBrokenMarkdownLinks: 'warn', // Even if you don't use internationalization, you can use this field to set @@ -56,7 +56,13 @@ const config = { lastVersion: 'current', versions: { current: { - label: '2.2 (Latest)', + label: '2.3 (Latest)', + }, + "2.2": { + banner: 'none', + }, + "2.0": { + banner: 'none', }, "1.4": { banner: 'none', @@ -103,7 +109,8 @@ themeConfig: type: "docsVersionDropdown", position: "right", versions: { - current: {label: '2.2 (Latest)'}, + current: {label: '2.3 (Latest)'}, + '2.2': {label: '2.2'}, '2.0': {label: '2.0'}, '1.4': {label: '1.x'}, }, @@ -138,19 +145,29 @@ themeConfig: { title: 'More', items: [ -/* - { - label: 'Blog', - to: '/blog', - }, - - */ { label: 'GitHub', href: 'https://github.com/lm-commons/lmcrbac', }, ], }, + { + title: 'Other', + items: [ + { + label: 'Disclaimer', + href: 'https://lm-commons.github.io/disclaimer' + }, + { + label: 'Privacy', + href: 'https://lm-commons.github.io/privacy' + }, + { + label: 'Disclaimer', + href: 'https://lm-commons.github.io/cookies-policy' + }, + ], + }, ], copyright: `Copyright © ${new Date().getFullYear()} LM-Commons Organization. Built with Docusaurus.`, }, diff --git a/docs/versioned_docs/version-2.0/role-providers.md b/docs/versioned_docs/version-2.0/role-providers.md index a011763e..88a48445 100644 --- a/docs/versioned_docs/version-2.0/role-providers.md +++ b/docs/versioned_docs/version-2.0/role-providers.md @@ -95,7 +95,7 @@ return [ ``` The `children` and `permissions` subkeys are entirely optional. Internally, the `Lmc\Rbac\Role\InMemoryRoleProvider` creates -`Lmc\Rbac\Role\Role` objects with children, if any. +`Laminas\Permissions\Rbac\Role` objects with children, if any. If you are more confident with flat RBAC, the previous config can be re-written to remove any inheritence between roles: @@ -168,7 +168,7 @@ In both cases, you need to specify the `role_name_property` value, which is the that holds the actual role name. This is used internally to only load the identity roles, instead of loading the whole table every time. -Please note that your entity fetched from the table MUST implement the `Lmc\Rbac\Role\RoleInterface` interface. +Please note that your entity fetched from the table MUST implement the `Laminas\Permissions\Rbac\RoleInterface` interface. Sample ORM entity models are provided in the `/data` folder for flat role, hierarchical role and permission. diff --git a/docs/versioned_docs/version-2.2/Guides/integrating.md b/docs/versioned_docs/version-2.2/Guides/integrating.md new file mode 100644 index 00000000..b3dcf644 --- /dev/null +++ b/docs/versioned_docs/version-2.2/Guides/integrating.md @@ -0,0 +1,92 @@ +--- +title: Integrating into applications +--- + +LmcRbac can be used in your application to implement role-based access control. + +However, it is important to note that Authorization service `isGranted()` method expects +an identity to be provided. The identity must also implement the `Lmc\Rbac\Identity\IdentityInterface`. + +User authentication is not in the scope of LmcRbac and must be implemented by your application. + +## Laminas MVC applications + +In a Laminas MVC application, you can use the ['laminas-authentication'](https://docs.laminas.dev/laminas-authentication) +component with an appropriate adapter to provide the identity. + +The `Laminas\Authentication\AuthenticationService` service provides the identity using the `getIdentity()` method. +However, it is not prescriptive on the signature of the returned identity object. It is up to the +authentication adapter to return a authentication result that contains an identity object that implements the +`IdentityInterface`. + +For example: + +```php +authenticationService = $authenticationService; + $this->authorizationService = $authorizationService; + } + + public function doSomething() + { + $identity = $this->authenticationService->hasIdentity() ? $this->authenticationService->getIdentity() : null; + + // Check for permission + if ($this->getAuthorizationService()->isGranted($identity, 'somepermssion')) { + // authorized + } else { + // not authorized + } + } + +} + +``` +### Other Laminas MVC components to use +To facilitate integration in an MVC application, you can use [LmcUser](https://lm-commons.github.io/LmcUser/) for +authentication. + +You can also use [LmcRbacMvc](https://lm-commons.github.io/LmcRbacMvc/) which extends LmcRbac by handling identities. +It also provides additional functionalities like route guards and strategies for handling unauthorized access. For example, +an unauthorized strategy could be to redirect to a login page. + +## Mezzio and PSR-7 applications + +In a Mezzio application, you can use the [`mezzio/mezzio-authentication`](https://docs.mezzio.dev/mezzio-authentication/) +component to provide the identity. `mezzio/mezzio-authentication` will add a `UserInterface` object to the request attributes. + +From this point, assuming that you have configured your application to use the `Mezzio\Authentication\AuthenticationMiddleware`, +you can use `MyUser` in your handler by retrieving it from the request: + +```php +// Retrieve the UserInterface object from the request. +$user = $request->getAttribute(UserInterface::class); + +// Check for permission, this works because $user implements IdentityInterface +if ($this->getAuthorizationService()->isGranted($user, 'somepermssion')) { + // authorized +} else { + // not authorized +} +``` + +How you define roles and permissions in your application is up to you. One way would be to use the route name as +a permission such that authorization can be set up based on routes and optionally on route+verb. + + +### Other Mezzio components to use + +A LmcRbac Mezzio component is under development to provide factories and middleware to facilitate integration of LmcRbac +in Mezzio applications. diff --git a/docs/versioned_docs/version-2.2/Upgrading/migration.md b/docs/versioned_docs/version-2.2/Upgrading/migration.md new file mode 100644 index 00000000..b5829389 --- /dev/null +++ b/docs/versioned_docs/version-2.2/Upgrading/migration.md @@ -0,0 +1,22 @@ +--- +sidebar_label: From ZF-Commons Rbac v3 +sidebar_position: 2 +title: Migrating from ZF-Commons RBAC v3 +--- + +The ZF-Commons Rbac was created for the Zend Framework. When the Zend Framework was migrated to +the Laminas project, the LM-Commons organization was created to provide components formerly provided by ZF-Commons. + +When ZfcRbac was moved to LM-Commons, it was split into two repositories: + +- [LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc) contains the old version 2 of ZfcRbac. +- LmcRbac contains the version 3 of ZfcRbac, which was only released as v3.alpha.1. + +To upgrade to LmcRbac v2, it is suggested to do it in two steps: + +1. Upgrade to LmcRbac v1 with the following steps: + * Uninstall `zf-commons/zfc-rbac:3.0.0-alpha.1`. + * Install `lm-commons/lmc-rbac:~1.0` + * Change `zfc-rbac.global.php` to `lmcrbac.global.php` and update the key `zfc_rbac` to `lmc_rbac`. + * Review your code for usages of the `ZfcRbac/*` namespace to `LmcRbac/*` namespace. +2. Upgrade to LmcRbac v2 using the instructions in this [section](to-v2.md). diff --git a/docs/versioned_docs/version-2.2/Upgrading/to-v2.md b/docs/versioned_docs/version-2.2/Upgrading/to-v2.md new file mode 100644 index 00000000..c43c345b --- /dev/null +++ b/docs/versioned_docs/version-2.2/Upgrading/to-v2.md @@ -0,0 +1,37 @@ +--- +sidebar_label: From v1 to v2 +sidebar_position: 1 +title: Upgrading from v1 to v2 +--- + +LmcRbac v2 is a major version upgrade with many breaking changes that prevent +straightforward upgrading. + +### Namespace change + +The namespace has been changed from LmcRbac to Lmc\Rbac. + +Please review your code to replace references to the `LmcRbac` namespace +by the `Lmc\Rbac` namespace. + +### LmcRbac is based on laminas-permissions-rbac + +LmcRbac is now based on the role class and interface provided by laminas-permissions-rbac which +provides a hierarchical role model only. + +Therefore the `Role`, `HierarchicalRole` classes and the `RoleInterface` and `HierarchicalRoleInterface` have been removed +in version 2. + +The `PermissionInterface` interface has been removed as permissions in `laminas-permissions-rbac` as just strings or any +objects that can be casted to a string. If you use objects to hold permissions, just make sure that the object can be +casted to a string by, for example, implementing a `__toString()` method. + +### Refactoring the factories + +The factories for services have been refactored from the `LmcRbac\Container` namespace +to be colocated with the service that a factory is creating. All factories in the `LmcRbac\Container` namespace have +been removed. + +### Refactoring the Assertion Plugin Manager + +The `AssertionContainer` class, interface and factory have been replaced by `AssertionPluginManager` class, interface and factory. diff --git a/docs/versioned_docs/version-2.2/assertions.md b/docs/versioned_docs/version-2.2/assertions.md new file mode 100644 index 00000000..193560f3 --- /dev/null +++ b/docs/versioned_docs/version-2.2/assertions.md @@ -0,0 +1,153 @@ +--- +sidebar_label: Dynamic Assertions +sidebar_position: 6 +title: Dynamic Assertions +--- + +Dynamic Assertions provide the capability to perform extra validations when +the authorization service's `isGranted()` method is called. + +As described in [Authorization Service](authorization-service#reference), it is possible to pass a context to the +`isGranted()` method. This context is then passed to dynamic assertion functions. This context can be any object type. + +You can define dynamic assertion functions and assigned them to permission via configuration. + +## Defining a dynamic assertion function + +A dynamic assertion must implement the `Lmc\Rbac\Assertion\AssertionInterace` which defines only one method: + +```php +public function assert( + string $permission, + object|null $identity = null, + mixed $context = null + ): bool +``` +The assertion returns `true` when the access is granted, `false` otherwise. + +A simple assertion could be to check that user represented by `$identity`, for the permission +represented by `$permission` owns the resource represented by `$context`. + +```php +getOwnerId() === $identity->getId(); + } + // This should not happen since this assertion should only be + // called when the 'edit' permission is checked + return true; + } +} +``` +## Configuring Assertions + +Dynamic assertions are configured in LmcRbac via an assertion map defined in the LmcRbac configuration where assertions +are associated with permissions. + +The `assertion_map` key in the configuration is used to define the assertion map. If an assertion needs to be created via +a factory, use the `assertion_manager` config key. The Assertion Manager is a standard +plugin manager and its configuration should be a service manager configuration array. + +```php + [ + /* the rest of the file */ + 'assertion_map' => [ + 'edit' => \My\Namespace\MyAssertion::class, + ], + 'assertion_manager' => [ + 'factories' => [ + \My\Namespace\MyAssertion::class => InvokableFactory::class + ], + ], + ], +]; +``` +It is also possible to configure an assertion using a callable instead of a class: + +```php + [ + /* the rest of the file */ + 'assertion_map' => [ + 'edit' => function assert(string $permission, object|null $identity = null, $context = null): bool + { + // for 'edit' permission + if ('edit' === $permission) { + /** @var MyObjectClass $context */ + return $context->getOwnerId() === $identity->getId(); + } + // This should not happen since this assertion should only be + // called when the 'edit' permission is checked + return true; + }, + ], + ], +]; +``` +## Dynamic Assertion sets + +LmcRbac supports the creation of dynamic assertion sets where multiple assertions can be combined using 'and/or' logic. +Assertion sets are configured by associating an array of assertions to a permission in the assertion map: + +```php + [ + /* the rest of the file */ + 'assertion_map' => [ + 'edit' => [ + \My\Namespace\AssertionA::class, + \My\Namespace\AssertionB::class, + ], + 'read' => [ + 'condition' => \Lmc\Rbac\Assertion\AssertionSet::CONDITION_OR, + \My\Namespace\AssertionC::class, + \My\Namespace\AssertionD::class, + ], + 'delete' => [ + 'condition' => \Lmc\Rbac\Assertion\AssertionSet::CONDITION_OR, + \My\Namespace\AssertionE::class, + [ + 'condition' => \Lmc\Rbac\Assertion\AssertionSet::CONDITION_AND, + \My\Namespace\AssertionF::class, + \My\Namespace\AssertionC::class, + ], + ], + /** the rest of the file */ + ], +]; +``` +By default, an assertion set combines assertions using a 'and' condition. This is demonstrated by the map associated with +the `'edit'` permission above. + +It is possible to combine assertions using a 'or' condition by adding a `condition` equal to `AssertionSet::CONDITION_OR` +to the assertion set as demonstrated by the map associated with the `'read'` permission above. + +Furthermore, it is possible to nest assertion sets in order to create more complex logic as demonstrated by the map +associated with the `'delete'` permission above. + +The default logic is to combine assertions using 'and' logic but this can be explicitly set as shown above for `'delete'` +permission. + +## Defining dynamic assertions at run-time + +Although dynamic assertions are typically defined in the application's configuration, it is possible to set +dynamic assertions at run-time by using the Authorization Service utility methods for adding/getting assertions. + +These methods are described in the Authorization Service [reference](authorization-service.md#reference). diff --git a/docs/versioned_docs/version-2.2/authorization-service.md b/docs/versioned_docs/version-2.2/authorization-service.md new file mode 100644 index 00000000..93db3587 --- /dev/null +++ b/docs/versioned_docs/version-2.2/authorization-service.md @@ -0,0 +1,166 @@ +--- +sidebar_label: Authorization service +sidebar_position: 5 +title: Authorization Service +--- + +### Usage + +The Authorization service can be retrieved from the service manager using the name +`Lmc\Rbac\Service\AuthorizationServiceInterface` and injected into your code: + +```php +get(Lmc\Rbac\Service\AuthorizationServiceInterface::class); + +``` +### Reference + +`Lmc\Rbac\Service\AuthorizationServiceInterface` defines the following methods: + +#### `isGranted(object|null $identity, string $permission, $context = null): bool` + +Checks that the identity has is granted the permission for the (optional) context. + + | Parameter | Description | + |----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + | `$identity` | The identity whose roles to checks.
If `$identity` is null, then the `guest` is used.
The `guest` role is definable via configuration and defaults to `'guest'`. | + | `$permission` | The permission to check against | + | `$context` | A context that will be passed to dynamic assertions that are defined for the permission | + +#### `setAssertions(array $assertions, bool $merge = false): void` + +Allows to define dynamic assertions at run-time. + + | Parameter | Description | + |---------------|-----------------------------------------------------------------------------------------| + | `$assertions` | An array of assertions to merge or to replace | + | `$merge` | if `true` the content of `$assertions` will be merged with existing assertions. | + + +#### `setAssertion(string $permission, AssertionInterface|callable|string $assertion): void` +Allows to define a dynamic assertion at run-time. + + | Parameter | Description | + |---------------|-----------------------------------------| + | `$permission` | Permission name | + | `$assertion` | The assertion to set for `$permission` | + +#### `hasAssertion(string $permission): bool` +Checks if the authorization has a dynamic assertion for a given permission. + + | Parameter | Description | + |---------------|--------------------------| + | `$permission` | Permission name | + + +#### `getAssertions(): array` + +Returns all the dynamic assertions defined. + +#### `getAssertion(string $permission): AssertionInterface|callable|string|null` + +Returns the dynamic assertion for the give permission + + | Parameter | Description | + |---------------|-----------------------------| + | `$permission` | Permission permission name | + +More on dynamic assertions can be found in the [Assertions](assertions.md) section. + +More on the `guest` role can be found in the [Configuration](configuration.md) section. + +## Injecting the Authorization Service + +There are a few methods to inject the Authorization Service into your service. + +### Using a factory + +You can inject the AuthorizationService into your own objects using a factory. The Authorization Service +can be retrieved from the container using `'Lmc\Rbac\Service\AuthorizationServiceInterface'`. + +Here is a classic example for injecting the Authorization Service into your own service + +*in your app's Module* + +```php +use Lmc\Rbac\Service\AuthorizationServiceInterface; +class Module +{ + public function getConfig() + { + return [ + 'service_manager' => [ + 'factories' => [ + 'MyService' => function($sm) { + $authService = $sm->get('AuthorizationServiceInterface'); + return new MyService($authService); + } + ], + ], + ]; + } +} +```` + +### Using traits + +For convenience, LmcRbac provides a `AuthorizationServiceAwareTrait` that adds the `$authorizationService` property and +setter/getter methods. + +### Using delegators + +LmcRbac ships with a `Lmc\Rbac\Service\AuthorizationServiceDelegatorFactory` [delegator factory](https://docs.laminas.dev/laminas-servicemanager/delegators/) +to automatically inject the authorization service into your classes. + +Your class must implement the `Lmc\Rbac\Service\AuthorizationServiceAwareInterface` and use the above trait, as shown below: + +```php +namespace MyModule; + +use Lmc\Rbac\Service\AuthorizationServiceAwareInterface; +use Lmc\Rbac\Service\AuthorizationServiceAwareTrait; + +class MyClass implements AuthorizationServiceAwareInterface +{ + use AuthorizationServiceAwareTrait; + + public function doSomethingThatRequiresAuth() + { + if (! $this->getAuthorizationService()->isGranted($identity, 'deletePost')) { + throw new \Exception('You are not allowed !'); + } + return true; + } +} +``` + +And add your class to the right delegator: + +```php +namespace MyModule; +use Lmc\Rbac\Service\AuthorizationServiceDelegatorFactory; +class Module +{ + // ... + + public function getConfig() + { + return [ + 'service_manager' => [ + 'factories' => [ + MyClass::class => InvokableFactory::class, + ], + 'delegators' => [ + MyClass::class => [ + AuthorizationServiceDelegatorFactory::class, + ], + ], + ], + ]; + } +} +``` + + diff --git a/docs/versioned_docs/version-2.2/configuration.md b/docs/versioned_docs/version-2.2/configuration.md new file mode 100644 index 00000000..7d940880 --- /dev/null +++ b/docs/versioned_docs/version-2.2/configuration.md @@ -0,0 +1,19 @@ +--- +sidebar_label: Configuration +sidebar_position: 7 +title: Configuring LmcRbac +--- + +LmcRbac is configured via the `lmc_rbac` key in the application config. + +This is typically achieved by creating +a `config/autoload/lmcrbac.global.php` file. A sample configuration file is provided in the `config/` folder. + +## Reference + +| Key | Description | +|--|------------------------------------------------------------------------------------------------------------------------------------------------| +| `guest_role` | Defines the name of the `guest` role when no identity exists.
Defaults to `'guest'`. | +| `role_provider` | Defines the role provider.
Defaults to `[]`
See the [Role Providers](role-providers) section. | +| `assertion_map` | Defines the dynamic assertions that are associated to permissions.
Defaults to `[]`.
See the [Dynamic Assertions](assertions) section. | +| `assertion_manager` | Provides a configuration for the Assertion Plugin Manager.
Defaults to `[]`.
See the [Dynamic Assertion](assertions.md) section. | diff --git a/docs/versioned_docs/version-2.2/quick-start.md b/docs/versioned_docs/version-2.2/quick-start.md new file mode 100644 index 00000000..05c9ef9c --- /dev/null +++ b/docs/versioned_docs/version-2.2/quick-start.md @@ -0,0 +1,196 @@ +--- +title: Quick Start +sidebar_position: 1 +--- + +LmcRbac offers components and services to implement role-based access control (RBAC) in your application. +LmcRbac extends the components provided by [laminas-permissions-rbac](https://github.com/laminas/laminas-permissions-rbac). + +LmcRbac can be used in Laminas MVC and in Mezzio applications. + +:::tip +If you are upgrading from LmcRbac v1 or from zfc-rbac v3, please read the [Upgrading section](Upgrading/to-v2.md) +::: + +## Concepts + +[Role-Based Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) +is an approach to restricting system access to authorized users by putting emphasis +on roles and their permissions. + +In the RBAC model: + +- an **identity** has one of more roles. +- a **role** has one of more permissions. +- a **permission** is typically an action like "read", "write", "delete". +- a **role** can have **child roles** thus providing a hierarchy of roles where a role will inherit the permissions of all its child roles. + +### Authorization + +An identity will be authorized to perform an action, such as accessing a resource, if it is granted +the permission that controls the execution of the action. + +For example, deleting an item could be restricted to identities that have at least one role that has the +`item.delete` permission. This could be implemented by defining a `member` role that has the `item.delete` and assigning +this role of an authenticated user. + +### Dynamic Assertions + +In some cases, just checking if the identity has the `item.delete` permission is not enough. +It would also be necessary to check, for example, that the `item` belongs to the identity. Dynamic assertion allow +to specify some extra checks before granting access to perform an action such as, in this case, being the owner of the +resource. + +### Identities + +An identity is typically provided by an authentication process within the application. + +Authentication is not in the scope of `LmcRbac` and it is assumed that an identity entity that can provide the assigned +roles is available when using the authorization service. If no identity is available, as it would be the case when no +user is "logged in", then a guest role is assumed. + +## Requirements + +- PHP 8.2 or higher + +## Installation + +LmcRbac only officially supports installation through Composer. + +Install the module: + +```sh +$ composer require lm-commons/lmc-rbac +``` + +You will be prompted by the Laminas Component Installer plugin to inject LM-Commons\LmcRbac. + +:::note +**Manual installation:** + +- In Mezzio applications, enable the component by adding 'Lmc\Rbac\ConfigProvider' to the `config/config.php` file +- In MVC applications, enable the module by adding `Lmc\Rbac` key to your +`application.config.php` or `modules.config.php`, +::: + +Customize the module by copy-pasting +the `lmcrbac.global.php` file to your `config/autoload` folder. + +## Defining roles + +By default, no roles and no permissions are defined. + +Roles and permissions are defined by a Role Provider. `LmcRbac` ships with two roles providers: +- a simple `InMemoryRoleProvider` that uses an associative array to define roles and their permission. This is the default. +- a `ObjectRepositoyRoleProvider` that is based on Doctrine ORM. + +To quickly get started, let's use the `InMemoryRoleProvider` role provider. + +In the `config/autoload/lmcrbac.global.php`, add the following: + +```php + [ + 'role_provider' => [ + Lmc\Rbac\Role\InMemoryRoleProvider::class => [ + 'guest', + 'user' => [ + 'permissions' => ['create', 'edit'], + ], + 'admin' => [ + 'children' => ['user'], + 'permissions' => ['delete'], + ], + ], + ], + ], +]; +``` + +This defines 3 roles: a `guest` role, a `user` role having 2 permissions, and a `admin` role which has the `user` role as +a child and with its own permission. If the hierarchy is flattened: + +- `guest` has no permission +- `user` has permissions `create` and `edit` +- `admin` has permissions `create`, `edit` and `delete` + +## Basic authorization + +The authorization service can get retrieved from the service manager container and used to check if a permission +is granted to an identity: + +```php +get('\Lmc\Rbac\Service\AuthorizationServiceInterface'); + + /** @var \Lmc\Rbac\Identity\IdentityInterface|object $identity */ + if ($authorizationService->isGranted($identity, 'create')) { + /** do something */ + } +``` + +If `$identity` has the role `user` and/or `admin` then the authorization is granted. If the identity has the role `guest`, then authorization +is denied. + +:::info +If `$identity` is null (no identity), then the guest role is assumed which is set to `'guest'` by default. The guest role +can be configured in the `lmcrbac.config.php` file. More on this in the [Configuration](configuration.md) section. +::: + +:::warning +`LmcRbac` does not provide any logic to instantiate an identity entity. +It is assumed that the application will instantiate an entity that either +implements `\Lmc\Rbac\Identity\IdentityInterface` which defines the `getRoles()` +method, or implements a `getRoles()` method. +::: + +## Using assertions + +Even if an identity has the `user` role granting it the `edit` permission, it should not have the authorization to edit another identity's resource. + +This can be achieved using dynamic assertion. + +An assertion is a function that implements the `\Lmc\Rbac\Assertion\AssertionInterface` and is configured in the configuration +file. + +Let's modify the `lmcrbac.config.php` file as follows: + +```php + [ + 'role_provider' => [ + /* roles and permissions */ + ], + 'assertion_map' => [ + 'edit' => function ($permission, $identity = null, $resource = null) { + if ($resource->getOwnerId() === $identity->getId()) { + return true; + } else { + return false; + } + }, + ], + ], +]; +``` + +Then use the authorization service passing the resource (called a 'context') in addition to the permission: + +```php +get('\Lmc\Rbac\Service\AuthorizationServiceInterface'); + + /** @var \Lmc\Rbac\Identity\IdentityInterface $identity */ + if ($authorizationService->isGranted($identity, 'edit', $resource)) { + /** do something */ + } +``` + +Dynanmic assertions are further discussed in the [Dynamic Assertions](assertions) section. diff --git a/docs/docs/role-providers.md b/docs/versioned_docs/version-2.2/role-providers.md similarity index 100% rename from docs/docs/role-providers.md rename to docs/versioned_docs/version-2.2/role-providers.md diff --git a/docs/versioned_sidebars/version-2.2-sidebars.json b/docs/versioned_sidebars/version-2.2-sidebars.json new file mode 100644 index 00000000..5f41a72e --- /dev/null +++ b/docs/versioned_sidebars/version-2.2-sidebars.json @@ -0,0 +1,8 @@ +{ + "documentationSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/docs/versions.json b/docs/versions.json index 84b64c94..8a05aa0d 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,5 +1,6 @@ [ "current", + "2.2", "2.0", "1.4" ] diff --git a/docs/yarn.lock b/docs/yarn.lock index 125306eb..a4285393 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@ai-sdk/gateway@2.0.17": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-2.0.17.tgz#b89819798c685f7c59c589b3e31ce13b1473aa5f" - integrity sha512-oVAG6q72KsjKlrYdLhWjRO7rcqAR8CjokAbYuyVZoCO4Uh2PH/VzZoxZav71w2ipwlXhHCNaInGYWNs889MMDA== +"@ai-sdk/gateway@2.0.18": + version "2.0.18" + resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-2.0.18.tgz#7e81bdedddb7363af2c38d2cf7f34ac2d5e5eaa7" + integrity sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ== dependencies: "@ai-sdk/provider" "2.0.0" "@ai-sdk/provider-utils" "3.0.18" @@ -28,24 +28,24 @@ json-schema "^0.4.0" "@ai-sdk/react@^2.0.30": - version "2.0.104" - resolved "https://registry.yarnpkg.com/@ai-sdk/react/-/react-2.0.104.tgz#ae42a57c6f4ad368ad0d96f80be97f673dd25e0e" - integrity sha512-vpRNUwOrHXSsywZuEge78/LPbYMR/3tkBnwijGpIGnORMa/SzYhuVsE+sZBFVo/v0m5K/tg+CXNNvuJrVZ/MBQ== + version "2.0.109" + resolved "https://registry.yarnpkg.com/@ai-sdk/react/-/react-2.0.109.tgz#1f5dd07643bd8371015d05e9c9348110889cbb68" + integrity sha512-5qM8KuN7bv7E+g6BXkSAYLFjwIfMSTKOA1prjg1zEShJXJyLSc+Yqkd3EfGibm75b7nJAqJNShurDmR/IlQqFQ== dependencies: "@ai-sdk/provider-utils" "3.0.18" - ai "5.0.104" + ai "5.0.108" swr "^2.2.5" throttleit "2.1.0" -"@algolia/abtesting@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.11.0.tgz#e6561f2cb17978445eb8b8aff339ee7a2f985daa" - integrity sha512-a7oQ8dwiyoyVmzLY0FcuBqyqcNSq78qlcOtHmNBumRlHCSnXDcuoYGBGPN1F6n8JoGhviDDsIaF/oQrzTzs6Lg== +"@algolia/abtesting@1.12.0": + version "1.12.0" + resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.12.0.tgz#fad11266e85f33acf1a37961f4110459491428f5" + integrity sha512-EfW0bfxjPs+C7ANkJDw2TATntfBKsFiy7APh+KO0pQ8A6HYa5I0NjFuCGCXWfzzzLXNZta3QUl3n5Kmm6aJo9Q== dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" "@algolia/autocomplete-core@1.19.2": version "1.19.2" @@ -67,126 +67,126 @@ resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz#c0b7b8dc30a5c65b70501640e62b009535e4578f" integrity sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w== -"@algolia/client-abtesting@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.45.0.tgz#7e1984b02d58ce965bf190080b3ffa10c738ddbd" - integrity sha512-WTW0VZA8xHMbzuQD5b3f41ovKZ0MNTIXkWfm0F2PU+XGcLxmxX15UqODzF2sWab0vSbi3URM1xLhJx+bXbd1eQ== - dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" - -"@algolia/client-analytics@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.45.0.tgz#4f58d6c1c8d43afeedfd8ef7b84b49e3e7cdff14" - integrity sha512-I3g7VtvG/QJOH3tQO7E7zWTwBfK/nIQXShFLR8RvPgWburZ626JNj332M3wHCYcaAMivN9WJG66S2JNXhm6+Xg== - dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" - -"@algolia/client-common@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.45.0.tgz#c674f7f47a5f0013e3ab717929e51a6109af2dd2" - integrity sha512-/nTqm1tLiPtbUr+8kHKyFiCOfhRfgC+JxLvOCq471gFZZOlsh6VtFRiKI60/zGmHTojFC6B0mD80PB7KeK94og== - -"@algolia/client-insights@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.45.0.tgz#ca7128edec95da8217da76f867b8cd2c2a05f682" - integrity sha512-suQTx/1bRL1g/K2hRtbK3ANmbzaZCi13487sxxmqok+alBDKKw0/TI73ZiHjjFXM2NV52inwwcmW4fUR45206Q== - dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" - -"@algolia/client-personalization@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.45.0.tgz#e674b76b4dd5a8e89d9f157cbc958d68fbeb1c60" - integrity sha512-CId/dbjpzI3eoUhPU6rt/z4GrRsDesqFISEMOwrqWNSrf4FJhiUIzN42Ac+Gzg69uC0RnzRYy60K1y4Na5VSMw== - dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" - -"@algolia/client-query-suggestions@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.45.0.tgz#6bad2a7eaa7e1a6ecf1a7960c2056e08ee0302b6" - integrity sha512-tjbBKfA8fjAiFtvl9g/MpIPiD6pf3fj7rirVfh1eMIUi8ybHP4ovDzIaE216vHuRXoePQVCkMd2CokKvYq1CLw== - dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" - -"@algolia/client-search@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.45.0.tgz#709f5fb2a13487fa6327b31306654a2c694c0fe9" - integrity sha512-nxuCid+Nszs4xqwIMDw11pRJPes2c+Th1yup/+LtpjFH8QWXkr3SirNYSD3OXAeM060HgWWPLA8/Fxk+vwxQOA== - dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" +"@algolia/client-abtesting@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.46.0.tgz#488eadc7220a53f585077010fc2093d0c58dc37e" + integrity sha512-eG5xV8rujK4ZIHXrRshvv9O13NmU/k42Rnd3w43iKH5RaQ2zWuZO6Q7XjaoJjAFVCsJWqRbXzbYyPGrbF3wGNg== + dependencies: + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" + +"@algolia/client-analytics@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.46.0.tgz#5523c9a6ed2b67a9bdafc033cc323f43e5163806" + integrity sha512-AYh2uL8IUW9eZrbbT+wZElyb7QkkeV3US2NEKY7doqMlyPWE8lErNfkVN1NvZdVcY4/SVic5GDbeDz2ft8YIiQ== + dependencies: + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" + +"@algolia/client-common@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.46.0.tgz#004ad40adbdc6da7e23e4ef4d7a0ff48422af012" + integrity sha512-0emZTaYOeI9WzJi0TcNd2k3SxiN6DZfdWc2x2gHt855Jl9jPUOzfVTL6gTvCCrOlT4McvpDGg5nGO+9doEjjig== + +"@algolia/client-insights@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.46.0.tgz#4166d8e135aa38cf81ad7103edddd0d2026b586a" + integrity sha512-wrBJ8fE+M0TDG1As4DDmwPn2TXajrvmvAN72Qwpuv8e2JOKNohF7+JxBoF70ZLlvP1A1EiH8DBu+JpfhBbNphQ== + dependencies: + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" + +"@algolia/client-personalization@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.46.0.tgz#fc9f8f3e5099810770be9524ffe9fcd4ef0644e3" + integrity sha512-LnkeX4p0ENt0DoftDJJDzQQJig/sFQmD1eQifl/iSjhUOGUIKC/7VTeXRcKtQB78naS8njUAwpzFvxy1CDDXDQ== + dependencies: + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" + +"@algolia/client-query-suggestions@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.46.0.tgz#94952019e3d73475ce255dae4b51b9a9bfd86fb0" + integrity sha512-aF9tc4ex/smypXw+W3lBPB1jjKoaGHpZezTqofvDOI/oK1dR2sdTpFpK2Ru+7IRzYgwtRqHF3znmTlyoNs9dpA== + dependencies: + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" + +"@algolia/client-search@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.46.0.tgz#5b84fd1530d5be8300a949b428560a4bbf432c06" + integrity sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q== + dependencies: + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" "@algolia/events@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== -"@algolia/ingestion@1.45.0": - version "1.45.0" - resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.45.0.tgz#f99eb766a0cff112d65834ee1a43e08d18bc5f2a" - integrity sha512-t+1doBzhkQTeOOjLHMlm4slmXBhvgtEGQhOmNpMPTnIgWOyZyESWdm+XD984qM4Ej1i9FRh8VttOGrdGnAjAng== +"@algolia/ingestion@1.46.0": + version "1.46.0" + resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.46.0.tgz#aeb7385e342f0ae8e030c4e69d6d0216f9e005e9" + integrity sha512-2LT0/Z+/sFwEpZLH6V17WSZ81JX2uPjgvv5eNlxgU7rPyup4NXXfuMbtCJ+6uc4RO/LQpEJd3Li59ke3wtyAsA== dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" -"@algolia/monitoring@1.45.0": - version "1.45.0" - resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.45.0.tgz#09e7320973741f829badb5fa47c54cbc512bb0bc" - integrity sha512-IaX3ZX1A/0wlgWZue+1BNWlq5xtJgsRo7uUk/aSiYD7lPbJ7dFuZ+yTLFLKgbl4O0QcyHTj1/mSBj9ryF1Lizg== +"@algolia/monitoring@1.46.0": + version "1.46.0" + resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.46.0.tgz#98feadf8a029ec7d43921bea15415647012dbcc6" + integrity sha512-uivZ9wSWZ8mz2ZU0dgDvQwvVZV8XBv6lYBXf8UtkQF3u7WeTqBPeU8ZoeTyLpf0jAXCYOvc1mAVmK0xPLuEwOQ== dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" -"@algolia/recommend@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.45.0.tgz#06dc022d85f8a0b6a93dd70c690a236d4ee745a3" - integrity sha512-1jeMLoOhkgezCCPsOqkScwYzAAc1Jr5T2hisZl0s32D94ZV7d1OHozBukgOjf8Dw+6Hgi6j52jlAdUWTtkX9Mg== +"@algolia/recommend@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.46.0.tgz#9b2fec2f9c0e74073ad9324330b8e3571aadf919" + integrity sha512-O2BB8DuySuddgOAbhyH4jsGbL+KyDGpzJRtkDZkv091OMomqIA78emhhMhX9d/nIRrzS1wNLWB/ix7Hb2eV5rg== dependencies: - "@algolia/client-common" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" + "@algolia/client-common" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" -"@algolia/requester-browser-xhr@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.45.0.tgz#0cb197014e2344a7f58aef245c10831597d23fdd" - integrity sha512-46FIoUkQ9N7wq4/YkHS5/W9Yjm4Ab+q5kfbahdyMpkBPJ7IBlwuNEGnWUZIQ6JfUZuJVojRujPRHMihX4awUMg== +"@algolia/requester-browser-xhr@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.46.0.tgz#099d980fe5b699b5f0d1ff6dfbd28fd76af43861" + integrity sha512-eW6xyHCyYrJD0Kjk9Mz33gQ40LfWiEA51JJTVfJy3yeoRSw/NXhAL81Pljpa0qslTs6+LO/5DYPZddct6HvISQ== dependencies: - "@algolia/client-common" "5.45.0" + "@algolia/client-common" "5.46.0" -"@algolia/requester-fetch@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.45.0.tgz#df040b30f67e70b074e7a740874befd191bb6067" - integrity sha512-XFTSAtCwy4HdBhSReN2rhSyH/nZOM3q3qe5ERG2FLbYId62heIlJBGVyAPRbltRwNlotlydbvSJ+SQ0ruWC2cw== +"@algolia/requester-fetch@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.46.0.tgz#3262bc1670fa8f47ad851c65573709f589e3fb1c" + integrity sha512-Vn2+TukMGHy4PIxmdvP667tN/MhS7MPT8EEvEhS6JyFLPx3weLcxSa1F9gVvrfHWCUJhLWoMVJVB2PT8YfRGcw== dependencies: - "@algolia/client-common" "5.45.0" + "@algolia/client-common" "5.46.0" -"@algolia/requester-node-http@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.45.0.tgz#d96196d538d3ad5c25e3e21b469e92b19b442153" - integrity sha512-8mTg6lHx5i44raCU52APsu0EqMsdm4+7Hch/e4ZsYZw0hzwkuaMFh826ngnkYf9XOl58nHoou63aZ874m8AbpQ== +"@algolia/requester-node-http@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.46.0.tgz#45b8a76e7391f9d11a7fce04b1e03dbef29af7f2" + integrity sha512-xaqXyna5yBZ+r1SJ9my/DM6vfTqJg9FJgVydRJ0lnO+D5NhqGW/qaRG/iBGKr/d4fho34el6WakV7BqJvrl/HQ== dependencies: - "@algolia/client-common" "5.45.0" + "@algolia/client-common" "5.46.0" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": version "7.27.1" @@ -1371,6 +1371,11 @@ "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" +"@csstools/postcss-position-area-property@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz#41f0cbc737a81a42890d5ec035fa26a45f4f4ad4" + integrity sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q== + "@csstools/postcss-progressive-custom-properties@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz#c39780b9ff0d554efb842b6bd75276aa6f1705db" @@ -1423,6 +1428,14 @@ "@csstools/css-parser-algorithms" "^3.0.5" "@csstools/css-tokenizer" "^3.0.4" +"@csstools/postcss-system-ui-font-family@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz#bd65b79078debf6f67b318dc9b71a8f9fa16f8c8" + integrity sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ== + dependencies: + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-text-decoration-shorthand@^4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz#fae1b70f07d1b7beb4c841c86d69e41ecc6f743c" @@ -2422,13 +2435,13 @@ "@types/send" "*" "@types/express@*": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.5.tgz#3ba069177caa34ab96585ca23b3984d752300cdc" - integrity sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ== + version "5.0.6" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.6.tgz#2d724b2c990dcb8c8444063f3580a903f6d500cc" + integrity sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^5.0.0" - "@types/serve-static" "^1" + "@types/serve-static" "^2" "@types/express@^4.17.21": version "4.17.25" @@ -2533,9 +2546,9 @@ "@types/node" "*" "@types/node@*": - version "24.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.1.tgz#91e92182c93db8bd6224fca031e2370cef9a8f01" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== + version "25.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.0.tgz#c0e0022c3c7b41635c49322e6b3a0279fffa7d62" + integrity sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew== dependencies: undici-types "~7.16.0" @@ -2635,6 +2648,14 @@ "@types/node" "*" "@types/send" "<1" +"@types/serve-static@^2": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-2.2.0.tgz#d4a447503ead0d1671132d1ab6bd58b805d8de6a" + integrity sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/sockjs@^0.3.36": version "0.3.36" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" @@ -2855,12 +2876,12 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ai@5.0.104, ai@^5.0.30: - version "5.0.104" - resolved "https://registry.yarnpkg.com/ai/-/ai-5.0.104.tgz#b2a1bf7fb2bd50e1f63c945353a28949595a29b7" - integrity sha512-MZOkL9++nY5PfkpWKBR3Rv+Oygxpb9S16ctv8h91GvrSif7UnNEdPMVZe3bUyMd2djxf0AtBk/csBixP0WwWZQ== +ai@5.0.108, ai@^5.0.30: + version "5.0.108" + resolved "https://registry.yarnpkg.com/ai/-/ai-5.0.108.tgz#b1b6e66b705baabcf98370336b03ebaacbf1874f" + integrity sha512-Jex3Lb7V41NNpuqJHKgrwoU6BCLHdI1Pg4qb4GJH4jRIDRXUBySJErHjyN4oTCwbiYCeb/8II9EnqSRPq9EifA== dependencies: - "@ai-sdk/gateway" "2.0.17" + "@ai-sdk/gateway" "2.0.18" "@ai-sdk/provider" "2.0.0" "@ai-sdk/provider-utils" "3.0.18" "@opentelemetry/api" "1.9.0" @@ -2912,24 +2933,24 @@ algoliasearch-helper@^3.26.0: "@algolia/events" "^4.0.1" algoliasearch@^5.28.0, algoliasearch@^5.37.0: - version "5.45.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.45.0.tgz#90abba15b26d6722360a97c1e1931bc920f9c555" - integrity sha512-wrj4FGr14heLOYkBKV3Fbq5ZBGuIFeDJkTilYq/G+hH1CSlQBtYvG2X1j67flwv0fUeQJwnWxxRIunSemAZirA== - dependencies: - "@algolia/abtesting" "1.11.0" - "@algolia/client-abtesting" "5.45.0" - "@algolia/client-analytics" "5.45.0" - "@algolia/client-common" "5.45.0" - "@algolia/client-insights" "5.45.0" - "@algolia/client-personalization" "5.45.0" - "@algolia/client-query-suggestions" "5.45.0" - "@algolia/client-search" "5.45.0" - "@algolia/ingestion" "1.45.0" - "@algolia/monitoring" "1.45.0" - "@algolia/recommend" "5.45.0" - "@algolia/requester-browser-xhr" "5.45.0" - "@algolia/requester-fetch" "5.45.0" - "@algolia/requester-node-http" "5.45.0" + version "5.46.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.46.0.tgz#109e8976a4aee611112306de735c8c205e7e6bf8" + integrity sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg== + dependencies: + "@algolia/abtesting" "1.12.0" + "@algolia/client-abtesting" "5.46.0" + "@algolia/client-analytics" "5.46.0" + "@algolia/client-common" "5.46.0" + "@algolia/client-insights" "5.46.0" + "@algolia/client-personalization" "5.46.0" + "@algolia/client-query-suggestions" "5.46.0" + "@algolia/client-search" "5.46.0" + "@algolia/ingestion" "1.46.0" + "@algolia/monitoring" "1.46.0" + "@algolia/recommend" "5.46.0" + "@algolia/requester-browser-xhr" "5.46.0" + "@algolia/requester-fetch" "5.46.0" + "@algolia/requester-node-http" "5.46.0" ansi-align@^3.0.1: version "3.0.1" @@ -3012,7 +3033,7 @@ astring@^1.8.0: resolved "https://registry.yarnpkg.com/astring/-/astring-1.9.0.tgz#cc73e6062a7eb03e7d19c22d8b0b3451fd9bfeef" integrity sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg== -autoprefixer@^10.4.19, autoprefixer@^10.4.21: +autoprefixer@^10.4.19, autoprefixer@^10.4.22: version "10.4.22" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.22.tgz#90b27ab55ec0cf0684210d1f056f7d65dac55f16" integrity sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg== @@ -3073,10 +3094,10 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -baseline-browser-mapping@^2.8.25: - version "2.8.32" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz#5de72358cf363ac41e7d642af239f6ac5ed1270a" - integrity sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw== +baseline-browser-mapping@^2.9.0: + version "2.9.6" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz#82de0f7ee5860df86d60daf0d9524ae7227eeee7" + integrity sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg== batch@0.6.1: version "0.6.1" @@ -3093,23 +3114,23 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -body-parser@1.20.3: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== +body-parser@~1.20.3: + version "1.20.4" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" + integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== dependencies: - bytes "3.1.2" + 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.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" + 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" + unpipe "~1.0.0" bonjour-service@^1.2.1: version "1.3.0" @@ -3167,16 +3188,16 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.23.0, browserslist@^4.24.0, browserslist@^4.26.0, browserslist@^4.26.3, browserslist@^4.27.0, browserslist@^4.28.0: - version "4.28.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929" - integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== +browserslist@^4.0.0, browserslist@^4.23.0, browserslist@^4.24.0, browserslist@^4.26.3, browserslist@^4.27.0, browserslist@^4.28.0: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: - baseline-browser-mapping "^2.8.25" - caniuse-lite "^1.0.30001754" - electron-to-chromium "^1.5.249" + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" node-releases "^2.0.27" - update-browserslist-db "^1.1.4" + update-browserslist-db "^1.2.0" buffer-from@^1.0.0: version "1.1.2" @@ -3195,7 +3216,7 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== -bytes@3.1.2: +bytes@3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -3277,10 +3298,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001754: - version "1.0.30001757" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz#a46ff91449c69522a462996c6aac4ef95d7ccc5e" - integrity sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001754, caniuse-lite@^1.0.30001759: + version "1.0.30001760" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz#bdd1960fafedf8d5f04ff16e81460506ff9b798f" + integrity sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw== ccount@^2.0.0: version "2.0.1" @@ -3541,7 +3562,7 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== -content-disposition@0.5.4: +content-disposition@~0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -3558,15 +3579,15 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +cookie-signature@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454" + integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cookie@~0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== copy-webpack-plugin@^11.0.0: version "11.0.0" @@ -3723,10 +3744,10 @@ css-what@^6.0.1, css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea" integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA== -cssdb@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.4.2.tgz#1a367ab1904c97af0bb2c7ae179764deae7b078b" - integrity sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg== +cssdb@^8.5.2: + version "8.5.2" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.5.2.tgz#8a8c16c43785e32749453e589f18debcd936c7d1" + integrity sha512-Pmoj9RmD8RIoIzA2EQWO4D4RMeDts0tgAH0VXdlNdxjuBGI3a9wMOIcUwaPNmD4r2qtIa06gqkIf7sECl+cBCg== cssesc@^3.0.0: version "3.0.0" @@ -3896,7 +3917,7 @@ define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -3911,7 +3932,7 @@ dequal@^2.0.0, dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -destroy@1.2.0: +destroy@1.2.0, destroy@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -4051,10 +4072,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.5.249: - version "1.5.262" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz#c31eed591c6628908451c9ca0f0758ed514aa003" - integrity sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ== +electron-to-chromium@^1.5.263: + version "1.5.267" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" + integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== emoji-regex@^8.0.0: version "8.0.0" @@ -4338,38 +4359,38 @@ execa@5.1.1: strip-final-newline "^2.0.0" express@^4.21.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" - integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + version "4.22.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069" + integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" + body-parser "~1.20.3" + content-disposition "~0.5.4" content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" + 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" + 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" + on-finished "~2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.12" + path-to-regexp "~0.1.12" proxy-addr "~2.0.7" - qs "6.13.0" + qs "~6.14.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" + send "~0.19.0" + serve-static "~1.16.2" setprototypeof "1.2.0" - statuses "2.0.1" + statuses "~2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -4462,17 +4483,17 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== +finalhandler@~1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88" + integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== dependencies: debug "2.6.9" encodeurl "~2.0.0" escape-html "~1.0.3" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - statuses "2.0.1" + statuses "~2.0.2" unpipe "~1.0.0" find-cache-dir@^4.0.0: @@ -4521,7 +4542,7 @@ fraction.js@^5.3.4: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-5.3.4.tgz#8c0fcc6a9908262df4ed197427bdeef563e0699a" integrity sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ== -fresh@0.5.2: +fresh@0.5.2, fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== @@ -4810,14 +4831,14 @@ hast-util-to-jsx-runtime@^2.0.0: vfile-message "^4.0.0" hast-util-to-parse5@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed" - integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== + version "8.0.1" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz#95aa391cc0514b4951418d01c883d1038af42f5d" + integrity sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA== dependencies: "@types/hast" "^3.0.0" comma-separated-tokens "^2.0.0" devlop "^1.0.0" - property-information "^6.0.0" + property-information "^7.0.0" space-separated-tokens "^2.0.0" web-namespaces "^2.0.0" zwitch "^2.0.0" @@ -4977,6 +4998,17 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@~2.0.0, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-parser-js@>=0.5.1: version "0.5.10" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.10.tgz#b3277bd6d7ed5588e20ea73bf724fcbe44609075" @@ -5020,7 +5052,7 @@ hyperdyperid@^1.2.0: resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== -iconv-lite@0.4.24: +iconv-lite@~0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5075,7 +5107,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6398,9 +6430,9 @@ node-emoji@^2.1.0: skin-tone "^2.0.0" node-forge@^1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.2.tgz#d0d2659a26eef778bf84d73e7f55c08144ee7750" - integrity sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw== + version "1.3.3" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.3.tgz#0ad80f6333b3a0045e827ac20b7f735f93716751" + integrity sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg== node-releases@^2.0.27: version "2.0.27" @@ -6481,7 +6513,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1, on-finished@^2.4.1: +on-finished@2.4.1, on-finished@^2.4.1, on-finished@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -6680,11 +6712,6 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" - integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== - path-to-regexp@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b" @@ -6697,6 +6724,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@~0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -7109,9 +7141,9 @@ postcss-place@^10.0.0: postcss-value-parser "^4.2.0" postcss-preset-env@^10.2.1: - version "10.4.0" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-10.4.0.tgz#fa6167a307f337b2bcdd1d125604ff97cdeb5142" - integrity sha512-2kqpOthQ6JhxqQq1FSAAZGe9COQv75Aw8WbsOvQVNJ2nSevc9Yx/IKZGuZ7XJ+iOTtVon7LfO7ELRzg8AZ+sdw== + version "10.5.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-10.5.0.tgz#fa9af5b635c053f6d68f91132eab3a0b5c4e53c8" + integrity sha512-xgxFQPAPxeWmsgy8cR7GM1PGAL/smA5E9qU7K//D4vucS01es3M0fDujhDJn3kY8Ip7/vVYcecbe1yY+vBo3qQ== dependencies: "@csstools/postcss-alpha-function" "^1.0.1" "@csstools/postcss-cascade-layers" "^5.0.2" @@ -7140,21 +7172,23 @@ postcss-preset-env@^10.2.1: "@csstools/postcss-nested-calc" "^4.0.0" "@csstools/postcss-normalize-display-values" "^4.0.0" "@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-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-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.21" - browserslist "^4.26.0" + autoprefixer "^10.4.22" + browserslist "^4.28.0" css-blank-pseudo "^7.0.1" css-has-pseudo "^7.0.3" css-prefers-color-scheme "^10.0.0" - cssdb "^8.4.2" + cssdb "^8.5.2" postcss-attribute-case-insensitive "^7.0.1" postcss-clamp "^4.1.0" postcss-color-functional-notation "^7.0.12" @@ -7327,11 +7361,6 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" -property-information@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" - integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== - property-information@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" @@ -7362,12 +7391,12 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== +qs@~6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== dependencies: - side-channel "^1.0.6" + side-channel "^1.1.0" queue-microtask@^1.2.2: version "1.2.3" @@ -7396,15 +7425,15 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== +raw-body@~2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" rc@1.2.8: version "1.2.8" @@ -7417,9 +7446,9 @@ rc@1.2.8: strip-json-comments "~2.0.1" react-dom@^19.0.0: - version "19.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8" - integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ== + version "19.2.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.1.tgz#ce3527560bda4f997e47d10dab754825b3061f59" + integrity sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg== dependencies: scheduler "^0.27.0" @@ -7499,9 +7528,9 @@ react-router@5.3.4, react-router@^5.3.4: tiny-warning "^1.0.0" react@^19.0.0: - version "19.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5" - integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== + version "19.2.1" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.1.tgz#8600fa205e58e2e807f6ef431c9f6492591a2700" + integrity sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw== readable-stream@^2.0.1: version "2.3.8" @@ -7926,6 +7955,25 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@~0.19.0: + version "0.19.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.1.tgz#1c2563b2ee4fe510b806b21ec46f355005a369f9" + integrity sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg== + 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.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + serialize-javascript@^6.0.0, serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" @@ -7959,7 +8007,7 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.16.2: +serve-static@~1.16.2: version "1.16.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== @@ -7986,7 +8034,7 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0: +setprototypeof@1.2.0, setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -8049,7 +8097,7 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.0.6: +side-channel@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== @@ -8199,6 +8247,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@~2.0.1, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + std-env@^3.7.0: version "3.10.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" @@ -8360,9 +8413,9 @@ tapable@^2.0.0, tapable@^2.2.0, tapable@^2.2.1, tapable@^2.3.0: integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== terser-webpack-plugin@^5.3.11, terser-webpack-plugin@^5.3.9: - version "5.3.14" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" - integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== + version "5.3.15" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.15.tgz#0a26860b765eaffa8e840170aabc5b3a3f6f6bb9" + integrity sha512-PGkOdpRFK+rb1TzVz+msVhw4YMRT9txLF4kRqvJhGhCM324xuR3REBSHALN+l+sAhKUmz0aotnjp5D+P83mLhQ== dependencies: "@jridgewell/trace-mapping" "^0.3.25" jest-worker "^27.4.5" @@ -8417,7 +8470,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.1: +toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -8580,15 +8633,15 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" - integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== +update-browserslist-db@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz#cfb4358afa08b3d5731a2ecd95eebf4ddef8033e" + integrity sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA== dependencies: escalade "^3.2.0" picocolors "^1.1.1"