From 3a06950ded309df49a03057f883d201ba277d5b7 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:11:27 +0200 Subject: [PATCH 01/17] Dev guide for the services, user guide still wip --- docs/developer-guide/external-services.md | 163 ++ docs/user-guide/services/_category_.json | 7 + docs/user-guide/services/services.md | 0 docusaurus.config.ts | 9 +- package.json | 1 + yarn.lock | 2343 ++++++++++++++++++++- 6 files changed, 2502 insertions(+), 21 deletions(-) create mode 100644 docs/developer-guide/external-services.md create mode 100644 docs/user-guide/services/_category_.json create mode 100644 docs/user-guide/services/services.md diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md new file mode 100644 index 0000000..77bfef4 --- /dev/null +++ b/docs/developer-guide/external-services.md @@ -0,0 +1,163 @@ +--- +sidebar_position: 17 +--- + +# External Services + +External services allow developers to extend the functionality of the OpenRemote platform. By registering an external service, developers can integrate their own applications directly into the OpenRemote Manager, giving users a seamless way to configure and manage them. + +The registration process is primarily intended for services that provide a **user interface**. When registered, the service’s web interface is embedded in the Manager Web UI using an **iframe**, allowing users to interact with it without leaving OpenRemote. + +Services without a UI can remain fully standalone applications. They can still make use of OpenRemote’s APIs but do not need to register as an external service. + +--- + +## What is an External Service? +An external service is a standalone application that communicates with OpenRemote through its APIs. When registered, it appears in the Manager Web UI with its own embedded web interface, enabling direct interaction from within OpenRemote. + +External services can be built using any programming language or framework. They should be viewed as independent applications that **extend the platform’s capabilities** while leveraging OpenRemote for integration and management. + +--- + +## Types of External Services +There are two types of external services in OpenRemote: + +- **Global Services** + - Registered using the `/services/global` endpoint. + - Must be registered via the **master realm**. + - Require a **Service User with Super User privileges** for registration. + - Once registered, they are available and listed on **all realms**. + - They are typically designed with **multi-tenancy** in mind, but this is not strictly required. + For example, a global service may implement its own authentication and authorization instead of relying on OpenRemote's Keycloak instance. + +- **Regular Services** + - Registered using the `/services` endpoint. + - Bound to a **specific realm** and only available within that realm (single-tenant). + - Simpler to implement when multi-tenancy is not required. + +Both global and regular services must be registered by **Service Users**. Global services specifically require a Super User account in the master realm, while regular services can be registered with a realm-specific Service User. + +--- + +## Registering an External Service +Registration is only required if the service provides a web interface and is expected to be embedded and used within the OpenRemote Manager UI. +This process is always performed using a **Service User** account (see [Service Users](../architecture/security.md) for details). + +It involves sending a `POST` request to the OpenRemote API with details about the service. + +**Example request:** + +```json +{ + "serviceId": "my-service", + "label": "My External Service", + "icon": "mdi-cloud", + "homepageUrl": "https://my-external-service.com/interface", + "version": "1.0.0", +} +``` + +OpenRemote responds with the same `ExternalService` object, but with an additional field, `instanceId`, which uniquely identifies the service and must be included in subsequent heartbeat requests. + +➡ The exact API endpoint and request format can be found in the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/). + +The diagram below illustrates the registration and heartbeat process for both regular and global services: + +```mermaid +sequenceDiagram + participant Service as External Service + participant OR as OpenRemote API + + alt Regular Service + Service->>OR: POST /services (serviceId, name, url, metadata…) + OR-->>Service: ExternalService object with instanceId + else Global Service + Service->>OR: POST /services/global (serviceId, name, url, metadata…) + OR-->>Service: ExternalService object with instanceId + end + + loop Every < 60s + Service->>OR: POST /services/heartbeat (serviceId, instanceId) + OR-->>Service: 204 No Content + end +``` + +### Key Endpoints + +| Endpoint | Method | Scope | Purpose | +|-------------------------|--------|-----------|--------------------------------------------------------| +| `/services` | POST | Realm | Register a realm-specific external service | +| `/services/global` | POST | Global | Register a global external service (master realm only) | +| `/services/heartbeat` | POST | Both | Send periodic heartbeat with `instanceId` | + +--- + +## Heartbeat Mechanism +After registration, each service must send periodic heartbeat requests to confirm its availability. + +- The request must include the `instanceId` received during registration. +- The default TTL (Time To Live) is **60 seconds**. +- If OpenRemote does not receive a heartbeat within this period, the service is marked as **unavailable**. + +➡ The heartbeat flow is illustrated in the diagram above. +➡ API details for heartbeats can be found in the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/). + +--- + +## Service Web Interface +Registered external services must expose a web interface via a URL. OpenRemote embeds this interface in the Manager Web UI using an **iframe**, allowing users to configure and interact with the service directly. + +### Requirements +- **Same origin and protocol**: The service must use the same domain and protocol (HTTP or HTTPS) as the OpenRemote Manager (no mixed content). +- **Headers**: Do not block embedding. Avoid `X-Frame-Options: DENY` or restrictive `Content-Security-Policy`. + - Recommended: + ``` + Content-Security-Policy: frame-ancestors 'self' https:// + ``` +- **Responsive design**: The iframe may resize; ensure the UI adapts dynamically. +- **Navigation**: Avoid pop-ups or full-page redirects; keep interactions within the iframe. + +### Building the Interface +Developers can use any web framework or technology stack. Optionally, OpenRemote provides a set of pre-made web components such as buttons, panels, forms, and tables that integrate seamlessly with the Manager UI for a consistent look and feel. + +These components are available on [npmjs](https://www.npmjs.com/~openremotedeveloper). + +--- + +## Security Considerations +When developing and integrating an external service, consider the following: + +- **Authentication**: OpenRemote uses Keycloak as an identity provider. External services should ensure that only authorized users can access them, either by integrating with Keycloak or implementing their own mechanism. +- **Protocol**: Always use **HTTPS** in production to protect data integrity and confidentiality. +- **Data validation**: Validate and sanitize all data received from OpenRemote or users to prevent vulnerabilities such as SQL injection or cross-site scripting (XSS). +- **CORS**: If hosting on a different domain, configure Cross-Origin Resource Sharing (CORS) appropriately. Note that using a different domain may complicate Keycloak integration. + +--- + +## Example Use Cases +External services can be used to extend OpenRemote in many ways, such as: + +- **AI/LLM Integration**: Connect AI services (e.g., ChatGPT, Claude) to provide contextual querying of devices, assets, and data. +- **Machine Learning**: Implement predictive maintenance, energy optimization, or anomaly detection. +- **Firmware Updates**: Manage and deploy firmware updates to connected devices. + +### Reference Implementation +We provide the [ML Forecasting Service](https://github.com/openremote/ml-forecasting-service) as a reference implementation. +This service connects to OpenRemote, retrieves historical data, and provides forecasting capabilities using machine learning and statistical models. It also demonstrates how to: + +- **Register** an external service +- **Send and manage** heartbeats +- **Interact** with OpenRemote's APIs (OAuth2 authentication, data retrieval, data writing) +- **Integrate** securely with Keycloak for authentication +- **Implement** a web interface (using OpenRemote’s web components) + +--- + +## Summary +By leveraging external services, developers can significantly enhance the OpenRemote platform. +- **Registration** connects services with a UI to the Manager, embedding their interface directly. +- **Global vs regular services** allow flexibility between multi-tenant and realm-specific use cases. +- **Heartbeats** ensure service availability is tracked in real time. +- **Security best practices** help ensure safe and reliable integrations. + +Together, these mechanisms provide a seamless way to extend OpenRemote while maintaining a unified and secure user experience. diff --git a/docs/user-guide/services/_category_.json b/docs/user-guide/services/_category_.json new file mode 100644 index 0000000..a73db4f --- /dev/null +++ b/docs/user-guide/services/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Services", + "position": 8, + "link": { + "type": "generated-index" + } +} diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md new file mode 100644 index 0000000..e69de29 diff --git a/docusaurus.config.ts b/docusaurus.config.ts index b7e6611..916020e 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -82,7 +82,14 @@ const config: Config = { ], ], - themes: ["docusaurus-theme-openapi-docs"], + themes: [ + "docusaurus-theme-openapi-docs", + "@docusaurus/theme-mermaid" + ], + + markdown: { + mermaid: true, + }, themeConfig: { // Replace with your project's social card diff --git a/package.json b/package.json index b0a3965..b578012 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@docusaurus/core": "^3.7.0", "@docusaurus/preset-classic": "^3.7.0", + "@docusaurus/theme-mermaid": "^3.8.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-plugin-openapi-docs": "4.3.6", diff --git a/yarn.lock b/yarn.lock index 29fe046..93e3172 100644 --- a/yarn.lock +++ b/yarn.lock @@ -207,6 +207,23 @@ __metadata: languageName: node linkType: hard +"@antfu/install-pkg@npm:^1.1.0": + version: 1.1.0 + resolution: "@antfu/install-pkg@npm:1.1.0" + dependencies: + package-manager-detector: "npm:^1.3.0" + tinyexec: "npm:^1.0.1" + checksum: 10/e20b7cd1c37eff832cc878cddd794f8c3779175681cf6d75c4cc1ae1475526126a4c1f71fa027161aa1ee35a8850782be9ca0ec01b621893defebe97ba9dc70e + languageName: node + linkType: hard + +"@antfu/utils@npm:^9.2.0": + version: 9.2.0 + resolution: "@antfu/utils@npm:9.2.0" + checksum: 10/cebc2666e61fc6364c3b2af8e384cf470b9243f38310ca22e86853ac7f2b33a265d0ca5935a697c2cc71db8c17e7419b2fd4a89215ba5c86c71fdd7f51f02b56 + languageName: node + linkType: hard + "@apidevtools/json-schema-ref-parser@npm:^11.5.4": version: 11.6.4 resolution: "@apidevtools/json-schema-ref-parser@npm:11.6.4" @@ -2863,6 +2880,55 @@ __metadata: languageName: node linkType: hard +"@braintree/sanitize-url@npm:^7.0.4": + version: 7.1.1 + resolution: "@braintree/sanitize-url@npm:7.1.1" + checksum: 10/a8a5535c5a0a459ba593a018c554b35493dff004fd09d7147db67243df83bce3d410b89ee7dc2d95cce195b85b877c72f8ca149e1040110a945d193c67293af0 + languageName: node + linkType: hard + +"@chevrotain/cst-dts-gen@npm:11.0.3": + version: 11.0.3 + resolution: "@chevrotain/cst-dts-gen@npm:11.0.3" + dependencies: + "@chevrotain/gast": "npm:11.0.3" + "@chevrotain/types": "npm:11.0.3" + lodash-es: "npm:4.17.21" + checksum: 10/601d23fa3312bd0e32816bd3f9ca2dcba775a52192a082fd6c5e4a2e8ee068523401191babbe2c346d6d2551900a67b549f2f74d7ebb7d5b2ee1b6fa3c8857a0 + languageName: node + linkType: hard + +"@chevrotain/gast@npm:11.0.3": + version: 11.0.3 + resolution: "@chevrotain/gast@npm:11.0.3" + dependencies: + "@chevrotain/types": "npm:11.0.3" + lodash-es: "npm:4.17.21" + checksum: 10/7169453a8fbfa994e91995523dea09eab87ab23062ad93f6e51f4a3b03f5e2958e0a8b99d5ca6fa067fccfbbbb8bcf1a4573ace2e1b5a455f6956af9eaccb35a + languageName: node + linkType: hard + +"@chevrotain/regexp-to-ast@npm:11.0.3": + version: 11.0.3 + resolution: "@chevrotain/regexp-to-ast@npm:11.0.3" + checksum: 10/7387a1c61c5a052de41e1172b33eaaedea166fcdb1ffe4c381b86d00051a8014855a031d28fb658768a62c833ef5f5b0689d0c40de3d7bed556f8fea24396e69 + languageName: node + linkType: hard + +"@chevrotain/types@npm:11.0.3": + version: 11.0.3 + resolution: "@chevrotain/types@npm:11.0.3" + checksum: 10/49a82b71d2de8ceb2383ff2709fa61d245f2ab2e42790b70c57102c80846edaa318d0b3645aedc904d23ea7bd9be8a58f2397b1341760a15eb5aa95a1336e2a9 + languageName: node + linkType: hard + +"@chevrotain/utils@npm:11.0.3": + version: 11.0.3 + resolution: "@chevrotain/utils@npm:11.0.3" + checksum: 10/29b5d84373a7761ad055c53e2f540a67b5b56550d5be1c473149f6b8923eef87ff391ce021c06ac7653843b0149f6ff0cf30b5e48c3f825d295eb06a6c517bd3 + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -2880,6 +2946,16 @@ __metadata: languageName: node linkType: hard +"@csstools/cascade-layer-name-parser@npm:^2.0.5": + version: 2.0.5 + resolution: "@csstools/cascade-layer-name-parser@npm:2.0.5" + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/fb26ae1db6f7a71ee0c3fdaea89f5325f88d7a0b2505fcf4b75e94f2c816ef1edb2961eecbc397df06f67d696ccc6bc99588ea9ee07dd7632bf10febf6b67ed9 + languageName: node + linkType: hard + "@csstools/color-helpers@npm:^5.0.1": version: 5.0.1 resolution: "@csstools/color-helpers@npm:5.0.1" @@ -2887,6 +2963,13 @@ __metadata: languageName: node linkType: hard +"@csstools/color-helpers@npm:^5.1.0": + version: 5.1.0 + resolution: "@csstools/color-helpers@npm:5.1.0" + checksum: 10/0138b3d5ccbe77aeccf6721fd008a53523c70e932f0c82dca24a1277ca780447e1d8357da47512ebf96358476f8764de57002f3e491920d67e69202f5a74c383 + languageName: node + linkType: hard + "@csstools/css-calc@npm:^2.1.1": version: 2.1.1 resolution: "@csstools/css-calc@npm:2.1.1" @@ -2897,6 +2980,16 @@ __metadata: languageName: node linkType: hard +"@csstools/css-calc@npm:^2.1.4": + version: 2.1.4 + resolution: "@csstools/css-calc@npm:2.1.4" + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/06975b650c0f44c60eeb7afdb3fd236f2dd607b2c622e0bc908d3f54de39eb84e0692833320d03dac04bd6c1ab0154aa3fa0dd442bd9e5f917cf14d8e2ba8d74 + languageName: node + linkType: hard + "@csstools/css-color-parser@npm:^3.0.7": version: 3.0.7 resolution: "@csstools/css-color-parser@npm:3.0.7" @@ -2910,6 +3003,19 @@ __metadata: languageName: node linkType: hard +"@csstools/css-color-parser@npm:^3.1.0": + version: 3.1.0 + resolution: "@csstools/css-color-parser@npm:3.1.0" + dependencies: + "@csstools/color-helpers": "npm:^5.1.0" + "@csstools/css-calc": "npm:^2.1.4" + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/4741095fdc4501e8e7ada4ed14fbf9dbbe6fea9b989818790ebca15657c29c62defbebacf18592cde2aa638a1d098bbe86d742d2c84ba932fbc00fac51cb8805 + languageName: node + linkType: hard + "@csstools/css-parser-algorithms@npm:^3.0.4": version: 3.0.4 resolution: "@csstools/css-parser-algorithms@npm:3.0.4" @@ -2919,6 +3025,15 @@ __metadata: languageName: node linkType: hard +"@csstools/css-parser-algorithms@npm:^3.0.5": + version: 3.0.5 + resolution: "@csstools/css-parser-algorithms@npm:3.0.5" + peerDependencies: + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/e93083b5cb36a3c1e7a47ce10cf62961d05bd1e4c608bb3ee50186ff740157ab0ec16a3956f7b86251efd10703034d849693201eea858ae904848c68d2d46ada + languageName: node + linkType: hard + "@csstools/css-tokenizer@npm:^3.0.3": version: 3.0.3 resolution: "@csstools/css-tokenizer@npm:3.0.3" @@ -2926,6 +3041,13 @@ __metadata: languageName: node linkType: hard +"@csstools/css-tokenizer@npm:^3.0.4": + version: 3.0.4 + resolution: "@csstools/css-tokenizer@npm:3.0.4" + checksum: 10/eb6c84c086312f6bb8758dfe2c85addd7475b0927333c5e39a4d59fb210b9810f8c346972046f95e60a721329cffe98895abe451e51de753ad1ca7a8c24ec65f + languageName: node + linkType: hard + "@csstools/media-query-list-parser@npm:^4.0.2": version: 4.0.2 resolution: "@csstools/media-query-list-parser@npm:4.0.2" @@ -2936,6 +3058,31 @@ __metadata: languageName: node linkType: hard +"@csstools/media-query-list-parser@npm:^4.0.3": + version: 4.0.3 + resolution: "@csstools/media-query-list-parser@npm:4.0.3" + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/ac4e34c21a1c7fc8b788274f316c29ff2f07e7a08996d27b9beb06454666591be9946362c1b7c4d342558c278c8e7d0e35655043e47a9ffd94c3fdb292fbe020 + languageName: node + linkType: hard + +"@csstools/postcss-alpha-function@npm:^1.0.0": + version: 1.0.0 + resolution: "@csstools/postcss-alpha-function@npm:1.0.0" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/5f21212f88a0bb14efc6b2c60a7a5861bb192452fea1b8b53a9bddd6dbde46123fb8e6c01bd1808f52da83279478eb124ebb018855a9f56b389c8a1f70083408 + languageName: node + linkType: hard + "@csstools/postcss-cascade-layers@npm:^5.0.1": version: 5.0.1 resolution: "@csstools/postcss-cascade-layers@npm:5.0.1" @@ -2948,6 +3095,48 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-cascade-layers@npm:^5.0.2": + version: 5.0.2 + resolution: "@csstools/postcss-cascade-layers@npm:5.0.2" + dependencies: + "@csstools/selector-specificity": "npm:^5.0.0" + postcss-selector-parser: "npm:^7.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/9b73c28338f75eebd1032d6375e76547f90683806971f1dd3a47e6305901c89642094e1a80815fcfbb10b0afb61174f9ab3207db860a5841ca92ae993dc87cbe + languageName: node + linkType: hard + +"@csstools/postcss-color-function-display-p3-linear@npm:^1.0.0": + version: 1.0.0 + resolution: "@csstools/postcss-color-function-display-p3-linear@npm:1.0.0" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/869b7d8e238b277556112aa262e838a88bd83a5f25f0ac42825750bcd68cdcf7a0462b83eab6135b2999e2ece60ab82ddeb361565b5b2f1c6b3110ce769f5a9c + languageName: node + linkType: hard + +"@csstools/postcss-color-function@npm:^4.0.11": + version: 4.0.11 + resolution: "@csstools/postcss-color-function@npm:4.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/4af639ecea7ff4acf0cadd226361dcc044a051648b9b40f751e67ec8a81efea2038275d57fd1a292940b7cea17b6e9440b8ebcba17e721870e6ed0ca92be0307 + languageName: node + linkType: hard + "@csstools/postcss-color-function@npm:^4.0.7": version: 4.0.7 resolution: "@csstools/postcss-color-function@npm:4.0.7" @@ -2963,6 +3152,21 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-color-mix-function@npm:^3.0.11": + version: 3.0.11 + resolution: "@csstools/postcss-color-mix-function@npm:3.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/35e43134047ed55ce3c0a97c13065ec03220db6334bc068960dd050fa565ab38bee7dc75e666e3272905bbf39df470acec81bae2b90b11b0de5ff0eb6f513ab8 + languageName: node + linkType: hard + "@csstools/postcss-color-mix-function@npm:^3.0.7": version: 3.0.7 resolution: "@csstools/postcss-color-mix-function@npm:3.0.7" @@ -2978,6 +3182,21 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-color-mix-variadic-function-arguments@npm:^1.0.1": + version: 1.0.1 + resolution: "@csstools/postcss-color-mix-variadic-function-arguments@npm:1.0.1" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/128c3cb1bb8bcc45797bc363d116c6f318e109e096ef3e8b35c7725a6d83f8f44f785ab075ef84f2a95a6e07a692840d8ea9e41b48fb70b2fd97f6d163258949 + languageName: node + linkType: hard + "@csstools/postcss-content-alt-text@npm:^2.0.4": version: 2.0.4 resolution: "@csstools/postcss-content-alt-text@npm:2.0.4" @@ -2992,6 +3211,20 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-content-alt-text@npm:^2.0.7": + version: 2.0.7 + resolution: "@csstools/postcss-content-alt-text@npm:2.0.7" + dependencies: + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/7d5824e22edc9bba24a61a8b97ec4af7fc97a7c1d43d59244927ef50bb4a2d0fcb45373df8aa6b1c19324337737e862218327262865dbbe597c978c4290d090a + languageName: node + linkType: hard + "@csstools/postcss-exponential-functions@npm:^2.0.6": version: 2.0.6 resolution: "@csstools/postcss-exponential-functions@npm:2.0.6" @@ -3005,6 +3238,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-exponential-functions@npm:^2.0.9": + version: 2.0.9 + resolution: "@csstools/postcss-exponential-functions@npm:2.0.9" + dependencies: + "@csstools/css-calc": "npm:^2.1.4" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + peerDependencies: + postcss: ^8.4 + checksum: 10/80d5847d747fc67c32ee3ba49f9c9290654fb086c58b2f13256b14124b7349dac68ba8e107f631248cef2448ca57ef18adbbbc816dd63a54ba91826345373f39 + languageName: node + linkType: hard + "@csstools/postcss-font-format-keywords@npm:^4.0.0": version: 4.0.0 resolution: "@csstools/postcss-font-format-keywords@npm:4.0.0" @@ -3017,6 +3263,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-gamut-mapping@npm:^2.0.11": + version: 2.0.11 + resolution: "@csstools/postcss-gamut-mapping@npm:2.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + peerDependencies: + postcss: ^8.4 + checksum: 10/be4cb5a14eef78acbd9dfca7cdad0ab4e8e4a11c9e8bbb27e427bfd276fd5d3aa37bc1bf36deb040d404398989a3123bd70fc51be970c4d944cf6a18d231c1b8 + languageName: node + linkType: hard + "@csstools/postcss-gamut-mapping@npm:^2.0.7": version: 2.0.7 resolution: "@csstools/postcss-gamut-mapping@npm:2.0.7" @@ -3030,6 +3289,21 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-gradients-interpolation-method@npm:^5.0.11": + version: 5.0.11 + resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/a45d9ca26706e15a5fc47da304f19d52db33672ab88f79f3fb41dfcc4247ee46dcd0bce4ef4d480867690593fb04e3b34eea3c724d4b5e514646cb328a16c271 + languageName: node + linkType: hard + "@csstools/postcss-gradients-interpolation-method@npm:^5.0.7": version: 5.0.7 resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.7" @@ -3045,6 +3319,21 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-hwb-function@npm:^4.0.11": + version: 4.0.11 + resolution: "@csstools/postcss-hwb-function@npm:4.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/aa29efe8a57c5bbabbc2d4a69c98d60d599422dfeb436d604b33147657ae68b377ba545c348d79f232d3cce632cca79fa07b0f54e578b7fc8b70a48c7a530a00 + languageName: node + linkType: hard + "@csstools/postcss-hwb-function@npm:^4.0.7": version: 4.0.7 resolution: "@csstools/postcss-hwb-function@npm:4.0.7" @@ -3073,6 +3362,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-ic-unit@npm:^4.0.3": + version: 4.0.3 + resolution: "@csstools/postcss-ic-unit@npm:4.0.3" + dependencies: + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/ca03e600639c79c740312a83274eb86c98e8e61733eb5a5e7b9f9d7c03d17840e6c2f02b2f65625cafc58976552e88a61498b5fb818142f229b9f6a52f055714 + languageName: node + linkType: hard + "@csstools/postcss-initial@npm:^2.0.1": version: 2.0.1 resolution: "@csstools/postcss-initial@npm:2.0.1" @@ -3094,6 +3396,32 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-is-pseudo-class@npm:^5.0.3": + version: 5.0.3 + resolution: "@csstools/postcss-is-pseudo-class@npm:5.0.3" + dependencies: + "@csstools/selector-specificity": "npm:^5.0.0" + postcss-selector-parser: "npm:^7.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/99abf2595c3b92ba993f26704c622837ec7546832037f75778d74a2c3d2e5009a027e52178d7f5c967d9c0dcda44244db9a8131c51e42fcbf4a0c22f21b3b1b6 + languageName: node + linkType: hard + +"@csstools/postcss-light-dark-function@npm:^2.0.10": + version: 2.0.10 + resolution: "@csstools/postcss-light-dark-function@npm:2.0.10" + dependencies: + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/b35170c2dccc243e8dbed1824e80fc9ecee8b681c75d4ad1dd2623dbcea71b944e7d98940ba018be7f6f15332b6f30e6add9700540e9625b8560cf0f7f99e2b4 + languageName: node + linkType: hard + "@csstools/postcss-light-dark-function@npm:^2.0.7": version: 2.0.7 resolution: "@csstools/postcss-light-dark-function@npm:2.0.7" @@ -3158,6 +3486,18 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-logical-viewport-units@npm:^3.0.4": + version: 3.0.4 + resolution: "@csstools/postcss-logical-viewport-units@npm:3.0.4" + dependencies: + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/ddb8d9b473c55cce1c1261652d657d33d9306d80112eac578d53b05dd48a5607ea2064fcf6bc298ccc1e63143e11517d35230bad6063dae14d445530c45a81ec + languageName: node + linkType: hard + "@csstools/postcss-media-minmax@npm:^2.0.6": version: 2.0.6 resolution: "@csstools/postcss-media-minmax@npm:2.0.6" @@ -3172,6 +3512,20 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-media-minmax@npm:^2.0.9": + version: 2.0.9 + resolution: "@csstools/postcss-media-minmax@npm:2.0.9" + dependencies: + "@csstools/css-calc": "npm:^2.1.4" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/media-query-list-parser": "npm:^4.0.3" + peerDependencies: + postcss: ^8.4 + checksum: 10/ddd35129dc482a2cffe44cc75c48844cee56370f551e7e3abcfa0a158c3a2a48d8a2196e82e223fdf794a066688d423558e211f69010cfbc6044c989464d3899 + languageName: node + linkType: hard + "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^3.0.4": version: 3.0.4 resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:3.0.4" @@ -3185,6 +3539,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^3.0.5": + version: 3.0.5 + resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:3.0.5" + dependencies: + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/media-query-list-parser": "npm:^4.0.3" + peerDependencies: + postcss: ^8.4 + checksum: 10/b0124a071c7880327b23ebcd77e2c74594a852bf9193f2f552630d9e8b0996789884c05cf4ebff4dbf5c3bfb5e6cb70e9e52a740f150034bfae87208898d3d9d + languageName: node + linkType: hard + "@csstools/postcss-nested-calc@npm:^4.0.0": version: 4.0.0 resolution: "@csstools/postcss-nested-calc@npm:4.0.0" @@ -3208,6 +3575,21 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-oklab-function@npm:^4.0.11": + version: 4.0.11 + resolution: "@csstools/postcss-oklab-function@npm:4.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/dd2bbbd449fa0dcbcc7549a76df663ea9df1ba632dae9966467490727092e0b827bee62daa1e9328ec925a58610bfd28c0b473e76471d1bb0f5c0cfb810c9d8a + languageName: node + linkType: hard + "@csstools/postcss-oklab-function@npm:^4.0.7": version: 4.0.7 resolution: "@csstools/postcss-oklab-function@npm:4.0.7" @@ -3234,6 +3616,17 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-progressive-custom-properties@npm:^4.2.0": + version: 4.2.0 + resolution: "@csstools/postcss-progressive-custom-properties@npm:4.2.0" + dependencies: + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/b862dbfc9590f2e0714e1331c982d6418e94647ff0d6c558e4f4baf6fc6c8f5175b8e5a0f7e90072ec607aa1ead6b6205703efbf42aa1b7c5431b74a69649f3f + languageName: node + linkType: hard + "@csstools/postcss-random-function@npm:^1.0.2": version: 1.0.2 resolution: "@csstools/postcss-random-function@npm:1.0.2" @@ -3247,6 +3640,34 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-random-function@npm:^2.0.1": + version: 2.0.1 + resolution: "@csstools/postcss-random-function@npm:2.0.1" + dependencies: + "@csstools/css-calc": "npm:^2.1.4" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + peerDependencies: + postcss: ^8.4 + checksum: 10/d421a790b11675edf493f3e48259636beca164c494ed2883042118b35674d26f04e1a46f9e89203a179e20acc2a1f5912078ec81b330a2c1a1abef7e7387e587 + languageName: node + linkType: hard + +"@csstools/postcss-relative-color-syntax@npm:^3.0.11": + version: 3.0.11 + resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/4a3db576a595d18475ccfc649a1516df30683cbe8fc485ea0fb4633855598223bd42c4e0e5a8dc048b6dc9d652c815e08a621adba9f1c3842291d55ffc5b43ce + languageName: node + linkType: hard + "@csstools/postcss-relative-color-syntax@npm:^3.0.7": version: 3.0.7 resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.7" @@ -3286,6 +3707,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-sign-functions@npm:^1.1.4": + version: 1.1.4 + resolution: "@csstools/postcss-sign-functions@npm:1.1.4" + dependencies: + "@csstools/css-calc": "npm:^2.1.4" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + peerDependencies: + postcss: ^8.4 + checksum: 10/0afcb008142a0a41df51267d79cf950f4f314394dca7c041e3a0be87df56517ac5400861630a979b5bef49f01c296025106622110384039e3c8f82802d6adcde + languageName: node + linkType: hard + "@csstools/postcss-stepped-value-functions@npm:^4.0.6": version: 4.0.6 resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.6" @@ -3299,6 +3733,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-stepped-value-functions@npm:^4.0.9": + version: 4.0.9 + resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.9" + dependencies: + "@csstools/css-calc": "npm:^2.1.4" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + peerDependencies: + postcss: ^8.4 + checksum: 10/6465a883be42d4cc4a4e83be2626a1351de4bfe84a63641c53e7c39d3c0e109152489ca2d8235625cdf6726341c676b9fbbca18fe80bb5eae8d488a0e42fc5e4 + languageName: node + linkType: hard + "@csstools/postcss-text-decoration-shorthand@npm:^4.0.1": version: 4.0.1 resolution: "@csstools/postcss-text-decoration-shorthand@npm:4.0.1" @@ -3311,6 +3758,18 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-text-decoration-shorthand@npm:^4.0.3": + version: 4.0.3 + resolution: "@csstools/postcss-text-decoration-shorthand@npm:4.0.3" + dependencies: + "@csstools/color-helpers": "npm:^5.1.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/afc350e389bae7fdceecb3876b9be00bdbd56e5f43054f9f5de2d42b3c55a163e5ba737212030479389c9c1fca5d066f5b051da1fdf72e13191a035d2cc6f4e0 + languageName: node + linkType: hard + "@csstools/postcss-trigonometric-functions@npm:^4.0.6": version: 4.0.6 resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.6" @@ -3324,6 +3783,19 @@ __metadata: languageName: node linkType: hard +"@csstools/postcss-trigonometric-functions@npm:^4.0.9": + version: 4.0.9 + resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.9" + dependencies: + "@csstools/css-calc": "npm:^2.1.4" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + peerDependencies: + postcss: ^8.4 + checksum: 10/c746cd986df061a87de4f2d0129aa2d2e98a2948e5005fe6fe419a9e9ec7a0f7382461847cbd3f67f8f66169bdf23a1d7f53ca6b9922ddd235ec45f2867a8825 + languageName: node + linkType: hard + "@csstools/postcss-unset-value@npm:^4.0.0": version: 4.0.0 resolution: "@csstools/postcss-unset-value@npm:4.0.0" @@ -3342,6 +3814,15 @@ __metadata: languageName: node linkType: hard +"@csstools/selector-resolve-nested@npm:^3.1.0": + version: 3.1.0 + resolution: "@csstools/selector-resolve-nested@npm:3.1.0" + peerDependencies: + postcss-selector-parser: ^7.0.0 + checksum: 10/eaad6a6c99345cae2849a2c73daf53381fabd75851eefd830ee743e4d454d4e2930aa99c8b9e651fed92b9a8361f352c6c754abf82c576bba4953f1e59c927e9 + languageName: node + linkType: hard + "@csstools/selector-specificity@npm:^5.0.0": version: 5.0.0 resolution: "@csstools/selector-specificity@npm:5.0.0" @@ -3423,8 +3904,31 @@ __metadata: languageName: node linkType: hard -"@docusaurus/bundler@npm:3.7.0": - version: 3.7.0 +"@docusaurus/babel@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/babel@npm:3.8.1" + dependencies: + "@babel/core": "npm:^7.25.9" + "@babel/generator": "npm:^7.25.9" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + "@babel/plugin-transform-runtime": "npm:^7.25.9" + "@babel/preset-env": "npm:^7.25.9" + "@babel/preset-react": "npm:^7.25.9" + "@babel/preset-typescript": "npm:^7.25.9" + "@babel/runtime": "npm:^7.25.9" + "@babel/runtime-corejs3": "npm:^7.25.9" + "@babel/traverse": "npm:^7.25.9" + "@docusaurus/logger": "npm:3.8.1" + "@docusaurus/utils": "npm:3.8.1" + babel-plugin-dynamic-import-node: "npm:^2.3.3" + fs-extra: "npm:^11.1.1" + tslib: "npm:^2.6.0" + checksum: 10/3294c488a3d6ee4569ecc7782b200af52fbfbb3f5a3dce505f8e7d2bd5979c206a245e7fb83ac1302b23035075b5c09943049f8cfe0a582032fcb9542ea180a0 + languageName: node + linkType: hard + +"@docusaurus/bundler@npm:3.7.0": + version: 3.7.0 resolution: "@docusaurus/bundler@npm:3.7.0" dependencies: "@babel/core": "npm:^7.25.9" @@ -3461,6 +3965,43 @@ __metadata: languageName: node linkType: hard +"@docusaurus/bundler@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/bundler@npm:3.8.1" + dependencies: + "@babel/core": "npm:^7.25.9" + "@docusaurus/babel": "npm:3.8.1" + "@docusaurus/cssnano-preset": "npm:3.8.1" + "@docusaurus/logger": "npm:3.8.1" + "@docusaurus/types": "npm:3.8.1" + "@docusaurus/utils": "npm:3.8.1" + babel-loader: "npm:^9.2.1" + clean-css: "npm:^5.3.3" + copy-webpack-plugin: "npm:^11.0.0" + css-loader: "npm:^6.11.0" + css-minimizer-webpack-plugin: "npm:^5.0.1" + cssnano: "npm:^6.1.2" + file-loader: "npm:^6.2.0" + html-minifier-terser: "npm:^7.2.0" + mini-css-extract-plugin: "npm:^2.9.2" + null-loader: "npm:^4.0.1" + postcss: "npm:^8.5.4" + postcss-loader: "npm:^7.3.4" + postcss-preset-env: "npm:^10.2.1" + terser-webpack-plugin: "npm:^5.3.9" + tslib: "npm:^2.6.0" + url-loader: "npm:^4.1.1" + webpack: "npm:^5.95.0" + webpackbar: "npm:^6.0.1" + peerDependencies: + "@docusaurus/faster": "*" + peerDependenciesMeta: + "@docusaurus/faster": + optional: true + checksum: 10/2373bb954020185e97c74a35de4c89f70f603bf1cd6679dcd4d04a5dfaf7336a9d42fb303d221c3b1c540dbd4f5b97a380e570d3e1971a9acd456d151798d1dd + languageName: node + linkType: hard + "@docusaurus/core@npm:3.7.0, @docusaurus/core@npm:^3.7.0": version: 3.7.0 resolution: "@docusaurus/core@npm:3.7.0" @@ -3517,6 +4058,62 @@ __metadata: languageName: node linkType: hard +"@docusaurus/core@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/core@npm:3.8.1" + dependencies: + "@docusaurus/babel": "npm:3.8.1" + "@docusaurus/bundler": "npm:3.8.1" + "@docusaurus/logger": "npm:3.8.1" + "@docusaurus/mdx-loader": "npm:3.8.1" + "@docusaurus/utils": "npm:3.8.1" + "@docusaurus/utils-common": "npm:3.8.1" + "@docusaurus/utils-validation": "npm:3.8.1" + boxen: "npm:^6.2.1" + chalk: "npm:^4.1.2" + chokidar: "npm:^3.5.3" + cli-table3: "npm:^0.6.3" + combine-promises: "npm:^1.1.0" + commander: "npm:^5.1.0" + core-js: "npm:^3.31.1" + detect-port: "npm:^1.5.1" + escape-html: "npm:^1.0.3" + eta: "npm:^2.2.0" + eval: "npm:^0.1.8" + execa: "npm:5.1.1" + fs-extra: "npm:^11.1.1" + html-tags: "npm:^3.3.1" + html-webpack-plugin: "npm:^5.6.0" + leven: "npm:^3.1.0" + lodash: "npm:^4.17.21" + open: "npm:^8.4.0" + p-map: "npm:^4.0.0" + prompts: "npm:^2.4.2" + react-helmet-async: "npm:@slorber/react-helmet-async@1.3.0" + react-loadable: "npm:@docusaurus/react-loadable@6.0.0" + react-loadable-ssr-addon-v5-slorber: "npm:^1.0.1" + react-router: "npm:^5.3.4" + react-router-config: "npm:^5.1.1" + react-router-dom: "npm:^5.3.4" + semver: "npm:^7.5.4" + serve-handler: "npm:^6.1.6" + tinypool: "npm:^1.0.2" + tslib: "npm:^2.6.0" + update-notifier: "npm:^6.0.2" + webpack: "npm:^5.95.0" + webpack-bundle-analyzer: "npm:^4.10.2" + webpack-dev-server: "npm:^4.15.2" + webpack-merge: "npm:^6.0.1" + peerDependencies: + "@mdx-js/react": ^3.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + bin: + docusaurus: bin/docusaurus.mjs + checksum: 10/278f194d0107ebe07169845293dc01dda3fa816881dffd1a02b5481676f97e69d2801819765baec4ea5a44101ce1cf82b34e17083754878ce886f6203cd61cb2 + languageName: node + linkType: hard + "@docusaurus/cssnano-preset@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/cssnano-preset@npm:3.7.0" @@ -3529,6 +4126,18 @@ __metadata: languageName: node linkType: hard +"@docusaurus/cssnano-preset@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/cssnano-preset@npm:3.8.1" + dependencies: + cssnano-preset-advanced: "npm:^6.1.2" + postcss: "npm:^8.5.4" + postcss-sort-media-queries: "npm:^5.2.0" + tslib: "npm:^2.6.0" + checksum: 10/4fada596bedf182007ec12ca4e4af373fa7763724d9219ea695a71f9325f2984c0b76a4dbbeb39f2fea14b174ff7e285915c463156f7cd02fe583c44e361c2ba + languageName: node + linkType: hard + "@docusaurus/logger@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/logger@npm:3.7.0" @@ -3539,6 +4148,16 @@ __metadata: languageName: node linkType: hard +"@docusaurus/logger@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/logger@npm:3.8.1" + dependencies: + chalk: "npm:^4.1.2" + tslib: "npm:^2.6.0" + checksum: 10/040d67de6d096fa251f277c3fc343797030a19858de4da08bd3dc304c2f5261ed03aa95b17d4d0b53a3b48681e7f867745db6a66855c2c6ab78852598e187821 + languageName: node + linkType: hard + "@docusaurus/mdx-loader@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/mdx-loader@npm:3.7.0" @@ -3574,6 +4193,41 @@ __metadata: languageName: node linkType: hard +"@docusaurus/mdx-loader@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/mdx-loader@npm:3.8.1" + dependencies: + "@docusaurus/logger": "npm:3.8.1" + "@docusaurus/utils": "npm:3.8.1" + "@docusaurus/utils-validation": "npm:3.8.1" + "@mdx-js/mdx": "npm:^3.0.0" + "@slorber/remark-comment": "npm:^1.0.0" + escape-html: "npm:^1.0.3" + estree-util-value-to-estree: "npm:^3.0.1" + file-loader: "npm:^6.2.0" + fs-extra: "npm:^11.1.1" + image-size: "npm:^2.0.2" + mdast-util-mdx: "npm:^3.0.0" + mdast-util-to-string: "npm:^4.0.0" + rehype-raw: "npm:^7.0.0" + remark-directive: "npm:^3.0.0" + remark-emoji: "npm:^4.0.0" + remark-frontmatter: "npm:^5.0.0" + remark-gfm: "npm:^4.0.0" + stringify-object: "npm:^3.3.0" + tslib: "npm:^2.6.0" + unified: "npm:^11.0.3" + unist-util-visit: "npm:^5.0.0" + url-loader: "npm:^4.1.1" + vfile: "npm:^6.0.1" + webpack: "npm:^5.88.1" + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10/7d59879ff5a39947b9cfd28e58217af4e86963fcf1ecfbe2809a9edacb459e5d439fec70770dd8907d06cddfddcbea8eb93ca1fd2ede401569e72a3f6e5446f5 + languageName: node + linkType: hard + "@docusaurus/module-type-aliases@npm:3.7.0, @docusaurus/module-type-aliases@npm:^3.7.0": version: 3.7.0 resolution: "@docusaurus/module-type-aliases@npm:3.7.0" @@ -3592,6 +4246,24 @@ __metadata: languageName: node linkType: hard +"@docusaurus/module-type-aliases@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/module-type-aliases@npm:3.8.1" + dependencies: + "@docusaurus/types": "npm:3.8.1" + "@types/history": "npm:^4.7.11" + "@types/react": "npm:*" + "@types/react-router-config": "npm:*" + "@types/react-router-dom": "npm:*" + react-helmet-async: "npm:@slorber/react-helmet-async@1.3.0" + react-loadable: "npm:@docusaurus/react-loadable@6.0.0" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/db3a23763837fc0155ec25053d36179ddd213cca2aeba5f1372894f0e5be82719d625f53057d5e9c5cef1fa862e37fe754a1a83a3005c9e1d22b6bb80c313024 + languageName: node + linkType: hard + "@docusaurus/plugin-content-blog@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/plugin-content-blog@npm:3.7.0" @@ -3857,6 +4529,48 @@ __metadata: languageName: node linkType: hard +"@docusaurus/theme-common@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/theme-common@npm:3.8.1" + dependencies: + "@docusaurus/mdx-loader": "npm:3.8.1" + "@docusaurus/module-type-aliases": "npm:3.8.1" + "@docusaurus/utils": "npm:3.8.1" + "@docusaurus/utils-common": "npm:3.8.1" + "@types/history": "npm:^4.7.11" + "@types/react": "npm:*" + "@types/react-router-config": "npm:*" + clsx: "npm:^2.0.0" + parse-numeric-range: "npm:^1.3.0" + prism-react-renderer: "npm:^2.3.0" + tslib: "npm:^2.6.0" + utility-types: "npm:^3.10.0" + peerDependencies: + "@docusaurus/plugin-content-docs": "*" + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10/c1a1ebe04991b7c23bdda91378c0550e0a046e13fb0c2885a7e18305c8662f294b87ac5bd74ad0c307be30b6e17e74e9a76ae911ee10124287f2ce55a7f0501c + languageName: node + linkType: hard + +"@docusaurus/theme-mermaid@npm:^3.8.1": + version: 3.8.1 + resolution: "@docusaurus/theme-mermaid@npm:3.8.1" + dependencies: + "@docusaurus/core": "npm:3.8.1" + "@docusaurus/module-type-aliases": "npm:3.8.1" + "@docusaurus/theme-common": "npm:3.8.1" + "@docusaurus/types": "npm:3.8.1" + "@docusaurus/utils-validation": "npm:3.8.1" + mermaid: "npm:>=11.6.0" + tslib: "npm:^2.6.0" + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10/0c0f5d13197d08c7dd8d4ea974d6760299419225c4d3ba508523882c52c2ac71c78ee4ddc967a9f1748f7be39aa67aea220e229586f4c045ca88516b365fc308 + languageName: node + linkType: hard + "@docusaurus/theme-search-algolia@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/theme-search-algolia@npm:3.7.0" @@ -3921,6 +4635,26 @@ __metadata: languageName: node linkType: hard +"@docusaurus/types@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/types@npm:3.8.1" + dependencies: + "@mdx-js/mdx": "npm:^3.0.0" + "@types/history": "npm:^4.7.11" + "@types/react": "npm:*" + commander: "npm:^5.1.0" + joi: "npm:^17.9.2" + react-helmet-async: "npm:@slorber/react-helmet-async@1.3.0" + utility-types: "npm:^3.10.0" + webpack: "npm:^5.95.0" + webpack-merge: "npm:^5.9.0" + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 10/5c1ad8f888d650edd56f57284a6913c2f69aaa201b4757c81f1e71ee41b3b9957db7a1d3f1d243326d1000d3b3157487f91ca1f3a7c1970ea2aa18f88aa4b011 + languageName: node + linkType: hard + "@docusaurus/utils-common@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/utils-common@npm:3.7.0" @@ -3931,6 +4665,16 @@ __metadata: languageName: node linkType: hard +"@docusaurus/utils-common@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/utils-common@npm:3.8.1" + dependencies: + "@docusaurus/types": "npm:3.8.1" + tslib: "npm:^2.6.0" + checksum: 10/2720815d2b96a9d419a3355ce727bb5bf4bd0554f3aab6f62f2510d77b30724745b08da4ebaae7c4d409af0192d92052718d9059902b3a37d83606ade80a62ac + languageName: node + linkType: hard + "@docusaurus/utils-validation@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/utils-validation@npm:3.7.0" @@ -3947,6 +4691,22 @@ __metadata: languageName: node linkType: hard +"@docusaurus/utils-validation@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/utils-validation@npm:3.8.1" + dependencies: + "@docusaurus/logger": "npm:3.8.1" + "@docusaurus/utils": "npm:3.8.1" + "@docusaurus/utils-common": "npm:3.8.1" + fs-extra: "npm:^11.2.0" + joi: "npm:^17.9.2" + js-yaml: "npm:^4.1.0" + lodash: "npm:^4.17.21" + tslib: "npm:^2.6.0" + checksum: 10/00982c3df405376576e32e96f9a056d2483ef589cadd1b5707e473b52a454567e926e461024b0d9bde4c307172eace5d8a27b0030fd2fefe238d2415360caa9a + languageName: node + linkType: hard + "@docusaurus/utils@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/utils@npm:3.7.0" @@ -3975,6 +4735,35 @@ __metadata: languageName: node linkType: hard +"@docusaurus/utils@npm:3.8.1": + version: 3.8.1 + resolution: "@docusaurus/utils@npm:3.8.1" + dependencies: + "@docusaurus/logger": "npm:3.8.1" + "@docusaurus/types": "npm:3.8.1" + "@docusaurus/utils-common": "npm:3.8.1" + escape-string-regexp: "npm:^4.0.0" + execa: "npm:5.1.1" + file-loader: "npm:^6.2.0" + fs-extra: "npm:^11.1.1" + github-slugger: "npm:^1.5.0" + globby: "npm:^11.1.0" + gray-matter: "npm:^4.0.3" + jiti: "npm:^1.20.0" + js-yaml: "npm:^4.1.0" + lodash: "npm:^4.17.21" + micromatch: "npm:^4.0.5" + p-queue: "npm:^6.6.2" + prompts: "npm:^2.4.2" + resolve-pathname: "npm:^3.0.0" + tslib: "npm:^2.6.0" + url-loader: "npm:^4.1.1" + utility-types: "npm:^3.10.0" + webpack: "npm:^5.88.1" + checksum: 10/c9622591dd5f76c6dd50711bdf0dbcda26ce68ef676092900f33052bea897ffe6b60d2a25d425364a5ddfe3531b7f4759865dc4463118c2f954a6a82fadd23ec + languageName: node + linkType: hard + "@exodus/schemasafe@npm:^1.0.0-rc.2": version: 1.3.0 resolution: "@exodus/schemasafe@npm:1.3.0" @@ -4016,6 +4805,29 @@ __metadata: languageName: node linkType: hard +"@iconify/types@npm:^2.0.0": + version: 2.0.0 + resolution: "@iconify/types@npm:2.0.0" + checksum: 10/1b3425ecbc0eef44f23d3f27355ae7ef306d5119c566f013ef1849995b016e1fdcc5af6b74c3bc0554485d70cf5179cb9c1095b14d662a55abcae1148e1a13c9 + languageName: node + linkType: hard + +"@iconify/utils@npm:^3.0.1": + version: 3.0.2 + resolution: "@iconify/utils@npm:3.0.2" + dependencies: + "@antfu/install-pkg": "npm:^1.1.0" + "@antfu/utils": "npm:^9.2.0" + "@iconify/types": "npm:^2.0.0" + debug: "npm:^4.4.1" + globals: "npm:^15.15.0" + kolorist: "npm:^1.8.0" + local-pkg: "npm:^1.1.1" + mlly: "npm:^1.7.4" + checksum: 10/b2db57b6a6b06d618b2625bf7bd056219a6c65e71a86c79c664d5e5fe03e531bc74fdd9cfa4d74e2ea469b6cd92b63012e2d588b32cade5aa7e3c31bc5124789 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -4162,6 +4974,15 @@ __metadata: languageName: node linkType: hard +"@mermaid-js/parser@npm:^0.6.2": + version: 0.6.2 + resolution: "@mermaid-js/parser@npm:0.6.2" + dependencies: + langium: "npm:3.3.1" + checksum: 10/b8174ef3e205e499cd315ab0bddb8ad752d767a6fa371d8d3c2e2317bb80d4375607162a15082101024088f94a52c1339a26f0cbb97314c71cabbd3fa0e6cc91 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -4728,6 +5549,278 @@ __metadata: languageName: node linkType: hard +"@types/d3-array@npm:*": + version: 3.2.2 + resolution: "@types/d3-array@npm:3.2.2" + checksum: 10/1afebd05b688cafaaea295f765b409789f088b274b8a7ca40a4bc2b79760044a898e06a915f40bbc59cf39eabdd2b5d32e960b136fc025fd05c9a9d4435614c6 + languageName: node + linkType: hard + +"@types/d3-axis@npm:*": + version: 3.0.6 + resolution: "@types/d3-axis@npm:3.0.6" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10/8af56b629a0597ac8ef5051b6ad5390818462d8e588e1b52fb181808b1c0525d12a658730fad757e1ae256d0db170a0e29076acdef21acc98b954608d1c37b84 + languageName: node + linkType: hard + +"@types/d3-brush@npm:*": + version: 3.0.6 + resolution: "@types/d3-brush@npm:3.0.6" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10/4095cee2512d965732147493c471a8dd97dfb5967479d9aef43397f8b0e074b03296302423b8379c4274f9249b52bd1d74cc021f98d4f64b5a8a4a7e6fe48335 + languageName: node + linkType: hard + +"@types/d3-chord@npm:*": + version: 3.0.6 + resolution: "@types/d3-chord@npm:3.0.6" + checksum: 10/ca9ba8b00debd24a2b51527b9c3db63eafa5541c08dc721d1c52ca19960c5cec93a7b1acfc0ec072dbca31d134924299755e20a4d1d4ee04b961fc0de841b418 + languageName: node + linkType: hard + +"@types/d3-color@npm:*": + version: 3.1.3 + resolution: "@types/d3-color@npm:3.1.3" + checksum: 10/1cf0f512c09357b25d644ab01b54200be7c9b15c808333b0ccacf767fff36f17520b2fcde9dad45e1bd7ce84befad39b43da42b4fded57680fa2127006ca3ece + languageName: node + linkType: hard + +"@types/d3-contour@npm:*": + version: 3.0.6 + resolution: "@types/d3-contour@npm:3.0.6" + dependencies: + "@types/d3-array": "npm:*" + "@types/geojson": "npm:*" + checksum: 10/e7b7e3972aa71003c21f2c864116ffb95a9175a62ec56ec656a855e5198a66a0830b2ad7fc26811214cfa8c98cdf4190d7d351913ca0913f799fbcf2a4c99b2d + languageName: node + linkType: hard + +"@types/d3-delaunay@npm:*": + version: 6.0.4 + resolution: "@types/d3-delaunay@npm:6.0.4" + checksum: 10/cb8d2c9ed0b39ade3107b9792544a745b2de3811a6bd054813e9dc708b1132fbacd796e54c0602c11b3a14458d14487c5276c1affb7c2b9f25fe55fff88d6d25 + languageName: node + linkType: hard + +"@types/d3-dispatch@npm:*": + version: 3.0.7 + resolution: "@types/d3-dispatch@npm:3.0.7" + checksum: 10/ce7ab5a7d5c64aacf563797c0c61f3862b9ff687cb35470fe462219f09e402185646f51707339beede616586d92ded6974c3958dbeb15e35a85b1ecfafdf13a8 + languageName: node + linkType: hard + +"@types/d3-drag@npm:*": + version: 3.0.7 + resolution: "@types/d3-drag@npm:3.0.7" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10/93aba299c3a8d41ee326c5304ab694ceea135ed115c3b2ccab727a5d9bfc935f7f36d3fc416c013010eb755ac536c52adfcb15c195f241dc61f62650cc95088e + languageName: node + linkType: hard + +"@types/d3-dsv@npm:*": + version: 3.0.7 + resolution: "@types/d3-dsv@npm:3.0.7" + checksum: 10/8507f542135cae472781dff1c3b391eceedad0f2032d24ac4a0814e72e2f6877e4ddcb66f44627069977ee61029dc0a729edf659ed73cbf1040f55a7451f05ef + languageName: node + linkType: hard + +"@types/d3-ease@npm:*": + version: 3.0.2 + resolution: "@types/d3-ease@npm:3.0.2" + checksum: 10/d8f92a8a7a008da71f847a16227fdcb53a8938200ecdf8d831ab6b49aba91e8921769761d3bfa7e7191b28f62783bfd8b0937e66bae39d4dd7fb0b63b50d4a94 + languageName: node + linkType: hard + +"@types/d3-fetch@npm:*": + version: 3.0.7 + resolution: "@types/d3-fetch@npm:3.0.7" + dependencies: + "@types/d3-dsv": "npm:*" + checksum: 10/d496475cec7750f75740936e750a0150ca45e924a4f4697ad2c564f3a8f6c4ebc1b1edf8e081936e896532516731dbbaf2efd4890d53274a8eae13f51f821557 + languageName: node + linkType: hard + +"@types/d3-force@npm:*": + version: 3.0.10 + resolution: "@types/d3-force@npm:3.0.10" + checksum: 10/9c35abed2af91b94fc72d6b477188626e628ed89a01016437502c1deaf558da934b5d0cc808c2f2979ac853b6302b3d6ef763eddaff3a55552a55c0be710d5ca + languageName: node + linkType: hard + +"@types/d3-format@npm:*": + version: 3.0.4 + resolution: "@types/d3-format@npm:3.0.4" + checksum: 10/b937ecd2712d4aa38d5b4f5daab9cc8a576383868be1809e046aec99eeb1f1798c139f2e862dc400a82494c763be46087d154891773417f8eb53c73762ba3eb8 + languageName: node + linkType: hard + +"@types/d3-geo@npm:*": + version: 3.1.0 + resolution: "@types/d3-geo@npm:3.1.0" + dependencies: + "@types/geojson": "npm:*" + checksum: 10/e759d98470fe605ff0088247af81c3197cefce72b16eafe8acae606216c3e0a9f908df4e7cd5005ecfe13b8ac8396a51aaa0d282f3ca7d1c3850313a13fac905 + languageName: node + linkType: hard + +"@types/d3-hierarchy@npm:*": + version: 3.1.7 + resolution: "@types/d3-hierarchy@npm:3.1.7" + checksum: 10/9ff6cdedf5557ef9e1e7a65ca3c6846c895c84c1184e11ec6fa48565e96ebf5482d8be5cc791a8bc7f7debbd0e62604ee3da3ddca4f9d58bf6c8b4030567c6c6 + languageName: node + linkType: hard + +"@types/d3-interpolate@npm:*": + version: 3.0.4 + resolution: "@types/d3-interpolate@npm:3.0.4" + dependencies: + "@types/d3-color": "npm:*" + checksum: 10/72a883afd52c91132598b02a8cdfced9e783c54ca7e4459f9e29d5f45d11fb33f2cabc844e42fd65ba6e28f2a931dcce1add8607d2f02ef6fb8ea5b83ae84127 + languageName: node + linkType: hard + +"@types/d3-path@npm:*": + version: 3.1.1 + resolution: "@types/d3-path@npm:3.1.1" + checksum: 10/0437994d45d852ecbe9c4484e5abe504cd48751796d23798b6d829503a15563fdd348d93ac44489ba9c656992d16157f695eb889d9ce1198963f8e1dbabb1266 + languageName: node + linkType: hard + +"@types/d3-polygon@npm:*": + version: 3.0.2 + resolution: "@types/d3-polygon@npm:3.0.2" + checksum: 10/7cf1eadb54f02dd3617512b558f4c0f3811f8a6a8c887d9886981c3cc251db28b68329b2b0707d9f517231a72060adbb08855227f89bef6ef30caedc0a67cab2 + languageName: node + linkType: hard + +"@types/d3-quadtree@npm:*": + version: 3.0.6 + resolution: "@types/d3-quadtree@npm:3.0.6" + checksum: 10/4c260c9857d496b7f112cf57680c411c1912cc72538a5846c401429e3ed89a097c66410cfd38b394bfb4733ec2cb47d345b4eb5e202cbfb8e78ab044b535be02 + languageName: node + linkType: hard + +"@types/d3-random@npm:*": + version: 3.0.3 + resolution: "@types/d3-random@npm:3.0.3" + checksum: 10/2c126dda6846f6c7e02c9123a30b4cdf27f3655d19b78456bbb330fbac27acceeeb987318055d3964dba8e6450377ff737db91d81f27c81ca6f4522c9b994ef2 + languageName: node + linkType: hard + +"@types/d3-scale-chromatic@npm:*": + version: 3.1.0 + resolution: "@types/d3-scale-chromatic@npm:3.1.0" + checksum: 10/6b04af931b7cd4aa09f21519970cab44aaae181faf076013ab93ccb0d550ec16f4c8d444c1e9dee1493be4261a8a8bb6f8e6356e6f4c6ba0650011b1e8a38aef + languageName: node + linkType: hard + +"@types/d3-scale@npm:*": + version: 4.0.9 + resolution: "@types/d3-scale@npm:4.0.9" + dependencies: + "@types/d3-time": "npm:*" + checksum: 10/2cae90a5e39252ae51388f3909ffb7009178582990462838a4edd53dd7e2e08121b38f0d2e1ac0e28e41167e88dea5b99e064ca139ba917b900a8020cf85362f + languageName: node + linkType: hard + +"@types/d3-selection@npm:*": + version: 3.0.11 + resolution: "@types/d3-selection@npm:3.0.11" + checksum: 10/2d2d993b9e9553d066566cb22916c632e5911090db99e247bd8c32855a344e6b7c25b674f3c27956c367a6b3b1214b09931ce854788c3be2072003e01f2c75d7 + languageName: node + linkType: hard + +"@types/d3-shape@npm:*": + version: 3.1.7 + resolution: "@types/d3-shape@npm:3.1.7" + dependencies: + "@types/d3-path": "npm:*" + checksum: 10/b7ddda2a9c916ba438308bfa6e53fa2bb11c2ce13537ba2a7816c16f9432287b57901921c7231d2924f2d7d360535c3795f017865ab05abe5057c6ca06ca81df + languageName: node + linkType: hard + +"@types/d3-time-format@npm:*": + version: 4.0.3 + resolution: "@types/d3-time-format@npm:4.0.3" + checksum: 10/9dfc1516502ac1c657d6024bdb88b6dc7e21dd7bff88f6187616cf9a0108250f63507a2004901ece4f97cc46602005a2ca2d05c6dbe53e8a0f6899bd60d4ff7a + languageName: node + linkType: hard + +"@types/d3-time@npm:*": + version: 3.0.4 + resolution: "@types/d3-time@npm:3.0.4" + checksum: 10/b1eb4255066da56023ad243fd4ae5a20462d73bd087a0297c7d49ece42b2304a4a04297568c604a38541019885b2bc35a9e0fd704fad218e9bc9c5f07dc685ce + languageName: node + linkType: hard + +"@types/d3-timer@npm:*": + version: 3.0.2 + resolution: "@types/d3-timer@npm:3.0.2" + checksum: 10/1643eebfa5f4ae3eb00b556bbc509444d88078208ec2589ddd8e4a24f230dd4cf2301e9365947e70b1bee33f63aaefab84cd907822aae812b9bc4871b98ab0e1 + languageName: node + linkType: hard + +"@types/d3-transition@npm:*": + version: 3.0.9 + resolution: "@types/d3-transition@npm:3.0.9" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10/dad647c485440f176117e8a45f31aee9427d8d4dfa174eaa2f01e702641db53ad0f752a144b20987c7189723c4f0afe0bf0f16d95b2a91aa28937eee4339c161 + languageName: node + linkType: hard + +"@types/d3-zoom@npm:*": + version: 3.0.8 + resolution: "@types/d3-zoom@npm:3.0.8" + dependencies: + "@types/d3-interpolate": "npm:*" + "@types/d3-selection": "npm:*" + checksum: 10/cc6ba975cf4f55f94933413954d81b87feb1ee8b8cee8f2202cf526f218dcb3ba240cbeb04ed80522416201c4a7394b37de3eb695d840a36d190dfb2d3e62cb5 + languageName: node + linkType: hard + +"@types/d3@npm:^7.4.3": + version: 7.4.3 + resolution: "@types/d3@npm:7.4.3" + dependencies: + "@types/d3-array": "npm:*" + "@types/d3-axis": "npm:*" + "@types/d3-brush": "npm:*" + "@types/d3-chord": "npm:*" + "@types/d3-color": "npm:*" + "@types/d3-contour": "npm:*" + "@types/d3-delaunay": "npm:*" + "@types/d3-dispatch": "npm:*" + "@types/d3-drag": "npm:*" + "@types/d3-dsv": "npm:*" + "@types/d3-ease": "npm:*" + "@types/d3-fetch": "npm:*" + "@types/d3-force": "npm:*" + "@types/d3-format": "npm:*" + "@types/d3-geo": "npm:*" + "@types/d3-hierarchy": "npm:*" + "@types/d3-interpolate": "npm:*" + "@types/d3-path": "npm:*" + "@types/d3-polygon": "npm:*" + "@types/d3-quadtree": "npm:*" + "@types/d3-random": "npm:*" + "@types/d3-scale": "npm:*" + "@types/d3-scale-chromatic": "npm:*" + "@types/d3-selection": "npm:*" + "@types/d3-shape": "npm:*" + "@types/d3-time": "npm:*" + "@types/d3-time-format": "npm:*" + "@types/d3-timer": "npm:*" + "@types/d3-transition": "npm:*" + "@types/d3-zoom": "npm:*" + checksum: 10/12234aa093c8661546168becdd8956e892b276f525d96f65a7b32fed886fc6a569fe5a1171bff26fef2a5663960635f460c9504a6f2d242ba281a2b6c8c6465c + languageName: node + linkType: hard + "@types/debug@npm:^4.0.0": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" @@ -4804,6 +5897,13 @@ __metadata: languageName: node linkType: hard +"@types/geojson@npm:*": + version: 7946.0.16 + resolution: "@types/geojson@npm:7946.0.16" + checksum: 10/34d07421bdd60e7b99fa265441d17ac6e9aef48e3ce22d04324127d0de1daf7fbaa0bd3be1cece2092eb6995f21da84afa5231e24621a2910ff7340bc98f496f + languageName: node + linkType: hard + "@types/gtag.js@npm:^0.0.12": version: 0.0.12 resolution: "@types/gtag.js@npm:0.0.12" @@ -5123,6 +6223,13 @@ __metadata: languageName: node linkType: hard +"@types/trusted-types@npm:^2.0.7": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 10/8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3 + languageName: node + linkType: hard + "@types/unist@npm:*, @types/unist@npm:^3.0.0": version: 3.0.2 resolution: "@types/unist@npm:3.0.2" @@ -5547,6 +6654,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 10/77f2de5051a631cf1729c090e5759148459cdb76b5f5c70f890503d629cf5052357b0ce783c0f976dd8a93c5150f59f6d18df1def3f502396a20f81282482fa4 + languageName: node + linkType: hard + "address@npm:^1.0.1, address@npm:^1.1.2": version: 1.2.2 resolution: "address@npm:1.2.2" @@ -5864,6 +6980,24 @@ __metadata: languageName: node linkType: hard +"autoprefixer@npm:^10.4.21": + version: 10.4.21 + resolution: "autoprefixer@npm:10.4.21" + dependencies: + browserslist: "npm:^4.24.4" + caniuse-lite: "npm:^1.0.30001702" + fraction.js: "npm:^4.3.7" + normalize-range: "npm:^0.1.2" + picocolors: "npm:^1.1.1" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.1.0 + bin: + autoprefixer: bin/autoprefixer + checksum: 10/5d7aeee78ef362a6838e12312908516a8ac5364414175273e5cff83bbff67612755b93d567f3aa01ce318342df48aeab4b291847b5800c780e58c458f61a98a6 + languageName: node + linkType: hard + "babel-loader@npm:^9.2.1": version: 9.2.1 resolution: "babel-loader@npm:9.2.1" @@ -5967,6 +7101,15 @@ __metadata: languageName: node linkType: hard +"baseline-browser-mapping@npm:^2.8.2": + version: 2.8.4 + resolution: "baseline-browser-mapping@npm:2.8.4" + bin: + baseline-browser-mapping: dist/cli.js + checksum: 10/97073cf5a009225f3ecb9dc6d480ce0c5c9cba5cc5cb152a1c02dad0d91fd676a1c9ebeb9707839a71b4de5d21ad4750978d9d9864372cb9f9ae508b95a4d4b6 + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -6113,6 +7256,21 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.25.1": + version: 4.26.0 + resolution: "browserslist@npm:4.26.0" + dependencies: + baseline-browser-mapping: "npm:^2.8.2" + caniuse-lite: "npm:^1.0.30001741" + electron-to-chromium: "npm:^1.5.218" + node-releases: "npm:^2.0.21" + update-browserslist-db: "npm:^1.1.3" + bin: + browserslist: cli.js + checksum: 10/8a354003e826e67085d1982836318387942981645a6afaa1111cfd06e176d8ddc99a13122e345b49a01c582070c892774384e479a65db0d3207dcc39f44d4b30 + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -6276,6 +7434,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001741": + version: 1.0.30001741 + resolution: "caniuse-lite@npm:1.0.30001741" + checksum: 10/d2fbc09f0941653d40b591e5460c4eb0b6838790d4adac7890a453dfb3b5e6bb84a64de2aded96cb4c6636816ef613614e07d9efb5818adaba566321feb95ffd + languageName: node + linkType: hard + "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -6382,6 +7547,31 @@ __metadata: languageName: node linkType: hard +"chevrotain-allstar@npm:~0.3.0": + version: 0.3.1 + resolution: "chevrotain-allstar@npm:0.3.1" + dependencies: + lodash-es: "npm:^4.17.21" + peerDependencies: + chevrotain: ^11.0.0 + checksum: 10/a12c0e408c17920b5f8fc095b7981d15609a63b3795946005fdfc77a5bbc692bbdb196ea29ad4655f55bfa7c93bbcbe7fe2e5782475bf65761b33f13a4aa1a77 + languageName: node + linkType: hard + +"chevrotain@npm:~11.0.3": + version: 11.0.3 + resolution: "chevrotain@npm:11.0.3" + dependencies: + "@chevrotain/cst-dts-gen": "npm:11.0.3" + "@chevrotain/gast": "npm:11.0.3" + "@chevrotain/regexp-to-ast": "npm:11.0.3" + "@chevrotain/types": "npm:11.0.3" + "@chevrotain/utils": "npm:11.0.3" + lodash-es: "npm:4.17.21" + checksum: 10/8fa6253e51320dd4c3d386315b925734943e509d7954a2cd917746c0604461191bea57b0fb8fbab1903e0508fd94bfd35ebd0f8eace77cd0f3f42a9ee4f8f676 + languageName: node + linkType: hard + "chokidar@npm:^3.4.2, chokidar@npm:^3.5.3": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -6431,7 +7621,7 @@ __metadata: languageName: node linkType: hard -"clean-css@npm:^5.2.2, clean-css@npm:^5.3.2, clean-css@npm:~5.3.2": +"clean-css@npm:^5.2.2, clean-css@npm:^5.3.2, clean-css@npm:^5.3.3, clean-css@npm:~5.3.2": version: 5.3.3 resolution: "clean-css@npm:5.3.3" dependencies: @@ -6584,6 +7774,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:7, commander@npm:^7.2.0": + version: 7.2.0 + resolution: "commander@npm:7.2.0" + checksum: 10/9973af10727ad4b44f26703bf3e9fdc323528660a7590efe3aa9ad5042b4584c0deed84ba443f61c9d6f02dade54a5a5d3c95e306a1e1630f8374ae6db16c06d + languageName: node + linkType: hard + "commander@npm:^10.0.0": version: 10.0.1 resolution: "commander@npm:10.0.1" @@ -6605,13 +7802,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^7.2.0": - version: 7.2.0 - resolution: "commander@npm:7.2.0" - checksum: 10/9973af10727ad4b44f26703bf3e9fdc323528660a7590efe3aa9ad5042b4584c0deed84ba443f61c9d6f02dade54a5a5d3c95e306a1e1630f8374ae6db16c06d - languageName: node - linkType: hard - "commander@npm:^8.3.0": version: 8.3.0 resolution: "commander@npm:8.3.0" @@ -6680,6 +7870,20 @@ __metadata: languageName: node linkType: hard +"confbox@npm:^0.1.8": + version: 0.1.8 + resolution: "confbox@npm:0.1.8" + checksum: 10/4ebcfb1c6a3b25276734ec5722e88768eb61fc02f98e11960b845c5c62bc27fd05f493d2a8244d9675b24ef95afe4c0d511cdcad02c72f5eeea463cc26687999 + languageName: node + linkType: hard + +"confbox@npm:^0.2.2": + version: 0.2.2 + resolution: "confbox@npm:0.2.2" + checksum: 10/988c7216f9b5aee5d8a8f32153a9164e1b58d92d8335c5daa323fd3fdee91f742ffc25f6c28b059474b6319204085eca985ab14c5a246988dc7ef1fe29414108 + languageName: node + linkType: hard + "config-chain@npm:^1.1.11": version: 1.1.13 resolution: "config-chain@npm:1.1.13" @@ -6823,6 +8027,24 @@ __metadata: languageName: node linkType: hard +"cose-base@npm:^1.0.0": + version: 1.0.3 + resolution: "cose-base@npm:1.0.3" + dependencies: + layout-base: "npm:^1.0.0" + checksum: 10/52e1f4ae173738aebe14395e3f865dc10ce430156554bab52f4b8ef0c583375644348c2a226b83d97eebc7d35340919e7bc10d23a3e2fe51b853bf56f27b5da7 + languageName: node + linkType: hard + +"cose-base@npm:^2.2.0": + version: 2.2.0 + resolution: "cose-base@npm:2.2.0" + dependencies: + layout-base: "npm:^2.0.0" + checksum: 10/4d4b16a84188b8f9419d9dbaffca62561f0e0ee125569339782141111aaf2bec1d180270bbaf5a13ac956f6a8c6b74ab2431e456da239982046b9ddb612bde6a + languageName: node + linkType: hard + "cosmiconfig@npm:^6.0.0": version: 6.0.0 resolution: "cosmiconfig@npm:6.0.0" @@ -6900,20 +8122,33 @@ __metadata: languageName: node linkType: hard -"css-has-pseudo@npm:^7.0.2": - version: 7.0.2 - resolution: "css-has-pseudo@npm:7.0.2" +"css-has-pseudo@npm:^7.0.2": + version: 7.0.2 + resolution: "css-has-pseudo@npm:7.0.2" + dependencies: + "@csstools/selector-specificity": "npm:^5.0.0" + postcss-selector-parser: "npm:^7.0.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/6032539b0dda70c77e39791a090d668cf1508ad1db65bfb044b5fc311298ac033244893494962191a1f74c4d74b1525c8969e06aaacbc0f50021da48bc65753e + languageName: node + linkType: hard + +"css-has-pseudo@npm:^7.0.3": + version: 7.0.3 + resolution: "css-has-pseudo@npm:7.0.3" dependencies: "@csstools/selector-specificity": "npm:^5.0.0" postcss-selector-parser: "npm:^7.0.0" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10/6032539b0dda70c77e39791a090d668cf1508ad1db65bfb044b5fc311298ac033244893494962191a1f74c4d74b1525c8969e06aaacbc0f50021da48bc65753e + checksum: 10/ffda6490aacb2903803f7d6c7b3ee41e654a3e9084c5eb0cf8f7602cf49ebce1b7891962016f622c36c67f4f685ed57628e7a0efbdba8cc55c5bf76ed58267d8 languageName: node linkType: hard -"css-loader@npm:^6.8.1": +"css-loader@npm:^6.11.0, css-loader@npm:^6.8.1": version: 6.11.0 resolution: "css-loader@npm:6.11.0" dependencies: @@ -7035,6 +8270,13 @@ __metadata: languageName: node linkType: hard +"cssdb@npm:^8.4.0": + version: 8.4.0 + resolution: "cssdb@npm:8.4.0" + checksum: 10/db3e249b7b4ae7cbcde6a0a924ed054acc234449e6e19b6338635967a4c2b4aeb63415fb117071fa751f7a3dde664413192bafe1595324124f7efcdd27fb680f + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -7138,6 +8380,405 @@ __metadata: languageName: node linkType: hard +"cytoscape-cose-bilkent@npm:^4.1.0": + version: 4.1.0 + resolution: "cytoscape-cose-bilkent@npm:4.1.0" + dependencies: + cose-base: "npm:^1.0.0" + peerDependencies: + cytoscape: ^3.2.0 + checksum: 10/9ec2999159af62f1a251bf1e146a9a779085c4fdb1b8146596208f0097c0512fc4bffda53d3b00c87a1e8ae5024db3ebfb97162115216f5b4d024e314f4a03bb + languageName: node + linkType: hard + +"cytoscape-fcose@npm:^2.2.0": + version: 2.2.0 + resolution: "cytoscape-fcose@npm:2.2.0" + dependencies: + cose-base: "npm:^2.2.0" + peerDependencies: + cytoscape: ^3.2.0 + checksum: 10/927aa3b29c1d514c6513c5a785d7af7a8d0499eb166de1f42b958ef20d264ef9cbe238da0b65ae01860424972dce1c73017cf2afdae4f02f9a247f7031b00de3 + languageName: node + linkType: hard + +"cytoscape@npm:^3.29.3": + version: 3.33.1 + resolution: "cytoscape@npm:3.33.1" + checksum: 10/0e8d3ea87eb624899341d6a765cfb732199af8a871beedeb94971061632ce814c2c39e8257d6628c5611ca9dadc1a723a00377d04f149e0d24f6c133a6ab8647 + languageName: node + linkType: hard + +"d3-array@npm:1 - 2": + version: 2.12.1 + resolution: "d3-array@npm:2.12.1" + dependencies: + internmap: "npm:^1.0.0" + checksum: 10/9fdfb91f428915006e126090fe9aa9d5fcbecc78e925eceb32de9dfb989135f6ad940a8f1b086d0b569523679f85453c5335772aa9e6d5d41b480c2610857c7f + languageName: node + linkType: hard + +"d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:2.5.0 - 3, d3-array@npm:3, d3-array@npm:^3.2.0": + version: 3.2.4 + resolution: "d3-array@npm:3.2.4" + dependencies: + internmap: "npm:1 - 2" + checksum: 10/5800c467f89634776a5977f6dae3f4e127d91be80f1d07e3e6e35303f9de93e6636d014b234838eea620f7469688d191b3f41207a30040aab750a63c97ec1d7c + languageName: node + linkType: hard + +"d3-axis@npm:3": + version: 3.0.0 + resolution: "d3-axis@npm:3.0.0" + checksum: 10/15ec43ecbd4e7b606fcda60f67a522e45576dfd6aa83dff47f3e91ef6c8448841a09cd91f630b492250dcec67c6ea64463510ead5e632ff6b827aeefae1d42ad + languageName: node + linkType: hard + +"d3-brush@npm:3": + version: 3.0.0 + resolution: "d3-brush@npm:3.0.0" + dependencies: + d3-dispatch: "npm:1 - 3" + d3-drag: "npm:2 - 3" + d3-interpolate: "npm:1 - 3" + d3-selection: "npm:3" + d3-transition: "npm:3" + checksum: 10/fa3a461b62f0f0ee6fe41f5babf45535a0a8f6d4999f675fb1dce932ee02eff72dec14c7296af31ca15998dc0141ccf5d02aa6499363f8bf2941d90688a1d644 + languageName: node + linkType: hard + +"d3-chord@npm:3": + version: 3.0.1 + resolution: "d3-chord@npm:3.0.1" + dependencies: + d3-path: "npm:1 - 3" + checksum: 10/4febcdca4fdc8ba91fc4f7545f4b6321c440150dff80c1ebef887db07bb4200395dfebede63b257393259de07f914da10842da5ab3135e1e281e33ad153e0849 + languageName: node + linkType: hard + +"d3-color@npm:1 - 3, d3-color@npm:3": + version: 3.1.0 + resolution: "d3-color@npm:3.1.0" + checksum: 10/536ba05bfd9f4fcd6fa289b5974f5c846b21d186875684637e22bf6855e6aba93e24a2eb3712985c6af3f502fbbfa03708edb72f58142f626241a8a17258e545 + languageName: node + linkType: hard + +"d3-contour@npm:4": + version: 4.0.2 + resolution: "d3-contour@npm:4.0.2" + dependencies: + d3-array: "npm:^3.2.0" + checksum: 10/0b252267e0c3c5e97d7e0c720bd35654de99f981199f7240d7dd1acfd4e2d5bf1638829f6db486452eff9c38608efa4a6ab5a0d1525131735c011ee7be3cb4ba + languageName: node + linkType: hard + +"d3-delaunay@npm:6": + version: 6.0.4 + resolution: "d3-delaunay@npm:6.0.4" + dependencies: + delaunator: "npm:5" + checksum: 10/4588e2872d4154daaf2c3f34fefe74e43b909cc460238a7b02823907ca6dd109f2c488c57c8551f1a2607fe4b44fdf24e3a190cea29bca70ef5606678dd9e2de + languageName: node + linkType: hard + +"d3-dispatch@npm:1 - 3, d3-dispatch@npm:3": + version: 3.0.1 + resolution: "d3-dispatch@npm:3.0.1" + checksum: 10/2b82f41bf4ef88c2f9033dfe32815b67e2ef1c5754a74137a74c7d44d6f0d6ecfa934ac56ed8afe358f6c1f06462e8aa42ca0a388397b5b77a42721570e80487 + languageName: node + linkType: hard + +"d3-drag@npm:2 - 3, d3-drag@npm:3": + version: 3.0.0 + resolution: "d3-drag@npm:3.0.0" + dependencies: + d3-dispatch: "npm:1 - 3" + d3-selection: "npm:3" + checksum: 10/80bc689935e5a46ee92b2d7f71e1c792279382affed9fbcf46034bff3ff7d3f50cf61a874da4bdf331037292b9e7dca5c6401a605d4bb699fdcb4e0c87e176ec + languageName: node + linkType: hard + +"d3-dsv@npm:1 - 3, d3-dsv@npm:3": + version: 3.0.1 + resolution: "d3-dsv@npm:3.0.1" + dependencies: + commander: "npm:7" + iconv-lite: "npm:0.6" + rw: "npm:1" + bin: + csv2json: bin/dsv2json.js + csv2tsv: bin/dsv2dsv.js + dsv2dsv: bin/dsv2dsv.js + dsv2json: bin/dsv2json.js + json2csv: bin/json2dsv.js + json2dsv: bin/json2dsv.js + json2tsv: bin/json2dsv.js + tsv2csv: bin/dsv2dsv.js + tsv2json: bin/dsv2json.js + checksum: 10/a628ac42a272466940f713f310db2e5246690b22035121dc1230077070c9135fb7c9b4d260f093fcadf63b0528202a1953107448a4be3a860c4f42f50d09504d + languageName: node + linkType: hard + +"d3-ease@npm:1 - 3, d3-ease@npm:3": + version: 3.0.1 + resolution: "d3-ease@npm:3.0.1" + checksum: 10/985d46e868494e9e6806fedd20bad712a50dcf98f357bf604a843a9f6bc17714a657c83dd762f183173dcde983a3570fa679b2bc40017d40b24163cdc4167796 + languageName: node + linkType: hard + +"d3-fetch@npm:3": + version: 3.0.1 + resolution: "d3-fetch@npm:3.0.1" + dependencies: + d3-dsv: "npm:1 - 3" + checksum: 10/cd35d55f8fbb1ea1e37be362a575bb0161449957133aa5b45b9891889b2aca1dc0769c240a236736e33cd823e820a0e73fb3744582307a5d26d1df7bed0ccecb + languageName: node + linkType: hard + +"d3-force@npm:3": + version: 3.0.0 + resolution: "d3-force@npm:3.0.0" + dependencies: + d3-dispatch: "npm:1 - 3" + d3-quadtree: "npm:1 - 3" + d3-timer: "npm:1 - 3" + checksum: 10/85945f8d444d78567009518f0ab54c0f0c8873eb8eb9a2ff0ab667b0f81b419e101a411415d4a2c752547ec7143f89675e8c33b8f111e55e5579a04cb7f4591c + languageName: node + linkType: hard + +"d3-format@npm:1 - 3, d3-format@npm:3": + version: 3.1.0 + resolution: "d3-format@npm:3.1.0" + checksum: 10/a0fe23d2575f738027a3db0ce57160e5a473ccf24808c1ed46d45ef4f3211076b34a18b585547d34e365e78dcc26dd4ab15c069731fc4b1c07a26bfced09ea31 + languageName: node + linkType: hard + +"d3-geo@npm:3": + version: 3.1.1 + resolution: "d3-geo@npm:3.1.1" + dependencies: + d3-array: "npm:2.5.0 - 3" + checksum: 10/dc5e980330d891dabf92869b98871b05ca2021c64d7ef253bcfd4f2348839ad33576fba474baecc2def86ebd3d943a11d93c0af26be0a2694f5bd59824838133 + languageName: node + linkType: hard + +"d3-hierarchy@npm:3": + version: 3.1.2 + resolution: "d3-hierarchy@npm:3.1.2" + checksum: 10/497b79dc6c35e28b21e8a7b94db92876abd1d4ec082d9803a07ea8964e55b0e71c511a21489363a36f1456f069adb8ff7d33c633678730d6ae961ed350b27733 + languageName: node + linkType: hard + +"d3-interpolate@npm:1 - 3, d3-interpolate@npm:1.2.0 - 3, d3-interpolate@npm:3": + version: 3.0.1 + resolution: "d3-interpolate@npm:3.0.1" + dependencies: + d3-color: "npm:1 - 3" + checksum: 10/988d66497ef5c190cf64f8c80cd66e1e9a58c4d1f8932d776a8e3ae59330291795d5a342f5a97602782ccbef21a5df73bc7faf1f0dc46a5145ba6243a82a0f0e + languageName: node + linkType: hard + +"d3-path@npm:1": + version: 1.0.9 + resolution: "d3-path@npm:1.0.9" + checksum: 10/6ce1747837ea2a449d9ea32e169a382978ab09a4805eb408feb6bbc12cb5f5f6ce29aefc252dd9a815d420f4813d672f75578b78b3bbaf7811f54d8c7f93fd11 + languageName: node + linkType: hard + +"d3-path@npm:1 - 3, d3-path@npm:3, d3-path@npm:^3.1.0": + version: 3.1.0 + resolution: "d3-path@npm:3.1.0" + checksum: 10/8e97a9ab4930a05b18adda64cf4929219bac913a5506cf8585631020253b39309549632a5cbeac778c0077994442ddaaee8316ee3f380e7baf7566321b84e76a + languageName: node + linkType: hard + +"d3-polygon@npm:3": + version: 3.0.1 + resolution: "d3-polygon@npm:3.0.1" + checksum: 10/c4fa2ed19dcba13fd341815361d27e64597aa0d38d377e401e1353c4acbe8bd73c0afb3e49a1cf4119fadc3651ec8073d06aa6d0e34e664c868d071e58912cd1 + languageName: node + linkType: hard + +"d3-quadtree@npm:1 - 3, d3-quadtree@npm:3": + version: 3.0.1 + resolution: "d3-quadtree@npm:3.0.1" + checksum: 10/1915b6a7b031fc312f9af61947072db9468c5a2b03837f6a90b38fdaebcd0ea17a883bffd94d16b8a6848e81711a06222f7d39f129386ef1850297219b8d32ba + languageName: node + linkType: hard + +"d3-random@npm:3": + version: 3.0.1 + resolution: "d3-random@npm:3.0.1" + checksum: 10/9f41d6ca3a1826cea8d88392917b5039504337d442a4d1357c870fa3031701e60209a2689a6ddae7df8fca824383d038c957eb545bc49a7428c71aaf3b11f56f + languageName: node + linkType: hard + +"d3-sankey@npm:^0.12.3": + version: 0.12.3 + resolution: "d3-sankey@npm:0.12.3" + dependencies: + d3-array: "npm:1 - 2" + d3-shape: "npm:^1.2.0" + checksum: 10/d5c679135a26d435e9970de3fc0778c6ef5c911f0c878b246939517b57a8daa2e2db6ef99318a0dad16e6079e4b89ef9166f1f661d8d247637875b764628094d + languageName: node + linkType: hard + +"d3-scale-chromatic@npm:3": + version: 3.1.0 + resolution: "d3-scale-chromatic@npm:3.1.0" + dependencies: + d3-color: "npm:1 - 3" + d3-interpolate: "npm:1 - 3" + checksum: 10/25df6a7c621b9171df8b2225e98e41c0a6bcac4de02deb4807280b31116e8f495c5ac93301796098ee5b698cb690154e8138d90d72fd1fe36744c60e02a3d8c4 + languageName: node + linkType: hard + +"d3-scale@npm:4": + version: 4.0.2 + resolution: "d3-scale@npm:4.0.2" + dependencies: + d3-array: "npm:2.10.0 - 3" + d3-format: "npm:1 - 3" + d3-interpolate: "npm:1.2.0 - 3" + d3-time: "npm:2.1.1 - 3" + d3-time-format: "npm:2 - 4" + checksum: 10/e2dc4243586eae2a0fdf91de1df1a90d51dfacb295933f0ca7e9184c31203b01436bef69906ad40f1100173a5e6197ae753cb7b8a1a8fcfda43194ea9cad6493 + languageName: node + linkType: hard + +"d3-selection@npm:2 - 3, d3-selection@npm:3": + version: 3.0.0 + resolution: "d3-selection@npm:3.0.0" + checksum: 10/0e5acfd305b31628b7be5009ba7303d84bb34817a88ed4dde9c8bd9c23528573fc5272f89fc04e5be03d2cbf5441a248d7274aaf55a8ef3dad46e16333d72298 + languageName: node + linkType: hard + +"d3-shape@npm:3": + version: 3.2.0 + resolution: "d3-shape@npm:3.2.0" + dependencies: + d3-path: "npm:^3.1.0" + checksum: 10/2e861f4d4781ee8abd85d2b435f848d667479dcf01a4e0db3a06600a5bdeddedb240f88229ec7b3bf7fa300c2b3526faeaf7e75f9a24dbf4396d3cc5358ff39d + languageName: node + linkType: hard + +"d3-shape@npm:^1.2.0": + version: 1.3.7 + resolution: "d3-shape@npm:1.3.7" + dependencies: + d3-path: "npm:1" + checksum: 10/1e40fdcfdc8edc9c53a77a6aaea2dbf31bf06df12ebd66cc8d91f76bbde753049ad21dfee0577f7dc5d0a4468554ede4783f6df7d809e291745334dba977c09e + languageName: node + linkType: hard + +"d3-time-format@npm:2 - 4, d3-time-format@npm:4": + version: 4.1.0 + resolution: "d3-time-format@npm:4.1.0" + dependencies: + d3-time: "npm:1 - 3" + checksum: 10/ffc0959258fbb90e3890bfb31b43b764f51502b575e87d0af2c85b85ac379120d246914d07fca9f533d1bcedc27b2841d308a00fd64848c3e2cad9eff5c9a0aa + languageName: node + linkType: hard + +"d3-time@npm:1 - 3, d3-time@npm:2.1.1 - 3, d3-time@npm:3": + version: 3.1.0 + resolution: "d3-time@npm:3.1.0" + dependencies: + d3-array: "npm:2 - 3" + checksum: 10/c110bed295ce63e8180e45b82a9b0ba114d5f33ff315871878f209c1a6d821caa505739a2b07f38d1396637155b8e7372632dacc018e11fbe8ceef58f6af806d + languageName: node + linkType: hard + +"d3-timer@npm:1 - 3, d3-timer@npm:3": + version: 3.0.1 + resolution: "d3-timer@npm:3.0.1" + checksum: 10/004128602bb187948d72c7dc153f0f063f38ac7a584171de0b45e3a841ad2e17f1e40ad396a4af9cce5551b6ab4a838d5246d23492553843d9da4a4050a911e2 + languageName: node + linkType: hard + +"d3-transition@npm:2 - 3, d3-transition@npm:3": + version: 3.0.1 + resolution: "d3-transition@npm:3.0.1" + dependencies: + d3-color: "npm:1 - 3" + d3-dispatch: "npm:1 - 3" + d3-ease: "npm:1 - 3" + d3-interpolate: "npm:1 - 3" + d3-timer: "npm:1 - 3" + peerDependencies: + d3-selection: 2 - 3 + checksum: 10/02571636acb82f5532117928a87fe25de68f088c38ab4a8b16e495f0f2d08a3fd2937eaebdefdfcf7f1461545524927d2632d795839b88d2e4c71e387aaaffac + languageName: node + linkType: hard + +"d3-zoom@npm:3": + version: 3.0.0 + resolution: "d3-zoom@npm:3.0.0" + dependencies: + d3-dispatch: "npm:1 - 3" + d3-drag: "npm:2 - 3" + d3-interpolate: "npm:1 - 3" + d3-selection: "npm:2 - 3" + d3-transition: "npm:2 - 3" + checksum: 10/0e6e5c14e33c4ecdff311a900dd037dea407734f2dd2818988ed6eae342c1799e8605824523678bd404f81e37824cc588f62dbde46912444c89acc7888036c6b + languageName: node + linkType: hard + +"d3@npm:^7.9.0": + version: 7.9.0 + resolution: "d3@npm:7.9.0" + dependencies: + d3-array: "npm:3" + d3-axis: "npm:3" + d3-brush: "npm:3" + d3-chord: "npm:3" + d3-color: "npm:3" + d3-contour: "npm:4" + d3-delaunay: "npm:6" + d3-dispatch: "npm:3" + d3-drag: "npm:3" + d3-dsv: "npm:3" + d3-ease: "npm:3" + d3-fetch: "npm:3" + d3-force: "npm:3" + d3-format: "npm:3" + d3-geo: "npm:3" + d3-hierarchy: "npm:3" + d3-interpolate: "npm:3" + d3-path: "npm:3" + d3-polygon: "npm:3" + d3-quadtree: "npm:3" + d3-random: "npm:3" + d3-scale: "npm:4" + d3-scale-chromatic: "npm:3" + d3-selection: "npm:3" + d3-shape: "npm:3" + d3-time: "npm:3" + d3-time-format: "npm:4" + d3-timer: "npm:3" + d3-transition: "npm:3" + d3-zoom: "npm:3" + checksum: 10/b0b418996bdf279b01f5c7a0117927f9ad3e833c9ce4657550ce6f6ace70b70cf829c4144b01df0be5a0f716d4e5f15ab0cadc5ff1ce1561d7be29ac86493d83 + languageName: node + linkType: hard + +"dagre-d3-es@npm:7.0.11": + version: 7.0.11 + resolution: "dagre-d3-es@npm:7.0.11" + dependencies: + d3: "npm:^7.9.0" + lodash-es: "npm:^4.17.21" + checksum: 10/5ea2faab020019a51e60791237239fc528bc20215503a846ad725c2e32dde6a270a16caf2ed6ec712b11e1c6616595b2b26e2c58f4f0e012218135629833e09b + languageName: node + linkType: hard + +"dayjs@npm:^1.11.13": + version: 1.11.18 + resolution: "dayjs@npm:1.11.18" + checksum: 10/7d29a90834cf4da2feb437c2f34b8235c3f94493a06d2f1bf9f506f1fa49eadf796f26e1d685b9fe8cb5e75ce6ee067825115e196f1af3d07b3552ff857bfc39 + languageName: node + linkType: hard + "debounce@npm:^1.2.1": version: 1.2.1 resolution: "debounce@npm:1.2.1" @@ -7166,6 +8807,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/9ada3434ea2993800bd9a1e320bd4aa7af69659fb51cca685d390949434bc0a8873c21ed7c9b852af6f2455a55c6d050aa3937d52b3c69f796dab666f762acad + languageName: node + linkType: hard + "decode-named-character-reference@npm:^1.0.0": version: 1.0.2 resolution: "decode-named-character-reference@npm:1.0.2" @@ -7259,6 +8912,15 @@ __metadata: languageName: node linkType: hard +"delaunator@npm:5": + version: 5.0.1 + resolution: "delaunator@npm:5.0.1" + dependencies: + robust-predicates: "npm:^3.0.2" + checksum: 10/c378a55138d81d471a7214635b1a2c5e74f8ee06582f558df72f0c7c82c25868599ce9a18fb25a245c6c03cab886d17fb574681c78371b539dd069818703f53a + languageName: node + linkType: hard + "depd@npm:2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -7488,6 +9150,18 @@ __metadata: languageName: node linkType: hard +"dompurify@npm:^3.2.5": + version: 3.2.6 + resolution: "dompurify@npm:3.2.6" + dependencies: + "@types/trusted-types": "npm:^2.0.7" + dependenciesMeta: + "@types/trusted-types": + optional: true + checksum: 10/b91631ed0e4d17fae950ef53613cc009ed7e73adc43ac94a41dd52f35483f7538d13caebdafa7626e0da145fc8184e7ac7935f14f25b7e841b32fda777e40447 + languageName: node + linkType: hard + "domutils@npm:^2.5.2, domutils@npm:^2.8.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" @@ -7568,6 +9242,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.5.218": + version: 1.5.218 + resolution: "electron-to-chromium@npm:1.5.218" + checksum: 10/2e661373bc2184b1c330119d9ff73ed5498ee1442866061caa94550ed89d57d56fa45ccec6e13bec8af2db1eecb25c4e590860de2067e2c5cafaf847413048ef + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.5.73": version: 1.5.101 resolution: "electron-to-chromium@npm:1.5.101" @@ -7919,7 +9600,7 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.0": +"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.4": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" checksum: 10/8030029382404942c01d0037079f1b1bc8fed524b5849c237b80549b01e2fc49709e1d0c557fa65ca4498fc9e24cff1475ef7b855121fcc15f9d61f93e282346 @@ -7933,7 +9614,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.0.0": +"execa@npm:5.1.1, execa@npm:^5.0.0": version: 5.1.1 resolution: "execa@npm:5.1.1" dependencies: @@ -8003,6 +9684,13 @@ __metadata: languageName: node linkType: hard +"exsolve@npm:^1.0.7": + version: 1.0.7 + resolution: "exsolve@npm:1.0.7" + checksum: 10/0c9fc0964da0154f38b55e612ed29bf5040f753d5d2db3a63559762237d0a86290e2f18997973343bb9900c07ab1e48596321de9d9d338e373b1f3f1a015e4c9 + languageName: node + linkType: hard + "extend-shallow@npm:^2.0.1": version: 2.0.1 resolution: "extend-shallow@npm:2.0.1" @@ -8551,6 +10239,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:^15.15.0": + version: 15.15.0 + resolution: "globals@npm:15.15.0" + checksum: 10/7f561c87b2fd381b27fc2db7df8a4ea7a9bb378667b8a7193e61fd2ca3a876479174e2a303a74345fbea6e1242e16db48915c1fd3bf35adcf4060a795b425e18 + languageName: node + linkType: hard + "globby@npm:^11.0.1, globby@npm:^11.0.4, globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" @@ -8657,6 +10352,13 @@ __metadata: languageName: node linkType: hard +"hachure-fill@npm:^0.5.2": + version: 0.5.2 + resolution: "hachure-fill@npm:0.5.2" + checksum: 10/d78f1b992d1c8951a4fc893bf32045748132a8b481c15d6d31c77c05557f5fa86913a2b66b3c3a3c8ce46ca8e0a46b0b2aa11f979bc804d8edba77b8c30eb1ca + languageName: node + linkType: hard + "handle-thing@npm:^2.0.0": version: 2.0.1 resolution: "handle-thing@npm:2.0.1" @@ -9224,7 +10926,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": +"iconv-lite@npm:0.6, iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" dependencies: @@ -9267,6 +10969,15 @@ __metadata: languageName: node linkType: hard +"image-size@npm:^2.0.2": + version: 2.0.2 + resolution: "image-size@npm:2.0.2" + bin: + image-size: bin/image-size.js + checksum: 10/d15203279fe7ada01252d8c56ba97516385d6d5ac2cbf3d734580fc88db4f5272b9b3f7f378ad63abc7d06b5500c43b90d9f84626e2bda1cab403c16eb469592 + languageName: node + linkType: hard + "immer@npm:^9.0.21, immer@npm:^9.0.7": version: 9.0.21 resolution: "immer@npm:9.0.21" @@ -9371,6 +11082,20 @@ __metadata: languageName: node linkType: hard +"internmap@npm:1 - 2": + version: 2.0.3 + resolution: "internmap@npm:2.0.3" + checksum: 10/873e0e7fcfe32f999aa0997a0b648b1244508e56e3ea6b8259b5245b50b5eeb3853fba221f96692bd6d1def501da76c32d64a5cb22a0b26cdd9b445664f805e0 + languageName: node + linkType: hard + +"internmap@npm:^1.0.0": + version: 1.0.1 + resolution: "internmap@npm:1.0.1" + checksum: 10/429cb9e28f393f10c73a826d71ba9e359711b7e42345bd684aba708f43b8139ce90f09b15abbf977a981474ac61615294854e5b9520d3f65187d0f6a2ff27665 + languageName: node + linkType: hard + "interpret@npm:^1.0.0": version: 1.4.0 resolution: "interpret@npm:1.4.0" @@ -9932,6 +11657,17 @@ __metadata: languageName: node linkType: hard +"katex@npm:^0.16.22": + version: 0.16.22 + resolution: "katex@npm:0.16.22" + dependencies: + commander: "npm:^8.3.0" + bin: + katex: cli.js + checksum: 10/fdb8667d9aa971154502b120ba340766754d202e3d3e322aca0a96de27032ad2dbb8a7295d798d310cd7ce4ddd21ed1f3318895541b61c9b4fdf611166589e02 + languageName: node + linkType: hard + "keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -9941,6 +11677,13 @@ __metadata: languageName: node linkType: hard +"khroma@npm:^2.1.0": + version: 2.1.0 + resolution: "khroma@npm:2.1.0" + checksum: 10/a195e317bf6f3a1cba98df2677bf9bf6d14195ee0b1c3e5bc20a542cd99652682f290c196a8963956d87aed4ad65ac0bc8a15d75cddf00801fdafd148e01a5d2 + languageName: node + linkType: hard + "kind-of@npm:^6.0.0, kind-of@npm:^6.0.2": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -9962,6 +11705,26 @@ __metadata: languageName: node linkType: hard +"kolorist@npm:^1.8.0": + version: 1.8.0 + resolution: "kolorist@npm:1.8.0" + checksum: 10/71d5d122951cc65f2f14c3e1d7f8fd91694b374647d4f6deec3816d018cd04a44edd9578d93e00c82c2053b925e5d30a0565746c4171f4ca9fce1a13bd5f3315 + languageName: node + linkType: hard + +"langium@npm:3.3.1": + version: 3.3.1 + resolution: "langium@npm:3.3.1" + dependencies: + chevrotain: "npm:~11.0.3" + chevrotain-allstar: "npm:~0.3.0" + vscode-languageserver: "npm:~9.0.1" + vscode-languageserver-textdocument: "npm:~1.0.11" + vscode-uri: "npm:~3.0.8" + checksum: 10/6b2e5bc1ff47c6048ec24471333f3397ddb4d6419f1c2262268faff00a8f0839ac4bd4877907261273e91e82f239951249155c3aff8d395ee5e3372dfc285e04 + languageName: node + linkType: hard + "latest-version@npm:^7.0.0": version: 7.0.0 resolution: "latest-version@npm:7.0.0" @@ -9981,6 +11744,20 @@ __metadata: languageName: node linkType: hard +"layout-base@npm:^1.0.0": + version: 1.0.2 + resolution: "layout-base@npm:1.0.2" + checksum: 10/34504e61e4770e563cf49d4a56c8c10f1da0fb452cff89a652118783189c642ebc86a300d97cbc247e59a9c1eb06a2d419982f7dd10e8eedcab2414bc46d32f8 + languageName: node + linkType: hard + +"layout-base@npm:^2.0.0": + version: 2.0.1 + resolution: "layout-base@npm:2.0.1" + checksum: 10/b5cca04a2e327ea16374a0058f73544291aeb0026972677a128594aca3b627d26949140ab7d275798c7d39193a33b41c5a856d4509c1518f49c9a5f1dad39a20 + languageName: node + linkType: hard + "leven@npm:^3.1.0": version: 3.1.0 resolution: "leven@npm:3.1.0" @@ -10034,6 +11811,17 @@ __metadata: languageName: node linkType: hard +"local-pkg@npm:^1.1.1": + version: 1.1.2 + resolution: "local-pkg@npm:1.1.2" + dependencies: + mlly: "npm:^1.7.4" + pkg-types: "npm:^2.3.0" + quansync: "npm:^0.2.11" + checksum: 10/761d82f40849e4721fa50d86782cf75bc2befb0696f32ac99869fb6f3033b904e4018f4bb8cdfde994d710816480dc1aba8e462c67ec20fe89d4700a245d17f8 + languageName: node + linkType: hard + "locate-path@npm:^3.0.0": version: 3.0.0 resolution: "locate-path@npm:3.0.0" @@ -10062,6 +11850,13 @@ __metadata: languageName: node linkType: hard +"lodash-es@npm:4.17.21, lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: 10/03f39878ea1e42b3199bd3f478150ab723f93cc8730ad86fec1f2804f4a07c6e30deaac73cad53a88e9c3db33348bb8ceeb274552390e7a75d7849021c02df43 + languageName: node + linkType: hard + "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -10199,6 +11994,15 @@ __metadata: languageName: node linkType: hard +"marked@npm:^15.0.7": + version: 15.0.12 + resolution: "marked@npm:15.0.12" + bin: + marked: bin/marked.js + checksum: 10/deeb619405c0c46af00c99b18b3365450abeb309104b24e3658f46142344f6b7c4117608c3b5834084d8738e92f81240c19f596e6ee369260f96e52b3457eaee + languageName: node + linkType: hard + "math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" @@ -10674,6 +12478,34 @@ __metadata: languageName: node linkType: hard +"mermaid@npm:>=11.6.0": + version: 11.11.0 + resolution: "mermaid@npm:11.11.0" + dependencies: + "@braintree/sanitize-url": "npm:^7.0.4" + "@iconify/utils": "npm:^3.0.1" + "@mermaid-js/parser": "npm:^0.6.2" + "@types/d3": "npm:^7.4.3" + cytoscape: "npm:^3.29.3" + cytoscape-cose-bilkent: "npm:^4.1.0" + cytoscape-fcose: "npm:^2.2.0" + d3: "npm:^7.9.0" + d3-sankey: "npm:^0.12.3" + dagre-d3-es: "npm:7.0.11" + dayjs: "npm:^1.11.13" + dompurify: "npm:^3.2.5" + katex: "npm:^0.16.22" + khroma: "npm:^2.1.0" + lodash-es: "npm:^4.17.21" + marked: "npm:^15.0.7" + roughjs: "npm:^4.6.6" + stylis: "npm:^4.3.6" + ts-dedent: "npm:^2.2.0" + uuid: "npm:^11.1.0" + checksum: 10/7ef3f12c64e517204ee30e80ffdfe48c661c1830f32848780e28978757196efb61eea233694b57a1962ad89470e117403edb663365363be416acb8eb4a6fdfab + languageName: node + linkType: hard + "methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -11583,6 +13415,18 @@ __metadata: languageName: node linkType: hard +"mini-css-extract-plugin@npm:^2.9.2": + version: 2.9.4 + resolution: "mini-css-extract-plugin@npm:2.9.4" + dependencies: + schema-utils: "npm:^4.0.0" + tapable: "npm:^2.2.1" + peerDependencies: + webpack: ^5.0.0 + checksum: 10/24a0418dc49baed58a10a8b81e4d2fe474e89ccdc9370a7c69bd0aeeb5a70d2b4ee12f33b98c95a68423c79c1de7cc2a25aaa2105600b712e7d594cb0d56c9d4 + languageName: node + linkType: hard + "minimalistic-assert@npm:^1.0.0": version: 1.0.1 resolution: "minimalistic-assert@npm:1.0.1" @@ -11717,6 +13561,18 @@ __metadata: languageName: node linkType: hard +"mlly@npm:^1.7.4": + version: 1.8.0 + resolution: "mlly@npm:1.8.0" + dependencies: + acorn: "npm:^8.15.0" + pathe: "npm:^2.0.3" + pkg-types: "npm:^1.3.1" + ufo: "npm:^1.6.1" + checksum: 10/4db690a421076d5fe88331679f702b77a4bfc9fe3f324bc6150270fb0b69ecd4b5e43570b8e4573dde341515b3eac4daa720a6ac9f2715c210b670852641ab1c + languageName: node + linkType: hard + "mri@npm:^1.1.0": version: 1.2.0 resolution: "mri@npm:1.2.0" @@ -11745,7 +13601,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3": +"ms@npm:2.1.3, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -11784,6 +13640,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.11": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10/73b5afe5975a307aaa3c95dfe3334c52cdf9ae71518176895229b8d65ab0d1c0417dd081426134eb7571c055720428ea5d57c645138161e7d10df80815527c48 + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -11911,6 +13776,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.21": + version: 2.0.21 + resolution: "node-releases@npm:2.0.21" + checksum: 10/5344d634b39d20f47c0d85a1c64567fdb9cf46f7b27ed3d141f752642faab47dae326835c2109636f823758afb16ffbed7b0c0fe6f800ef91cec9f2beb4f2b4a + languageName: node + linkType: hard + "nopt@npm:^7.0.0": version: 7.2.1 resolution: "nopt@npm:7.2.1" @@ -12197,6 +14069,7 @@ __metadata: "@docusaurus/plugin-content-docs": "npm:^3.7.0" "@docusaurus/preset-classic": "npm:^3.7.0" "@docusaurus/theme-common": "npm:^3.7.0" + "@docusaurus/theme-mermaid": "npm:^3.8.1" "@docusaurus/tsconfig": "npm:^3.7.0" "@docusaurus/types": "npm:^3.7.0" "@mdx-js/react": "npm:^3.0.0" @@ -12218,6 +14091,13 @@ __metadata: languageName: node linkType: hard +"p-finally@npm:^1.0.0": + version: 1.0.0 + resolution: "p-finally@npm:1.0.0" + checksum: 10/93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 + languageName: node + linkType: hard + "p-limit@npm:^2.0.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -12281,6 +14161,16 @@ __metadata: languageName: node linkType: hard +"p-queue@npm:^6.6.2": + version: 6.6.2 + resolution: "p-queue@npm:6.6.2" + dependencies: + eventemitter3: "npm:^4.0.4" + p-timeout: "npm:^3.2.0" + checksum: 10/60fe227ffce59fbc5b1b081305b61a2f283ff145005853702b7d4d3f99a0176bd21bb126c99a962e51fe1e01cb8aa10f0488b7bbe73b5dc2e84b5cc650b8ffd2 + languageName: node + linkType: hard + "p-retry@npm:^4.5.0": version: 4.6.2 resolution: "p-retry@npm:4.6.2" @@ -12291,6 +14181,15 @@ __metadata: languageName: node linkType: hard +"p-timeout@npm:^3.2.0": + version: 3.2.0 + resolution: "p-timeout@npm:3.2.0" + dependencies: + p-finally: "npm:^1.0.0" + checksum: 10/3dd0eaa048780a6f23e5855df3dd45c7beacff1f820476c1d0d1bcd6648e3298752ba2c877aa1c92f6453c7dd23faaf13d9f5149fc14c0598a142e2c5e8d649c + languageName: node + linkType: hard + "p-try@npm:^2.0.0": version: 2.2.0 resolution: "p-try@npm:2.2.0" @@ -12317,6 +14216,13 @@ __metadata: languageName: node linkType: hard +"package-manager-detector@npm:^1.3.0": + version: 1.3.0 + resolution: "package-manager-detector@npm:1.3.0" + checksum: 10/b21155d53a8ab96d5be3bfae43cc1d397bf363782b922d1f6967d220d2a9f08234ebb76035318bf92822ce761d10451959f01019faebc08fdb4d4a8bc3103da6 + languageName: node + linkType: hard + "pako@npm:^2.1.0": version: 2.1.0 resolution: "pako@npm:2.1.0" @@ -12428,6 +14334,13 @@ __metadata: languageName: node linkType: hard +"path-data-parser@npm:0.1.0, path-data-parser@npm:^0.1.0": + version: 0.1.0 + resolution: "path-data-parser@npm:0.1.0" + checksum: 10/a23a214adb38074576a8873d25e8dea7e090b8396d86f58f83f3f6c6298ff56b06adc694147b67f0ed22f14dc478efa1d525710d3ec7b2d7b1efbac57e3fafe6 + languageName: node + linkType: hard + "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -12527,6 +14440,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^2.0.1, pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10/01e9a69928f39087d96e1751ce7d6d50da8c39abf9a12e0ac2389c42c83bc76f78c45a475bd9026a02e6a6f79be63acc75667df855862fe567d99a00a540d23d + languageName: node + linkType: hard + "periscopic@npm:^3.0.0": version: 3.1.0 resolution: "periscopic@npm:3.1.0" @@ -12575,6 +14495,28 @@ __metadata: languageName: node linkType: hard +"pkg-types@npm:^1.3.1": + version: 1.3.1 + resolution: "pkg-types@npm:1.3.1" + dependencies: + confbox: "npm:^0.1.8" + mlly: "npm:^1.7.4" + pathe: "npm:^2.0.1" + checksum: 10/6d491f2244597b24fb59a50e3c258f27da3839555d2a4e112b31bcf536e9359fc4edc98639cd74d2cf16fcd4269e5a09d99fc05d89e2acc896a2f027c2f6ec44 + languageName: node + linkType: hard + +"pkg-types@npm:^2.3.0": + version: 2.3.0 + resolution: "pkg-types@npm:2.3.0" + dependencies: + confbox: "npm:^0.2.2" + exsolve: "npm:^1.0.7" + pathe: "npm:^2.0.3" + checksum: 10/4b36e4eb12693a1beb145573c564ec6fb74b1008d3b457eaa1f0072331edf05cb7c479c47fe0c4bfdec76c2caff5b68215ff270e5fe49634c07984a7a0197118 + languageName: node + linkType: hard + "pkg-up@npm:^3.1.0": version: 3.1.0 resolution: "pkg-up@npm:3.1.0" @@ -12591,6 +14533,23 @@ __metadata: languageName: node linkType: hard +"points-on-curve@npm:0.2.0, points-on-curve@npm:^0.2.0": + version: 0.2.0 + resolution: "points-on-curve@npm:0.2.0" + checksum: 10/3f9a4a9f5a624bb307a72f5cdf1f7c29bedc546716664a2cfd7228085308575e63b461a3e64a88d3b451031655714eb49469d2ced392ee014b709132cd59be93 + languageName: node + linkType: hard + +"points-on-path@npm:^0.2.1": + version: 0.2.1 + resolution: "points-on-path@npm:0.2.1" + dependencies: + path-data-parser: "npm:0.1.0" + points-on-curve: "npm:0.2.0" + checksum: 10/8b3f42feb24433b4a3e0b1c1f951340f06f523b26ed4d87446829f500f1468ad1484a6bf7fedf076ff4b492ae6b1daa7ffc07c7a8f7c00f4d072f17f79fe9ed0 + languageName: node + linkType: hard + "postcss-attribute-case-insensitive@npm:^7.0.1": version: 7.0.1 resolution: "postcss-attribute-case-insensitive@npm:7.0.1" @@ -12625,6 +14584,21 @@ __metadata: languageName: node linkType: hard +"postcss-color-functional-notation@npm:^7.0.11": + version: 7.0.11 + resolution: "postcss-color-functional-notation@npm:7.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/78db22c195e85934ba6e3d46ad5ac354a9966ea1cc6955914f6ca7623a96f4b62a00b3c37c6217b205fe864f52bab47e8ed204047fabe25eb5d4c122e4032675 + languageName: node + linkType: hard + "postcss-color-functional-notation@npm:^7.0.7": version: 7.0.7 resolution: "postcss-color-functional-notation@npm:7.0.7" @@ -12704,6 +14678,20 @@ __metadata: languageName: node linkType: hard +"postcss-custom-media@npm:^11.0.6": + version: 11.0.6 + resolution: "postcss-custom-media@npm:11.0.6" + dependencies: + "@csstools/cascade-layer-name-parser": "npm:^2.0.5" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/media-query-list-parser": "npm:^4.0.3" + peerDependencies: + postcss: ^8.4 + checksum: 10/0fa7ec309065590ce9921bb455947b0a2b8354a1e2f04dae1b3b01e1e4832fd0bb01c983d2bdfc886ceb7ba5d90ffaffc7082afa696aac350f55de0f960c3786 + languageName: node + linkType: hard + "postcss-custom-properties@npm:^14.0.4": version: 14.0.4 resolution: "postcss-custom-properties@npm:14.0.4" @@ -12719,6 +14707,21 @@ __metadata: languageName: node linkType: hard +"postcss-custom-properties@npm:^14.0.6": + version: 14.0.6 + resolution: "postcss-custom-properties@npm:14.0.6" + dependencies: + "@csstools/cascade-layer-name-parser": "npm:^2.0.5" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/utilities": "npm:^2.0.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/fe57781d58990f8061aac2a07c6aedb66c5715b3350af11f4eb26121ff27a735610228a7e18b243a0cfeb24bd10cb5a2359e3056d693ba69c3a09bbef8a8c461 + languageName: node + linkType: hard + "postcss-custom-selectors@npm:^8.0.4": version: 8.0.4 resolution: "postcss-custom-selectors@npm:8.0.4" @@ -12733,6 +14736,20 @@ __metadata: languageName: node linkType: hard +"postcss-custom-selectors@npm:^8.0.5": + version: 8.0.5 + resolution: "postcss-custom-selectors@npm:8.0.5" + dependencies: + "@csstools/cascade-layer-name-parser": "npm:^2.0.5" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + postcss-selector-parser: "npm:^7.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/191cfe62ad3eaf3d8bff75ed461baebbb3b9a52de9c1c75bded61da4ed2302d7c53c457e9febfa7cffc9a1fb7f6ed98cab8c4b2a071a1097e487e0117018e6cf + languageName: node + linkType: hard + "postcss-dir-pseudo-class@npm:^9.0.1": version: 9.0.1 resolution: "postcss-dir-pseudo-class@npm:9.0.1" @@ -12804,6 +14821,19 @@ __metadata: languageName: node linkType: hard +"postcss-double-position-gradients@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-double-position-gradients@npm:6.0.3" + dependencies: + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/335c3c916aea28ad7359b4a675c60199929d18b0bf7547850c68ac84b91945683ed58c51dc369c93985128555ccbd2ff69750b0e321659600c3d5ba433bbede3 + languageName: node + linkType: hard + "postcss-focus-visible@npm:^10.0.1": version: 10.0.1 resolution: "postcss-focus-visible@npm:10.0.1" @@ -12856,6 +14886,21 @@ __metadata: languageName: node linkType: hard +"postcss-lab-function@npm:^7.0.11": + version: 7.0.11 + resolution: "postcss-lab-function@npm:7.0.11" + dependencies: + "@csstools/css-color-parser": "npm:^3.1.0" + "@csstools/css-parser-algorithms": "npm:^3.0.5" + "@csstools/css-tokenizer": "npm:^3.0.4" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/utilities": "npm:^2.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/9ca551b9665e45816bedf9541ed5371244590f97c3e49cfe4857b8ff664768672f3128e35d1a3ee8d5ac268287146dfe993112906445d1047dc687da506475b8 + languageName: node + linkType: hard + "postcss-lab-function@npm:^7.0.7": version: 7.0.7 resolution: "postcss-lab-function@npm:7.0.7" @@ -12871,7 +14916,7 @@ __metadata: languageName: node linkType: hard -"postcss-loader@npm:^7.3.3": +"postcss-loader@npm:^7.3.3, postcss-loader@npm:^7.3.4": version: 7.3.4 resolution: "postcss-loader@npm:7.3.4" dependencies: @@ -12896,6 +14941,17 @@ __metadata: languageName: node linkType: hard +"postcss-logical@npm:^8.1.0": + version: 8.1.0 + resolution: "postcss-logical@npm:8.1.0" + dependencies: + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/495ce49a1fb831eb30d848909a54430b0170ec7681dcd84da9f1cb531cad167b9fa358dc242b70c2cae5c92b1a3fbbf43e625a54c3e615ea9dcce8d15cee5926 + languageName: node + linkType: hard + "postcss-merge-idents@npm:^6.0.3": version: 6.0.3 resolution: "postcss-merge-idents@npm:6.0.3" @@ -13039,6 +15095,19 @@ __metadata: languageName: node linkType: hard +"postcss-nesting@npm:^13.0.2": + version: 13.0.2 + resolution: "postcss-nesting@npm:13.0.2" + dependencies: + "@csstools/selector-resolve-nested": "npm:^3.1.0" + "@csstools/selector-specificity": "npm:^5.0.0" + postcss-selector-parser: "npm:^7.0.0" + peerDependencies: + postcss: ^8.4 + checksum: 10/ac82d7d2d2621d76ca42e065b90728c91206480ef01f874c21d032c5964b723818ab13194b09c7871edee8e4220241160f72d29ece65acbfe8827937a5b6ace0 + languageName: node + linkType: hard + "postcss-normalize-charset@npm:^6.0.2": version: 6.0.2 resolution: "postcss-normalize-charset@npm:6.0.2" @@ -13262,6 +15331,82 @@ __metadata: languageName: node linkType: hard +"postcss-preset-env@npm:^10.2.1": + version: 10.3.1 + resolution: "postcss-preset-env@npm:10.3.1" + dependencies: + "@csstools/postcss-alpha-function": "npm:^1.0.0" + "@csstools/postcss-cascade-layers": "npm:^5.0.2" + "@csstools/postcss-color-function": "npm:^4.0.11" + "@csstools/postcss-color-function-display-p3-linear": "npm:^1.0.0" + "@csstools/postcss-color-mix-function": "npm:^3.0.11" + "@csstools/postcss-color-mix-variadic-function-arguments": "npm:^1.0.1" + "@csstools/postcss-content-alt-text": "npm:^2.0.7" + "@csstools/postcss-exponential-functions": "npm:^2.0.9" + "@csstools/postcss-font-format-keywords": "npm:^4.0.0" + "@csstools/postcss-gamut-mapping": "npm:^2.0.11" + "@csstools/postcss-gradients-interpolation-method": "npm:^5.0.11" + "@csstools/postcss-hwb-function": "npm:^4.0.11" + "@csstools/postcss-ic-unit": "npm:^4.0.3" + "@csstools/postcss-initial": "npm:^2.0.1" + "@csstools/postcss-is-pseudo-class": "npm:^5.0.3" + "@csstools/postcss-light-dark-function": "npm:^2.0.10" + "@csstools/postcss-logical-float-and-clear": "npm:^3.0.0" + "@csstools/postcss-logical-overflow": "npm:^2.0.0" + "@csstools/postcss-logical-overscroll-behavior": "npm:^2.0.0" + "@csstools/postcss-logical-resize": "npm:^3.0.0" + "@csstools/postcss-logical-viewport-units": "npm:^3.0.4" + "@csstools/postcss-media-minmax": "npm:^2.0.9" + "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^3.0.5" + "@csstools/postcss-nested-calc": "npm:^4.0.0" + "@csstools/postcss-normalize-display-values": "npm:^4.0.0" + "@csstools/postcss-oklab-function": "npm:^4.0.11" + "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" + "@csstools/postcss-random-function": "npm:^2.0.1" + "@csstools/postcss-relative-color-syntax": "npm:^3.0.11" + "@csstools/postcss-scope-pseudo-class": "npm:^4.0.1" + "@csstools/postcss-sign-functions": "npm:^1.1.4" + "@csstools/postcss-stepped-value-functions": "npm:^4.0.9" + "@csstools/postcss-text-decoration-shorthand": "npm:^4.0.3" + "@csstools/postcss-trigonometric-functions": "npm:^4.0.9" + "@csstools/postcss-unset-value": "npm:^4.0.0" + autoprefixer: "npm:^10.4.21" + browserslist: "npm:^4.25.1" + css-blank-pseudo: "npm:^7.0.1" + css-has-pseudo: "npm:^7.0.3" + css-prefers-color-scheme: "npm:^10.0.0" + cssdb: "npm:^8.4.0" + postcss-attribute-case-insensitive: "npm:^7.0.1" + postcss-clamp: "npm:^4.1.0" + postcss-color-functional-notation: "npm:^7.0.11" + postcss-color-hex-alpha: "npm:^10.0.0" + postcss-color-rebeccapurple: "npm:^10.0.0" + postcss-custom-media: "npm:^11.0.6" + postcss-custom-properties: "npm:^14.0.6" + postcss-custom-selectors: "npm:^8.0.5" + postcss-dir-pseudo-class: "npm:^9.0.1" + postcss-double-position-gradients: "npm:^6.0.3" + postcss-focus-visible: "npm:^10.0.1" + postcss-focus-within: "npm:^9.0.1" + postcss-font-variant: "npm:^5.0.0" + postcss-gap-properties: "npm:^6.0.0" + postcss-image-set-function: "npm:^7.0.0" + postcss-lab-function: "npm:^7.0.11" + postcss-logical: "npm:^8.1.0" + postcss-nesting: "npm:^13.0.2" + postcss-opacity-percentage: "npm:^3.0.0" + postcss-overflow-shorthand: "npm:^6.0.0" + postcss-page-break: "npm:^3.0.4" + postcss-place: "npm:^10.0.0" + postcss-pseudo-class-any-link: "npm:^10.0.1" + postcss-replace-overflow-wrap: "npm:^4.0.0" + postcss-selector-not: "npm:^8.0.1" + peerDependencies: + postcss: ^8.4 + checksum: 10/72c0e9ecd1e4f59201e686c4ada78d1052153816084b20544abc938f0dc612a551c2617abc618a7fd481fe1a89abbca4b4423ea8073e4daf77d9072d84de9f5c + languageName: node + linkType: hard + "postcss-pseudo-class-any-link@npm:^10.0.1": version: 10.0.1 resolution: "postcss-pseudo-class-any-link@npm:10.0.1" @@ -13408,6 +15553,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.4": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10/9e4fbe97574091e9736d0e82a591e29aa100a0bf60276a926308f8c57249698935f35c5d2f4e80de778d0cbb8dcffab4f383d85fd50c5649aca421c3df729b86 + languageName: node + linkType: hard + "postman-code-generators@npm:^1.10.1": version: 1.10.1 resolution: "postman-code-generators@npm:1.10.1" @@ -13649,6 +15805,13 @@ __metadata: languageName: node linkType: hard +"quansync@npm:^0.2.11": + version: 0.2.11 + resolution: "quansync@npm:0.2.11" + checksum: 10/d4f0cc21a25052a8a6183f17752a6221829c4795b40641de67c06945b356841ff00296d3700d0332dfe8e86100fdcc02f4be7559f3f1774a753b05adb7800d01 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -14513,6 +16676,25 @@ __metadata: languageName: node linkType: hard +"robust-predicates@npm:^3.0.2": + version: 3.0.2 + resolution: "robust-predicates@npm:3.0.2" + checksum: 10/88bd7d45a6b89e88da2631d4c111aaaf0443de4d7078e9ab7f732245790a3645cf79bf91882a9740dbc959cf56ba75d5dced5bf2259410f8b6de19fd240cd08c + languageName: node + linkType: hard + +"roughjs@npm:^4.6.6": + version: 4.6.6 + resolution: "roughjs@npm:4.6.6" + dependencies: + hachure-fill: "npm:^0.5.2" + path-data-parser: "npm:^0.1.0" + points-on-curve: "npm:^0.2.0" + points-on-path: "npm:^0.2.1" + checksum: 10/76bd1e892d79b002dbc0591a28442462e027a77edfcdcd3dbbd2e404fa6d248891ade84ca656b24b1d40a29e3a9df5831633b7a7bb5c8551adcdac480a3dce79 + languageName: node + linkType: hard + "rtlcss@npm:^4.1.0": version: 4.1.1 resolution: "rtlcss@npm:4.1.1" @@ -14536,6 +16718,13 @@ __metadata: languageName: node linkType: hard +"rw@npm:1": + version: 1.3.3 + resolution: "rw@npm:1.3.3" + checksum: 10/e90985d64777a00f4ab5f8c0bfea2fb5645c6bda5238840afa339c8a4f86f776e8ce83731155643a7425a0b27ce89077dab27b2f57519996ba4d2fe54cac1941 + languageName: node + linkType: hard + "sade@npm:^1.7.3": version: 1.8.1 resolution: "sade@npm:1.8.1" @@ -15152,6 +17341,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10/ff9d8c8bf096d534a5b7707e0382ef827b4dd360a577d3f34d2b9f48e12c9d230b5747974ee7c607f0df65113732711bb701fe9ece3c7edbd43cb2294d707df3 + languageName: node + linkType: hard + "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -15398,6 +17594,13 @@ __metadata: languageName: node linkType: hard +"stylis@npm:^4.3.6": + version: 4.3.6 + resolution: "stylis@npm:4.3.6" + checksum: 10/6ebe8a37827124e0caf0704c13d39c121f6e6a8433eb8c67cfce508477b24a4434d1731198ba0b6e453655022bbf5beda93585f38ff420545e5356f925f83761 + languageName: node + linkType: hard + "sucrase@npm:^3.31.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -15643,6 +17846,20 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^1.0.1": + version: 1.0.1 + resolution: "tinyexec@npm:1.0.1" + checksum: 10/1f3c3281912d4ab168e067baf46627bb85a803eba0bcea113bba9fe8bdfdcc279cad08052a600d4b8fb603dd57e1af0c500e50a5e7e6b29b2574c88556f41fa6 + languageName: node + linkType: hard + +"tinypool@npm:^1.0.2": + version: 1.1.1 + resolution: "tinypool@npm:1.1.1" + checksum: 10/0d54139e9dbc6ef33349768fa78890a4d708d16a7ab68e4e4ef3bb740609ddf0f9fd13292c2f413fbba756166c97051a657181c8f7ae92ade690604f183cc01d + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -15701,6 +17918,13 @@ __metadata: languageName: node linkType: hard +"ts-dedent@npm:^2.2.0": + version: 2.2.0 + resolution: "ts-dedent@npm:2.2.0" + checksum: 10/93ed8f7878b6d5ed3c08d99b740010eede6bccfe64bce61c5a4da06a2c17d6ddbb80a8c49c2d15251de7594a4f93ffa21dd10e7be75ef66a4dc9951b4a94e2af + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" @@ -15775,6 +17999,13 @@ __metadata: languageName: node linkType: hard +"ufo@npm:^1.6.1": + version: 1.6.1 + resolution: "ufo@npm:1.6.1" + checksum: 10/088a68133b93af183b093e5a8730a40fe7fd675d3dc0656ea7512f180af45c92300c294f14d4d46d4b2b553e3e52d3b13d4856b9885e620e7001edf85531234e + languageName: node + linkType: hard + "undici-types@npm:~5.26.4": version: 5.26.5 resolution: "undici-types@npm:5.26.5" @@ -16041,6 +18272,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.1.3": + version: 1.1.3 + resolution: "update-browserslist-db@npm:1.1.3" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10/87af2776054ffb9194cf95e0201547d041f72ee44ce54b144da110e65ea7ca01379367407ba21de5c9edd52c74d95395366790de67f3eb4cc4afa0fe4424e76f + languageName: node + linkType: hard + "update-notifier@npm:^6.0.2": version: 6.0.2 resolution: "update-notifier@npm:6.0.2" @@ -16154,6 +18399,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^11.1.0": + version: 11.1.0 + resolution: "uuid@npm:11.1.0" + bin: + uuid: dist/esm/bin/uuid + checksum: 10/d2da43b49b154d154574891ced66d0c83fc70caaad87e043400cf644423b067542d6f3eb641b7c819224a7cd3b4c2f21906acbedd6ec9c6a05887aa9115a9cf5 + languageName: node + linkType: hard + "uvu@npm:^0.5.0": version: 0.5.6 resolution: "uvu@npm:0.5.6" @@ -16285,6 +18539,55 @@ __metadata: languageName: node linkType: hard +"vscode-jsonrpc@npm:8.2.0": + version: 8.2.0 + resolution: "vscode-jsonrpc@npm:8.2.0" + checksum: 10/6d57c3aed591d0bc89d1c226061d265b04de528582bef183f5998cac5de78a736887e5238fe48b9f6a14ec32f05d8fda71599f92862ac5dacc7f26bf7399b532 + languageName: node + linkType: hard + +"vscode-languageserver-protocol@npm:3.17.5": + version: 3.17.5 + resolution: "vscode-languageserver-protocol@npm:3.17.5" + dependencies: + vscode-jsonrpc: "npm:8.2.0" + vscode-languageserver-types: "npm:3.17.5" + checksum: 10/aeb9c190184c365fa6b835e5aa7574c86cb3ecb2789386bcff76a09b22bc8b8e0d5da47c28193a9c73cfb32c10a12a91191779280324a38efb401e3ef7bad294 + languageName: node + linkType: hard + +"vscode-languageserver-textdocument@npm:~1.0.11": + version: 1.0.12 + resolution: "vscode-languageserver-textdocument@npm:1.0.12" + checksum: 10/2bc0fde952d40f35a31179623d1491b0fafdee156aaf58557f40f5d394a25fc84826763cdde55fa6ce2ed9cd35a931355ad6dd7fe5db82e7f21e5d865f0af8c6 + languageName: node + linkType: hard + +"vscode-languageserver-types@npm:3.17.5": + version: 3.17.5 + resolution: "vscode-languageserver-types@npm:3.17.5" + checksum: 10/900d0b81df5bef8d90933e75be089142f6989cc70fdb2d5a3a5f11fa20feb396aaea23ccffc8fbcc83a2f0e1b13c6ee48ff8151f236cbd6e61a4f856efac1a58 + languageName: node + linkType: hard + +"vscode-languageserver@npm:~9.0.1": + version: 9.0.1 + resolution: "vscode-languageserver@npm:9.0.1" + dependencies: + vscode-languageserver-protocol: "npm:3.17.5" + bin: + installServerIntoExtension: bin/installServerIntoExtension + checksum: 10/1cb643b1b1f41a620aaf4a62e152acad694c22b4d98de73fa614a0bddf3b4b4832460465bdbc43f27ba23dad7e61aba533e77b8bfac74cc8de310c39623a7ba1 + languageName: node + linkType: hard + +"vscode-uri@npm:~3.0.8": + version: 3.0.8 + resolution: "vscode-uri@npm:3.0.8" + checksum: 10/e882d6b679e0d053cbc042893c0951a135d899a192b62cd07f0a8924f11ae722067a8d6b1b5b147034becf57faf9fff9fb543b17b749fd0f17db1f54f783f07c + languageName: node + linkType: hard + "warning@npm:^4.0.3": version: 4.0.3 resolution: "warning@npm:4.0.3" From a6b5034b4f056742337b5c4749ae09d791d3c869 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:42:57 +0200 Subject: [PATCH 02/17] WIP --- docs/developer-guide/external-services.md | 13 +- .../img/service-ml-forecast-config-list.png | Bin 0 -> 35005 bytes .../services/img/service-ml-forecast-tree.png | Bin 0 -> 6102 bytes .../services/service-ml-forecast.md | 31 +++++ docs/user-guide/services/services.md | 114 ++++++++++++++++++ 5 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 docs/user-guide/services/img/service-ml-forecast-config-list.png create mode 100644 docs/user-guide/services/img/service-ml-forecast-tree.png create mode 100644 docs/user-guide/services/service-ml-forecast.md diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md index 77bfef4..7ab88f7 100644 --- a/docs/developer-guide/external-services.md +++ b/docs/developer-guide/external-services.md @@ -137,13 +137,14 @@ When developing and integrating an external service, consider the following: ## Example Use Cases External services can be used to extend OpenRemote in many ways, such as: -- **AI/LLM Integration**: Connect AI services (e.g., ChatGPT, Claude) to provide contextual querying of devices, assets, and data. -- **Machine Learning**: Implement predictive maintenance, energy optimization, or anomaly detection. -- **Firmware Updates**: Manage and deploy firmware updates to connected devices. +**Examples:** +- **AI/LLM Service**: Connect AI services (e.g., ChatGPT, Claude) to provide contextual querying of devices, assets, and data. +- **Machine Learning Service**: Implement predictive maintenance, energy optimization, or anomaly detection. +- **Firmware Management Service**: Manage and deploy firmware updates to connected devices. ### Reference Implementation -We provide the [ML Forecasting Service](https://github.com/openremote/ml-forecasting-service) as a reference implementation. -This service connects to OpenRemote, retrieves historical data, and provides forecasting capabilities using machine learning and statistical models. It also demonstrates how to: +We provide the [ML Forecast Service](https://github.com/openremote/service-ml-forecast) which can serve as a reference implementation. +This service connects to OpenRemote, retrieves historical data, and provides forecasting capabilities using machine learning/statistical models. It also demonstrates how to: - **Register** an external service - **Send and manage** heartbeats @@ -154,7 +155,7 @@ This service connects to OpenRemote, retrieves historical data, and provides for --- ## Summary -By leveraging external services, developers can significantly enhance the OpenRemote platform. +By leveraging external services, developers can extend and enhance the OpenRemote platform. - **Registration** connects services with a UI to the Manager, embedding their interface directly. - **Global vs regular services** allow flexibility between multi-tenant and realm-specific use cases. - **Heartbeats** ensure service availability is tracked in real time. diff --git a/docs/user-guide/services/img/service-ml-forecast-config-list.png b/docs/user-guide/services/img/service-ml-forecast-config-list.png new file mode 100644 index 0000000000000000000000000000000000000000..3a11b7f44b840745c87c84015d060955bbfbd9d2 GIT binary patch literal 35005 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 M85LNilxf)i0Y;R2TL1t6 literal 0 HcmV?d00001 diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md new file mode 100644 index 0000000..2a4f697 --- /dev/null +++ b/docs/user-guide/services/service-ml-forecast.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 2 +--- + +# ML Forecasting Service + +The ML Forecasting Service is an **optional** external service that can be used to leverage machine learning to generate forecasts for any attribute that has a history of data. + +:::note +This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](services.md) page. +::: + +## Features + +- Create forecast configurations for any attribute that has a history of data +- Forecasts are automatically generated at a specified interval +- Manage forecast configurations in the UI +- Fine-tune the configuration by adjusting model parameters +- Generated forecasts can be viewed as predicted data points when visualized via the Insights UI + +## Accessing the Service + +To access the service, you must first navigate to the `Services` page in the Manager UI. + +If the service is properly installed, you will see the `ML Forecasting Service` in the list of services. + +![ML Forecasting Service](img/service-ml-forecast-tree.png) + +## Usage + +When the service is selected, you should see a list of forecast configurations. If none are present, you can create one by clicking the `Configure New Forecast` button. diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md index e69de29..7383a51 100644 --- a/docs/user-guide/services/services.md +++ b/docs/user-guide/services/services.md @@ -0,0 +1,114 @@ +--- +sidebar_position: 1 +--- + +# External Services + +External services add extra features and tools to your OpenRemote system. They appear as new sections in the Manager UI, giving you access to additional capabilities like AI assistants, analytics dashboards, or custom tools that your administrator has set up. + +When you click on an external service, it opens right within OpenRemote just like any other page, so you don't need to switch between different applications. + +--- + +## What are External Services? + +External services are additional applications that work alongside OpenRemote. You'll see them as new menu items or sections in your Manager UI. Each service provides different functionality, such as AI assistants, analytics dashboards, device management tools, or custom integrations. + +When you use an external service, it looks and feels like part of OpenRemote, but it's actually a separate application that's been integrated for you. + +--- + +## Finding External Services + +### Where to Look + +External services can appear in several places in the Manager UI: + +1. **Main Menu** - Look for the `Services` menu item, this will show all services available to you + +### Service Status + +You can see if a service is working properly by looking at its status icon on the right side of the service name: + +- **Available** - The icon will be a simple play icon +- **Unavailable** - The icon will be a red hexagon with an X, indicating that the service is unavailable + +If you see a service marked as unavailable, it might be updating or experiencing temporary issues. Try refreshing your browser or check back in a few minutes. + +--- + +## Using External Services + +### Opening a Service + +To use an external service: + +1. **Click** on the service name in your navigation menu +2. **Wait** for the service interface to load within OpenRemote +3. **Use** the service just like you would use any web application + +The service will open in the same browser window, but you'll see its own interface and controls. + +--- + +## Types of Services + +You might encounter two types of external services: + +### Organization-wide Services +- Available to **everyone** in your organization, across all realms +- Usually managed by your IT department or system administrators +- Examples: Company-wide analytics, centralized AI tools + +### Realm-specific Services +- Only available within **your specific realm** or project +- Configured for your particular use case +- Examples: Project-specific dashboards, local integrations + +You don't need to worry about which type a service is - if you can see it in your menu, you can use it. + +--- + +## Troubleshooting + +### Service Won't Load + +If an external service isn't working: + +1. **Check the status** - Is it showing as "Available"? +2. **Refresh your browser** - Press F5 or click the refresh button +3. **Clear your browser cache** - This can solve many loading issues +4. **Check your internet connection** - External services may need internet access +5. **Try a different browser** - Sometimes browser compatibility can cause issues + +### Can't Access a Service + +If you can see a service but can't use it: + +- **Check with your administrator** - You might need additional permissions +- **Verify your permissions** - Different user roles have access to different services + +### Getting Help + +When you encounter problems: + +1. **Note the error message** - Write down exactly what you see +2. **Try the basic troubleshooting steps** above +3. **Contact your system administrator** with details about the problem +4. **Include screenshots** if possible - they help explain the issue + +--- + +## Adding New Services + +Adding new services is done by your administrator. They can add any existing service that may already be available, or they can create a new service. + +If you need additional functionality that isn't currently available, contact your administrator to discuss what options might be available. + +--- + +## Summary + +External services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. + +If you see new menu items or sections that weren't there before, those are likely external services that have been added to enhance your system. Don't hesitate to explore them or ask your administrator about what's available - they're there to make your work easier and more effective. \ No newline at end of file From 5bdb85876da7ce97331e5db59df385ca59a7af56 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:59:06 +0200 Subject: [PATCH 03/17] Structural changes --- docs/developer-guide/external-services.md | 239 ++++++++++++------ .../services/service-ml-forecast.md | 2 +- docs/user-guide/services/services.md | 42 ++- 3 files changed, 174 insertions(+), 109 deletions(-) diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md index 7ab88f7..359de2e 100644 --- a/docs/developer-guide/external-services.md +++ b/docs/developer-guide/external-services.md @@ -4,48 +4,81 @@ sidebar_position: 17 # External Services -External services allow developers to extend the functionality of the OpenRemote platform. By registering an external service, developers can integrate their own applications directly into the OpenRemote Manager, giving users a seamless way to configure and manage them. +External services allow developers to extend the functionality of the OpenRemote platform by integrating their own applications directly into the OpenRemote Manager, giving users a seamless way to configure and manage them. -The registration process is primarily intended for services that provide a **user interface**. When registered, the service’s web interface is embedded in the Manager Web UI using an **iframe**, allowing users to interact with it without leaving OpenRemote. +The registration process is primarily intended for services that provide a **user interface**. When registered, the service's web interface is embedded in the Manager Web UI using an **iframe**, allowing users to interact with it without leaving OpenRemote. -Services without a UI can remain fully standalone applications. They can still make use of OpenRemote’s APIs but do not need to register as an external service. +Services without a UI can remain fully standalone applications. They can still make use of OpenRemote's APIs but do not need to register as an external service. --- -## What is an External Service? -An external service is a standalone application that communicates with OpenRemote through its APIs. When registered, it appears in the Manager Web UI with its own embedded web interface, enabling direct interaction from within OpenRemote. +## Overview -External services can be built using any programming language or framework. They should be viewed as independent applications that **extend the platform’s capabilities** while leveraging OpenRemote for integration and management. +### What is an External Service? ---- +An external service is a standalone application that communicates with OpenRemote through its APIs. When registered, it appears in the Manager Web UI with its own embedded web interface, enabling direct interaction from within OpenRemote. + +External services can be built using any programming language or framework. They should be viewed as independent applications that **extend the platform's capabilities** while leveraging OpenRemote for integration and management. + +### Types of External Services -## Types of External Services There are two types of external services in OpenRemote: -- **Global Services** - - Registered using the `/services/global` endpoint. - - Must be registered via the **master realm**. - - Require a **Service User with Super User privileges** for registration. - - Once registered, they are available and listed on **all realms**. - - They are typically designed with **multi-tenancy** in mind, but this is not strictly required. - For example, a global service may implement its own authentication and authorization instead of relying on OpenRemote's Keycloak instance. +**Global Services** +- Registered using the `/services/global` endpoint +- Must be registered via the **master realm** +- Require a **Service User with Super User privileges** for registration +- Once registered, they are available and listed on **all realms** +- Typically designed with **multi-tenancy** in mind, but this is not strictly required -- **Regular Services** - - Registered using the `/services` endpoint. - - Bound to a **specific realm** and only available within that realm (single-tenant). - - Simpler to implement when multi-tenancy is not required. +**Regular Services** +- Registered using the `/services` endpoint +- Bound to a **specific realm** and only available within that realm (single-tenant) +- Simpler to implement when multi-tenancy is not required Both global and regular services must be registered by **Service Users**. Global services specifically require a Super User account in the master realm, while regular services can be registered with a realm-specific Service User. --- -## Registering an External Service -Registration is only required if the service provides a web interface and is expected to be embedded and used within the OpenRemote Manager UI. -This process is always performed using a **Service User** account (see [Service Users](../architecture/security.md) for details). +## Development + +### Service Web Interface Requirements + +Registered external services must expose a web interface via a URL. OpenRemote embeds this interface in the Manager Web UI using an **iframe**. + +**Technical Requirements:** +- **Same origin and protocol**: The service must use the same domain and protocol (HTTP or HTTPS) as the OpenRemote Manager (no mixed content) +- **Headers**: Do not block embedding. Avoid `X-Frame-Options: DENY` or restrictive `Content-Security-Policy` + - Recommended: `Content-Security-Policy: frame-ancestors 'self' https://` +- **Responsive design**: The iframe may resize; ensure the UI adapts dynamically +- **Navigation**: Avoid pop-ups or full-page redirects; keep interactions within the iframe + +### Building the Interface + +Developers can use any web framework or technology stack. Optionally, OpenRemote provides a set of pre-made web components such as buttons, panels, forms, and tables that integrate seamlessly with the Manager UI for a consistent look and feel. + +These components are available on [npmjs](https://www.npmjs.com/~openremotedeveloper). + +### Security Considerations + +When developing and integrating an external service, consider the following: + +- **Authentication**: OpenRemote uses Keycloak as an identity provider. External services should ensure that only authorized users can access them, either by integrating with Keycloak or implementing their own mechanism +- **Protocol**: Always use **HTTPS** in production to protect data integrity and confidentiality +- **Data validation**: Validate and sanitize all data received from OpenRemote or users to prevent vulnerabilities such as SQL injection or cross-site scripting (XSS) +- **CORS**: If hosting on a different domain, configure Cross-Origin Resource Sharing (CORS) appropriately. Note that using a different domain may complicate Keycloak integration + +--- + +## Registration + +### Registration Process + +Registration is only required if the service provides a web interface and is expected to be embedded and used within the OpenRemote Manager UI. This process is always performed using a **Service User** account (see [Service Users](../architecture/security.md) for details). -It involves sending a `POST` request to the OpenRemote API with details about the service. +It involves sending a `POST` request to the OpenRemote API with details about the service. -**Example request:** +**Example request:** ```json { @@ -53,20 +86,36 @@ It involves sending a `POST` request to the OpenRemote API with details about th "label": "My External Service", "icon": "mdi-cloud", "homepageUrl": "https://my-external-service.com/interface", - "version": "1.0.0", + "version": "1.0.0" } ``` -OpenRemote responds with the same `ExternalService` object, but with an additional field, `instanceId`, which uniquely identifies the service and must be included in subsequent heartbeat requests. +OpenRemote responds with the same `ExternalService` object, but with an additional field, `instanceId`, which uniquely identifies the service and must be included in subsequent heartbeat requests. + +### API Endpoints + +| Endpoint | Method | Scope | Purpose | +|-------------------------|--------|-----------|--------------------------------------------------------| +| `/services` | POST | Realm | Register a realm-specific external service | +| `/services/global` | POST | Global | Register a global external service (master realm only) | +| `/services/heartbeat` | POST | Both | Send periodic heartbeat with `instanceId` | ➡ The exact API endpoint and request format can be found in the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/). -The diagram below illustrates the registration and heartbeat process for both regular and global services: +### Heartbeat Mechanism + +After registration, each service must send periodic heartbeat requests to confirm its availability. + +- The request must include the `instanceId` received during registration +- The default TTL (Time To Live) is **60 seconds** +- If OpenRemote does not receive a heartbeat within this period, the service is marked as **unavailable** + +The diagram below illustrates the registration and heartbeat process: ```mermaid sequenceDiagram participant Service as External Service - participant OR as OpenRemote API + participant OR as OpenRemote Manager API alt Regular Service Service->>OR: POST /services (serviceId, name, url, metadata…) @@ -82,83 +131,105 @@ sequenceDiagram end ``` -### Key Endpoints - -| Endpoint | Method | Scope | Purpose | -|-------------------------|--------|-----------|--------------------------------------------------------| -| `/services` | POST | Realm | Register a realm-specific external service | -| `/services/global` | POST | Global | Register a global external service (master realm only) | -| `/services/heartbeat` | POST | Both | Send periodic heartbeat with `instanceId` | - --- -## Heartbeat Mechanism -After registration, each service must send periodic heartbeat requests to confirm its availability. +## Deployment + +### Docker Compose Setup + +External services can be deployed alongside the OpenRemote stack using Docker Compose. The easiest approach is to add your service to an existing Docker Compose profile or create a custom one. + +**Example service configuration:** + +```yaml +volumes: + service-ml-forecast-data: + +services: + ml-forecast: + image: openremote/service-ml-forecast:latest + restart: always + environment: + ML_LOG_LEVEL: INFO + ML_ENVIRONMENT: production + ML_WEBSERVER_ORIGINS: '["https://${OR_HOSTNAME:-localhost}"]' + ML_SERVICE_HOSTNAME: https://${OR_HOSTNAME:-localhost} + ML_OR_URL: https://${OR_HOSTNAME:-localhost} + ML_OR_KEYCLOAK_URL: https://${OR_HOSTNAME:-localhost}/auth + ML_OR_SERVICE_USER: ${ML_OR_SERVICE_USER:-mlserviceuser} + ML_OR_SERVICE_USER_SECRET: ${ML_OR_SERVICE_USER_SECRET:-secret} + volumes: + - service-ml-forecast-data:/app/deployment/data +``` -- The request must include the `instanceId` received during registration. -- The default TTL (Time To Live) is **60 seconds**. -- If OpenRemote does not receive a heartbeat within this period, the service is marked as **unavailable**. +### Service User Configuration -➡ The heartbeat flow is illustrated in the diagram above. -➡ API details for heartbeats can be found in the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/). +Your external service will need a service user account in OpenRemote for API authentication. The service user credentials should be provided via environment variables and kept secure. ---- +### Reverse Proxy Configuration (Optional) -## Service Web Interface -Registered external services must expose a web interface via a URL. OpenRemote embeds this interface in the Manager Web UI using an **iframe**, allowing users to configure and interact with the service directly. +If you want to use OpenRemote's reverse proxy (HAProxy) to route traffic to your service: -### Requirements -- **Same origin and protocol**: The service must use the same domain and protocol (HTTP or HTTPS) as the OpenRemote Manager (no mixed content). -- **Headers**: Do not block embedding. Avoid `X-Frame-Options: DENY` or restrictive `Content-Security-Policy`. - - Recommended: - ``` - Content-Security-Policy: frame-ancestors 'self' https:// - ``` -- **Responsive design**: The iframe may resize; ensure the UI adapts dynamically. -- **Navigation**: Avoid pop-ups or full-page redirects; keep interactions within the iframe. +**1. Enable Custom HAProxy Configuration** -### Building the Interface -Developers can use any web framework or technology stack. Optionally, OpenRemote provides a set of pre-made web components such as buttons, panels, forms, and tables that integrate seamlessly with the Manager UI for a consistent look and feel. +```yaml +proxy: + environment: + HAPROXY_CONFIG: '/data/proxy/haproxy.cfg' + volumes: + - proxy-data:/deployment + - deployment-data:/data +``` -These components are available on [npmjs](https://www.npmjs.com/~openremotedeveloper). +**2. Create Custom HAProxy Configuration** ---- +Create a custom `haproxy.cfg` file based on the [base configuration](https://github.com/openremote/proxy/blob/main/haproxy.cfg) and place it at `deployment/proxy/haproxy.cfg`. -## Security Considerations -When developing and integrating an external service, consider the following: +Add frontend routing: +```haproxy +acl ml-forecast path_beg /services/ml-forecast +use_backend ml-forecast if ml-forecast +``` -- **Authentication**: OpenRemote uses Keycloak as an identity provider. External services should ensure that only authorized users can access them, either by integrating with Keycloak or implementing their own mechanism. -- **Protocol**: Always use **HTTPS** in production to protect data integrity and confidentiality. -- **Data validation**: Validate and sanitize all data received from OpenRemote or users to prevent vulnerabilities such as SQL injection or cross-site scripting (XSS). -- **CORS**: If hosting on a different domain, configure Cross-Origin Resource Sharing (CORS) appropriately. Note that using a different domain may complicate Keycloak integration. +Add backend configuration: +```haproxy +backend ml-forecast + server ml-forecast ml-forecast:8000 resolvers docker_resolver +``` --- -## Example Use Cases -External services can be used to extend OpenRemote in many ways, such as: +## Examples -**Examples:** -- **AI/LLM Service**: Connect AI services (e.g., ChatGPT, Claude) to provide contextual querying of devices, assets, and data. -- **Machine Learning Service**: Implement predictive maintenance, energy optimization, or anomaly detection. -- **Firmware Management Service**: Manage and deploy firmware updates to connected devices. +### Use Cases + +External services can be used to extend OpenRemote in many ways: + +- **AI/LLM Integration**: Connect AI services (e.g., ChatGPT, Claude) to provide contextual querying of devices, assets, and data +- **Machine Learning**: Implement predictive maintenance, energy optimization, or anomaly detection +- **Firmware Management**: Manage and deploy firmware updates to connected devices ### Reference Implementation -We provide the [ML Forecast Service](https://github.com/openremote/service-ml-forecast) which can serve as a reference implementation. -This service connects to OpenRemote, retrieves historical data, and provides forecasting capabilities using machine learning/statistical models. It also demonstrates how to: -- **Register** an external service -- **Send and manage** heartbeats -- **Interact** with OpenRemote's APIs (OAuth2 authentication, data retrieval, data writing) -- **Integrate** securely with Keycloak for authentication -- **Implement** a web interface (using OpenRemote’s web components) +We provide the [ML Forecast Service](https://github.com/openremote/service-ml-forecast) which can serve as a reference implementation. This service connects to OpenRemote, retrieves historical data, and provides forecasting capabilities using machine learning/statistical models. + +It demonstrates how to: + +- **Register** an external service +- **Send and manage** heartbeats +- **Interact** with OpenRemote's APIs (OAuth2 authentication, data retrieval, data writing) +- **Integrate** securely with Keycloak for authentication +- **Implement** a web interface (using OpenRemote's web components) --- ## Summary -By leveraging external services, developers can extend and enhance the OpenRemote platform. -- **Registration** connects services with a UI to the Manager, embedding their interface directly. -- **Global vs regular services** allow flexibility between multi-tenant and realm-specific use cases. -- **Heartbeats** ensure service availability is tracked in real time. -- **Security best practices** help ensure safe and reliable integrations. -Together, these mechanisms provide a seamless way to extend OpenRemote while maintaining a unified and secure user experience. +By leveraging external services, developers can significantly extend and enhance the OpenRemote platform: + +- **Registration** connects services with a UI to the Manager, embedding their interface directly +- **Global vs regular services** allow flexibility between multi-tenant and realm-specific use cases +- **Heartbeats** ensure service availability is tracked in real time +- **Security best practices** help ensure safe and reliable integrations + +Together, these mechanisms provide a seamless way to extend OpenRemote while maintaining a unified and secure user experience. \ No newline at end of file diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index 2a4f697..397d7ed 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -4,7 +4,7 @@ sidebar_position: 2 # ML Forecasting Service -The ML Forecasting Service is an **optional** external service that can be used to leverage machine learning to generate forecasts for any attribute that has a history of data. +The ML Forecasting Service is an **optional** external service that can be used to leverage machine learning models to generate forecasts for any attribute that has a history of data. :::note This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](services.md) page. diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md index 7383a51..599c307 100644 --- a/docs/user-guide/services/services.md +++ b/docs/user-guide/services/services.md @@ -16,9 +16,25 @@ External services are additional applications that work alongside OpenRemote. Yo When you use an external service, it looks and feels like part of OpenRemote, but it's actually a separate application that's been integrated for you. +### Types of Services + +You might encounter two types of external services: + +**Organization-wide Services** +- Available to **everyone** in your organization, across all realms +- Usually managed by your IT department or system administrators +- Examples: Company-wide analytics, centralized AI tools + +**Realm-specific Services** +- Only available within **your specific realm** or project +- Configured for your particular use case +- Examples: Project-specific dashboards, local integrations + +You don't need to worry about which type a service is - if you can see it in your menu, you can use it. + --- -## Finding External Services +## Finding and Using Services ### Where to Look @@ -35,10 +51,6 @@ You can see if a service is working properly by looking at its status icon on th If you see a service marked as unavailable, it might be updating or experiencing temporary issues. Try refreshing your browser or check back in a few minutes. ---- - -## Using External Services - ### Opening a Service To use an external service: @@ -51,24 +63,6 @@ The service will open in the same browser window, but you'll see its own interfa --- -## Types of Services - -You might encounter two types of external services: - -### Organization-wide Services -- Available to **everyone** in your organization, across all realms -- Usually managed by your IT department or system administrators -- Examples: Company-wide analytics, centralized AI tools - -### Realm-specific Services -- Only available within **your specific realm** or project -- Configured for your particular use case -- Examples: Project-specific dashboards, local integrations - -You don't need to worry about which type a service is - if you can see it in your menu, you can use it. - ---- - ## Troubleshooting ### Service Won't Load @@ -111,4 +105,4 @@ If you need additional functionality that isn't currently available, contact you External services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. -If you see new menu items or sections that weren't there before, those are likely external services that have been added to enhance your system. Don't hesitate to explore them or ask your administrator about what's available - they're there to make your work easier and more effective. \ No newline at end of file +Don't hesitate to explore them or ask your administrator about what's available - they're there to make your work easier and more effective. \ No newline at end of file From b6c4edd6294b98b174e29fbe599eccaa19707af6 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:58:46 +0200 Subject: [PATCH 04/17] User guide for the ML service --- .../img/service-ml-forecast-insights.png | Bin 0 -> 39474 bytes .../services/service-ml-forecast.md | 57 +++++++++++++++++- docs/user-guide/services/services.md | 42 ++++--------- 3 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 docs/user-guide/services/img/service-ml-forecast-insights.png diff --git a/docs/user-guide/services/img/service-ml-forecast-insights.png b/docs/user-guide/services/img/service-ml-forecast-insights.png new file mode 100644 index 0000000000000000000000000000000000000000..f72f92d91920a2e1089a70a4f88adcca2a1fe089 GIT binary patch literal 39474 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 CA8pD2 literal 0 HcmV?d00001 diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index 397d7ed..efbf8ba 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -4,15 +4,15 @@ sidebar_position: 2 # ML Forecasting Service -The ML Forecasting Service is an **optional** external service that can be used to leverage machine learning models to generate forecasts for any attribute that has a history of data. +The ML Forecasting Service is an **optional** service that uses machine learning models to generate forecasts for any attribute that has historical data. :::note -This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](services.md) page. +This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](../developer-guide/external-services.md) page. ::: ## Features -- Create forecast configurations for any attribute that has a history of data +- Create forecast configurations for any attribute that has historical data - Forecasts are automatically generated at a specified interval - Manage forecast configurations in the UI - Fine-tune the configuration by adjusting model parameters @@ -29,3 +29,54 @@ If the service is properly installed, you will see the `ML Forecasting Service` ## Usage When the service is selected, you should see a list of forecast configurations. If none are present, you can create one by clicking the `Configure New Forecast` button. + +![ML Forecasting Service](img/service-ml-forecast-config-list.png) + +You can then configure the forecast by filling in the form. + +### Configuration Form + +The form contains sensible defaults for forecast generation, training, and model parameters. It is recommended to keep the `Parameters` fields at their defaults unless you have a specific reason to change them. + +| Section | Field | Description | +|---------|-------|-------------| +| **General** | `Name` | The name of the forecast configuration | +| | `Model Type` | The model to use for forecasting | +| **Forecast Generation** | `Generate new forecast every` | How often to create new predictions (increase if data changes quickly) | +| | `Unit` | Time measurement (hours, minutes, etc.) | +| | `Forecasted datapoints` | How many future points to predict (more for long-term planning) | +| | `Time between datapoints` | Gap between each predicted point | +| | `Unit` | Time measurement for prediction gaps | +| **Forecast Target** | `Asset` | The device or system with the data you want to predict | +| | `Attribute` | The specific data you want to predict | +| | `Training data period` | How much past data to learn from (more for complex patterns, less if there has been a systemic change) | +| | `Unit` | Time measurement (days, weeks, months, years) | +| **Model Training** | `Train model every` | How often the system learns from new data (keep same as forecast generation) | +| | `Unit` | Time measurement for relearning | +| **Parameters** | `Changepoint range` | How much of your data to check for changes (0.8 = 80%). Reduce if predictions are too jumpy | +| | `Changepoint prior scale` | How likely the system thinks changes will happen (0.05 = 5%). Increase for more flexible forecasts that adapt quickly to changes, decrease for smoother forecasts that ignore minor variations | +| | `Seasonality mode` | How seasonal patterns work (`additive` or `multiplicative`). Use `multiplicative` if seasonal effects grow with trend | +| | `Daily seasonality` | Look for daily patterns. Enable for daily cycles (energy, temperature) | +| | `Weekly seasonality` | Look for weekly patterns. Enable for weekday/weekend differences | +| | `Yearly seasonality` | Look for yearly patterns. Enable for seasonal data (needs 1+ year history) | +| **Regressors** | `Asset` | Asset containing the regressor attribute | +| *(Optional)* | `Attribute` | The attribute to use as a regressor | +| | `Training data period` | Historical period for regressor data (should match or exceed target training period) | +| | `Unit` | Time unit for regressor training data | + +Regressors can be added when there's a relationship between your target variable and other attributes. For example, when forecasting energy consumption, you might add weather data or solar production as regressors. + + + +#### Saving Configuration + +Once you've configured all fields, click the `Save` button to store the forecast configuration. The model will automatically train and begin generating forecasts according to your specified intervals. + +## Viewing the Forecast + +Once the forecast is generated, you can view it in the Insights UI. + +![ML Forecasting Service](img/service-ml-forecast-insights.png) + +The forecast will be displayed as a dotted line in the chart. + diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md index 599c307..2358b2a 100644 --- a/docs/user-guide/services/services.md +++ b/docs/user-guide/services/services.md @@ -2,35 +2,19 @@ sidebar_position: 1 --- -# External Services +# Services -External services add extra features and tools to your OpenRemote system. They appear as new sections in the Manager UI, giving you access to additional capabilities like AI assistants, analytics dashboards, or custom tools that your administrator has set up. +Services add extra features and tools to your OpenRemote system. They appear as new sections in the Manager UI, giving you access to additional capabilities like AI assistants, analytics dashboards, or custom tools that your administrator has set up. -When you click on an external service, it opens right within OpenRemote just like any other page, so you don't need to switch between different applications. +When you click on a service, it opens right within OpenRemote just like any other page, so you don't need to switch between different applications. --- -## What are External Services? +## What are Services? -External services are additional applications that work alongside OpenRemote. You'll see them as new menu items or sections in your Manager UI. Each service provides different functionality, such as AI assistants, analytics dashboards, device management tools, or custom integrations. +services are additional applications that work alongside OpenRemote. You'll see them as new menu items or sections in your Manager UI. Each service provides different functionality, such as AI assistants, analytics dashboards, device management tools, or custom integrations. -When you use an external service, it looks and feels like part of OpenRemote, but it's actually a separate application that's been integrated for you. - -### Types of Services - -You might encounter two types of external services: - -**Organization-wide Services** -- Available to **everyone** in your organization, across all realms -- Usually managed by your IT department or system administrators -- Examples: Company-wide analytics, centralized AI tools - -**Realm-specific Services** -- Only available within **your specific realm** or project -- Configured for your particular use case -- Examples: Project-specific dashboards, local integrations - -You don't need to worry about which type a service is - if you can see it in your menu, you can use it. +When you use an service, it looks and feels like part of OpenRemote, but it's actually a separate application that's been integrated for you. --- @@ -38,7 +22,7 @@ You don't need to worry about which type a service is - if you can see it in you ### Where to Look -External services can appear in several places in the Manager UI: +services can appear in several places in the Manager UI: 1. **Main Menu** - Look for the `Services` menu item, this will show all services available to you @@ -53,13 +37,11 @@ If you see a service marked as unavailable, it might be updating or experiencing ### Opening a Service -To use an external service: +To use an service: 1. **Click** on the service name in your navigation menu 2. **Wait** for the service interface to load within OpenRemote -3. **Use** the service just like you would use any web application - -The service will open in the same browser window, but you'll see its own interface and controls. +3. **Use** the service just like you would use any other part of OpenRemote --- @@ -67,12 +49,12 @@ The service will open in the same browser window, but you'll see its own interfa ### Service Won't Load -If an external service isn't working: +If an service isn't working: 1. **Check the status** - Is it showing as "Available"? 2. **Refresh your browser** - Press F5 or click the refresh button 3. **Clear your browser cache** - This can solve many loading issues -4. **Check your internet connection** - External services may need internet access +4. **Check your internet connection** - services may need internet access 5. **Try a different browser** - Sometimes browser compatibility can cause issues ### Can't Access a Service @@ -103,6 +85,6 @@ If you need additional functionality that isn't currently available, contact you ## Summary -External services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. +services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. Don't hesitate to explore them or ask your administrator about what's available - they're there to make your work easier and more effective. \ No newline at end of file From 7f451de2b7f5268f4197f182c92f4db541b4e2a7 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 12:47:37 +0200 Subject: [PATCH 05/17] Match mermaid version with docusaurus version. --- package.json | 2 +- yarn.lock | 1135 ++------------------------------------------------ 2 files changed, 34 insertions(+), 1103 deletions(-) diff --git a/package.json b/package.json index b578012..ad95807 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "@docusaurus/core": "^3.7.0", "@docusaurus/preset-classic": "^3.7.0", - "@docusaurus/theme-mermaid": "^3.8.1", + "@docusaurus/theme-mermaid": "3.7.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-plugin-openapi-docs": "4.3.6", diff --git a/yarn.lock b/yarn.lock index 93e3172..e189e8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2880,7 +2880,7 @@ __metadata: languageName: node linkType: hard -"@braintree/sanitize-url@npm:^7.0.4": +"@braintree/sanitize-url@npm:^7.1.1": version: 7.1.1 resolution: "@braintree/sanitize-url@npm:7.1.1" checksum: 10/a8a5535c5a0a459ba593a018c554b35493dff004fd09d7147db67243df83bce3d410b89ee7dc2d95cce195b85b877c72f8ca149e1040110a945d193c67293af0 @@ -2946,16 +2946,6 @@ __metadata: languageName: node linkType: hard -"@csstools/cascade-layer-name-parser@npm:^2.0.5": - version: 2.0.5 - resolution: "@csstools/cascade-layer-name-parser@npm:2.0.5" - peerDependencies: - "@csstools/css-parser-algorithms": ^3.0.5 - "@csstools/css-tokenizer": ^3.0.4 - checksum: 10/fb26ae1db6f7a71ee0c3fdaea89f5325f88d7a0b2505fcf4b75e94f2c816ef1edb2961eecbc397df06f67d696ccc6bc99588ea9ee07dd7632bf10febf6b67ed9 - languageName: node - linkType: hard - "@csstools/color-helpers@npm:^5.0.1": version: 5.0.1 resolution: "@csstools/color-helpers@npm:5.0.1" @@ -2963,13 +2953,6 @@ __metadata: languageName: node linkType: hard -"@csstools/color-helpers@npm:^5.1.0": - version: 5.1.0 - resolution: "@csstools/color-helpers@npm:5.1.0" - checksum: 10/0138b3d5ccbe77aeccf6721fd008a53523c70e932f0c82dca24a1277ca780447e1d8357da47512ebf96358476f8764de57002f3e491920d67e69202f5a74c383 - languageName: node - linkType: hard - "@csstools/css-calc@npm:^2.1.1": version: 2.1.1 resolution: "@csstools/css-calc@npm:2.1.1" @@ -2980,16 +2963,6 @@ __metadata: languageName: node linkType: hard -"@csstools/css-calc@npm:^2.1.4": - version: 2.1.4 - resolution: "@csstools/css-calc@npm:2.1.4" - peerDependencies: - "@csstools/css-parser-algorithms": ^3.0.5 - "@csstools/css-tokenizer": ^3.0.4 - checksum: 10/06975b650c0f44c60eeb7afdb3fd236f2dd607b2c622e0bc908d3f54de39eb84e0692833320d03dac04bd6c1ab0154aa3fa0dd442bd9e5f917cf14d8e2ba8d74 - languageName: node - linkType: hard - "@csstools/css-color-parser@npm:^3.0.7": version: 3.0.7 resolution: "@csstools/css-color-parser@npm:3.0.7" @@ -3003,19 +2976,6 @@ __metadata: languageName: node linkType: hard -"@csstools/css-color-parser@npm:^3.1.0": - version: 3.1.0 - resolution: "@csstools/css-color-parser@npm:3.1.0" - dependencies: - "@csstools/color-helpers": "npm:^5.1.0" - "@csstools/css-calc": "npm:^2.1.4" - peerDependencies: - "@csstools/css-parser-algorithms": ^3.0.5 - "@csstools/css-tokenizer": ^3.0.4 - checksum: 10/4741095fdc4501e8e7ada4ed14fbf9dbbe6fea9b989818790ebca15657c29c62defbebacf18592cde2aa638a1d098bbe86d742d2c84ba932fbc00fac51cb8805 - languageName: node - linkType: hard - "@csstools/css-parser-algorithms@npm:^3.0.4": version: 3.0.4 resolution: "@csstools/css-parser-algorithms@npm:3.0.4" @@ -3025,15 +2985,6 @@ __metadata: languageName: node linkType: hard -"@csstools/css-parser-algorithms@npm:^3.0.5": - version: 3.0.5 - resolution: "@csstools/css-parser-algorithms@npm:3.0.5" - peerDependencies: - "@csstools/css-tokenizer": ^3.0.4 - checksum: 10/e93083b5cb36a3c1e7a47ce10cf62961d05bd1e4c608bb3ee50186ff740157ab0ec16a3956f7b86251efd10703034d849693201eea858ae904848c68d2d46ada - languageName: node - linkType: hard - "@csstools/css-tokenizer@npm:^3.0.3": version: 3.0.3 resolution: "@csstools/css-tokenizer@npm:3.0.3" @@ -3041,13 +2992,6 @@ __metadata: languageName: node linkType: hard -"@csstools/css-tokenizer@npm:^3.0.4": - version: 3.0.4 - resolution: "@csstools/css-tokenizer@npm:3.0.4" - checksum: 10/eb6c84c086312f6bb8758dfe2c85addd7475b0927333c5e39a4d59fb210b9810f8c346972046f95e60a721329cffe98895abe451e51de753ad1ca7a8c24ec65f - languageName: node - linkType: hard - "@csstools/media-query-list-parser@npm:^4.0.2": version: 4.0.2 resolution: "@csstools/media-query-list-parser@npm:4.0.2" @@ -3058,31 +3002,6 @@ __metadata: languageName: node linkType: hard -"@csstools/media-query-list-parser@npm:^4.0.3": - version: 4.0.3 - resolution: "@csstools/media-query-list-parser@npm:4.0.3" - peerDependencies: - "@csstools/css-parser-algorithms": ^3.0.5 - "@csstools/css-tokenizer": ^3.0.4 - checksum: 10/ac4e34c21a1c7fc8b788274f316c29ff2f07e7a08996d27b9beb06454666591be9946362c1b7c4d342558c278c8e7d0e35655043e47a9ffd94c3fdb292fbe020 - languageName: node - linkType: hard - -"@csstools/postcss-alpha-function@npm:^1.0.0": - version: 1.0.0 - resolution: "@csstools/postcss-alpha-function@npm:1.0.0" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/5f21212f88a0bb14efc6b2c60a7a5861bb192452fea1b8b53a9bddd6dbde46123fb8e6c01bd1808f52da83279478eb124ebb018855a9f56b389c8a1f70083408 - languageName: node - linkType: hard - "@csstools/postcss-cascade-layers@npm:^5.0.1": version: 5.0.1 resolution: "@csstools/postcss-cascade-layers@npm:5.0.1" @@ -3095,48 +3014,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-cascade-layers@npm:^5.0.2": - version: 5.0.2 - resolution: "@csstools/postcss-cascade-layers@npm:5.0.2" - dependencies: - "@csstools/selector-specificity": "npm:^5.0.0" - postcss-selector-parser: "npm:^7.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/9b73c28338f75eebd1032d6375e76547f90683806971f1dd3a47e6305901c89642094e1a80815fcfbb10b0afb61174f9ab3207db860a5841ca92ae993dc87cbe - languageName: node - linkType: hard - -"@csstools/postcss-color-function-display-p3-linear@npm:^1.0.0": - version: 1.0.0 - resolution: "@csstools/postcss-color-function-display-p3-linear@npm:1.0.0" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/869b7d8e238b277556112aa262e838a88bd83a5f25f0ac42825750bcd68cdcf7a0462b83eab6135b2999e2ece60ab82ddeb361565b5b2f1c6b3110ce769f5a9c - languageName: node - linkType: hard - -"@csstools/postcss-color-function@npm:^4.0.11": - version: 4.0.11 - resolution: "@csstools/postcss-color-function@npm:4.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/4af639ecea7ff4acf0cadd226361dcc044a051648b9b40f751e67ec8a81efea2038275d57fd1a292940b7cea17b6e9440b8ebcba17e721870e6ed0ca92be0307 - languageName: node - linkType: hard - "@csstools/postcss-color-function@npm:^4.0.7": version: 4.0.7 resolution: "@csstools/postcss-color-function@npm:4.0.7" @@ -3152,21 +3029,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-color-mix-function@npm:^3.0.11": - version: 3.0.11 - resolution: "@csstools/postcss-color-mix-function@npm:3.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/35e43134047ed55ce3c0a97c13065ec03220db6334bc068960dd050fa565ab38bee7dc75e666e3272905bbf39df470acec81bae2b90b11b0de5ff0eb6f513ab8 - languageName: node - linkType: hard - "@csstools/postcss-color-mix-function@npm:^3.0.7": version: 3.0.7 resolution: "@csstools/postcss-color-mix-function@npm:3.0.7" @@ -3182,21 +3044,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-color-mix-variadic-function-arguments@npm:^1.0.1": - version: 1.0.1 - resolution: "@csstools/postcss-color-mix-variadic-function-arguments@npm:1.0.1" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/128c3cb1bb8bcc45797bc363d116c6f318e109e096ef3e8b35c7725a6d83f8f44f785ab075ef84f2a95a6e07a692840d8ea9e41b48fb70b2fd97f6d163258949 - languageName: node - linkType: hard - "@csstools/postcss-content-alt-text@npm:^2.0.4": version: 2.0.4 resolution: "@csstools/postcss-content-alt-text@npm:2.0.4" @@ -3211,20 +3058,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-content-alt-text@npm:^2.0.7": - version: 2.0.7 - resolution: "@csstools/postcss-content-alt-text@npm:2.0.7" - dependencies: - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/7d5824e22edc9bba24a61a8b97ec4af7fc97a7c1d43d59244927ef50bb4a2d0fcb45373df8aa6b1c19324337737e862218327262865dbbe597c978c4290d090a - languageName: node - linkType: hard - "@csstools/postcss-exponential-functions@npm:^2.0.6": version: 2.0.6 resolution: "@csstools/postcss-exponential-functions@npm:2.0.6" @@ -3238,19 +3071,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-exponential-functions@npm:^2.0.9": - version: 2.0.9 - resolution: "@csstools/postcss-exponential-functions@npm:2.0.9" - dependencies: - "@csstools/css-calc": "npm:^2.1.4" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 10/80d5847d747fc67c32ee3ba49f9c9290654fb086c58b2f13256b14124b7349dac68ba8e107f631248cef2448ca57ef18adbbbc816dd63a54ba91826345373f39 - languageName: node - linkType: hard - "@csstools/postcss-font-format-keywords@npm:^4.0.0": version: 4.0.0 resolution: "@csstools/postcss-font-format-keywords@npm:4.0.0" @@ -3263,19 +3083,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-gamut-mapping@npm:^2.0.11": - version: 2.0.11 - resolution: "@csstools/postcss-gamut-mapping@npm:2.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 10/be4cb5a14eef78acbd9dfca7cdad0ab4e8e4a11c9e8bbb27e427bfd276fd5d3aa37bc1bf36deb040d404398989a3123bd70fc51be970c4d944cf6a18d231c1b8 - languageName: node - linkType: hard - "@csstools/postcss-gamut-mapping@npm:^2.0.7": version: 2.0.7 resolution: "@csstools/postcss-gamut-mapping@npm:2.0.7" @@ -3289,21 +3096,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-gradients-interpolation-method@npm:^5.0.11": - version: 5.0.11 - resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/a45d9ca26706e15a5fc47da304f19d52db33672ab88f79f3fb41dfcc4247ee46dcd0bce4ef4d480867690593fb04e3b34eea3c724d4b5e514646cb328a16c271 - languageName: node - linkType: hard - "@csstools/postcss-gradients-interpolation-method@npm:^5.0.7": version: 5.0.7 resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.7" @@ -3319,21 +3111,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-hwb-function@npm:^4.0.11": - version: 4.0.11 - resolution: "@csstools/postcss-hwb-function@npm:4.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/aa29efe8a57c5bbabbc2d4a69c98d60d599422dfeb436d604b33147657ae68b377ba545c348d79f232d3cce632cca79fa07b0f54e578b7fc8b70a48c7a530a00 - languageName: node - linkType: hard - "@csstools/postcss-hwb-function@npm:^4.0.7": version: 4.0.7 resolution: "@csstools/postcss-hwb-function@npm:4.0.7" @@ -3362,19 +3139,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-ic-unit@npm:^4.0.3": - version: 4.0.3 - resolution: "@csstools/postcss-ic-unit@npm:4.0.3" - dependencies: - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/ca03e600639c79c740312a83274eb86c98e8e61733eb5a5e7b9f9d7c03d17840e6c2f02b2f65625cafc58976552e88a61498b5fb818142f229b9f6a52f055714 - languageName: node - linkType: hard - "@csstools/postcss-initial@npm:^2.0.1": version: 2.0.1 resolution: "@csstools/postcss-initial@npm:2.0.1" @@ -3396,32 +3160,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-is-pseudo-class@npm:^5.0.3": - version: 5.0.3 - resolution: "@csstools/postcss-is-pseudo-class@npm:5.0.3" - dependencies: - "@csstools/selector-specificity": "npm:^5.0.0" - postcss-selector-parser: "npm:^7.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/99abf2595c3b92ba993f26704c622837ec7546832037f75778d74a2c3d2e5009a027e52178d7f5c967d9c0dcda44244db9a8131c51e42fcbf4a0c22f21b3b1b6 - languageName: node - linkType: hard - -"@csstools/postcss-light-dark-function@npm:^2.0.10": - version: 2.0.10 - resolution: "@csstools/postcss-light-dark-function@npm:2.0.10" - dependencies: - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/b35170c2dccc243e8dbed1824e80fc9ecee8b681c75d4ad1dd2623dbcea71b944e7d98940ba018be7f6f15332b6f30e6add9700540e9625b8560cf0f7f99e2b4 - languageName: node - linkType: hard - "@csstools/postcss-light-dark-function@npm:^2.0.7": version: 2.0.7 resolution: "@csstools/postcss-light-dark-function@npm:2.0.7" @@ -3486,18 +3224,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-logical-viewport-units@npm:^3.0.4": - version: 3.0.4 - resolution: "@csstools/postcss-logical-viewport-units@npm:3.0.4" - dependencies: - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/ddb8d9b473c55cce1c1261652d657d33d9306d80112eac578d53b05dd48a5607ea2064fcf6bc298ccc1e63143e11517d35230bad6063dae14d445530c45a81ec - languageName: node - linkType: hard - "@csstools/postcss-media-minmax@npm:^2.0.6": version: 2.0.6 resolution: "@csstools/postcss-media-minmax@npm:2.0.6" @@ -3512,20 +3238,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-media-minmax@npm:^2.0.9": - version: 2.0.9 - resolution: "@csstools/postcss-media-minmax@npm:2.0.9" - dependencies: - "@csstools/css-calc": "npm:^2.1.4" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/media-query-list-parser": "npm:^4.0.3" - peerDependencies: - postcss: ^8.4 - checksum: 10/ddd35129dc482a2cffe44cc75c48844cee56370f551e7e3abcfa0a158c3a2a48d8a2196e82e223fdf794a066688d423558e211f69010cfbc6044c989464d3899 - languageName: node - linkType: hard - "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^3.0.4": version: 3.0.4 resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:3.0.4" @@ -3539,19 +3251,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-media-queries-aspect-ratio-number-values@npm:^3.0.5": - version: 3.0.5 - resolution: "@csstools/postcss-media-queries-aspect-ratio-number-values@npm:3.0.5" - dependencies: - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/media-query-list-parser": "npm:^4.0.3" - peerDependencies: - postcss: ^8.4 - checksum: 10/b0124a071c7880327b23ebcd77e2c74594a852bf9193f2f552630d9e8b0996789884c05cf4ebff4dbf5c3bfb5e6cb70e9e52a740f150034bfae87208898d3d9d - languageName: node - linkType: hard - "@csstools/postcss-nested-calc@npm:^4.0.0": version: 4.0.0 resolution: "@csstools/postcss-nested-calc@npm:4.0.0" @@ -3575,21 +3274,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-oklab-function@npm:^4.0.11": - version: 4.0.11 - resolution: "@csstools/postcss-oklab-function@npm:4.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/dd2bbbd449fa0dcbcc7549a76df663ea9df1ba632dae9966467490727092e0b827bee62daa1e9328ec925a58610bfd28c0b473e76471d1bb0f5c0cfb810c9d8a - languageName: node - linkType: hard - "@csstools/postcss-oklab-function@npm:^4.0.7": version: 4.0.7 resolution: "@csstools/postcss-oklab-function@npm:4.0.7" @@ -3616,17 +3300,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-progressive-custom-properties@npm:^4.2.0": - version: 4.2.0 - resolution: "@csstools/postcss-progressive-custom-properties@npm:4.2.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/b862dbfc9590f2e0714e1331c982d6418e94647ff0d6c558e4f4baf6fc6c8f5175b8e5a0f7e90072ec607aa1ead6b6205703efbf42aa1b7c5431b74a69649f3f - languageName: node - linkType: hard - "@csstools/postcss-random-function@npm:^1.0.2": version: 1.0.2 resolution: "@csstools/postcss-random-function@npm:1.0.2" @@ -3640,34 +3313,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-random-function@npm:^2.0.1": - version: 2.0.1 - resolution: "@csstools/postcss-random-function@npm:2.0.1" - dependencies: - "@csstools/css-calc": "npm:^2.1.4" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 10/d421a790b11675edf493f3e48259636beca164c494ed2883042118b35674d26f04e1a46f9e89203a179e20acc2a1f5912078ec81b330a2c1a1abef7e7387e587 - languageName: node - linkType: hard - -"@csstools/postcss-relative-color-syntax@npm:^3.0.11": - version: 3.0.11 - resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/4a3db576a595d18475ccfc649a1516df30683cbe8fc485ea0fb4633855598223bd42c4e0e5a8dc048b6dc9d652c815e08a621adba9f1c3842291d55ffc5b43ce - languageName: node - linkType: hard - "@csstools/postcss-relative-color-syntax@npm:^3.0.7": version: 3.0.7 resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.7" @@ -3707,19 +3352,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-sign-functions@npm:^1.1.4": - version: 1.1.4 - resolution: "@csstools/postcss-sign-functions@npm:1.1.4" - dependencies: - "@csstools/css-calc": "npm:^2.1.4" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 10/0afcb008142a0a41df51267d79cf950f4f314394dca7c041e3a0be87df56517ac5400861630a979b5bef49f01c296025106622110384039e3c8f82802d6adcde - languageName: node - linkType: hard - "@csstools/postcss-stepped-value-functions@npm:^4.0.6": version: 4.0.6 resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.6" @@ -3733,19 +3365,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-stepped-value-functions@npm:^4.0.9": - version: 4.0.9 - resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.9" - dependencies: - "@csstools/css-calc": "npm:^2.1.4" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 10/6465a883be42d4cc4a4e83be2626a1351de4bfe84a63641c53e7c39d3c0e109152489ca2d8235625cdf6726341c676b9fbbca18fe80bb5eae8d488a0e42fc5e4 - languageName: node - linkType: hard - "@csstools/postcss-text-decoration-shorthand@npm:^4.0.1": version: 4.0.1 resolution: "@csstools/postcss-text-decoration-shorthand@npm:4.0.1" @@ -3758,18 +3377,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-text-decoration-shorthand@npm:^4.0.3": - version: 4.0.3 - resolution: "@csstools/postcss-text-decoration-shorthand@npm:4.0.3" - dependencies: - "@csstools/color-helpers": "npm:^5.1.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/afc350e389bae7fdceecb3876b9be00bdbd56e5f43054f9f5de2d42b3c55a163e5ba737212030479389c9c1fca5d066f5b051da1fdf72e13191a035d2cc6f4e0 - languageName: node - linkType: hard - "@csstools/postcss-trigonometric-functions@npm:^4.0.6": version: 4.0.6 resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.6" @@ -3783,19 +3390,6 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-trigonometric-functions@npm:^4.0.9": - version: 4.0.9 - resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.9" - dependencies: - "@csstools/css-calc": "npm:^2.1.4" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - peerDependencies: - postcss: ^8.4 - checksum: 10/c746cd986df061a87de4f2d0129aa2d2e98a2948e5005fe6fe419a9e9ec7a0f7382461847cbd3f67f8f66169bdf23a1d7f53ca6b9922ddd235ec45f2867a8825 - languageName: node - linkType: hard - "@csstools/postcss-unset-value@npm:^4.0.0": version: 4.0.0 resolution: "@csstools/postcss-unset-value@npm:4.0.0" @@ -3814,15 +3408,6 @@ __metadata: languageName: node linkType: hard -"@csstools/selector-resolve-nested@npm:^3.1.0": - version: 3.1.0 - resolution: "@csstools/selector-resolve-nested@npm:3.1.0" - peerDependencies: - postcss-selector-parser: ^7.0.0 - checksum: 10/eaad6a6c99345cae2849a2c73daf53381fabd75851eefd830ee743e4d454d4e2930aa99c8b9e651fed92b9a8361f352c6c754abf82c576bba4953f1e59c927e9 - languageName: node - linkType: hard - "@csstools/selector-specificity@npm:^5.0.0": version: 5.0.0 resolution: "@csstools/selector-specificity@npm:5.0.0" @@ -3904,29 +3489,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/babel@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/babel@npm:3.8.1" - dependencies: - "@babel/core": "npm:^7.25.9" - "@babel/generator": "npm:^7.25.9" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" - "@babel/plugin-transform-runtime": "npm:^7.25.9" - "@babel/preset-env": "npm:^7.25.9" - "@babel/preset-react": "npm:^7.25.9" - "@babel/preset-typescript": "npm:^7.25.9" - "@babel/runtime": "npm:^7.25.9" - "@babel/runtime-corejs3": "npm:^7.25.9" - "@babel/traverse": "npm:^7.25.9" - "@docusaurus/logger": "npm:3.8.1" - "@docusaurus/utils": "npm:3.8.1" - babel-plugin-dynamic-import-node: "npm:^2.3.3" - fs-extra: "npm:^11.1.1" - tslib: "npm:^2.6.0" - checksum: 10/3294c488a3d6ee4569ecc7782b200af52fbfbb3f5a3dce505f8e7d2bd5979c206a245e7fb83ac1302b23035075b5c09943049f8cfe0a582032fcb9542ea180a0 - languageName: node - linkType: hard - "@docusaurus/bundler@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/bundler@npm:3.7.0" @@ -3965,43 +3527,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/bundler@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/bundler@npm:3.8.1" - dependencies: - "@babel/core": "npm:^7.25.9" - "@docusaurus/babel": "npm:3.8.1" - "@docusaurus/cssnano-preset": "npm:3.8.1" - "@docusaurus/logger": "npm:3.8.1" - "@docusaurus/types": "npm:3.8.1" - "@docusaurus/utils": "npm:3.8.1" - babel-loader: "npm:^9.2.1" - clean-css: "npm:^5.3.3" - copy-webpack-plugin: "npm:^11.0.0" - css-loader: "npm:^6.11.0" - css-minimizer-webpack-plugin: "npm:^5.0.1" - cssnano: "npm:^6.1.2" - file-loader: "npm:^6.2.0" - html-minifier-terser: "npm:^7.2.0" - mini-css-extract-plugin: "npm:^2.9.2" - null-loader: "npm:^4.0.1" - postcss: "npm:^8.5.4" - postcss-loader: "npm:^7.3.4" - postcss-preset-env: "npm:^10.2.1" - terser-webpack-plugin: "npm:^5.3.9" - tslib: "npm:^2.6.0" - url-loader: "npm:^4.1.1" - webpack: "npm:^5.95.0" - webpackbar: "npm:^6.0.1" - peerDependencies: - "@docusaurus/faster": "*" - peerDependenciesMeta: - "@docusaurus/faster": - optional: true - checksum: 10/2373bb954020185e97c74a35de4c89f70f603bf1cd6679dcd4d04a5dfaf7336a9d42fb303d221c3b1c540dbd4f5b97a380e570d3e1971a9acd456d151798d1dd - languageName: node - linkType: hard - "@docusaurus/core@npm:3.7.0, @docusaurus/core@npm:^3.7.0": version: 3.7.0 resolution: "@docusaurus/core@npm:3.7.0" @@ -4058,62 +3583,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/core@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/core@npm:3.8.1" - dependencies: - "@docusaurus/babel": "npm:3.8.1" - "@docusaurus/bundler": "npm:3.8.1" - "@docusaurus/logger": "npm:3.8.1" - "@docusaurus/mdx-loader": "npm:3.8.1" - "@docusaurus/utils": "npm:3.8.1" - "@docusaurus/utils-common": "npm:3.8.1" - "@docusaurus/utils-validation": "npm:3.8.1" - boxen: "npm:^6.2.1" - chalk: "npm:^4.1.2" - chokidar: "npm:^3.5.3" - cli-table3: "npm:^0.6.3" - combine-promises: "npm:^1.1.0" - commander: "npm:^5.1.0" - core-js: "npm:^3.31.1" - detect-port: "npm:^1.5.1" - escape-html: "npm:^1.0.3" - eta: "npm:^2.2.0" - eval: "npm:^0.1.8" - execa: "npm:5.1.1" - fs-extra: "npm:^11.1.1" - html-tags: "npm:^3.3.1" - html-webpack-plugin: "npm:^5.6.0" - leven: "npm:^3.1.0" - lodash: "npm:^4.17.21" - open: "npm:^8.4.0" - p-map: "npm:^4.0.0" - prompts: "npm:^2.4.2" - react-helmet-async: "npm:@slorber/react-helmet-async@1.3.0" - react-loadable: "npm:@docusaurus/react-loadable@6.0.0" - react-loadable-ssr-addon-v5-slorber: "npm:^1.0.1" - react-router: "npm:^5.3.4" - react-router-config: "npm:^5.1.1" - react-router-dom: "npm:^5.3.4" - semver: "npm:^7.5.4" - serve-handler: "npm:^6.1.6" - tinypool: "npm:^1.0.2" - tslib: "npm:^2.6.0" - update-notifier: "npm:^6.0.2" - webpack: "npm:^5.95.0" - webpack-bundle-analyzer: "npm:^4.10.2" - webpack-dev-server: "npm:^4.15.2" - webpack-merge: "npm:^6.0.1" - peerDependencies: - "@mdx-js/react": ^3.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - bin: - docusaurus: bin/docusaurus.mjs - checksum: 10/278f194d0107ebe07169845293dc01dda3fa816881dffd1a02b5481676f97e69d2801819765baec4ea5a44101ce1cf82b34e17083754878ce886f6203cd61cb2 - languageName: node - linkType: hard - "@docusaurus/cssnano-preset@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/cssnano-preset@npm:3.7.0" @@ -4126,18 +3595,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/cssnano-preset@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/cssnano-preset@npm:3.8.1" - dependencies: - cssnano-preset-advanced: "npm:^6.1.2" - postcss: "npm:^8.5.4" - postcss-sort-media-queries: "npm:^5.2.0" - tslib: "npm:^2.6.0" - checksum: 10/4fada596bedf182007ec12ca4e4af373fa7763724d9219ea695a71f9325f2984c0b76a4dbbeb39f2fea14b174ff7e285915c463156f7cd02fe583c44e361c2ba - languageName: node - linkType: hard - "@docusaurus/logger@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/logger@npm:3.7.0" @@ -4148,16 +3605,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/logger@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/logger@npm:3.8.1" - dependencies: - chalk: "npm:^4.1.2" - tslib: "npm:^2.6.0" - checksum: 10/040d67de6d096fa251f277c3fc343797030a19858de4da08bd3dc304c2f5261ed03aa95b17d4d0b53a3b48681e7f867745db6a66855c2c6ab78852598e187821 - languageName: node - linkType: hard - "@docusaurus/mdx-loader@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/mdx-loader@npm:3.7.0" @@ -4193,41 +3640,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/mdx-loader@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/mdx-loader@npm:3.8.1" - dependencies: - "@docusaurus/logger": "npm:3.8.1" - "@docusaurus/utils": "npm:3.8.1" - "@docusaurus/utils-validation": "npm:3.8.1" - "@mdx-js/mdx": "npm:^3.0.0" - "@slorber/remark-comment": "npm:^1.0.0" - escape-html: "npm:^1.0.3" - estree-util-value-to-estree: "npm:^3.0.1" - file-loader: "npm:^6.2.0" - fs-extra: "npm:^11.1.1" - image-size: "npm:^2.0.2" - mdast-util-mdx: "npm:^3.0.0" - mdast-util-to-string: "npm:^4.0.0" - rehype-raw: "npm:^7.0.0" - remark-directive: "npm:^3.0.0" - remark-emoji: "npm:^4.0.0" - remark-frontmatter: "npm:^5.0.0" - remark-gfm: "npm:^4.0.0" - stringify-object: "npm:^3.3.0" - tslib: "npm:^2.6.0" - unified: "npm:^11.0.3" - unist-util-visit: "npm:^5.0.0" - url-loader: "npm:^4.1.1" - vfile: "npm:^6.0.1" - webpack: "npm:^5.88.1" - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - checksum: 10/7d59879ff5a39947b9cfd28e58217af4e86963fcf1ecfbe2809a9edacb459e5d439fec70770dd8907d06cddfddcbea8eb93ca1fd2ede401569e72a3f6e5446f5 - languageName: node - linkType: hard - "@docusaurus/module-type-aliases@npm:3.7.0, @docusaurus/module-type-aliases@npm:^3.7.0": version: 3.7.0 resolution: "@docusaurus/module-type-aliases@npm:3.7.0" @@ -4246,24 +3658,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/module-type-aliases@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/module-type-aliases@npm:3.8.1" - dependencies: - "@docusaurus/types": "npm:3.8.1" - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - "@types/react-router-config": "npm:*" - "@types/react-router-dom": "npm:*" - react-helmet-async: "npm:@slorber/react-helmet-async@1.3.0" - react-loadable: "npm:@docusaurus/react-loadable@6.0.0" - peerDependencies: - react: "*" - react-dom: "*" - checksum: 10/db3a23763837fc0155ec25053d36179ddd213cca2aeba5f1372894f0e5be82719d625f53057d5e9c5cef1fa862e37fe754a1a83a3005c9e1d22b6bb80c313024 - languageName: node - linkType: hard - "@docusaurus/plugin-content-blog@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/plugin-content-blog@npm:3.7.0" @@ -4525,49 +3919,25 @@ __metadata: "@docusaurus/plugin-content-docs": "*" react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - checksum: 10/9b27a2c74f63cf398b81357301ae4e0d35ea1c478ce495a909609c51e22b81e9e925261b90e999e7f4820599a0a3576ab797b6a14df52d64729b29b04af6e10c - languageName: node - linkType: hard - -"@docusaurus/theme-common@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/theme-common@npm:3.8.1" - dependencies: - "@docusaurus/mdx-loader": "npm:3.8.1" - "@docusaurus/module-type-aliases": "npm:3.8.1" - "@docusaurus/utils": "npm:3.8.1" - "@docusaurus/utils-common": "npm:3.8.1" - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - "@types/react-router-config": "npm:*" - clsx: "npm:^2.0.0" - parse-numeric-range: "npm:^1.3.0" - prism-react-renderer: "npm:^2.3.0" - tslib: "npm:^2.6.0" - utility-types: "npm:^3.10.0" - peerDependencies: - "@docusaurus/plugin-content-docs": "*" - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - checksum: 10/c1a1ebe04991b7c23bdda91378c0550e0a046e13fb0c2885a7e18305c8662f294b87ac5bd74ad0c307be30b6e17e74e9a76ae911ee10124287f2ce55a7f0501c + checksum: 10/9b27a2c74f63cf398b81357301ae4e0d35ea1c478ce495a909609c51e22b81e9e925261b90e999e7f4820599a0a3576ab797b6a14df52d64729b29b04af6e10c languageName: node linkType: hard -"@docusaurus/theme-mermaid@npm:^3.8.1": - version: 3.8.1 - resolution: "@docusaurus/theme-mermaid@npm:3.8.1" +"@docusaurus/theme-mermaid@npm:3.7.0": + version: 3.7.0 + resolution: "@docusaurus/theme-mermaid@npm:3.7.0" dependencies: - "@docusaurus/core": "npm:3.8.1" - "@docusaurus/module-type-aliases": "npm:3.8.1" - "@docusaurus/theme-common": "npm:3.8.1" - "@docusaurus/types": "npm:3.8.1" - "@docusaurus/utils-validation": "npm:3.8.1" - mermaid: "npm:>=11.6.0" + "@docusaurus/core": "npm:3.7.0" + "@docusaurus/module-type-aliases": "npm:3.7.0" + "@docusaurus/theme-common": "npm:3.7.0" + "@docusaurus/types": "npm:3.7.0" + "@docusaurus/utils-validation": "npm:3.7.0" + mermaid: "npm:>=10.4" tslib: "npm:^2.6.0" peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 - checksum: 10/0c0f5d13197d08c7dd8d4ea974d6760299419225c4d3ba508523882c52c2ac71c78ee4ddc967a9f1748f7be39aa67aea220e229586f4c045ca88516b365fc308 + checksum: 10/5d17eb7e531db050bcb0a4d6bc8779bf5934ecaca8166c27e2e8b273a3965b4c2238c099d06795b30a47b62c113c6b457caa6bf36fd40d3a16f5d154d67a4c88 languageName: node linkType: hard @@ -4635,26 +4005,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/types@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/types@npm:3.8.1" - dependencies: - "@mdx-js/mdx": "npm:^3.0.0" - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - commander: "npm:^5.1.0" - joi: "npm:^17.9.2" - react-helmet-async: "npm:@slorber/react-helmet-async@1.3.0" - utility-types: "npm:^3.10.0" - webpack: "npm:^5.95.0" - webpack-merge: "npm:^5.9.0" - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - checksum: 10/5c1ad8f888d650edd56f57284a6913c2f69aaa201b4757c81f1e71ee41b3b9957db7a1d3f1d243326d1000d3b3157487f91ca1f3a7c1970ea2aa18f88aa4b011 - languageName: node - linkType: hard - "@docusaurus/utils-common@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/utils-common@npm:3.7.0" @@ -4665,16 +4015,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/utils-common@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/utils-common@npm:3.8.1" - dependencies: - "@docusaurus/types": "npm:3.8.1" - tslib: "npm:^2.6.0" - checksum: 10/2720815d2b96a9d419a3355ce727bb5bf4bd0554f3aab6f62f2510d77b30724745b08da4ebaae7c4d409af0192d92052718d9059902b3a37d83606ade80a62ac - languageName: node - linkType: hard - "@docusaurus/utils-validation@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/utils-validation@npm:3.7.0" @@ -4691,22 +4031,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/utils-validation@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/utils-validation@npm:3.8.1" - dependencies: - "@docusaurus/logger": "npm:3.8.1" - "@docusaurus/utils": "npm:3.8.1" - "@docusaurus/utils-common": "npm:3.8.1" - fs-extra: "npm:^11.2.0" - joi: "npm:^17.9.2" - js-yaml: "npm:^4.1.0" - lodash: "npm:^4.17.21" - tslib: "npm:^2.6.0" - checksum: 10/00982c3df405376576e32e96f9a056d2483ef589cadd1b5707e473b52a454567e926e461024b0d9bde4c307172eace5d8a27b0030fd2fefe238d2415360caa9a - languageName: node - linkType: hard - "@docusaurus/utils@npm:3.7.0": version: 3.7.0 resolution: "@docusaurus/utils@npm:3.7.0" @@ -4735,35 +4059,6 @@ __metadata: languageName: node linkType: hard -"@docusaurus/utils@npm:3.8.1": - version: 3.8.1 - resolution: "@docusaurus/utils@npm:3.8.1" - dependencies: - "@docusaurus/logger": "npm:3.8.1" - "@docusaurus/types": "npm:3.8.1" - "@docusaurus/utils-common": "npm:3.8.1" - escape-string-regexp: "npm:^4.0.0" - execa: "npm:5.1.1" - file-loader: "npm:^6.2.0" - fs-extra: "npm:^11.1.1" - github-slugger: "npm:^1.5.0" - globby: "npm:^11.1.0" - gray-matter: "npm:^4.0.3" - jiti: "npm:^1.20.0" - js-yaml: "npm:^4.1.0" - lodash: "npm:^4.17.21" - micromatch: "npm:^4.0.5" - p-queue: "npm:^6.6.2" - prompts: "npm:^2.4.2" - resolve-pathname: "npm:^3.0.0" - tslib: "npm:^2.6.0" - url-loader: "npm:^4.1.1" - utility-types: "npm:^3.10.0" - webpack: "npm:^5.88.1" - checksum: 10/c9622591dd5f76c6dd50711bdf0dbcda26ce68ef676092900f33052bea897ffe6b60d2a25d425364a5ddfe3531b7f4759865dc4463118c2f954a6a82fadd23ec - languageName: node - linkType: hard - "@exodus/schemasafe@npm:^1.0.0-rc.2": version: 1.3.0 resolution: "@exodus/schemasafe@npm:1.3.0" @@ -6980,24 +6275,6 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.21": - version: 10.4.21 - resolution: "autoprefixer@npm:10.4.21" - dependencies: - browserslist: "npm:^4.24.4" - caniuse-lite: "npm:^1.0.30001702" - fraction.js: "npm:^4.3.7" - normalize-range: "npm:^0.1.2" - picocolors: "npm:^1.1.1" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.1.0 - bin: - autoprefixer: bin/autoprefixer - checksum: 10/5d7aeee78ef362a6838e12312908516a8ac5364414175273e5cff83bbff67612755b93d567f3aa01ce318342df48aeab4b291847b5800c780e58c458f61a98a6 - languageName: node - linkType: hard - "babel-loader@npm:^9.2.1": version: 9.2.1 resolution: "babel-loader@npm:9.2.1" @@ -7101,15 +6378,6 @@ __metadata: languageName: node linkType: hard -"baseline-browser-mapping@npm:^2.8.2": - version: 2.8.4 - resolution: "baseline-browser-mapping@npm:2.8.4" - bin: - baseline-browser-mapping: dist/cli.js - checksum: 10/97073cf5a009225f3ecb9dc6d480ce0c5c9cba5cc5cb152a1c02dad0d91fd676a1c9ebeb9707839a71b4de5d21ad4750978d9d9864372cb9f9ae508b95a4d4b6 - languageName: node - linkType: hard - "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -7256,21 +6524,6 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.25.1": - version: 4.26.0 - resolution: "browserslist@npm:4.26.0" - dependencies: - baseline-browser-mapping: "npm:^2.8.2" - caniuse-lite: "npm:^1.0.30001741" - electron-to-chromium: "npm:^1.5.218" - node-releases: "npm:^2.0.21" - update-browserslist-db: "npm:^1.1.3" - bin: - browserslist: cli.js - checksum: 10/8a354003e826e67085d1982836318387942981645a6afaa1111cfd06e176d8ddc99a13122e345b49a01c582070c892774384e479a65db0d3207dcc39f44d4b30 - languageName: node - linkType: hard - "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -7434,13 +6687,6 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001741": - version: 1.0.30001741 - resolution: "caniuse-lite@npm:1.0.30001741" - checksum: 10/d2fbc09f0941653d40b591e5460c4eb0b6838790d4adac7890a453dfb3b5e6bb84a64de2aded96cb4c6636816ef613614e07d9efb5818adaba566321feb95ffd - languageName: node - linkType: hard - "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -7621,7 +6867,7 @@ __metadata: languageName: node linkType: hard -"clean-css@npm:^5.2.2, clean-css@npm:^5.3.2, clean-css@npm:^5.3.3, clean-css@npm:~5.3.2": +"clean-css@npm:^5.2.2, clean-css@npm:^5.3.2, clean-css@npm:~5.3.2": version: 5.3.3 resolution: "clean-css@npm:5.3.3" dependencies: @@ -8135,20 +7381,7 @@ __metadata: languageName: node linkType: hard -"css-has-pseudo@npm:^7.0.3": - version: 7.0.3 - resolution: "css-has-pseudo@npm:7.0.3" - dependencies: - "@csstools/selector-specificity": "npm:^5.0.0" - postcss-selector-parser: "npm:^7.0.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/ffda6490aacb2903803f7d6c7b3ee41e654a3e9084c5eb0cf8f7602cf49ebce1b7891962016f622c36c67f4f685ed57628e7a0efbdba8cc55c5bf76ed58267d8 - languageName: node - linkType: hard - -"css-loader@npm:^6.11.0, css-loader@npm:^6.8.1": +"css-loader@npm:^6.8.1": version: 6.11.0 resolution: "css-loader@npm:6.11.0" dependencies: @@ -8270,13 +7503,6 @@ __metadata: languageName: node linkType: hard -"cssdb@npm:^8.4.0": - version: 8.4.0 - resolution: "cssdb@npm:8.4.0" - checksum: 10/db3e249b7b4ae7cbcde6a0a924ed054acc234449e6e19b6338635967a4c2b4aeb63415fb117071fa751f7a3dde664413192bafe1595324124f7efcdd27fb680f - languageName: node - linkType: hard - "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -8772,7 +7998,7 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.11.13": +"dayjs@npm:^1.11.18": version: 1.11.18 resolution: "dayjs@npm:1.11.18" checksum: 10/7d29a90834cf4da2feb437c2f34b8235c3f94493a06d2f1bf9f506f1fa49eadf796f26e1d685b9fe8cb5e75ce6ee067825115e196f1af3d07b3552ff857bfc39 @@ -9151,14 +8377,14 @@ __metadata: linkType: hard "dompurify@npm:^3.2.5": - version: 3.2.6 - resolution: "dompurify@npm:3.2.6" + version: 3.2.7 + resolution: "dompurify@npm:3.2.7" dependencies: "@types/trusted-types": "npm:^2.0.7" dependenciesMeta: "@types/trusted-types": optional: true - checksum: 10/b91631ed0e4d17fae950ef53613cc009ed7e73adc43ac94a41dd52f35483f7538d13caebdafa7626e0da145fc8184e7ac7935f14f25b7e841b32fda777e40447 + checksum: 10/51b7866fb834ee62d6c415f41ece5ce11db7b463f60a822932a1f832573a40b98be7715550298690e7647988fbe086db1098bda9b10548b3166fc975eb9bd849 languageName: node linkType: hard @@ -9242,13 +8468,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.5.218": - version: 1.5.218 - resolution: "electron-to-chromium@npm:1.5.218" - checksum: 10/2e661373bc2184b1c330119d9ff73ed5498ee1442866061caa94550ed89d57d56fa45ccec6e13bec8af2db1eecb25c4e590860de2067e2c5cafaf847413048ef - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.5.73": version: 1.5.101 resolution: "electron-to-chromium@npm:1.5.101" @@ -9600,7 +8819,7 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.4": +"eventemitter3@npm:^4.0.0": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" checksum: 10/8030029382404942c01d0037079f1b1bc8fed524b5849c237b80549b01e2fc49709e1d0c557fa65ca4498fc9e24cff1475ef7b855121fcc15f9d61f93e282346 @@ -9614,7 +8833,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:5.1.1, execa@npm:^5.0.0": +"execa@npm:^5.0.0": version: 5.1.1 resolution: "execa@npm:5.1.1" dependencies: @@ -10969,15 +10188,6 @@ __metadata: languageName: node linkType: hard -"image-size@npm:^2.0.2": - version: 2.0.2 - resolution: "image-size@npm:2.0.2" - bin: - image-size: bin/image-size.js - checksum: 10/d15203279fe7ada01252d8c56ba97516385d6d5ac2cbf3d734580fc88db4f5272b9b3f7f378ad63abc7d06b5500c43b90d9f84626e2bda1cab403c16eb469592 - languageName: node - linkType: hard - "immer@npm:^9.0.21, immer@npm:^9.0.7": version: 9.0.21 resolution: "immer@npm:9.0.21" @@ -11994,12 +11204,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:^15.0.7": - version: 15.0.12 - resolution: "marked@npm:15.0.12" +"marked@npm:^16.2.1": + version: 16.3.0 + resolution: "marked@npm:16.3.0" bin: marked: bin/marked.js - checksum: 10/deeb619405c0c46af00c99b18b3365450abeb309104b24e3658f46142344f6b7c4117608c3b5834084d8738e92f81240c19f596e6ee369260f96e52b3457eaee + checksum: 10/60497834b9acfb3b3994222509d359ecb9a197c885dfeb77e2050a287cd2f4ab19f00d5597172b47f9e0c54d9e1e13d8b2dd73322b7838599e1f16d1d6283f5b languageName: node linkType: hard @@ -12478,11 +11688,11 @@ __metadata: languageName: node linkType: hard -"mermaid@npm:>=11.6.0": - version: 11.11.0 - resolution: "mermaid@npm:11.11.0" +"mermaid@npm:>=10.4": + version: 11.12.0 + resolution: "mermaid@npm:11.12.0" dependencies: - "@braintree/sanitize-url": "npm:^7.0.4" + "@braintree/sanitize-url": "npm:^7.1.1" "@iconify/utils": "npm:^3.0.1" "@mermaid-js/parser": "npm:^0.6.2" "@types/d3": "npm:^7.4.3" @@ -12492,17 +11702,17 @@ __metadata: d3: "npm:^7.9.0" d3-sankey: "npm:^0.12.3" dagre-d3-es: "npm:7.0.11" - dayjs: "npm:^1.11.13" + dayjs: "npm:^1.11.18" dompurify: "npm:^3.2.5" katex: "npm:^0.16.22" khroma: "npm:^2.1.0" lodash-es: "npm:^4.17.21" - marked: "npm:^15.0.7" + marked: "npm:^16.2.1" roughjs: "npm:^4.6.6" stylis: "npm:^4.3.6" ts-dedent: "npm:^2.2.0" uuid: "npm:^11.1.0" - checksum: 10/7ef3f12c64e517204ee30e80ffdfe48c661c1830f32848780e28978757196efb61eea233694b57a1962ad89470e117403edb663365363be416acb8eb4a6fdfab + checksum: 10/2f15c62f5150282127374a0579ec2a91531eb30d86b8f0a28d492787f6034400ec41d0149f66713b1335e4e3747ca17f7995f994ab357db928839cd505d0bd36 languageName: node linkType: hard @@ -13415,18 +12625,6 @@ __metadata: languageName: node linkType: hard -"mini-css-extract-plugin@npm:^2.9.2": - version: 2.9.4 - resolution: "mini-css-extract-plugin@npm:2.9.4" - dependencies: - schema-utils: "npm:^4.0.0" - tapable: "npm:^2.2.1" - peerDependencies: - webpack: ^5.0.0 - checksum: 10/24a0418dc49baed58a10a8b81e4d2fe474e89ccdc9370a7c69bd0aeeb5a70d2b4ee12f33b98c95a68423c79c1de7cc2a25aaa2105600b712e7d594cb0d56c9d4 - languageName: node - linkType: hard - "minimalistic-assert@npm:^1.0.0": version: 1.0.1 resolution: "minimalistic-assert@npm:1.0.1" @@ -13640,15 +12838,6 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.11": - version: 3.3.11 - resolution: "nanoid@npm:3.3.11" - bin: - nanoid: bin/nanoid.cjs - checksum: 10/73b5afe5975a307aaa3c95dfe3334c52cdf9ae71518176895229b8d65ab0d1c0417dd081426134eb7571c055720428ea5d57c645138161e7d10df80815527c48 - languageName: node - linkType: hard - "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -13776,13 +12965,6 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.21": - version: 2.0.21 - resolution: "node-releases@npm:2.0.21" - checksum: 10/5344d634b39d20f47c0d85a1c64567fdb9cf46f7b27ed3d141f752642faab47dae326835c2109636f823758afb16ffbed7b0c0fe6f800ef91cec9f2beb4f2b4a - languageName: node - linkType: hard - "nopt@npm:^7.0.0": version: 7.2.1 resolution: "nopt@npm:7.2.1" @@ -14069,7 +13251,7 @@ __metadata: "@docusaurus/plugin-content-docs": "npm:^3.7.0" "@docusaurus/preset-classic": "npm:^3.7.0" "@docusaurus/theme-common": "npm:^3.7.0" - "@docusaurus/theme-mermaid": "npm:^3.8.1" + "@docusaurus/theme-mermaid": "npm:3.7.0" "@docusaurus/tsconfig": "npm:^3.7.0" "@docusaurus/types": "npm:^3.7.0" "@mdx-js/react": "npm:^3.0.0" @@ -14091,13 +13273,6 @@ __metadata: languageName: node linkType: hard -"p-finally@npm:^1.0.0": - version: 1.0.0 - resolution: "p-finally@npm:1.0.0" - checksum: 10/93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 - languageName: node - linkType: hard - "p-limit@npm:^2.0.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -14161,16 +13336,6 @@ __metadata: languageName: node linkType: hard -"p-queue@npm:^6.6.2": - version: 6.6.2 - resolution: "p-queue@npm:6.6.2" - dependencies: - eventemitter3: "npm:^4.0.4" - p-timeout: "npm:^3.2.0" - checksum: 10/60fe227ffce59fbc5b1b081305b61a2f283ff145005853702b7d4d3f99a0176bd21bb126c99a962e51fe1e01cb8aa10f0488b7bbe73b5dc2e84b5cc650b8ffd2 - languageName: node - linkType: hard - "p-retry@npm:^4.5.0": version: 4.6.2 resolution: "p-retry@npm:4.6.2" @@ -14181,15 +13346,6 @@ __metadata: languageName: node linkType: hard -"p-timeout@npm:^3.2.0": - version: 3.2.0 - resolution: "p-timeout@npm:3.2.0" - dependencies: - p-finally: "npm:^1.0.0" - checksum: 10/3dd0eaa048780a6f23e5855df3dd45c7beacff1f820476c1d0d1bcd6648e3298752ba2c877aa1c92f6453c7dd23faaf13d9f5149fc14c0598a142e2c5e8d649c - languageName: node - linkType: hard - "p-try@npm:^2.0.0": version: 2.2.0 resolution: "p-try@npm:2.2.0" @@ -14584,21 +13740,6 @@ __metadata: languageName: node linkType: hard -"postcss-color-functional-notation@npm:^7.0.11": - version: 7.0.11 - resolution: "postcss-color-functional-notation@npm:7.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/78db22c195e85934ba6e3d46ad5ac354a9966ea1cc6955914f6ca7623a96f4b62a00b3c37c6217b205fe864f52bab47e8ed204047fabe25eb5d4c122e4032675 - languageName: node - linkType: hard - "postcss-color-functional-notation@npm:^7.0.7": version: 7.0.7 resolution: "postcss-color-functional-notation@npm:7.0.7" @@ -14678,20 +13819,6 @@ __metadata: languageName: node linkType: hard -"postcss-custom-media@npm:^11.0.6": - version: 11.0.6 - resolution: "postcss-custom-media@npm:11.0.6" - dependencies: - "@csstools/cascade-layer-name-parser": "npm:^2.0.5" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/media-query-list-parser": "npm:^4.0.3" - peerDependencies: - postcss: ^8.4 - checksum: 10/0fa7ec309065590ce9921bb455947b0a2b8354a1e2f04dae1b3b01e1e4832fd0bb01c983d2bdfc886ceb7ba5d90ffaffc7082afa696aac350f55de0f960c3786 - languageName: node - linkType: hard - "postcss-custom-properties@npm:^14.0.4": version: 14.0.4 resolution: "postcss-custom-properties@npm:14.0.4" @@ -14707,21 +13834,6 @@ __metadata: languageName: node linkType: hard -"postcss-custom-properties@npm:^14.0.6": - version: 14.0.6 - resolution: "postcss-custom-properties@npm:14.0.6" - dependencies: - "@csstools/cascade-layer-name-parser": "npm:^2.0.5" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/utilities": "npm:^2.0.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/fe57781d58990f8061aac2a07c6aedb66c5715b3350af11f4eb26121ff27a735610228a7e18b243a0cfeb24bd10cb5a2359e3056d693ba69c3a09bbef8a8c461 - languageName: node - linkType: hard - "postcss-custom-selectors@npm:^8.0.4": version: 8.0.4 resolution: "postcss-custom-selectors@npm:8.0.4" @@ -14736,20 +13848,6 @@ __metadata: languageName: node linkType: hard -"postcss-custom-selectors@npm:^8.0.5": - version: 8.0.5 - resolution: "postcss-custom-selectors@npm:8.0.5" - dependencies: - "@csstools/cascade-layer-name-parser": "npm:^2.0.5" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - postcss-selector-parser: "npm:^7.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/191cfe62ad3eaf3d8bff75ed461baebbb3b9a52de9c1c75bded61da4ed2302d7c53c457e9febfa7cffc9a1fb7f6ed98cab8c4b2a071a1097e487e0117018e6cf - languageName: node - linkType: hard - "postcss-dir-pseudo-class@npm:^9.0.1": version: 9.0.1 resolution: "postcss-dir-pseudo-class@npm:9.0.1" @@ -14821,19 +13919,6 @@ __metadata: languageName: node linkType: hard -"postcss-double-position-gradients@npm:^6.0.3": - version: 6.0.3 - resolution: "postcss-double-position-gradients@npm:6.0.3" - dependencies: - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/335c3c916aea28ad7359b4a675c60199929d18b0bf7547850c68ac84b91945683ed58c51dc369c93985128555ccbd2ff69750b0e321659600c3d5ba433bbede3 - languageName: node - linkType: hard - "postcss-focus-visible@npm:^10.0.1": version: 10.0.1 resolution: "postcss-focus-visible@npm:10.0.1" @@ -14886,21 +13971,6 @@ __metadata: languageName: node linkType: hard -"postcss-lab-function@npm:^7.0.11": - version: 7.0.11 - resolution: "postcss-lab-function@npm:7.0.11" - dependencies: - "@csstools/css-color-parser": "npm:^3.1.0" - "@csstools/css-parser-algorithms": "npm:^3.0.5" - "@csstools/css-tokenizer": "npm:^3.0.4" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/utilities": "npm:^2.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/9ca551b9665e45816bedf9541ed5371244590f97c3e49cfe4857b8ff664768672f3128e35d1a3ee8d5ac268287146dfe993112906445d1047dc687da506475b8 - languageName: node - linkType: hard - "postcss-lab-function@npm:^7.0.7": version: 7.0.7 resolution: "postcss-lab-function@npm:7.0.7" @@ -14916,7 +13986,7 @@ __metadata: languageName: node linkType: hard -"postcss-loader@npm:^7.3.3, postcss-loader@npm:^7.3.4": +"postcss-loader@npm:^7.3.3": version: 7.3.4 resolution: "postcss-loader@npm:7.3.4" dependencies: @@ -14941,17 +14011,6 @@ __metadata: languageName: node linkType: hard -"postcss-logical@npm:^8.1.0": - version: 8.1.0 - resolution: "postcss-logical@npm:8.1.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/495ce49a1fb831eb30d848909a54430b0170ec7681dcd84da9f1cb531cad167b9fa358dc242b70c2cae5c92b1a3fbbf43e625a54c3e615ea9dcce8d15cee5926 - languageName: node - linkType: hard - "postcss-merge-idents@npm:^6.0.3": version: 6.0.3 resolution: "postcss-merge-idents@npm:6.0.3" @@ -15095,19 +14154,6 @@ __metadata: languageName: node linkType: hard -"postcss-nesting@npm:^13.0.2": - version: 13.0.2 - resolution: "postcss-nesting@npm:13.0.2" - dependencies: - "@csstools/selector-resolve-nested": "npm:^3.1.0" - "@csstools/selector-specificity": "npm:^5.0.0" - postcss-selector-parser: "npm:^7.0.0" - peerDependencies: - postcss: ^8.4 - checksum: 10/ac82d7d2d2621d76ca42e065b90728c91206480ef01f874c21d032c5964b723818ab13194b09c7871edee8e4220241160f72d29ece65acbfe8827937a5b6ace0 - languageName: node - linkType: hard - "postcss-normalize-charset@npm:^6.0.2": version: 6.0.2 resolution: "postcss-normalize-charset@npm:6.0.2" @@ -15331,82 +14377,6 @@ __metadata: languageName: node linkType: hard -"postcss-preset-env@npm:^10.2.1": - version: 10.3.1 - resolution: "postcss-preset-env@npm:10.3.1" - dependencies: - "@csstools/postcss-alpha-function": "npm:^1.0.0" - "@csstools/postcss-cascade-layers": "npm:^5.0.2" - "@csstools/postcss-color-function": "npm:^4.0.11" - "@csstools/postcss-color-function-display-p3-linear": "npm:^1.0.0" - "@csstools/postcss-color-mix-function": "npm:^3.0.11" - "@csstools/postcss-color-mix-variadic-function-arguments": "npm:^1.0.1" - "@csstools/postcss-content-alt-text": "npm:^2.0.7" - "@csstools/postcss-exponential-functions": "npm:^2.0.9" - "@csstools/postcss-font-format-keywords": "npm:^4.0.0" - "@csstools/postcss-gamut-mapping": "npm:^2.0.11" - "@csstools/postcss-gradients-interpolation-method": "npm:^5.0.11" - "@csstools/postcss-hwb-function": "npm:^4.0.11" - "@csstools/postcss-ic-unit": "npm:^4.0.3" - "@csstools/postcss-initial": "npm:^2.0.1" - "@csstools/postcss-is-pseudo-class": "npm:^5.0.3" - "@csstools/postcss-light-dark-function": "npm:^2.0.10" - "@csstools/postcss-logical-float-and-clear": "npm:^3.0.0" - "@csstools/postcss-logical-overflow": "npm:^2.0.0" - "@csstools/postcss-logical-overscroll-behavior": "npm:^2.0.0" - "@csstools/postcss-logical-resize": "npm:^3.0.0" - "@csstools/postcss-logical-viewport-units": "npm:^3.0.4" - "@csstools/postcss-media-minmax": "npm:^2.0.9" - "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^3.0.5" - "@csstools/postcss-nested-calc": "npm:^4.0.0" - "@csstools/postcss-normalize-display-values": "npm:^4.0.0" - "@csstools/postcss-oklab-function": "npm:^4.0.11" - "@csstools/postcss-progressive-custom-properties": "npm:^4.2.0" - "@csstools/postcss-random-function": "npm:^2.0.1" - "@csstools/postcss-relative-color-syntax": "npm:^3.0.11" - "@csstools/postcss-scope-pseudo-class": "npm:^4.0.1" - "@csstools/postcss-sign-functions": "npm:^1.1.4" - "@csstools/postcss-stepped-value-functions": "npm:^4.0.9" - "@csstools/postcss-text-decoration-shorthand": "npm:^4.0.3" - "@csstools/postcss-trigonometric-functions": "npm:^4.0.9" - "@csstools/postcss-unset-value": "npm:^4.0.0" - autoprefixer: "npm:^10.4.21" - browserslist: "npm:^4.25.1" - css-blank-pseudo: "npm:^7.0.1" - css-has-pseudo: "npm:^7.0.3" - css-prefers-color-scheme: "npm:^10.0.0" - cssdb: "npm:^8.4.0" - postcss-attribute-case-insensitive: "npm:^7.0.1" - postcss-clamp: "npm:^4.1.0" - postcss-color-functional-notation: "npm:^7.0.11" - postcss-color-hex-alpha: "npm:^10.0.0" - postcss-color-rebeccapurple: "npm:^10.0.0" - postcss-custom-media: "npm:^11.0.6" - postcss-custom-properties: "npm:^14.0.6" - postcss-custom-selectors: "npm:^8.0.5" - postcss-dir-pseudo-class: "npm:^9.0.1" - postcss-double-position-gradients: "npm:^6.0.3" - postcss-focus-visible: "npm:^10.0.1" - postcss-focus-within: "npm:^9.0.1" - postcss-font-variant: "npm:^5.0.0" - postcss-gap-properties: "npm:^6.0.0" - postcss-image-set-function: "npm:^7.0.0" - postcss-lab-function: "npm:^7.0.11" - postcss-logical: "npm:^8.1.0" - postcss-nesting: "npm:^13.0.2" - postcss-opacity-percentage: "npm:^3.0.0" - postcss-overflow-shorthand: "npm:^6.0.0" - postcss-page-break: "npm:^3.0.4" - postcss-place: "npm:^10.0.0" - postcss-pseudo-class-any-link: "npm:^10.0.1" - postcss-replace-overflow-wrap: "npm:^4.0.0" - postcss-selector-not: "npm:^8.0.1" - peerDependencies: - postcss: ^8.4 - checksum: 10/72c0e9ecd1e4f59201e686c4ada78d1052153816084b20544abc938f0dc612a551c2617abc618a7fd481fe1a89abbca4b4423ea8073e4daf77d9072d84de9f5c - languageName: node - linkType: hard - "postcss-pseudo-class-any-link@npm:^10.0.1": version: 10.0.1 resolution: "postcss-pseudo-class-any-link@npm:10.0.1" @@ -15553,17 +14523,6 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.5.4": - version: 8.5.6 - resolution: "postcss@npm:8.5.6" - dependencies: - nanoid: "npm:^3.3.11" - picocolors: "npm:^1.1.1" - source-map-js: "npm:^1.2.1" - checksum: 10/9e4fbe97574091e9736d0e82a591e29aa100a0bf60276a926308f8c57249698935f35c5d2f4e80de778d0cbb8dcffab4f383d85fd50c5649aca421c3df729b86 - languageName: node - linkType: hard - "postman-code-generators@npm:^1.10.1": version: 1.10.1 resolution: "postman-code-generators@npm:1.10.1" @@ -17341,13 +16300,6 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.2.1": - version: 1.2.1 - resolution: "source-map-js@npm:1.2.1" - checksum: 10/ff9d8c8bf096d534a5b7707e0382ef827b4dd360a577d3f34d2b9f48e12c9d230b5747974ee7c607f0df65113732711bb701fe9ece3c7edbd43cb2294d707df3 - languageName: node - linkType: hard - "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -17853,13 +16805,6 @@ __metadata: languageName: node linkType: hard -"tinypool@npm:^1.0.2": - version: 1.1.1 - resolution: "tinypool@npm:1.1.1" - checksum: 10/0d54139e9dbc6ef33349768fa78890a4d708d16a7ab68e4e4ef3bb740609ddf0f9fd13292c2f413fbba756166c97051a657181c8f7ae92ade690604f183cc01d - languageName: node - linkType: hard - "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -18272,20 +17217,6 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.1.3": - version: 1.1.3 - resolution: "update-browserslist-db@npm:1.1.3" - dependencies: - escalade: "npm:^3.2.0" - picocolors: "npm:^1.1.1" - peerDependencies: - browserslist: ">= 4.21.0" - bin: - update-browserslist-db: cli.js - checksum: 10/87af2776054ffb9194cf95e0201547d041f72ee44ce54b144da110e65ea7ca01379367407ba21de5c9edd52c74d95395366790de67f3eb4cc4afa0fe4424e76f - languageName: node - linkType: hard - "update-notifier@npm:^6.0.2": version: 6.0.2 resolution: "update-notifier@npm:6.0.2" From a07314b1b0d83014bdcc4c203ed4c866ec2dc11a Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 12:47:56 +0200 Subject: [PATCH 06/17] Note that low intervals are fine --- docs/user-guide/services/service-ml-forecast.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index efbf8ba..5e31dbd 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -7,7 +7,7 @@ sidebar_position: 2 The ML Forecasting Service is an **optional** service that uses machine learning models to generate forecasts for any attribute that has historical data. :::note -This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](../developer-guide/external-services.md) page. +This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](../../developer-guide/external-services.md) page. ::: ## Features @@ -72,6 +72,8 @@ Regressors can be added when there's a relationship between your target variable Once you've configured all fields, click the `Save` button to store the forecast configuration. The model will automatically train and begin generating forecasts according to your specified intervals. +Note: it is safe to use a low interval for forecast generation and training if you want the forecast to show up faster. The system will not run into any issues with this. For example, if you want the forecast to be updated every 5 minutes, you can set the forecast generation and training to 5 minutes. + ## Viewing the Forecast Once the forecast is generated, you can view it in the Insights UI. From 8dc56fe871a6fe1ad22a59d70cf124ef1060d15c Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:05:29 +0200 Subject: [PATCH 07/17] Minor tweaks --- docs/user-guide/services/_category_.json | 2 +- docs/user-guide/services/services.md | 20 ++++++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/docs/user-guide/services/_category_.json b/docs/user-guide/services/_category_.json index a73db4f..1ca6587 100644 --- a/docs/user-guide/services/_category_.json +++ b/docs/user-guide/services/_category_.json @@ -1,6 +1,6 @@ { "label": "Services", - "position": 8, + "position": 10, "link": { "type": "generated-index" } diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md index 2358b2a..a648bbf 100644 --- a/docs/user-guide/services/services.md +++ b/docs/user-guide/services/services.md @@ -4,17 +4,9 @@ sidebar_position: 1 # Services -Services add extra features and tools to your OpenRemote system. They appear as new sections in the Manager UI, giving you access to additional capabilities like AI assistants, analytics dashboards, or custom tools that your administrator has set up. +Services are additional applications that work alongside OpenRemote to add extra features and tools to your system. You'll see them as entries in the `Services` page in your Manager UI, giving you access to additional functionality like AI assistants, forecasting, device management tools, or custom integrations. -When you click on a service, it opens right within OpenRemote just like any other page, so you don't need to switch between different applications. - ---- - -## What are Services? - -services are additional applications that work alongside OpenRemote. You'll see them as new menu items or sections in your Manager UI. Each service provides different functionality, such as AI assistants, analytics dashboards, device management tools, or custom integrations. - -When you use an service, it looks and feels like part of OpenRemote, but it's actually a separate application that's been integrated for you. +When you select a service, it opens right within OpenRemote just like any other page. --- @@ -22,9 +14,9 @@ When you use an service, it looks and feels like part of OpenRemote, but it's ac ### Where to Look -services can appear in several places in the Manager UI: +services can appear in the `Services` page in your Manager UI. -1. **Main Menu** - Look for the `Services` menu item, this will show all services available to you +1. **Services** - Look for the `Services` page, this will show all services available to you ### Service Status @@ -39,7 +31,7 @@ If you see a service marked as unavailable, it might be updating or experiencing To use an service: -1. **Click** on the service name in your navigation menu +1. **Click** on the service name in the `Services` page 2. **Wait** for the service interface to load within OpenRemote 3. **Use** the service just like you would use any other part of OpenRemote @@ -87,4 +79,4 @@ If you need additional functionality that isn't currently available, contact you services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. -Don't hesitate to explore them or ask your administrator about what's available - they're there to make your work easier and more effective. \ No newline at end of file +Don't hesitate to explore them or ask your administrator about what's available. They're there to provide you with additional tools and features. \ No newline at end of file From d943a3cee5c3e6083dc77c0735a666bbb1ca5b93 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:07:38 +0200 Subject: [PATCH 08/17] Small tweaks --- docs/user-guide/services/services.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md index a648bbf..532f187 100644 --- a/docs/user-guide/services/services.md +++ b/docs/user-guide/services/services.md @@ -14,9 +14,7 @@ When you select a service, it opens right within OpenRemote just like any other ### Where to Look -services can appear in the `Services` page in your Manager UI. - -1. **Services** - Look for the `Services` page, this will show all services available to you +You can find all services available to you in the `Services` page of the Manager UI. ### Service Status @@ -29,7 +27,7 @@ If you see a service marked as unavailable, it might be updating or experiencing ### Opening a Service -To use an service: +To use a service: 1. **Click** on the service name in the `Services` page 2. **Wait** for the service interface to load within OpenRemote @@ -41,12 +39,12 @@ To use an service: ### Service Won't Load -If an service isn't working: +If a service isn't working: 1. **Check the status** - Is it showing as "Available"? 2. **Refresh your browser** - Press F5 or click the refresh button 3. **Clear your browser cache** - This can solve many loading issues -4. **Check your internet connection** - services may need internet access +4. **Check your internet connection** - Services may need internet access 5. **Try a different browser** - Sometimes browser compatibility can cause issues ### Can't Access a Service @@ -63,7 +61,7 @@ When you encounter problems: 1. **Note the error message** - Write down exactly what you see 2. **Try the basic troubleshooting steps** above 3. **Contact your system administrator** with details about the problem -4. **Include screenshots** if possible - they help explain the issue +4. **Include screenshots** if possible, they help explain the issue --- @@ -77,6 +75,6 @@ If you need additional functionality that isn't currently available, contact you ## Summary -services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. +Services expand what you can do with OpenRemote by adding specialized tools and features. They appear right in your Manager UI, so you can access everything from one place. Don't hesitate to explore them or ask your administrator about what's available. They're there to provide you with additional tools and features. \ No newline at end of file From 6fd99f4acfa3d5e754144a62a98d652698b38501 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Sat, 20 Sep 2025 00:03:57 +0200 Subject: [PATCH 09/17] Update service-ml-forecast.md --- docs/user-guide/services/service-ml-forecast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index 5e31dbd..bed2565 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -4,7 +4,7 @@ sidebar_position: 2 # ML Forecasting Service -The ML Forecasting Service is an **optional** service that uses machine learning models to generate forecasts for any attribute that has historical data. +The ML Forecasting Service is an **optional** service that applies machine learning and statistical models to time series forecasting. It lets you create forecast configurations for attributes with historical data. Currently, it supports [Prophet](https://facebook.github.io/prophet/), an additive regression model widely recognized in machine learning for capturing trend, seasonality, and holiday effects. Additional models will be supported in future releases. :::note This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](../../developer-guide/external-services.md) page. From 44681aad0884e3dfda1d847e8286c2f091508c1b Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Sat, 20 Sep 2025 00:37:56 +0200 Subject: [PATCH 10/17] Update service-ml-forecast.md --- docs/user-guide/services/service-ml-forecast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index bed2565..51b949f 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -4,7 +4,7 @@ sidebar_position: 2 # ML Forecasting Service -The ML Forecasting Service is an **optional** service that applies machine learning and statistical models to time series forecasting. It lets you create forecast configurations for attributes with historical data. Currently, it supports [Prophet](https://facebook.github.io/prophet/), an additive regression model widely recognized in machine learning for capturing trend, seasonality, and holiday effects. Additional models will be supported in future releases. +The ML Forecasting Service is an **optional** service that applies machine learning and statistical models to time series forecasting. It lets you create forecast configurations for attributes with historical data. Currently, it supports [Prophet](https://facebook.github.io/prophet/), an additive regression model which is suitable for capturing trend, seasonality, and holiday effects. Additional models will be supported in future releases. :::note This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](../../developer-guide/external-services.md) page. From abb7283debb788be891636aa9ca38ebab867da6c Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Sat, 20 Sep 2025 00:39:43 +0200 Subject: [PATCH 11/17] Update service-ml-forecast.md --- docs/user-guide/services/service-ml-forecast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index 51b949f..7a3408a 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -4,7 +4,7 @@ sidebar_position: 2 # ML Forecasting Service -The ML Forecasting Service is an **optional** service that applies machine learning and statistical models to time series forecasting. It lets you create forecast configurations for attributes with historical data. Currently, it supports [Prophet](https://facebook.github.io/prophet/), an additive regression model which is suitable for capturing trend, seasonality, and holiday effects. Additional models will be supported in future releases. +The ML Forecasting Service is an **optional** service that applies machine learning and statistical models to time series forecasting. It lets you create forecast configurations for attributes with historical data. Currently, it supports [Prophet](https://facebook.github.io/prophet/), an additive regression model which is known for its ability to efficiently capture trends, seasonality and holidays effects. Additional models will be supported in future releases. :::note This service is not installed by default. It can be installed by your administrator by following the instructions in the [External Services](../../developer-guide/external-services.md) page. From 528a1ec691573d91b4e6253e4fb9c150350acec1 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 26 Sep 2025 08:42:58 +0200 Subject: [PATCH 12/17] Better explanation for the seasonality mode --- docs/user-guide/services/service-ml-forecast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index 7a3408a..804baf5 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -55,7 +55,7 @@ The form contains sensible defaults for forecast generation, training, and model | | `Unit` | Time measurement for relearning | | **Parameters** | `Changepoint range` | How much of your data to check for changes (0.8 = 80%). Reduce if predictions are too jumpy | | | `Changepoint prior scale` | How likely the system thinks changes will happen (0.05 = 5%). Increase for more flexible forecasts that adapt quickly to changes, decrease for smoother forecasts that ignore minor variations | -| | `Seasonality mode` | How seasonal patterns work (`additive` or `multiplicative`). Use `multiplicative` if seasonal effects grow with trend | +| | `Seasonality mode` | How seasonal patterns work (`additive` or `multiplicative`). `Additive` applies fixed seasonal adjustments (e.g., +10 units in summer). `Multiplicative` applies percentage-based adjustments (e.g., +20% in summer) | | | `Daily seasonality` | Look for daily patterns. Enable for daily cycles (energy, temperature) | | | `Weekly seasonality` | Look for weekly patterns. Enable for weekday/weekend differences | | | `Yearly seasonality` | Look for yearly patterns. Enable for seasonal data (needs 1+ year history) | From 0e5d8a401d7a8e532c580d9197009ad6237585a7 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:03:22 +0200 Subject: [PATCH 13/17] A couple extra notes, and links to other parts of the documentation --- docs/developer-guide/external-services.md | 10 +++++----- .../user-guide/services/service-ml-forecast.md | 18 ++++++++++++++---- docs/user-guide/services/services.md | 4 ++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md index 359de2e..7e9a14d 100644 --- a/docs/developer-guide/external-services.md +++ b/docs/developer-guide/external-services.md @@ -36,7 +36,7 @@ There are two types of external services in OpenRemote: - Bound to a **specific realm** and only available within that realm (single-tenant) - Simpler to implement when multi-tenancy is not required -Both global and regular services must be registered by **Service Users**. Global services specifically require a Super User account in the master realm, while regular services can be registered with a realm-specific Service User. +Both global and regular services must be registered by **Service Users**. Global services specifically require a Super User account in the master realm, while regular services can be registered with a realm-specific Service User. For more information on Service Users, see [Realm Users and Roles](../user-guide/identity-and-security/realms-users-and-roles.md) and [Security](../architecture/security.md). --- @@ -86,7 +86,7 @@ It involves sending a `POST` request to the OpenRemote API with details about th "label": "My External Service", "icon": "mdi-cloud", "homepageUrl": "https://my-external-service.com/interface", - "version": "1.0.0" + "status": "AVAILABLE" } ``` @@ -164,9 +164,9 @@ services: ### Service User Configuration -Your external service will need a service user account in OpenRemote for API authentication. The service user credentials should be provided via environment variables and kept secure. +Your external service will need a **service user account** in OpenRemote for API authentication. The service user credentials should be provided via environment variables and kept secure. -### Reverse Proxy Configuration (Optional) +### Reverse Proxy Configuration If you want to use OpenRemote's reverse proxy (HAProxy) to route traffic to your service: @@ -232,4 +232,4 @@ By leveraging external services, developers can significantly extend and enhance - **Heartbeats** ensure service availability is tracked in real time - **Security best practices** help ensure safe and reliable integrations -Together, these mechanisms provide a seamless way to extend OpenRemote while maintaining a unified and secure user experience. \ No newline at end of file +Together, these mechanisms provide a way to extend OpenRemote while maintaining a unified and secure user experience. \ No newline at end of file diff --git a/docs/user-guide/services/service-ml-forecast.md b/docs/user-guide/services/service-ml-forecast.md index 804baf5..c7e29ed 100644 --- a/docs/user-guide/services/service-ml-forecast.md +++ b/docs/user-guide/services/service-ml-forecast.md @@ -66,13 +66,15 @@ The form contains sensible defaults for forecast generation, training, and model Regressors can be added when there's a relationship between your target variable and other attributes. For example, when forecasting energy consumption, you might add weather data or solar production as regressors. - +:::important +When using regressors, they must have predicted datapoints available for the entire forecast period. If a regressor doesn't have future data points, the forecast will fail. This means you'll need to either: +- Use another forecast configuration to predict the regressor values first +- Use another mechanism such as a custom rule or meta configuration to predict the regressor values +::: #### Saving Configuration -Once you've configured all fields, click the `Save` button to store the forecast configuration. The model will automatically train and begin generating forecasts according to your specified intervals. - -Note: it is safe to use a low interval for forecast generation and training if you want the forecast to show up faster. The system will not run into any issues with this. For example, if you want the forecast to be updated every 5 minutes, you can set the forecast generation and training to 5 minutes. +Once you've configured all fields, click the `Save` button to store the forecast configuration. The model will automatically train and begin generating forecasts according to your specified intervals. After this step, you can now safely navigate away from the configuration page. ## Viewing the Forecast @@ -82,3 +84,11 @@ Once the forecast is generated, you can view it in the Insights UI. The forecast will be displayed as a dotted line in the chart. +## Next Steps + +Now that you understand how to set up and use the ML Forecasting Service, you can: + +1. **Monitor the results** in the Insights UI to verify forecast quality +2. **Experiment with parameters** to optimize forecast accuracy for your specific use case +3. **Add regressors** if you identify relationships between different data sources +4. **Add additional forecasts** by creating additional configurations for other assets and attributes diff --git a/docs/user-guide/services/services.md b/docs/user-guide/services/services.md index 532f187..2c04031 100644 --- a/docs/user-guide/services/services.md +++ b/docs/user-guide/services/services.md @@ -4,7 +4,7 @@ sidebar_position: 1 # Services -Services are additional applications that work alongside OpenRemote to add extra features and tools to your system. You'll see them as entries in the `Services` page in your Manager UI, giving you access to additional functionality like AI assistants, forecasting, device management tools, or custom integrations. +Services are additional applications that work alongside OpenRemote to add extra features and tools to your system. You'll see them as entries in the `Services` page in your Manager UI, giving you access to additional functionality like for example advanced forecasting, device management tools, or custom integrations. When you select a service, it opens right within OpenRemote just like any other page. @@ -30,7 +30,7 @@ If you see a service marked as unavailable, it might be updating or experiencing To use a service: 1. **Click** on the service name in the `Services` page -2. **Wait** for the service interface to load within OpenRemote +2. **Wait** for the service interface to load 3. **Use** the service just like you would use any other part of OpenRemote --- From 012c57aba724c5d852b850bc36498e41a13cbb2c Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Mon, 6 Oct 2025 09:45:39 +0200 Subject: [PATCH 14/17] Improved developer documentation for creating your own external service + configuration fix for the reference docker compose instructions --- docs/developer-guide/external-services.md | 146 ++++++++++++++++------ 1 file changed, 105 insertions(+), 41 deletions(-) diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md index 7e9a14d..f33a0cc 100644 --- a/docs/developer-guide/external-services.md +++ b/docs/developer-guide/external-services.md @@ -42,43 +42,96 @@ Both global and regular services must be registered by **Service Users**. Global ## Development -### Service Web Interface Requirements +Creating an external service involves building a standalone application that integrates with OpenRemote. Below is a step-by-step guide to developing your own service. -Registered external services must expose a web interface via a URL. OpenRemote embeds this interface in the Manager Web UI using an **iframe**. +### Step 1: Build Your Application + +External services can be built using **any programming language or framework** (Python, Node.js, Java, Go, etc.). Your application should: + +- Provide the core functionality you want to extend OpenRemote with (e.g., ML forecasting, data analysis, device management) +- Expose a web server with both: + - A **web interface** (HTML/CSS/JavaScript) that users will interact with + - **API endpoints** for communication with OpenRemote +- Be packaged as a Docker container for easy deployment + +**Technology choices:** +- **Backend**: Use any language/framework (FastAPI, Express, Spring Boot, etc.) +- **Frontend**: Use any web framework (React, Vue, Svelte, vanilla JavaScript, etc.) +- **Optional**: Use OpenRemote's [web components](https://www.npmjs.com/~openremotedeveloper) (buttons, panels, forms, tables) for a consistent UI that matches the Manager + +### Step 2: Make Your Web Interface Iframe-Compatible + +Since your service's web interface will be embedded in the OpenRemote Manager using an **iframe**, ensure it meets these requirements: **Technical Requirements:** -- **Same origin and protocol**: The service must use the same domain and protocol (HTTP or HTTPS) as the OpenRemote Manager (no mixed content) -- **Headers**: Do not block embedding. Avoid `X-Frame-Options: DENY` or restrictive `Content-Security-Policy` +- **Same origin and protocol**: Use the same domain and protocol (HTTP/HTTPS) as the OpenRemote Manager (no mixed content) +- **Headers**: Configure your web server to allow iframe embedding: + - Avoid `X-Frame-Options: DENY` or overly restrictive `Content-Security-Policy` - Recommended: `Content-Security-Policy: frame-ancestors 'self' https://` -- **Responsive design**: The iframe may resize; ensure the UI adapts dynamically -- **Navigation**: Avoid pop-ups or full-page redirects; keep interactions within the iframe +- **Responsive design**: The iframe may resize; ensure your UI adapts dynamically +- **Navigation**: Avoid pop-ups or full-page redirects; keep all interactions within the iframe + +### Step 3: Implement Registration Logic + +Your service must register itself with OpenRemote on startup. This involves: + +1. **On application startup**, send a `POST` request to `/services` (realm-specific) or `/services/global` (global) +2. Include service details in the request body (see Registration section below for format) +3. **Store the `instanceId`** returned in the response—you'll need it for heartbeats +4. Use your **Service User credentials** for authentication + +This logic should run during your application's initialization phase, typically in your main startup code or initialization function. + +### Step 4: Implement Heartbeat Mechanism + +Your service must continuously signal that it's alive and operational: -### Building the Interface +1. **Implement a background task** (scheduled job, async loop, cron job, etc.) that runs periodically +2. Send `POST /services/heartbeat` with your `serviceId` and `instanceId` +3. Send heartbeats **every 30-50 seconds** (must be <60 seconds to avoid being marked unavailable) +4. Handle failures gracefully—if a heartbeat fails, retry or re-register if needed -Developers can use any web framework or technology stack. Optionally, OpenRemote provides a set of pre-made web components such as buttons, panels, forms, and tables that integrate seamlessly with the Manager UI for a consistent look and feel. +Example pseudo-code structure: +```python +# On startup +instance_id = register_service() -These components are available on [npmjs](https://www.npmjs.com/~openremotedeveloper). +# Background task +while True: + send_heartbeat(instance_id) + sleep(30) # seconds +``` + +### Step 5: Integrate with OpenRemote APIs + +Your service can interact with OpenRemote's APIs to read and write data: + +- **Authentication**: Use OAuth2 with your Service User credentials +- **Read data**: Query assets, attributes, historical data +- **Write data**: Update attribute values, create assets +- **Listen for events**: Subscribe to asset/attribute changes via WebSocket or MQTT + +Refer to the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/) for available endpoints. ### Security Considerations -When developing and integrating an external service, consider the following: +When developing your external service, follow these security best practices: -- **Authentication**: OpenRemote uses Keycloak as an identity provider. External services should ensure that only authorized users can access them, either by integrating with Keycloak or implementing their own mechanism +- **Authentication**: Integrate with Keycloak (OpenRemote's identity provider) to ensure only authorized users can access your service UI +- **Authorization**: Validate that users have appropriate permissions before exposing sensitive functionality - **Protocol**: Always use **HTTPS** in production to protect data integrity and confidentiality -- **Data validation**: Validate and sanitize all data received from OpenRemote or users to prevent vulnerabilities such as SQL injection or cross-site scripting (XSS) +- **Secrets management**: Store Service User credentials and API keys securely using environment variables or secret management tools - **CORS**: If hosting on a different domain, configure Cross-Origin Resource Sharing (CORS) appropriately. Note that using a different domain may complicate Keycloak integration --- ## Registration -### Registration Process - -Registration is only required if the service provides a web interface and is expected to be embedded and used within the OpenRemote Manager UI. This process is always performed using a **Service User** account (see [Service Users](../architecture/security.md) for details). +### Request Format -It involves sending a `POST` request to the OpenRemote API with details about the service. +As described in Step 3 of the Development section, your service must send a registration request on startup. Below are the details of the API format. -**Example request:** +**Example registration request body:** ```json { @@ -102,15 +155,21 @@ OpenRemote responds with the same `ExternalService` object, but with an addition ➡ The exact API endpoint and request format can be found in the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/). -### Heartbeat Mechanism +### Heartbeat Format -After registration, each service must send periodic heartbeat requests to confirm its availability. +As described in Step 4 of the Development section, your service must send periodic heartbeat requests. Below are the technical details. -- The request must include the `instanceId` received during registration -- The default TTL (Time To Live) is **60 seconds** -- If OpenRemote does not receive a heartbeat within this period, the service is marked as **unavailable** +**Request details:** +- **Endpoint**: `POST /services/heartbeat` +- **Body**: Include `serviceId` and `instanceId` (received during registration) +- **Frequency**: Every 30-50 seconds (TTL is 60 seconds) +- **Response**: `204 No Content` on success -The diagram below illustrates the registration and heartbeat process: +If OpenRemote does not receive a heartbeat within the 60-second TTL, the service is marked as **unavailable** in the Manager UI. + +**Registration and Heartbeat Flow:** + +The diagram below illustrates the complete registration and heartbeat process: ```mermaid sequenceDiagram @@ -118,10 +177,10 @@ sequenceDiagram participant OR as OpenRemote Manager API alt Regular Service - Service->>OR: POST /services (serviceId, name, url, metadata…) + Service->>OR: POST /services (serviceId, label, url, metadata…) OR-->>Service: ExternalService object with instanceId else Global Service - Service->>OR: POST /services/global (serviceId, name, url, metadata…) + Service->>OR: POST /services/global (serviceId, label, url, metadata…) OR-->>Service: ExternalService object with instanceId end @@ -137,9 +196,9 @@ sequenceDiagram ### Docker Compose Setup -External services can be deployed alongside the OpenRemote stack using Docker Compose. The easiest approach is to add your service to an existing Docker Compose profile or create a custom one. +External services can be deployed alongside the OpenRemote stack using Docker Compose. The easiest approach is to add your service to an existing Docker Compose profile or create a custom one. For the instructions below, we will use the [ML Forecast Service](https://github.com/openremote/service-ml-forecast) as an example. -**Example service configuration:** +**Example service (service-ml-forecast) configuration:** ```yaml volumes: @@ -153,22 +212,25 @@ services: ML_LOG_LEVEL: INFO ML_ENVIRONMENT: production ML_WEBSERVER_ORIGINS: '["https://${OR_HOSTNAME:-localhost}"]' - ML_SERVICE_HOSTNAME: https://${OR_HOSTNAME:-localhost} - ML_OR_URL: https://${OR_HOSTNAME:-localhost} - ML_OR_KEYCLOAK_URL: https://${OR_HOSTNAME:-localhost}/auth - ML_OR_SERVICE_USER: ${ML_OR_SERVICE_USER:-mlserviceuser} - ML_OR_SERVICE_USER_SECRET: ${ML_OR_SERVICE_USER_SECRET:-secret} + ML_OR_URL: https://${OR_HOSTNAME:-localhost} # OpenRemote Manager URL + ML_OR_KEYCLOAK_URL: https://${OR_HOSTNAME:-localhost}/auth # Keycloak Auth URL + ML_OR_SERVICE_USER: ${ML_OR_SERVICE_USER:-serviceuser} # Service User + ML_OR_SERVICE_USER_SECRET: ${ML_OR_SERVICE_USER_SECRET:-secret} # Service User Secret + ML_OR_SERVICE_URL: https://${OR_HOSTNAME:-localhost} # The homepageUrl of the service volumes: - service-ml-forecast-data:/app/deployment/data ``` +For more information on the service configuration, refer to the [ML Forecast Service](https://github.com/openremote/service-ml-forecast) repository. + + ### Service User Configuration -Your external service will need a **service user account** in OpenRemote for API authentication. The service user credentials should be provided via environment variables and kept secure. +Your external service will need a **service user account** in OpenRemote for API authentication. You can create the Service User via the OpenRemote Manager UI. The service user credentials should be provided via environment variables and kept secure. ### Reverse Proxy Configuration -If you want to use OpenRemote's reverse proxy (HAProxy) to route traffic to your service: +If you want to use OpenRemote's reverse proxy (HAProxy) to route traffic to your external service: **1. Enable Custom HAProxy Configuration** @@ -213,19 +275,21 @@ External services can be used to extend OpenRemote in many ways: We provide the [ML Forecast Service](https://github.com/openremote/service-ml-forecast) which can serve as a reference implementation. This service connects to OpenRemote, retrieves historical data, and provides forecasting capabilities using machine learning/statistical models. -It demonstrates how to: +It demonstrates how to implement: + +- **Registration logic** at service startup +- **Heartbeat mechanism** as a background task +- **OAuth2 authentication** with OpenRemote's APIs for data retrieval and writing +- **Keycloak integration** for user authentication +- **Web interface** using OpenRemote's web components for a consistent UI -- **Register** an external service -- **Send and manage** heartbeats -- **Interact** with OpenRemote's APIs (OAuth2 authentication, data retrieval, data writing) -- **Integrate** securely with Keycloak for authentication -- **Implement** a web interface (using OpenRemote's web components) +Reviewing this implementation will give you a clear example of how to structure your own external service, including the complete registration and heartbeat implementation code. --- ## Summary -By leveraging external services, developers can significantly extend and enhance the OpenRemote platform: +By leveraging external services, developers can extend and enhance the OpenRemote platform in a flexible manner: - **Registration** connects services with a UI to the Manager, embedding their interface directly - **Global vs regular services** allow flexibility between multi-tenant and realm-specific use cases From c633376e3edd2254bd09c43e08a5f8db2cd47b68 Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Mon, 6 Oct 2025 09:50:14 +0200 Subject: [PATCH 15/17] Update openapi.yaml --- api/openapi.yaml | 364 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) diff --git a/api/openapi.yaml b/api/openapi.yaml index e0e8a28..4763c70 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -53,6 +53,8 @@ tags: description: Operations on realms - name: User description: Operations on users +- name: Services + description: Registration and management of external services - name: Syslog description: Operations on syslog events - name: Status @@ -1138,6 +1140,38 @@ paths: application/json: schema: $ref: "#/components/schemas/AssetObject" + /asset/tree: + post: + tags: + - Asset + summary: "Retrieve part of the asset tree using a query, returns an optimized\ + \ structure for tree display" + operationId: queryAssetTree + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/AssetQuery" + responses: + "200": + description: default response + content: + application/json: + schema: + $ref: "#/components/schemas/AssetTree" /asset/query: post: tags: @@ -4428,6 +4462,250 @@ paths: description: default response content: '*/*': {} + /service/{serviceId}/{instanceId}: + get: + tags: + - Services + summary: Retrieve a specific external service by its serviceId and instanceId + operationId: getService + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + - name: serviceId + in: path + required: true + schema: + maxLength: 2147483647 + minLength: 1 + type: string + - name: instanceId + in: path + required: true + schema: + type: integer + format: int32 + responses: + "200": + description: ExternalService retrieved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ExternalService" + "404": + description: ExternalService not found + put: + tags: + - Services + summary: Update the active registration lease for the specified external service + operationId: heartbeat + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + - name: serviceId + in: path + required: true + schema: + maxLength: 2147483647 + minLength: 1 + type: string + - name: instanceId + in: path + required: true + schema: + type: integer + format: int32 + responses: + "204": + description: Heartbeat sent successfully + "404": + description: Service instance not found + delete: + tags: + - Services + summary: Deregister an external service + operationId: deregisterService + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + - name: serviceId + in: path + required: true + schema: + type: string + - name: instanceId + in: path + required: true + schema: + type: integer + format: int32 + responses: + "204": + description: Service deregistered successfully + "404": + description: Service instance not found + /service/global: + get: + tags: + - Services + summary: List all registered external services that are globally accessible + within the OpenRemote manager + operationId: getGlobalServices + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + responses: + "200": + description: List of registered external services + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ExternalService" + post: + tags: + - Services + summary: Register a global external service with the OpenRemote manager + operationId: registerGlobalService + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ExternalService" + required: true + responses: + "200": + description: Service registered successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ExternalService" + "400": + description: Invalid external service object + "409": + description: ExternalService instance already registered + /service: + get: + tags: + - Services + summary: List all registered external services for the given realm within the + OpenRemote manager + operationId: getServices + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + - name: realm + in: query + required: true + schema: + type: string + responses: + "200": + description: List of registered external services + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ExternalService" + post: + tags: + - Services + summary: Register an external service with the OpenRemote manager + operationId: registerService + parameters: + - name: Authorization + in: header + schema: + type: string + - name: X-Forwarded-Proto + in: header + schema: + type: string + - name: X-Forwarded-Host + in: header + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ExternalService" + required: true + responses: + "200": + description: Service registered successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ExternalService" + "400": + description: Invalid external service object + "409": + description: ExternalService instance already registered /syslog/event: get: tags: @@ -4627,6 +4905,9 @@ components: items: type: object AssetObject: + required: + - name + - realm type: object properties: id: @@ -4688,6 +4969,8 @@ components: additionalProperties: $ref: "#/components/schemas/AttributeObject" AttributeObject: + required: + - name type: object properties: type: @@ -4728,6 +5011,8 @@ components: max: type: number MetaItemObject: + required: + - name type: object properties: type: @@ -5031,6 +5316,9 @@ components: binary: type: boolean AgentObjectObjectObject: + required: + - name + - realm type: object properties: id: @@ -5416,6 +5704,29 @@ components: type: string userFullName: type: string + AssetTree: + type: object + properties: + assets: + type: array + items: + $ref: "#/components/schemas/AssetTreeAsset" + hasMore: + type: boolean + AssetTreeAsset: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + hasChildren: + type: boolean + createdOn: + type: string + format: date-time ArrayPredicate: type: object allOf: @@ -5484,6 +5795,9 @@ components: limit: type: integer format: int32 + offset: + type: integer + format: int32 AttributePredicate: type: object properties: @@ -5703,6 +6017,8 @@ components: discriminator: propertyName: predicateType AttributeRef: + required: + - name type: object properties: id: @@ -5824,7 +6140,10 @@ components: type: object ConsoleRegistration: required: + - name + - platform - providers + - version type: object properties: id: @@ -5850,6 +6169,8 @@ components: type: string Dashboard: required: + - displayName + - realm - template type: object properties: @@ -5917,6 +6238,7 @@ components: type: boolean DashboardScreenPreset: required: + - displayName - scalingPreset type: object properties: @@ -6569,6 +6891,8 @@ components: - read:rules - read:insights - read:alarms + - read:services + - write:services - write:user - write:admin - write:logs @@ -7091,6 +7415,46 @@ components: type: string temporary: type: boolean + ExternalService: + required: + - homepageUrl + - label + - serviceId + - status + type: object + properties: + realm: + type: string + readOnly: true + isGlobal: + type: boolean + readOnly: true + serviceId: + maxLength: 255 + minLength: 3 + type: string + instanceId: + type: integer + format: int32 + version: + maxLength: 255 + minLength: 3 + type: string + icon: + type: string + label: + maxLength: 255 + minLength: 1 + type: string + homepageUrl: + maxLength: 512 + minLength: 3 + type: string + status: + type: string + enum: + - AVAILABLE + - UNAVAILABLE SyslogConfig: required: - storedCategories From 844948fb28183cd117b6ea10ca7578857ab1080a Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:02:20 +0200 Subject: [PATCH 16/17] Update external-services.md --- docs/developer-guide/external-services.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md index f33a0cc..b6fdccd 100644 --- a/docs/developer-guide/external-services.md +++ b/docs/developer-guide/external-services.md @@ -88,7 +88,7 @@ Your service must continuously signal that it's alive and operational: 1. **Implement a background task** (scheduled job, async loop, cron job, etc.) that runs periodically 2. Send `POST /services/heartbeat` with your `serviceId` and `instanceId` -3. Send heartbeats **every 30-50 seconds** (must be <60 seconds to avoid being marked unavailable) +3. Send heartbeats **every 30-50 seconds** (must be less than 60 seconds to avoid being marked unavailable) 4. Handle failures gracefully—if a heartbeat fails, retry or re-register if needed Example pseudo-code structure: From 45fdc883da6266d33ebdba2aa3b82f334a2aa2ee Mon Sep 17 00:00:00 2001 From: Dominique Kleeven <10584854+dominiquekleeven@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:08:54 +0200 Subject: [PATCH 17/17] Fix rest api docs url --- docs/developer-guide/external-services.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/developer-guide/external-services.md b/docs/developer-guide/external-services.md index b6fdccd..76abf8d 100644 --- a/docs/developer-guide/external-services.md +++ b/docs/developer-guide/external-services.md @@ -111,10 +111,9 @@ Your service can interact with OpenRemote's APIs to read and write data: - **Write data**: Update attribute values, create assets - **Listen for events**: Subscribe to asset/attribute changes via WebSocket or MQTT -Refer to the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/) for available endpoints. +Refer to the [OpenRemote API documentation](https://docs.openremote.io/docs/category/rest-api) for available endpoints. ### Security Considerations - When developing your external service, follow these security best practices: - **Authentication**: Integrate with Keycloak (OpenRemote's identity provider) to ensure only authorized users can access your service UI @@ -153,7 +152,7 @@ OpenRemote responds with the same `ExternalService` object, but with an addition | `/services/global` | POST | Global | Register a global external service (master realm only) | | `/services/heartbeat` | POST | Both | Send periodic heartbeat with `instanceId` | -➡ The exact API endpoint and request format can be found in the [OpenRemote API documentation](https://docs.openremote.io/developer-guide/api/). +The exact API endpoint and request format can be found in the [OpenRemote API documentation](https://docs.openremote.io/docs/category/rest-api). ### Heartbeat Format @@ -184,7 +183,7 @@ sequenceDiagram OR-->>Service: ExternalService object with instanceId end - loop Every < 60s + loop Every 30-50s (60s TTL) Service->>OR: POST /services/heartbeat (serviceId, instanceId) OR-->>Service: 204 No Content end