diff --git a/.vale.ini b/.vale.ini index e38aae5..ff7458f 100644 --- a/.vale.ini +++ b/.vale.ini @@ -19,3 +19,4 @@ TokenIgnores = (\*\*\{\w*\}\*\*), (Master License and Services Agreement) # Modify rule alert levels write-good.ThereIs = warning +Microsoft.GeneralURL = NO diff --git a/content/resources/custom/custom_resource_glossary.md b/content/resources/custom/custom_resource_glossary.md index a14a9b8..4fd24fc 100644 --- a/content/resources/custom/custom_resource_glossary.md +++ b/content/resources/custom/custom_resource_glossary.md @@ -1,10 +1,6 @@ +++ title = "Custom resources glossary" - - - - [menu] [menu.resources] title = "Glossary" diff --git a/content/resources/custom/rest_resource.md b/content/resources/custom/rest_resource.md new file mode 100644 index 0000000..c5f848f --- /dev/null +++ b/content/resources/custom/rest_resource.md @@ -0,0 +1,1139 @@ ++++ +title = "Create a RESTful custom resource" + + +[menu] + [menu.resources] + title = "REST resource" + identifier = "resources/custom/rest" + parent = "resources/custom" + weight = 50 ++++ + + + +The REST resource DSL is a base resource class in Chef Infra Client that allows you to create custom resources that interact with RESTful APIs. +Instead of writing HTTP request handling code from scratch, you can extend this resource to create custom resources that automatically handle API interactions, JSON mapping, and state management. + +With the REST resource, you can: + +- Define resource properties that map directly to API fields. +- Declare the target API endpoint directly in the resource class. +- Use built-in actions to create, update, and delete resources with REST APIs. +- Create nested JSON structures using JMESPath expressions. +- Handle authentication, pagination, and error conditions cleanly. + +## Requirements + +The REST custom resource DSL has the following requirements: + +- The custom resource must use the `core::rest_resource` partial. +- Use `property` to define the properties you want to map to the REST API. +- The target REST API endpoints (collection and document URLs) must be accessible. +- The Chef Infra Client node must have network access to the REST API endpoints. +- Any required API authentication (tokens, credentials) must be handled, typically with resource properties or configuration. + +## Configure the API endpoint + +You can configure an API endpoint using Train transport. + + + +### Train transport endpoint + +Configure the base URL in the Chef target mode transport (Train). The resource then uses relative paths only, and Train prepends its configured endpoint. + +Set the transport endpoint using either a Policyfile or a knife configuration file: + +```ruby +# .chef/config.rb or Policyfile transport block +transport: + name: rest + endpoint: https://api.example.com +``` + +With the endpoint's base URL configured, define the resource's relative paths: + +```ruby +class Chef::Resource::ApiUser < Chef::Resource + use "core::rest_resource" + + resource_name :api_user + provides :api_user + + # Base URL comes from the Train transport + rest_api_collection "/api/v1/users" + rest_api_document "/api/v1/users/{username}" + + property :username, String, name_property: true + property :email, String + + rest_property_map({ username: "username", email: "email" }) +end +``` + +Once defined, you can use the custom resource to add and remove a user in a recipe. For example: + +```ruby +# Create or update a user +api_user "jdoe" do + email "jdoe@example.com" + active true + action :configure +end + +# Delete a user +api_user "jdoe" do + action :delete +end +``` + + + + + +## Methods and actions + +The `rest_resource` has the following methods and actions. + +### Methods + +The REST resource provides several DSL methods for configuring API interactions. +These methods are called within your custom resource class definition. + + + +#### `rest_api_collection` + +This method defines the base URL path for the resource collection. Chef Infra Client uses this URL to list resources and create new ones. + +This method has the following syntax: + +```ruby +rest_api_collection "/path/to/collection" +``` + +- Path must be absolute (start with `/`) +- Used for GET (list all) and POST (create) operations +- Subclasses inherit this value unless they override it + +For example: + +```ruby +rest_api_collection "/api/v1/users" +``` + +#### `rest_api_document` + +This method defines the URL pattern for individual resource documents and supports RFC 6570 URI templates for dynamic URLs. + +This method has the following syntax: + +```ruby +rest_api_document "/path/to/{resource_id}", first_element_only: false +``` + +Parameters: + +- `path` (String): URL pattern with optional `{template}` placeholders matching property names +- `first_element_only` (Boolean): If `true`, extracts the first element from an array response. Default is `false`. + + + +For example: + +- Path-based selection: + + ```ruby + rest_api_document "/api/v1/users/{username}" + ``` + +- Query-based selection: + + ```ruby + rest_api_document "/api/v1/users?name={username}&org={organization}" + ``` + +- Get the first item in an array: + + ```ruby + rest_api_document "/api/v1/search?q={name}", first_element_only: true + ``` + + With `first_element_only: true`, if the API response returns an array, the resource extracts only the first element. + + For example, if this is the response: + + ```ruby + [{"name": "alice", "email": "alice@example.com"}, {"name": "bob", "email": "bob@example.com"}] + ``` + + The resource extracts the following: `{"name": "alice", "email": "alice@example.com"}`. + +#### `rest_identity_map` + +This method explicitly defines which properties uniquely identify a resource. This is usually inferred automatically from the document URL template variables, but can be specified for composite keys or when the JSON field name differs from the URI template variable. + +This method has the following syntax: + +```ruby +rest_identity_map() +``` + +Replace `` with a hash mapping JSON field paths to property symbols. For example: + +```ruby +# Single identity property +rest_identity_map({ "username" => :username }) + +# Composite identity +rest_identity_map({ + "user.name" => :username, + "organization.id" => :org_id +}) +``` + +#### `rest_post_only_properties` + +Declares properties that should only be sent during resource creation (POST) and excluded from updates (PATCH). + +This method has the following syntax: + +```ruby +rest_post_only_properties +``` + +Replace `` with a single symbol or array of symbols representing property names. For example: + +- Single property: + + ```ruby + rest_post_only_properties :password + ``` + +- Multiple properties: + + ```ruby + rest_post_only_properties [:password, :initial_role, :creation_token] + ``` + +Common use cases: + +- Passwords or secrets that can't be updated with the API +- Resource size or capacity that's immutable after creation +- Template or source identifiers used only during initialization + +#### `rest_property_map` + +The `rest_property_map` method maps resource properties to JSON API fields. This supports simple mappings and complex nested structures using JMESPath. + +This method has the following syntax: + +```ruby +rest_property_map +``` + +Replace `` with: + +- An array of 1:1 mappings +- A hash mapping resource properties to JSON fields or [JMESPaths](https://jmespath.org/) +- A hash mapping resource properties to symbols for custom serialization functions + +For example: + +- Array of mappings. If your property names match the JSON field names, you can use an array: + + ```ruby + rest_property_map [:username, :email, :role] + # Equivalent to: { username: 'username', email: 'email', role: 'role' } + ``` + +- String values. If your property names differ from the JSON fields, or you need to map to nested fields, use a hash: + + ```ruby + rest_property_map({ + full_name: "profile.fullName", + email: "contact.email.primary" + }) + ``` + +- Symbol values. Map a property to a symbol to use custom serialization and deserialization methods: + + ```ruby + rest_property_map({ + tags: :tags_mapping # Uses tags_from_json and tags_to_json methods + }) + ``` + +See the following examples for more information: + +- [Use JMESPath expressions to map data](#use-jmespath-expressions-to-map-data-in-a-json-structure) + +### Actions + +The REST resource provides two built-in actions. + +#### `:configure` (default) + +The `:configure` action creates a new resource or updates an existing one. This action is idempotent and does the following: + +- Checks if the resource exists by querying the API +- If it doesn't exist: sends a POST request to create it +- If it exists and properties are changed: sends a PATCH request to update it +- If it exists and nothing changed: takes no action + +For example: + +```ruby +api_user "john" do + email "john@example.com" + role "admin" + action :configure # This is the default action +end +``` + +#### `:delete` + +The `:delete` action deletes a resource from the REST API. This action is idempotent and does the following: + +- Checks if the resource exists +- If it exists: sends a DELETE request +- If it doesn't exist: takes no action + +For example: + +```ruby +api_user "john" do + action :delete +end +``` + +## More features + +### Custom headers and authentication + +Override the `rest_headers` method in your action class to add custom headers such as authentication tokens. + +```ruby +class Chef::Resource::ApiResource < Chef::Resource + use "core::rest_resource" + + rest_api_collection "/api/v1/resources" + rest_api_document "/api/v1/resources/{id}" + + property :id, String, name_property: true + + rest_property_map({ id: "id" }) + + action_class do + def rest_headers + { + "Authorization" => "Bearer #{node['api_token']}", + "X-API-Version" => "2024-01-01", + "Content-Type" => "application/json" + } + end + end +end +``` + +### Response post-processing + +Override the `rest_postprocess` method to transform API responses, handle pagination, or extract embedded data. + +```ruby +action_class do + def rest_postprocess(response) + # Extract data from paginated response + if response.data.is_a?(Hash) && response.data["items"] + response.data = response.data["items"] + end + + # Add custom logging + Chef::Log.debug("API response: #{response.data.inspect}") + + response + end +end +``` + +### Custom error handling + +Override the `rest_errorhandler` method to provide user-friendly error messages or handle specific error codes. + +```ruby +action_class do + def rest_errorhandler(error_obj) + case error_obj.response&.code + when 404 + Chef::Log.warn("Resource not found - it may have been deleted externally") + nil + when 429 + raise "API rate limit exceeded. Please try again later." + when 401, 403 + raise "Authentication failed. Check your API credentials." + else + raise error_obj # Re-raise for unexpected errors + end + end +end +``` + +### Conditional property requirements + +Use the `conditionally_require_on_setting` helper to enforce dependencies between properties. + +```ruby +action_class do + def load_current_resource + super + + # If ssl_enabled is true, require ssl_cert and ssl_key + conditionally_require_on_setting(:ssl_enabled, [:ssl_cert, :ssl_key]) + end +end +``` + +## Examples + + + +### Create a REST resource using the Train transport endpoint + +The following `api_user` resource uses the Chef target mode Train transport to provide the base URL. The resource uses relative paths only, and Train prepends its configured endpoint. + +```ruby +class Chef::Resource::ApiUser < Chef::Resource + use "core::rest_resource" + + resource_name :api_user + provides :api_user + + # Base URL comes from the Train transport + rest_api_collection "/api/v1/users" + rest_api_document "/api/v1/users/{username}" + + property :username, String, name_property: true, identity: true + property :email, String, required: true + property :first_name, String + property :last_name, String + property :role, String, equal_to: ["admin", "user", "readonly"], default: "user" + property :active, [true, false], default: true + property :password, String, sensitive: true + + rest_property_map({ + username: "username", + email: "email", + first_name: "profile.firstName", + last_name: "profile.lastName", + role: "permissions.role", + active: "status.active", + password: "password" + }) + + rest_post_only_properties :password +end +``` + +Configure the base URL in the Chef target mode transport: + +```ruby +# .chef/config.rb or Policyfile transport block +transport: + name: rest + endpoint: https://api.example.com +``` + +The recipe usage is identical in both cases. + +### Use JMESPath expressions to map data in a JSON structure + +JMESPath navigates and extracts data from JSON structures. The REST resource supports JMESPath for both reading from and writing to APIs. + +#### JMESPath dot notation + +You can use dot notation to specify nested data. + +This code extracts data from the following JSON: + +```ruby +rest_property_map({ + username: "username", # Top-level field + email: "contact.email", # Nested field + city: "address.location.city" # Deeply nested field +}) +``` + +```json +{ + "username": "jdoe", + "contact": { + "email": "jdoe@example.com", + "phone": "+1-555-0100" + }, + "address": { + "location": { + "city": "San Francisco", + "state": "CA", + "zip": "94102" + } + } +} +``` + +#### JMESPath wildcard notation + +You can use a JMESPath wildcard expression to extract data from a JSON structure. + +For example, the following extracts the email address from each member in this JSON: + +```ruby +rest_property_map({ + member_emails: "members[*].email" +}) +``` + +```json +{ + "members": [ + { + "name": "Admin1", + "email": "admin1@example.com", + "role": "admin" + }, + { + "name": "User1", + "email": "user1@example.com", + "role": "user" + } + ] +} +``` + +#### JMESPath filter projection + +You can use a filter projection to extract JSON data matching a condition. + +For example, the following returns the names of active users (`["Alice Johnson", "Carol White"]`) from this JSON: + +```ruby +rest_property_map({ + active_users: "users[?active==`true`].name" # Filter and extract +}) +``` + +```json +{ + "users": [ + { + "id": "user-001", + "name": "Alice Johnson", + "email": "alice@example.com", + "active": true, + "department": "Engineering" + }, + { + "id": "user-002", + "name": "Bob Smith", + "email": "bob@example.com", + "active": false, + "department": "Sales" + }, + { + "id": "user-003", + "name": "Carol White", + "email": "carol@example.com", + "active": true, + "department": "Marketing" + } + ] +} +``` + + + + + + + + + +## Troubleshooting + +### Debugging API requests + +Enable debug logging to see API requests and responses: + +```ruby +action_class do + def rest_postprocess(response) + Chef::Log.debug("API Request: #{rest_url_document}") + Chef::Log.debug("API Response: #{response.data.inspect}") + response + end +end +``` + +### Common issues + +#### Issue: "No such file" error for identity property + +This usually means the identity mapping is incorrect or the document URL template doesn't match the property name. + +```ruby +# Ensure template variables match property names +rest_api_document "/api/v1/users/{username}" # Template variable: username +property :username, String, identity: true # Property name: username +``` + +#### Issue: Properties not updating + +Check if properties are accidentally marked as post-only: + +```ruby +rest_post_only_properties [:password] # Only password is post-only +# Don't include properties that should be updatable +``` + +#### Issue: "Can't resolve property to JSON" + +Verify your property map includes all properties you are trying to set: + +```ruby +property :email, String +property :role, String + +rest_property_map({ + email: "email", + role: "role" +}) +``` + + + +## Additional resources + +- [Custom resources documentation](/resources/custom/) +- [JMESPath Tutorial](https://jmespath.org/tutorial.html) +- [RFC 6570 URI Template Specification](https://tools.ietf.org/html/rfc6570) +- [REST API Tutorial](https://restfulapi.net/) diff --git a/cspell.yaml b/cspell.yaml index c5c5d25..fc8fc80 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -63,3 +63,7 @@ ignoreRegExpList: - "/'d\\b/" - "/^\\s*```[\\s\\S]*?^\\s*```/gm" - "{{(.+)(?=}})" + +words: + - postprocess + - JMES diff --git a/package-lock.json b/package-lock.json index 414ce8a..ffcf98b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,13 +30,15 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.2.tgz", "integrity": "sha512-UNtPCbrwrenpmrXuRwn9jYpPoweNXj8X5sMvYgsqYyaH8jQ6LfUJSk3dJLnBK+6sfYPrF4iAIo5sd5HQ+tg75A==", - "license": "(Apache-2.0 AND BSD-3-Clause)" + "license": "(Apache-2.0 AND BSD-3-Clause)", + "peer": true }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", "license": "MIT", + "peer": true, "dependencies": { "@emotion/memoize": "^0.8.1" } @@ -45,13 +47,15 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@emotion/unitless": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@exodus/schemasafe": { "version": "1.3.0", @@ -114,7 +118,8 @@ "version": "4.2.5", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/trusted-types": { "version": "2.0.7", @@ -172,9 +177,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -184,7 +189,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", - "license": "MIT/X11" + "license": "MIT/X11", + "peer": true }, "node_modules/call-me-maybe": { "version": "1.0.2", @@ -197,6 +203,7 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -258,7 +265,8 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/core-js": { "version": "3.39.0", @@ -277,6 +285,7 @@ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", "license": "ISC", + "peer": true, "engines": { "node": ">=4" } @@ -286,6 +295,7 @@ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", "license": "MIT", + "peer": true, "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -296,7 +306,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.3.7", @@ -321,9 +332,9 @@ "integrity": "sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ==" }, "node_modules/dompurify": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", - "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", + "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -369,17 +380,13 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.6.tgz", + "integrity": "sha512-Yd4vkROfJf8AuJrDIVMVmYfULKmIJszVsMv7Vo71aocsKgFxpdlpSHXSaInvyYfgw2PRuObQSW2GFpVMUjxu9A==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], "license": "MIT", @@ -424,6 +431,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -448,10 +456,11 @@ } }, "node_modules/immutable": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", - "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", - "license": "MIT" + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "license": "MIT", + "peer": true }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -466,8 +475,7 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/js-levenshtein": { "version": "1.1.6", @@ -554,9 +562,9 @@ } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -655,6 +663,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -809,7 +818,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/pluralize": { "version": "8.0.0", @@ -851,6 +861,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -864,7 +875,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/prismjs": { "version": "1.30.0", @@ -1010,6 +1022,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -1019,6 +1032,7 @@ "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.81.0.tgz", "integrity": "sha512-uZQ2Faxb1oWBHpeSSzjxnhClbMb3QadN0ql0ZFNuqWOLUxwaVhrMlMhPq6TDPbbfDUjihuwrMCuy695Bgna5RA==", "license": "MIT", + "peer": true, "dependencies": { "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", @@ -1058,6 +1072,108 @@ "sass-embedded-win32-x64": "1.81.0" } }, + "node_modules/sass-embedded-android-arm": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.81.0.tgz", + "integrity": "sha512-NWEmIuaIEsGFNsIRa+5JpIpPJyZ32H15E85CNZqEIhhwWlk9UNw7vlOCmTH8MtabtnACwC/2NG8VyNa3nxKzUQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-arm64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.81.0.tgz", + "integrity": "sha512-I36P77/PKAHx6sqOmexO2iEY5kpsmQ1VxcgITZSOxPMQhdB6m4t3bTabfDuWQQmCrqqiNFtLQHeytB65bUqwiw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-ia32": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.81.0.tgz", + "integrity": "sha512-k8V1usXw30w1GVxvrteG1RzgYJzYQ9PfL2aeOqGdroBN7zYTD9VGJXTGcxA4IeeRxmRd7szVW2mKXXS472fh8g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-riscv64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.81.0.tgz", + "integrity": "sha512-RXlanyLXEpN/DEehXgLuKPsqT//GYlsGFxKXgRiCc8hIPAueFLQXKJmLWlL3BEtHgmFdbsStIu4aZCcb1hOFlQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-x64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.81.0.tgz", + "integrity": "sha512-RQG0FxGQ1DERNyUDED8+BDVaLIjI+BNg8lVcyqlLZUrWY6NhzjwYEeiN/DNZmMmHtqDucAPNDcsdVUNQqsBy2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-arm64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.81.0.tgz", + "integrity": "sha512-gLKbsfII9Ppua76N41ODFnKGutla9qv0OGAas8gxe0jYBeAQFi/1iKQYdNtQtKi4mA9n5TQTqz+HHCKszZCoyA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/sass-embedded-darwin-x64": { "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.81.0.tgz", @@ -1070,6 +1186,228 @@ "os": [ "darwin" ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.81.0.tgz", + "integrity": "sha512-REqR9qM4RchCE3cKqzRy9Q4zigIV82SbSpCi/O4O3oK3pg2I1z7vkb3TiJsivusG/li7aqKZGmYOtAXjruGQDA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.81.0.tgz", + "integrity": "sha512-jy4bvhdUmqbyw1jv1f3Uxl+MF8EU/Y/GDx4w6XPJm4Ds+mwH/TwnyAwsxxoBhWfnBnW8q2ADy039DlS5p+9csQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-ia32": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.81.0.tgz", + "integrity": "sha512-ga/Jk4q5Bn1aC+iHJteDZuLSKnmBUiS3dEg1fnl/Z7GaHIChceKDJOw0zNaILRXI0qT2E1at9MwzoRaRA5Nn/g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.81.0.tgz", + "integrity": "sha512-oWVUvQ4d5Kx1Md75YXZl5z1WBjc+uOhfRRqzkJ3nWc8tjszxJN+y/5EOJavhsNI3/2yoTt6eMXRTqDD9b0tWSQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.81.0.tgz", + "integrity": "sha512-hpntWf5kjkoxncA1Vh8vhsUOquZ8AROZKx0rQh7ZjSRs4JrYZASz1cfevPKaEM3wIim/nYa6TJqm0VqWsrERlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-ia32": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.81.0.tgz", + "integrity": "sha512-UEXUYkBuqTSwg5JNWiNlfMZ1Jx6SJkaEdx+fsL3Tk099L8cKSoJWH2EPz4ZJjNbyIMymrSdVfymheTeZ8u24xA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-riscv64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.81.0.tgz", + "integrity": "sha512-1D7OznytbIhx2XDHWi1nuQ8d/uCVR7FGGzELgaU//T8A9DapVTUgPKvB70AF1k4GzChR9IXU/WvFZs2hDTbaJg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-x64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.81.0.tgz", + "integrity": "sha512-ia6VCTeVDQtBSMktXRFza1AZCt8/6aUoujot6Ugf4KmdytQqPJIHxkHaGftm5xwi9WdrMGYS7zgolToPijR11A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-riscv64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.81.0.tgz", + "integrity": "sha512-KbxSsqu4tT1XbhZfJV/5NfW0VtJIGlD58RjqJqJBi8Rnjrx29/upBsuwoDWtsPV/LhoGwwU1XkSa9Q1ifCz4fQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-x64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.81.0.tgz", + "integrity": "sha512-AMDeVY2T9WAnSFkuQcsOn5c29GRs/TuqnCiblKeXfxCSKym5uKdBl/N7GnTV6OjzoxiJBbkYKdVIaS5By7Gj4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-arm64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.81.0.tgz", + "integrity": "sha512-YOmBRYnygwWUmCoH14QbMRHjcvCJufeJBAp0m61tOJXIQh64ziwV4mjdqjS/Rx3zhTT4T+nulDUw4d3kLiMncA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-ia32": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.81.0.tgz", + "integrity": "sha512-HFfr/C+uLJGGTENdnssuNTmXI/xnIasUuEHEKqI+2J0FHCWT5cpz3PGAOHymPyJcZVYGUG/7gIxIx/d7t0LFYw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-x64": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.81.0.tgz", + "integrity": "sha512-wxj52jDcIAwWcXb7ShZ7vQYKcVUkJ+04YM9l46jDY+qwHzliGuorAUyujLyKTE9heGD3gShJ3wPPC1lXzq6v9A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { "node": ">=14.0.0" } @@ -1079,6 +1417,7 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -1087,7 +1426,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/should": { "version": "13.2.3", @@ -1157,6 +1497,7 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -1203,6 +1544,7 @@ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==", "license": "MIT", + "peer": true, "dependencies": { "@emotion/is-prop-valid": "1.2.2", "@emotion/unitless": "0.8.1", @@ -1230,19 +1572,22 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/stylis": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -1285,6 +1630,7 @@ "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", "license": "MIT", + "peer": true, "dependencies": { "sync-message-port": "^1.0.0" }, @@ -1297,6 +1643,7 @@ "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=16.0.0" } @@ -1311,7 +1658,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/uri-js-replace": { "version": "1.0.1", @@ -1338,7 +1686,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/webidl-conversions": { "version": "3.0.1", @@ -1390,9 +1739,9 @@ } }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6"