diff --git a/.ci_cd/README.md b/.ci_cd/README.md
index f923f24bb..f6c95c9d0 100644
--- a/.ci_cd/README.md
+++ b/.ci_cd/README.md
@@ -1,2 +1,3 @@
# CI/CD workflow files
+
This directory follows the same structure as the main [OpenRemote repo](https://github.com/openremote/openremote/tree/master/.ci_cd).
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..acfb8bb43
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# https://editorconfig.org/
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml
index 586b69803..ac2f4dcbd 100644
--- a/.github/workflows/ci_cd.yml
+++ b/.github/workflows/ci_cd.yml
@@ -5,13 +5,13 @@ on:
# Push excluding tags and workflow changes
push:
branches:
- - 'main'
+ - "main"
tags-ignore:
- - '*.*'
+ - "*.*"
paths-ignore:
- - '.github/**'
- - '.ci_cd/**'
- - '**/*.md'
+ - ".github/**"
+ - ".ci_cd/**"
+ - "**/*.md"
# PR
pull_request:
@@ -24,16 +24,16 @@ on:
workflow_dispatch:
inputs:
ENVIRONMENT:
- description: 'Environment to use (if any)'
+ description: "Environment to use (if any)"
CLEAN_INSTALL:
- description: 'Delete data before starting'
+ description: "Delete data before starting"
type: boolean
COMMIT:
- description: 'Repo branch or commit SHA to checkout'
+ description: "Repo branch or commit SHA to checkout"
OR_HOSTNAME:
- description: 'Host to deploy to (e.g. demo.openremote.app)'
+ description: "Host to deploy to (e.g. demo.openremote.app)"
OR_ADMIN_PASSWORD:
- description: 'Admin password override'
+ description: "Admin password override"
# Un-comment to monitor manager docker image tags for changes and trigger a redeploy when they change (see .ci_cd/README.md)
# schedule:
diff --git a/.gitignore b/.gitignore
index 363c917d3..27718914c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ console/iOS/Pods
manager/.factorypath/
Pods/
node_modules/
+.eslintcache
.pnp.*
.yarn/*
!.yarn/patches
diff --git a/README.md b/README.md
index efb77888b..0126ed9e8 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,11 @@ This repository is a template for custom projects; showing the recommended proje
**[You can find the documentation here](https://docs.openremote.io/docs/user-guide/deploying/custom-deployment/)**.
-
---
# (PROJECT_NAME)
-*(Please describe, in a short summary, the context of the project.)*
+_(Please describe, in a short summary, the context of the project.)_
> This repository is set up using the [Custom Project template](https://github.com/openremote/custom-project/). This repository uses the same standards and folder structure. More information about how to use this repository as a template to develop your own agents, services, model classes, setup tasks, tests, and new UI apps can be found in the [OpenRemote documentation](https://docs.openremote.io/docs/developer-guide/creating-a-custom-project).
+
## Project context
### Features
-*(Please insert a bullet point list with features specific to this custom project.)*
+
+_(Please insert a bullet point list with features specific to this custom project.)_
### Vocabulary / common terms
-*(Please insert a bullet point list with common terms in this project, with a short explanation.)*
+
+_(Please insert a bullet point list with common terms in this project, with a short explanation.)_
### Company background
-*(If applicable, write context about the company this custom project is meant for.)*
+
+_(If applicable, write context about the company this custom project is meant for.)_
## Architecture
-*(Please insert, preferably a diagram, or a short explanation of the high level architecture)*
+
+_(Please insert, preferably a diagram, or a short explanation of the high level architecture)_
### Keycloak setup
+
The identity provider in place is [Keycloak](https://github.com/openremote/keycloak), running in its own container. The default configuration from the repository ([link](https://github.com/openremote/keycloak)) is used.
### Proxy setup
+
All requests from and towards running services go through the [HAProxy](https://github.com/openremote/proxy) container. The default configuration from the repository ([haproxy.cfg](https://github.com/openremote/proxy/blob/main/haproxy.cfg)) is used.
@@ -64,13 +70,16 @@ All requests from and towards running services go through the [HAProxy](https://
## Developer Guide
### Quickstart
+
Before starting, make sure you have cloned the Git repository locally, as this is required.
Follow the initial guides on the OpenRemote documentation on [preparing the environment](https://docs.openremote.io/docs/developer-guide/preparing-the-environment), [installing and using Docker](https://docs.openremote.io/docs/developer-guide/installing-and-using-docker), and on [setting up an IDE](https://docs.openremote.io/docs/developer-guide/setting-up-an-ide).
-*(Please describe the steps necessary to run this custom project locally.)*
+_(Please describe the steps necessary to run this custom project locally.)_
### Docker Compose files
+
In the `profile` directory you can find different Docker Compose files, each serving a different purpose. To be able to use them, you'll need to download a copy of the `deploy.yml` file from the main OpenRemote repository and place it in the `openremote/profile` directory, to ensure you always have the latest version of the file:
+
```bash
mkdir -p openremote/profile && curl -L https://github.com/openremote/openremote/raw/refs/heads/master/profile/deploy.yml -o openremote/profile/deploy.yml
```
@@ -78,7 +87,7 @@ mkdir -p openremote/profile && curl -L https://github.com/openremote/openremote/
### Environment variables
| Key | Containers | Description | Default |
-|----------------------|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------|----------|
+| -------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `OR_HOSTNAME` | All services | **(REQUIRED)** FQDN hostname of where this instance will be exposed (localhost, IP address or public domain) | - |
| `OR_ADMIN_PASSWORD` | `keycloak`, `manager` | **(REQUIRED)** Initial admin user password | - |
| `DEPLOYMENT_VERSION` | `deployment` | **(REQUIRED)** The custom project version in use. This tag is used for building and deploying the artifacts from this repository. | - |
@@ -95,19 +104,31 @@ This custom project is deployed by OpenRemote on their managed infrastructure. I
The list of available environments:
+
### `staging`
+
Used by OpenRemote to test new functionality and bugfixes before publishing them to production. Important practices and agreements to be aware of:
+
- This environment is only used for development purposes, so can be offline at any time.
- There is no guarantee that this data will be persisted in the long-term.
+
+
- **OpenRemote Manager:** https://(staging.CUSTOM_HOSTNAME).com/manager
- **Custom app:** https://(staging.CUSTOM_HOSTNAME).com/custom
+
+
### `production`
+
Used for the live system with devices in the field, with a guarantee of stability and data persistence. Important practices and agreements to be aware of:
+
- There is a daily backup active for this instance.
- This deployment is **manually updated**, and should be communicated with stakeholders.
+
+
- **OpenRemote Manager:** https://(CUSTOM_HOSTNAME).com/manager
- **Custom app:** https://(CUSTOM_HOSTNAME).com/custom
-
\ No newline at end of file
+
+
diff --git a/agent/build.gradle b/agent/build.gradle
index c2f7a8158..6178fbfee 100644
--- a/agent/build.gradle
+++ b/agent/build.gradle
@@ -1,12 +1,12 @@
plugins {
- id 'java-library'
+ id 'java-library'
}
dependencies {
- api project(":model")
- api libs.openremote.agent
+ api project(":model")
+ api libs.openremote.agent
}
tasks.register('installDist') {
- dependsOn jar
+ dependsOn jar
}
diff --git a/agent/src/main/java/org/openremote/agent/custom/CustomAgent.java b/agent/src/main/java/org/openremote/agent/custom/CustomAgent.java
index 2a152dec8..a404dc36a 100644
--- a/agent/src/main/java/org/openremote/agent/custom/CustomAgent.java
+++ b/agent/src/main/java/org/openremote/agent/custom/CustomAgent.java
@@ -1,9 +1,6 @@
/*
* Copyright 2017, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,10 +12,14 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.agent.custom;
+import jakarta.persistence.Entity;
+import java.util.Optional;
import org.openremote.model.asset.Asset;
import org.openremote.model.asset.agent.Agent;
import org.openremote.model.asset.agent.AgentDescriptor;
@@ -26,58 +27,56 @@
import org.openremote.model.value.AttributeDescriptor;
import org.openremote.model.value.ValueDescriptor;
-import jakarta.persistence.Entity;
-import java.util.Optional;
-
/**
- * This is an example of a custom {@link Agent} type; this must be registered via an
- * {@link org.openremote.model.AssetModelProvider} and must conform to the same requirements as custom {@link Asset}s and
- * in addition the following requirements:
+ * This is an example of a custom {@link Agent} type; this must be registered via an {@link
+ * org.openremote.model.AssetModelProvider} and must conform to the same requirements as custom
+ * {@link Asset}s and in addition the following requirements:
*
*
- * Optionally add a custom {@link org.openremote.model.asset.agent.AgentLink} (the {@link Class#getSimpleName} must
- * be unique compared to all other registered {@link org.openremote.model.asset.agent.AgentLink}s)
- * Must define a {@link org.openremote.model.asset.agent.Protocol} implementation that corresponds to this {@link Agent}
- * Must have a public static final {@link org.openremote.model.asset.agent.AgentDescriptor} rather than an
- * {@link org.openremote.model.asset.AssetDescriptor}
+ * Optionally add a custom {@link org.openremote.model.asset.agent.AgentLink} (the {@link
+ * Class#getSimpleName} must be unique compared to all other registered {@link
+ * org.openremote.model.asset.agent.AgentLink}s)
+ * Must define a {@link org.openremote.model.asset.agent.Protocol} implementation that
+ * corresponds to this {@link Agent}
+ * Must have a public static final {@link org.openremote.model.asset.agent.AgentDescriptor}
+ * rather than an {@link org.openremote.model.asset.AssetDescriptor}
*
*/
@Entity
public class CustomAgent extends Agent {
- public enum Option {
- ONE,
- TWO,
- THREE
- };
+ public enum Option {
+ ONE,
+ TWO,
+ THREE
+ };
- public static final ValueDescriptor OPTION_VALUE_DESCRIPTOR = new ValueDescriptor<>("customAgentOption", Option.class);
+ public static final ValueDescriptor OPTION_VALUE_DESCRIPTOR =
+ new ValueDescriptor<>("customAgentOption", Option.class);
- public static final AttributeDescriptor OPTION_ATTRIBUTE_DESCRIPTOR = new AttributeDescriptor<>("option", OPTION_VALUE_DESCRIPTOR);
+ public static final AttributeDescriptor OPTION_ATTRIBUTE_DESCRIPTOR =
+ new AttributeDescriptor<>("option", OPTION_VALUE_DESCRIPTOR);
- public static final AgentDescriptor DESCRIPTOR = new AgentDescriptor<>(
- CustomAgent.class, CustomProtocol.class, DefaultAgentLink.class
- );
+ public static final AgentDescriptor DESCRIPTOR =
+ new AgentDescriptor<>(CustomAgent.class, CustomProtocol.class, DefaultAgentLink.class);
- protected CustomAgent() {
- }
+ protected CustomAgent() {}
- public CustomAgent(String name) {
- super(name);
- }
+ public CustomAgent(String name) {
+ super(name);
+ }
- @Override
- public CustomProtocol getProtocolInstance() {
- return new CustomProtocol(this);
- }
+ @Override
+ public CustomProtocol getProtocolInstance() {
+ return new CustomProtocol(this);
+ }
- public Optional getOption() {
- return getAttributes().getValue(OPTION_ATTRIBUTE_DESCRIPTOR);
- }
+ public Optional getOption() {
+ return getAttributes().getValue(OPTION_ATTRIBUTE_DESCRIPTOR);
+ }
- public CustomAgent setOption(Option value) {
- getAttributes().getOrCreate(OPTION_ATTRIBUTE_DESCRIPTOR).setValue(value);
- return this;
- }
+ public CustomAgent setOption(Option value) {
+ getAttributes().getOrCreate(OPTION_ATTRIBUTE_DESCRIPTOR).setValue(value);
+ return this;
+ }
}
-
diff --git a/agent/src/main/java/org/openremote/agent/custom/CustomAgentModelProvider.java b/agent/src/main/java/org/openremote/agent/custom/CustomAgentModelProvider.java
index 6d1e4629f..ce1810d50 100644
--- a/agent/src/main/java/org/openremote/agent/custom/CustomAgentModelProvider.java
+++ b/agent/src/main/java/org/openremote/agent/custom/CustomAgentModelProvider.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,17 +12,18 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
package org.openremote.agent.custom;
import org.openremote.model.AssetModelProvider;
public class CustomAgentModelProvider implements AssetModelProvider {
- @Override
- public boolean useAutoScan() {
- return true;
- }
+ @Override
+ public boolean useAutoScan() {
+ return true;
+ }
}
diff --git a/agent/src/main/java/org/openremote/agent/custom/CustomProtocol.java b/agent/src/main/java/org/openremote/agent/custom/CustomProtocol.java
index 6000f8693..d55999edc 100644
--- a/agent/src/main/java/org/openremote/agent/custom/CustomProtocol.java
+++ b/agent/src/main/java/org/openremote/agent/custom/CustomProtocol.java
@@ -1,9 +1,6 @@
/*
* Copyright 2017, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,10 +12,15 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.agent.custom;
+import static org.openremote.model.syslog.SyslogCategory.PROTOCOL;
+
+import java.util.logging.Logger;
import org.openremote.agent.protocol.AbstractProtocol;
import org.openremote.model.Container;
import org.openremote.model.asset.agent.DefaultAgentLink;
@@ -26,57 +28,49 @@
import org.openremote.model.attribute.AttributeEvent;
import org.openremote.model.syslog.SyslogCategory;
-import java.util.logging.Logger;
-
-import static org.openremote.model.syslog.SyslogCategory.PROTOCOL;
-
/**
- * A custom protocol that is used by the {@link CustomAgent}; there is a one-to-one mapping between an {@link
- * CustomAgent} {@link org.openremote.model.asset.Asset} and its' {@link org.openremote.model.asset.agent.Protocol}.
- * This example does nothing useful but is intended to show where protocol classes should be created.
+ * A custom protocol that is used by the {@link CustomAgent}; there is a one-to-one mapping between
+ * an {@link CustomAgent} {@link org.openremote.model.asset.Asset} and its' {@link
+ * org.openremote.model.asset.agent.Protocol}. This example does nothing useful but is intended to
+ * show where protocol classes should be created.
*/
public class CustomProtocol extends AbstractProtocol {
- public static final String PROTOCOL_DISPLAY_NAME = "Custom";
- private static final Logger LOG = SyslogCategory.getLogger(PROTOCOL, CustomProtocol.class);
- protected boolean running;
-
- public CustomProtocol(CustomAgent agent) {
- super(agent);
- }
-
- @Override
- protected void doStart(Container container) throws Exception {
- running = true;
- }
-
- @Override
- protected void doStop(Container container) throws Exception {
-
- }
+ public static final String PROTOCOL_DISPLAY_NAME = "Custom";
+ private static final Logger LOG = SyslogCategory.getLogger(PROTOCOL, CustomProtocol.class);
+ protected boolean running;
- @Override
- protected void doLinkAttribute(String assetId, Attribute> attribute, DefaultAgentLink agentLink) throws RuntimeException {
+ public CustomProtocol(CustomAgent agent) {
+ super(agent);
+ }
- }
+ @Override
+ protected void doStart(Container container) throws Exception {
+ running = true;
+ }
- @Override
- protected void doUnlinkAttribute(String assetId, Attribute> attribute, DefaultAgentLink agentLink) {
+ @Override
+ protected void doStop(Container container) throws Exception {}
- }
+ @Override
+ protected void doLinkAttribute(String assetId, Attribute> attribute, DefaultAgentLink agentLink)
+ throws RuntimeException {}
- @Override
- protected void doLinkedAttributeWrite(DefaultAgentLink agentLink, AttributeEvent event, Object processedValue) {
+ @Override
+ protected void doUnlinkAttribute(
+ String assetId, Attribute> attribute, DefaultAgentLink agentLink) {}
- }
+ @Override
+ protected void doLinkedAttributeWrite(
+ DefaultAgentLink agentLink, AttributeEvent event, Object processedValue) {}
- @Override
- public String getProtocolName() {
- return PROTOCOL_DISPLAY_NAME;
- }
+ @Override
+ public String getProtocolName() {
+ return PROTOCOL_DISPLAY_NAME;
+ }
- @Override
- public String getProtocolInstanceUri() {
- return "custom://" + agent.getOption();
- }
+ @Override
+ public String getProtocolInstanceUri() {
+ return "custom://" + agent.getOption();
+ }
}
diff --git a/build.gradle b/build.gradle
index f3af11100..38c235dd9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,26 +2,26 @@ import static org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS
import static org.apache.tools.ant.taskdefs.condition.Os.isFamily
plugins {
- alias libs.plugins.axionRelease
- alias libs.plugins.fileEncrypt apply false
+ alias libs.plugins.axionRelease
+ alias libs.plugins.fileEncrypt apply false
}
scmVersion {
- releaseOnlyOnReleaseBranches = true
- releaseBranchNames = ['main']
- unshallowRepoOnCI.set(true)
- versionCreator('simple')
- repository {
- remote.set('origin')
- }
- tag {
- prefix.set('')
- }
+ releaseOnlyOnReleaseBranches = true
+ releaseBranchNames = ['main']
+ unshallowRepoOnCI.set(true)
+ versionCreator('simple')
+ repository {
+ remote.set('origin')
+ }
+ tag {
+ prefix.set('')
+ }
}
allprojects {
- // Apply common project setup
- apply from: "${project.rootDir}/project.gradle"
+ // Apply common project setup
+ apply from: "${rootDir}/project.gradle"
}
// Uncomment the following to configure files to be encrypted/decrypted
@@ -70,4 +70,3 @@ allprojects {
// }
// }
//}
-
diff --git a/buildSrc/src/main/groovy/TestSummaryListener.groovy b/buildSrc/src/main/groovy/TestSummaryListener.groovy
index f1071bf1c..656c7cc3e 100644
--- a/buildSrc/src/main/groovy/TestSummaryListener.groovy
+++ b/buildSrc/src/main/groovy/TestSummaryListener.groovy
@@ -1,3 +1,21 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.api.tasks.testing.TestListener
import org.gradle.api.tasks.testing.TestResult
diff --git a/deployment/build.gradle b/deployment/build.gradle
index 416545682..bab54210c 100644
--- a/deployment/build.gradle
+++ b/deployment/build.gradle
@@ -1,56 +1,62 @@
plugins {
- id 'java-library'
+ id 'java-library'
}
configurations {
- developmentRuntime
- managerRuntime
+ developmentRuntime
+ managerRuntime
}
dependencies {
- api project(":setup")
- managerRuntime libs.openremote.manager
+ api project(":setup")
+ managerRuntime libs.openremote.manager
}
tasks.register('license') {
- doLast {
- def licenseFiles = new ArrayList<>()
- licenseFiles.add("${rootDir}/LICENSE.txt")
-
- def toConcatenate = files(licenseFiles.toArray())
- def outputFileName = "${buildDir}/image/manager/app/LICENSE.txt"
- def output = new File(outputFileName)
- if (output.exists()) {
- output.delete()
- }
- output.getParentFile().mkdirs()
- output.createNewFile()
- output.write('') // truncate output if needed
- toConcatenate.each { f -> output << f.text }
+ doLast {
+ def licenseFiles = new ArrayList<>()
+ licenseFiles.add("${rootDir}/LICENSE.txt")
+
+ def toConcatenate = files(licenseFiles.toArray())
+ def outputFileName = "${buildDir}/image/manager/app/LICENSE.txt"
+ def output = new File(outputFileName)
+ if (output.exists()) {
+ output.delete()
}
+ output.getParentFile().mkdirs()
+ output.createNewFile()
+ output.write('') // truncate output if needed
+ toConcatenate.each { f -> output << f.text }
+ }
}
tasks.register('installDist', Copy) {
- duplicatesStrategy = DuplicatesStrategy.EXCLUDE
- dependsOn(parent.getTasksByName('installDist', true).findAll {
- // Don't create circular dependency
- it.project != project
- })
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ dependsOn(parent.getTasksByName('installDist', true).findAll {
+ // Don't create circular dependency
+ it.project != project
+ })
- into("$buildDir")
+ into("$buildDir")
- from "Dockerfile"
+ from "Dockerfile"
- into("image") {
- from projectDir
- exclude "build.gradle", "Dockerfile", "build", "**/*.mbtiles", "src", "**/*.md", ".gitignore", "**/*.encrypted"
- }
+ into("image") {
+ from projectDir
+ exclude "build.gradle", "Dockerfile", "build", "**/*.mbtiles", "src", "**/*.md", ".gitignore", "**/*.encrypted"
+ }
- into("image/manager/extensions") {
- from configurations.runtimeClasspath.resolvedConfiguration.resolvedArtifacts.collect {it.file }
- // Don't include deps baked into the manager docker image
- exclude configurations.managerRuntime.resolvedConfiguration.resolvedArtifacts.collect {it.file.name }
- // Don't include deps used for dev purposes only
- exclude configurations.developmentRuntime.resolvedConfiguration.resolvedArtifacts.collect {it.file.name }
+ into("image/manager/extensions") {
+ from configurations.runtimeClasspath.resolvedConfiguration.resolvedArtifacts.collect {
+ it.file
+ }
+ // Don't include deps baked into the manager docker image
+ exclude configurations.managerRuntime.resolvedConfiguration.resolvedArtifacts.collect {
+ it.file.name
+ }
+ // Don't include deps used for dev purposes only
+ exclude configurations.developmentRuntime.resolvedConfiguration.resolvedArtifacts.collect {
+ it.file.name
}
+ }
}
diff --git a/deployment/keycloak/themes/README.md b/deployment/keycloak/themes/README.md
index 439e1f928..9ae3fe503 100644
--- a/deployment/keycloak/themes/README.md
+++ b/deployment/keycloak/themes/README.md
@@ -1,7 +1,9 @@
## Custom Keycloak themes
+
Add a directory for each custom theme (the directory name is the theme name) and add the required theme templates within; you can use the default [openremote theme](https://github.com/openremote/keycloak/tree/main/theme/src/main/resources/theme/openremote) as a template alternatively refer to the keycloak themes documentation.
This themes directory can then be volume mapped into the keycloak container at `/deployment/keycloak/themes`.
### Development
+
To be able to see the custom theme in development (i.e. when using `dev-testing.yml` compose profile) you need to create a copy of `openremote/profile/dev-testing.yml` in the custom project `profile` directory.
diff --git a/deployment/manager/README.md b/deployment/manager/README.md
index 425ace940..f47e16d18 100644
--- a/deployment/manager/README.md
+++ b/deployment/manager/README.md
@@ -1,23 +1,31 @@
## Manager customisation
+
As well as the below information please see the [Manager endpoints and file paths](https://github.com/openremote/openremote/wiki/Architecture:-Manager-endpoints-and-file-paths) wiki.
### Custom provisioning files (`provisioning/`)
+
As an alternative to writing `java` setup code you can also provide `json` representations of Assets which will be automatically deserialized and added to the system when doing a clean install.
### Console App Configurations (`consoleappconfig/`)
+
Console app configurations that can be loaded by Android and iOS consoles.
### Custom App Files (`app/`)
+
This `app` directory is used as the `$CUSTOM_APP_DOCROOT` and can be used to store any custom static content; the Manager UI also checks the `/manager_config.json` path for a custom Manager UI configuration `json` file.
### FCM Configuration (`fcm.json`)
+
This is where your Firebase cloud messaging config file should be placed to enable push notification for Android/iOS.
### Logging Configuration (`logging.properties`)
+
Custom `JUL` logging configuration file; default log file can be found [here](https://github.com/openremote/openremote/blob/master/manager/src/main/resources/logging.properties).
### Keycloak Credentials (`keycloak.json`)
+
This is where custom keycloak credentials are stored/can be supplied; by default the manager will auto generate these during a clean install.
### Custom Java Code (`extensions/`)
+
Any custom java code should be compiled and made available in this directory; if it is compiled as part of the custom project then only the compiled code should be copied to the `deployment/build/image/manager/extensions` dir and not to this source directory.
diff --git a/deployment/map/README.md b/deployment/map/README.md
index e329dfbd6..8b99392d0 100644
--- a/deployment/map/README.md
+++ b/deployment/map/README.md
@@ -1,4 +1,5 @@
## Map customisation
+
As well as the below information please see the [Manager endpoints and file paths](https://github.com/openremote/openremote/wiki/Architecture:-Manager-endpoints-and-file-paths) wiki.
The default endpoints and file paths look in the `/deployment/map`, see [here](https://github.com/openremote/openremote/wiki/User-Guide%3A-Custom-deployment#customising-the-map) for more details.
diff --git a/docker-compose.yml b/docker-compose.yml
index de0ed5860..7b4303868 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -48,11 +48,10 @@ x-logging: &awslogs
options:
awslogs-region: ${AWS_REGION:-eu-west-1}
awslogs-group: ${OR_HOSTNAME}
- awslogs-create-group: 'true'
+ awslogs-create-group: "true"
tag: "{{.Name}}/{{.ID}}"
services:
-
# This service will only populate an empty volume on startup and then exit.
# If the volume already contains data, it exits immediately.
deployment:
diff --git a/gradle.properties b/gradle.properties
index 8263acd14..86cb93470 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,4 +6,5 @@ projectName = custom-project
version = 1.0-SNAPSHOT
ideaExtPluginVersion = 1.4.1
+spotlessVersion = 8.9.0
typescriptGeneratorVersion = 4.1.1
diff --git a/manager/build.gradle b/manager/build.gradle
index e16ae1aae..382d7807f 100644
--- a/manager/build.gradle
+++ b/manager/build.gradle
@@ -1,14 +1,14 @@
plugins {
- id 'java-library'
+ id 'java-library'
}
dependencies {
- api project(":agent")
- api project(":model")
- api libs.openremote.container
- api libs.openremote.manager
+ api project(":agent")
+ api project(":model")
+ api libs.openremote.container
+ api libs.openremote.manager
}
tasks.register('installDist') {
- dependsOn jar
+ dependsOn jar
}
diff --git a/manager/src/main/java/org/openremote/manager/custom/CustomService.java b/manager/src/main/java/org/openremote/manager/custom/CustomService.java
index 7cf039cf0..46eb7a112 100644
--- a/manager/src/main/java/org/openremote/manager/custom/CustomService.java
+++ b/manager/src/main/java/org/openremote/manager/custom/CustomService.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,7 +12,9 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.manager.custom;
@@ -23,18 +22,12 @@
import org.openremote.model.ContainerService;
public class CustomService implements ContainerService {
- @Override
- public void init(Container container) throws Exception {
-
- }
-
- @Override
- public void start(Container container) throws Exception {
-
- }
+ @Override
+ public void init(Container container) throws Exception {}
- @Override
- public void stop(Container container) throws Exception {
+ @Override
+ public void start(Container container) throws Exception {}
- }
+ @Override
+ public void stop(Container container) throws Exception {}
}
diff --git a/model/build.gradle b/model/build.gradle
index 0a8b8d432..512a9c968 100644
--- a/model/build.gradle
+++ b/model/build.gradle
@@ -1,11 +1,11 @@
plugins {
- id 'java-library'
+ id 'java-library'
}
dependencies {
- api libs.openremote.model
+ api libs.openremote.model
}
tasks.register('installDist') {
- dependsOn jar
+ dependsOn jar
}
diff --git a/model/src/main/java/org/openremote/model/custom/CustomAsset.java b/model/src/main/java/org/openremote/model/custom/CustomAsset.java
index 0ae34c479..d54bb0c31 100644
--- a/model/src/main/java/org/openremote/model/custom/CustomAsset.java
+++ b/model/src/main/java/org/openremote/model/custom/CustomAsset.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,57 +12,62 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.model.custom;
+import jakarta.persistence.Entity;
+import java.util.Optional;
import org.openremote.model.asset.Asset;
import org.openremote.model.asset.AssetDescriptor;
import org.openremote.model.value.AttributeDescriptor;
import org.openremote.model.value.ValueDescriptor;
-import jakarta.persistence.Entity;
-import java.util.Optional;
-
/**
- * This is an example of a custom {@link Asset} type; this must be registered via an
- * {@link org.openremote.model.AssetModelProvider} and must conform to the following requirements:
+ * This is an example of a custom {@link Asset} type; this must be registered via an {@link
+ * org.openremote.model.AssetModelProvider} and must conform to the following requirements:
*
*
- * Must have {@link Entity} annotation
- * Optionally add {@link org.openremote.model.value.ValueDescriptor}s
- * Optionally add {@link org.openremote.model.value.MetaItemDescriptor}s
- * Optionally add {@link org.openremote.model.value.AttributeDescriptor}s
- * Must have a public static final {@link org.openremote.model.asset.AssetDescriptor}
- * Must have a protected no args constructor (for hydrators i.e. JPA/Jackson)
- * For a given {@link Asset} type only one {@link org.openremote.model.asset.AssetDescriptor} can exist
- * {@link org.openremote.model.value.AttributeDescriptor}s that override a super class descriptor cannot change the
- * value type; just the formatting etc.
- * {@link org.openremote.model.value.MetaItemDescriptor}s names must be unique
- * {@link org.openremote.model.value.ValueDescriptor}s names must be unique
+ * Must have {@link Entity} annotation
+ * Optionally add {@link org.openremote.model.value.ValueDescriptor}s
+ * Optionally add {@link org.openremote.model.value.MetaItemDescriptor}s
+ * Optionally add {@link org.openremote.model.value.AttributeDescriptor}s
+ * Must have a public static final {@link org.openremote.model.asset.AssetDescriptor}
+ * Must have a protected no args constructor (for hydrators i.e. JPA/Jackson)
+ * For a given {@link Asset} type only one {@link org.openremote.model.asset.AssetDescriptor}
+ * can exist
+ * {@link org.openremote.model.value.AttributeDescriptor}s that override a super class
+ * descriptor cannot change the value type; just the formatting etc.
+ * {@link org.openremote.model.value.MetaItemDescriptor}s names must be unique
+ * {@link org.openremote.model.value.ValueDescriptor}s names must be unique
*
*/
@Entity
public class CustomAsset extends Asset {
- public enum CustomValueType {
- ONE,
- TWO,
- THREE
- }
+ public enum CustomValueType {
+ ONE,
+ TWO,
+ THREE
+ }
- public static final ValueDescriptor CUSTOM_VALUE_TYPE_VALUE_DESCRIPTOR = new ValueDescriptor<>("customValueType", CustomValueType.class);
+ public static final ValueDescriptor CUSTOM_VALUE_TYPE_VALUE_DESCRIPTOR =
+ new ValueDescriptor<>("customValueType", CustomValueType.class);
- public static final AttributeDescriptor CUSTOM_VALUE_TYPE_ATTRIBUTE_DESCRIPTOR = new AttributeDescriptor<>("customAttribute", CUSTOM_VALUE_TYPE_VALUE_DESCRIPTOR);
+ public static final AttributeDescriptor CUSTOM_VALUE_TYPE_ATTRIBUTE_DESCRIPTOR =
+ new AttributeDescriptor<>("customAttribute", CUSTOM_VALUE_TYPE_VALUE_DESCRIPTOR);
- public static final AssetDescriptor CUSTOM_ASSET_ASSET_DESCRIPTOR = new AssetDescriptor<>("brightness-auto", "00aaaa", CustomAsset.class);
+ public static final AssetDescriptor CUSTOM_ASSET_ASSET_DESCRIPTOR =
+ new AssetDescriptor<>("brightness-auto", "00aaaa", CustomAsset.class);
- public Optional getCustomAttribute() {
- return getAttributes().getValue(CUSTOM_VALUE_TYPE_ATTRIBUTE_DESCRIPTOR);
- }
+ public Optional getCustomAttribute() {
+ return getAttributes().getValue(CUSTOM_VALUE_TYPE_ATTRIBUTE_DESCRIPTOR);
+ }
- public CustomAsset setCustomAttribute(CustomValueType value) {
- getAttributes().getOrCreate(CUSTOM_VALUE_TYPE_ATTRIBUTE_DESCRIPTOR).setValue(value);
- return this;
- }
+ public CustomAsset setCustomAttribute(CustomValueType value) {
+ getAttributes().getOrCreate(CUSTOM_VALUE_TYPE_ATTRIBUTE_DESCRIPTOR).setValue(value);
+ return this;
+ }
}
diff --git a/model/src/main/java/org/openremote/model/custom/CustomAssetModelProvider.java b/model/src/main/java/org/openremote/model/custom/CustomAssetModelProvider.java
index 206f7cc4a..07c275b37 100644
--- a/model/src/main/java/org/openremote/model/custom/CustomAssetModelProvider.java
+++ b/model/src/main/java/org/openremote/model/custom/CustomAssetModelProvider.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,7 +12,9 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.model.custom;
@@ -23,8 +22,8 @@
public class CustomAssetModelProvider implements AssetModelProvider {
- @Override
- public boolean useAutoScan() {
- return true;
- }
+ @Override
+ public boolean useAutoScan() {
+ return true;
+ }
}
diff --git a/model/src/main/java/org/openremote/model/custom/CustomData.java b/model/src/main/java/org/openremote/model/custom/CustomData.java
index 653cb1221..520c69c43 100644
--- a/model/src/main/java/org/openremote/model/custom/CustomData.java
+++ b/model/src/main/java/org/openremote/model/custom/CustomData.java
@@ -1,9 +1,6 @@
/*
* Copyright 2022, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,23 +12,24 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.model.custom;
public class CustomData {
- protected String name;
- protected Integer age;
+ protected String name;
+ protected Integer age;
- protected CustomData() {
- }
+ protected CustomData() {}
- public String getName() {
- return name;
- }
+ public String getName() {
+ return name;
+ }
- public Integer getAge() {
- return age;
- }
+ public Integer getAge() {
+ return age;
+ }
}
diff --git a/model/src/main/java/org/openremote/model/custom/CustomEndpointResource.java b/model/src/main/java/org/openremote/model/custom/CustomEndpointResource.java
index 59ef3c73b..a6f71696f 100644
--- a/model/src/main/java/org/openremote/model/custom/CustomEndpointResource.java
+++ b/model/src/main/java/org/openremote/model/custom/CustomEndpointResource.java
@@ -1,9 +1,6 @@
/*
* Copyright 2022, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,7 +12,9 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.model.custom;
@@ -23,11 +22,12 @@
import jakarta.ws.rs.Path;
/**
- * This is an example custom JAX-RS endpoint; this will be compiled and made available in the typescript model as well
+ * This is an example custom JAX-RS endpoint; this will be compiled and made available in the
+ * typescript model as well
*/
@Path("custom")
public interface CustomEndpointResource {
- @POST
- void submitData(CustomData customData);
+ @POST
+ void submitData(CustomData customData);
}
diff --git a/package.json b/package.json
index 95488c09d..29690a6b3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,12 @@
{
"packageManager": "yarn@4.17.1",
"private": true,
+ "scripts": {
+ "lint": "cd ui && eslint . --cache --cache-location ../.eslintcache",
+ "lint:fix": "cd ui && eslint . --fix --cache --cache-location ../.eslintcache",
+ "format": "cd ui && prettier . --write",
+ "format:check": "cd ui && prettier . --check"
+ },
"resolutions": {
"axios": "1.18.1"
},
@@ -8,5 +14,19 @@
"ui/app/*",
"ui/component/*",
"ui/demo/*"
- ]
+ ],
+ "devDependencies": {
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
+ "eslint": "8.57.1",
+ "eslint-config-prettier": "10.1.8",
+ "eslint-config-standard": "17.1.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-lit": "2.3.1",
+ "eslint-plugin-n": "16.6.2",
+ "eslint-plugin-promise": "6.6.0",
+ "eslint-plugin-wc": "3.1.0",
+ "prettier": "3.9.6",
+ "typescript": "5.8.3"
+ }
}
diff --git a/profile/dev-testing.yml b/profile/dev-testing.yml
index e08e383b8..187822e6b 100644
--- a/profile/dev-testing.yml
+++ b/profile/dev-testing.yml
@@ -21,7 +21,7 @@ services:
postgresql:
condition: service_healthy
environment:
- KC_HOSTNAME_STRICT_HTTPS: 'false'
+ KC_HOSTNAME_STRICT_HTTPS: "false"
KC_HOSTNAME_PORT: ${KC_HOSTNAME_PORT:-8080}
# Prevent theme caching during dev
KEYCLOAK_START_OPTS: --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false
diff --git a/profile/dev-ui.yml b/profile/dev-ui.yml
index 7e54e9c73..ba0da7c14 100644
--- a/profile/dev-ui.yml
+++ b/profile/dev-ui.yml
@@ -10,7 +10,6 @@ volumes:
manager-data:
services:
-
keycloak:
extends:
file: *base
@@ -26,7 +25,7 @@ services:
condition: service_healthy
environment:
KC_HOSTNAME: ${OR_HOSTNAME:-localhost}
- KC_HOSTNAME_STRICT_HTTPS: 'false'
+ KC_HOSTNAME_STRICT_HTTPS: "false"
KC_HOSTNAME_PORT: ${KC_HOSTNAME_PORT:-8080}
# Prevent theme caching during dev
KEYCLOAK_START_OPTS: --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false
diff --git a/project.gradle b/project.gradle
index 7239692cb..270f19e5b 100644
--- a/project.gradle
+++ b/project.gradle
@@ -1,11 +1,20 @@
// Common configuration applied to all projects
-import java.nio.file.*
-import java.nio.file.attribute.*
+
+import java.io.Serializable
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+import java.nio.file.attribute.PosixFileAttributes
+import java.nio.file.attribute.PosixFilePermission
import java.util.stream.Collectors
-import java.util.stream.Stream
import java.util.stream.StreamSupport
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility
+
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+import org.jetbrains.gradle.ext.Application
+import org.jetbrains.gradle.ext.JUnit
+
import cz.habarta.typescript.generator.DateMapping
import cz.habarta.typescript.generator.EnumMapping
import cz.habarta.typescript.generator.Jackson2Configuration
@@ -19,51 +28,49 @@ import cz.habarta.typescript.generator.TypeScriptOutputKind
import static org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS
import static org.apache.tools.ant.taskdefs.condition.Os.isFamily
import static org.jetbrains.gradle.ext.ShortenCommandLine.MANIFEST
-import org.jetbrains.gradle.ext.Application
-import org.jetbrains.gradle.ext.JUnit
configurations.all {
- resolutionStrategy {
+ resolutionStrategy {
- // check for changing (i.e. SNAPSHOT) updates every build
- cacheChangingModulesFor 0, 'seconds'
+ // check for changing (i.e. SNAPSHOT) updates every build
+ cacheChangingModulesFor 0, 'seconds'
- //failOnVersionConflict()
+ //failOnVersionConflict()
- // This has been replaced with eclipse angus implementation
- exclude group: "com.sun.activation", module: "jakarta.activation"
+ // This has been replaced with eclipse angus implementation
+ exclude group: "com.sun.activation", module: "jakarta.activation"
- // This has been replaced with org.bouncycastle bcprov-jdk18on
- exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
+ // This has been replaced with org.bouncycastle bcprov-jdk18on
+ exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
- eachDependency { DependencyResolveDetails details ->
- if (details.requested.group == 'org.eclipse.angus' && details.requested.name == 'angus-activation' && details.requested.version == '1.0.0') {
- details.useVersion '2.0.0'
- }
- if (details.requested.group == 'commons-io' && details.requested.name == 'commons-io') {
- details.useVersion '2.14.0'
- }
- }
+ eachDependency { DependencyResolveDetails details ->
+ if (details.requested.group == 'org.eclipse.angus' && details.requested.name == 'angus-activation' && details.requested.version == '1.0.0') {
+ details.useVersion '2.0.0'
+ }
+ if (details.requested.group == 'commons-io' && details.requested.name == 'commons-io') {
+ details.useVersion '2.14.0'
+ }
}
+ }
}
// Ensure git hook creation task is executed
if (project == rootProject) {
- project.afterEvaluate {
- tasks.configureEach { task ->
- if (task.name in ['encryptFiles', 'decryptFiles']) {
- task.notCompatibleWithConfigurationCache("GradleFileEncrypt stores javax.crypto.SecretKey/SecretKeySpec task state")
- }
- }
+ project.afterEvaluate {
+ tasks.configureEach { task ->
+ if (task.name in ['encryptFiles', 'decryptFiles']) {
+ task.notCompatibleWithConfigurationCache("GradleFileEncrypt stores javax.crypto.SecretKey/SecretKeySpec task state")
+ }
+ }
- if (rootProject.hasProperty("gradleFileEncrypt")) {
- println("File encryption plugin config found, configuring git pre commit hook and decrypt task dependency")
- try {
- // Write git hook for encryption plugin checks before any commit
- def path = Paths.get(rootProject.projectDir.path, ".git/hooks/pre-commit")
- def f = path.toFile()
- f.text = """#!/bin/sh
+ if (rootProject.hasProperty("gradleFileEncrypt")) {
+ println("File encryption plugin config found, configuring git pre commit hook and decrypt task dependency")
+ try {
+ // Write git hook for encryption plugin checks before any commit
+ def path = Paths.get(rootProject.projectDir.path, ".git/hooks/pre-commit")
+ def f = path.toFile()
+ f.text = """#!/bin/sh
echo "***** Running gradle encryption plugin checkFilesGitIgnored task ******"
./gradlew checkFilesGitIgnoredNew
status=\$?
@@ -72,111 +79,111 @@ if [ \$status != 0 ]; then
fi
exit \$status
"""
- Set perms = Files.readAttributes(path, PosixFileAttributes.class).permissions()
- perms.add(PosixFilePermission.OWNER_WRITE)
- perms.add(PosixFilePermission.OWNER_READ)
- perms.add(PosixFilePermission.OWNER_EXECUTE)
- perms.add(PosixFilePermission.GROUP_WRITE)
- perms.add(PosixFilePermission.GROUP_READ)
- perms.add(PosixFilePermission.GROUP_EXECUTE)
- perms.add(PosixFilePermission.OTHERS_READ)
- perms.add(PosixFilePermission.OTHERS_EXECUTE)
- Files.setPosixFilePermissions(path, perms)
- } catch (Exception ignored) {}
-
- // Add dependency on decrypt task for deployment installDist only if GFE_PASSWORD defined
- def password = System.env.GFE_PASSWORD
- if (password != null) {
- project(':deployment').afterEvaluate { deploymentProject ->
- try {
- def decryptTaskProvider = rootProject.tasks.named("decryptFiles")
- deploymentProject.tasks.named('installDist').configure { installDist ->
- installDist.dependsOn(decryptTaskProvider)
- installDist.mustRunAfter(decryptTaskProvider)
- }
- } catch (Exception ex) {
- println("Failed to add decryptFiles task dependency: " + ex)
- }
- }
- }
- } else {
- // Remove git hook
- try {
- Files.delete(Paths.get(rootProject.projectDir.path, ".git/hooks/pre-commit"))
- } catch (Exception ignored) {
+ Set perms = Files.readAttributes(path, PosixFileAttributes.class).permissions()
+ perms.add(PosixFilePermission.OWNER_WRITE)
+ perms.add(PosixFilePermission.OWNER_READ)
+ perms.add(PosixFilePermission.OWNER_EXECUTE)
+ perms.add(PosixFilePermission.GROUP_WRITE)
+ perms.add(PosixFilePermission.GROUP_READ)
+ perms.add(PosixFilePermission.GROUP_EXECUTE)
+ perms.add(PosixFilePermission.OTHERS_READ)
+ perms.add(PosixFilePermission.OTHERS_EXECUTE)
+ Files.setPosixFilePermissions(path, perms)
+ } catch (Exception ignored) {}
+
+ // Add dependency on decrypt task for deployment installDist only if GFE_PASSWORD defined
+ def password = System.env.GFE_PASSWORD
+ if (password != null) {
+ project(':deployment').afterEvaluate { deploymentProject ->
+ try {
+ def decryptTaskProvider = rootProject.tasks.named("decryptFiles")
+ deploymentProject.tasks.named('installDist').configure { installDist ->
+ installDist.dependsOn(decryptTaskProvider)
+ installDist.mustRunAfter(decryptTaskProvider)
}
+ } catch (Exception ex) {
+ println("Failed to add decryptFiles task dependency: " + ex)
+ }
}
+ }
+ } else {
+ // Remove git hook
+ try {
+ Files.delete(Paths.get(rootProject.projectDir.path, ".git/hooks/pre-commit"))
+ } catch (Exception ignored) {
+ }
}
+ }
}
// Configure Conditional plugins
if (project == rootProject) {
- pluginManager.apply('org.jetbrains.gradle.plugin.idea-ext')
-
- // Configure IDEA
- if (project.hasProperty("idea") && idea.project) {
- // IDEA settings
- idea.project.settings {
- compiler {
- javac {
- javacAdditionalOptions = "-parameters"
- }
- }
- runConfigurations {
- defaults(JUnit) {
- shortenCommandLine = MANIFEST
- workingDirectory = projectDir.toString()
- }
- defaults(Application) {
- mainClass = 'org.openremote.manager.Main'
- shortenCommandLine = MANIFEST
- workingDirectory = projectDir.toString()
- }
- "Empty"(Application) {
- moduleName = getProject().idea.module.name + ".openremote.manager.main"
- }
- "Custom Deployment"(Application) {
- moduleName = "${getProject().idea.module.name}.setup.main"
- envs = [
- OR_MAP_SETTINGS_PATH: "deployment/map/mapsettings.json",
- OR_MAP_TILES_PATH: "deployment/map/mapdata.mbtiles",
- OR_CUSTOM_APP_DOCROOT: "deployment/build/image/manager/app",
- OR_CONSOLE_APP_CONFIG_DOCROOT: "deployment/manager/consoleappconfig"
- ]
- }
- }
+ pluginManager.apply('org.jetbrains.gradle.plugin.idea-ext')
+
+ // Configure IDEA
+ if (project.hasProperty("idea") && idea.project) {
+ // IDEA settings
+ idea.project.settings {
+ compiler {
+ javac {
+ javacAdditionalOptions = "-parameters"
+ }
+ }
+ runConfigurations {
+ defaults(JUnit) {
+ shortenCommandLine = MANIFEST
+ workingDirectory = projectDir.toString()
+ }
+ defaults(Application) {
+ mainClass = 'org.openremote.manager.Main'
+ shortenCommandLine = MANIFEST
+ workingDirectory = projectDir.toString()
}
+ "Empty"(Application) {
+ moduleName = getProject().idea.module.name + ".openremote.manager.main"
+ }
+ "Custom Deployment"(Application) {
+ moduleName = "${getProject().idea.module.name}.setup.main"
+ envs = [
+ OR_MAP_SETTINGS_PATH: "deployment/map/mapsettings.json",
+ OR_MAP_TILES_PATH: "deployment/map/mapdata.mbtiles",
+ OR_CUSTOM_APP_DOCROOT: "deployment/build/image/manager/app",
+ OR_CONSOLE_APP_CONFIG_DOCROOT: "deployment/manager/consoleappconfig"
+ ]
+ }
+ }
}
+ }
}
// Give test projects more memory (Gradle 5 reduced this to 512MB)
subprojects {
- tasks.withType(Test) {
- maxHeapSize = "2g"
- }
+ tasks.withType(Test) {
+ maxHeapSize = "2g"
+ }
}
// Default repositories for dependency resolution
repositories {
- mavenLocal()
- // Needs to be above central due to org.geotools:gt-main issues
- maven {
- url = "https://repo.osgeo.org/repository/release/"
- }
- mavenCentral()
- maven {
- url = "https://pkgs.dev.azure.com/OpenRemote/OpenRemote/_packaging/OpenRemote/maven/v1"
- }
- maven {
- url = "https://central.sonatype.com/repository/maven-snapshots"
- }
- maven {
- url = "https://repo.jenkins-ci.org/releases/"
- }
- maven {
- url = 'https://jitpack.io'
- }
+ mavenLocal()
+ // Needs to be above central due to org.geotools:gt-main issues
+ maven {
+ url = "https://repo.osgeo.org/repository/release/"
+ }
+ mavenCentral()
+ maven {
+ url = "https://pkgs.dev.azure.com/OpenRemote/OpenRemote/_packaging/OpenRemote/maven/v1"
+ }
+ maven {
+ url = "https://central.sonatype.com/repository/maven-snapshots"
+ }
+ maven {
+ url = "https://repo.jenkins-ci.org/releases/"
+ }
+ maven {
+ url = 'https://jitpack.io'
+ }
}
// Eclipse needs help
@@ -186,212 +193,214 @@ pluginManager.apply('eclipse')
pluginManager.apply('idea')
// Use the same output directories in IDE as in gradle
idea {
- module {
- outputDir = file('build/classes/main')
- testOutputDir = file('build/classes/test')
- excludeDirs += file(".node")
- excludeDirs += file("node_modules")
- excludeDirs += file("dist")
- excludeDirs += file("lib")
- excludeDirs += file("build")
- }
+ module {
+ outputDir = file('build/classes/main')
+ testOutputDir = file('build/classes/test')
+ excludeDirs += file(".node")
+ excludeDirs += file("node_modules")
+ excludeDirs += file("dist")
+ excludeDirs += file("lib")
+ excludeDirs += file("build")
+ }
}
/**
* This defines reusable config for the typescript generator plugin
*/
def createTSGeneratorConfigForModel(String outputFileName, Project...customProjectsToScan) {
- def config = createTSGeneratorConfig(false, outputFileName, customProjectsToScan) <<
- {
- extensionClasses = [
- "org.openremote.model.util.AssetModelInfoExtension",
- "CustomExtension",
- "JsonSerializeExtension"
- ]
- customTypeMappings = [
- "com.fasterxml.jackson.databind.node.ObjectNode:{ [id: string]: any }",
- "java.lang.Class:string",
- "org.openremote.model.attribute.MetaItem:any"
- ]
- customTypeProcessor = "CustomTypeProcessor"
- generateInfoJson = true
- }
- return config
+ def config = createTSGeneratorConfig(false, outputFileName, customProjectsToScan) << {
+ extensionClasses = [
+ "org.openremote.model.util.AssetModelInfoExtension",
+ "CustomExtension",
+ "JsonSerializeExtension"
+ ]
+ customTypeMappings = [
+ "com.fasterxml.jackson.databind.node.ObjectNode:{ [id: string]: any }",
+ "java.lang.Class:string",
+ "org.openremote.model.attribute.MetaItem:any"
+ ]
+ customTypeProcessor = "CustomTypeProcessor"
+ generateInfoJson = true
+ }
+ return config
}
def createTSGeneratorConfigForClient(String outputFileName, File modelInfoJson, Project...customProjectsToScan) {
- def config = createTSGeneratorConfig(true, outputFileName, customProjectsToScan) <<
- {
- extensionClasses = [
- "CustomExtension",
- "JsonSerializeExtension",
- "CustomAggregatedApiClient",
- "cz.habarta.typescript.generator.ext.AxiosClientExtension"
- ]
- customTypeMappings = [
- "com.fasterxml.jackson.databind.node.ObjectNode:{ [id: string]: any }",
- "java.lang.Class:string",
- "org.openremote.model.attribute.MetaItem:any",
- "org.openremote.model.asset.Asset:Model.Asset",
- "org.openremote.model.asset.AssetDescriptor:Model.AssetDescriptor",
- "org.openremote.model.asset.agent.Agent:Model.Agent",
- "org.openremote.model.asset.agent.AgentDescriptor:Model.AgentDescriptor",
- "org.openremote.model.value.MetaItemDescriptor:Model.MetaItemDescriptor",
- "org.openremote.model.value.ValueDescriptor:Model.ValueDescriptor"
- ]
- moduleDependencies = [
- ModuleDependency.module(
- "model",
- "Model",
- modelInfoJson,
- (String) null,
- (String) null
- )
- ]
- restNamespacing = RestNamespacing.perResource
- }
- return config
+ def config = createTSGeneratorConfig(true, outputFileName, customProjectsToScan) << {
+ extensionClasses = [
+ "CustomExtension",
+ "JsonSerializeExtension",
+ "CustomAggregatedApiClient",
+ "cz.habarta.typescript.generator.ext.AxiosClientExtension"
+ ]
+ customTypeMappings = [
+ "com.fasterxml.jackson.databind.node.ObjectNode:{ [id: string]: any }",
+ "java.lang.Class:string",
+ "org.openremote.model.attribute.MetaItem:any",
+ "org.openremote.model.asset.Asset:Model.Asset",
+ "org.openremote.model.asset.AssetDescriptor:Model.AssetDescriptor",
+ "org.openremote.model.asset.agent.Agent:Model.Agent",
+ "org.openremote.model.asset.agent.AgentDescriptor:Model.AgentDescriptor",
+ "org.openremote.model.value.MetaItemDescriptor:Model.MetaItemDescriptor",
+ "org.openremote.model.value.ValueDescriptor:Model.ValueDescriptor"
+ ]
+ moduleDependencies = [
+ ModuleDependency.module(
+ "model",
+ "Model",
+ modelInfoJson,
+ (String) null,
+ (String) null
+ )
+ ]
+ restNamespacing = RestNamespacing.perResource
+ }
+ return config
}
def createTSGeneratorConfig(boolean outputAPIClient, String outputFileName, Project...customProjectsToScan) {
- def classPatternGlobs = Arrays.stream(customProjectsToScan).flatMap { project ->
- return project.sourceSets.findByName('main').java.srcDirs.stream().map {
- def srcPath = it
- def isPackageDir = true
- while (srcPath != null && isPackageDir) {
- def files = srcPath.listFiles()
- isPackageDir = files != null && files.length == 1 && files[0].isDirectory()
- if (isPackageDir) {
- srcPath = files[0]
- }
- }
- Path packagePath = it.toPath().relativize(srcPath.toPath())
- return StreamSupport
- .stream(packagePath.spliterator(), false)
- .map(Path::toString)
- .collect(Collectors.joining(".")) + (outputAPIClient ? ".**Resource" : ".**")
+ def classPatternGlobs = Arrays.stream(customProjectsToScan).flatMap { project ->
+ return project.sourceSets.findByName('main').java.srcDirs.stream().map {
+ def srcPath = it
+ def isPackageDir = true
+ while (srcPath != null && isPackageDir) {
+ def files = srcPath.listFiles()
+ isPackageDir = files != null && files.length == 1 && files[0].isDirectory()
+ if (isPackageDir) {
+ srcPath = files[0]
}
- }.toList()
-
- def baseClassPattern = outputAPIClient
- ? customProjectsToScan.length == 0 ? [ "org.openremote.model.**Resource" ] : []
- : [ "org.openremote.model.**" ]
-
- return {
- jsonLibrary = JsonLibrary.jackson2
- classPatterns = baseClassPattern + classPatternGlobs
- customTypeNamingFunction = "function(name, simpleName) { if (name.indexOf(\"\$\") > 0) return name.substr(name.lastIndexOf(\".\")+1).replace(\"\$\",\"\"); }"
- excludeClassPatterns = [
- "org.openremote.model.event.shared.*Filter**",
- "org.openremote.model.util.**",
- "org.openremote.model.flow.**",
- "java.io.**",
- "java.lang.**",
- "org.hibernate.**",
- "jakarta.**"
- ]
- mapEnum = EnumMapping.asEnum
- mapDate = DateMapping.asNumber
- optionalProperties = OptionalProperties.all // TODO: cleanup model to be more explicit about optional params
- outputFileType = TypeScriptFileType.implementationFile
- outputKind = TypeScriptOutputKind.module
- outputFile = outputFileName
-
- def jackson2Config = new Jackson2Configuration()
- jackson2Config.fieldVisibility = Visibility.ANY
- jackson2Config.creatorVisibility = Visibility.ANY
- jackson2Config.getterVisibility = Visibility.NONE
- jackson2Config.isGetterVisibility = Visibility.NONE
- jackson2Config.setterVisibility = Visibility.NONE
- jackson2Configuration.set(jackson2Config)
-
- jackson2Modules = [
- "com.fasterxml.jackson.datatype.jdk8.Jdk8Module",
- "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule",
- "com.fasterxml.jackson.module.paramnames.ParameterNamesModule"
- ]
+ }
+ Path packagePath = it.toPath().relativize(srcPath.toPath())
+ return StreamSupport
+ .stream(packagePath.spliterator(), false)
+ .map({ java.nio.file.Path path -> path.toString() })
+ .collect(Collectors.joining(".")) + (outputAPIClient ? ".**Resource" : ".**")
}
+ }.toList()
+
+ def baseClassPattern = outputAPIClient
+ ? customProjectsToScan.length == 0 ? ["org.openremote.model.**Resource"] : []
+ : ["org.openremote.model.**"]
+
+ return {
+ jsonLibrary = JsonLibrary.jackson2
+ classPatterns = baseClassPattern + classPatternGlobs
+ customTypeNamingFunction = "function(name, simpleName) { if (name.indexOf(\"\$\") > 0) return name.substr(name.lastIndexOf(\".\")+1).replace(\"\$\",\"\"); }"
+ excludeClassPatterns = [
+ "org.openremote.model.event.shared.*Filter**",
+ "org.openremote.model.util.**",
+ "org.openremote.model.flow.**",
+ "java.io.**",
+ "java.lang.**",
+ "org.hibernate.**",
+ "jakarta.**"
+ ]
+ mapEnum = EnumMapping.asEnum
+ mapDate = DateMapping.asNumber
+ optionalProperties = OptionalProperties.all // TODO: cleanup model to be more explicit about optional params
+ outputFileType = TypeScriptFileType.implementationFile
+ outputKind = TypeScriptOutputKind.module
+ outputFile = outputFileName
+
+ def jackson2Config = new Jackson2Configuration()
+ jackson2Config.fieldVisibility = Visibility.ANY
+ jackson2Config.creatorVisibility = Visibility.ANY
+ jackson2Config.getterVisibility = Visibility.NONE
+ jackson2Config.isGetterVisibility = Visibility.NONE
+ jackson2Config.setterVisibility = Visibility.NONE
+ jackson2Configuration.set(jackson2Config)
+
+ jackson2Modules = [
+ "com.fasterxml.jackson.datatype.jdk8.Jdk8Module",
+ "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule",
+ "com.fasterxml.jackson.module.paramnames.ParameterNamesModule"
+ ]
+ }
}
def getYarnInstallTask() {
- tasks.getByPath(":yarnInstall")
+ tasks.getByPath(":yarnInstall")
}
ext {
- getYarnInstallTask = this.&getYarnInstallTask
- createTSGeneratorConfigForClient = this.&createTSGeneratorConfigForClient
- createTSGeneratorConfigForModel = this.&createTSGeneratorConfigForModel
+ getYarnInstallTask = this.&getYarnInstallTask
+ createTSGeneratorConfigForClient = this.&createTSGeneratorConfigForClient
+ createTSGeneratorConfigForModel = this.&createTSGeneratorConfigForModel
}
// Add UI tasks
-ext.npmCommand = {
- cmd ->
- isFamily(FAMILY_WINDOWS) ? "${cmd}.cmd" : cmd
+ext.npmCommand = { cmd ->
+ isFamily(FAMILY_WINDOWS) ? "${cmd}.cmd" : cmd
}
// Add yarn tasks
tasks.register('yarnInstall', Exec) {
- commandLine npmCommand("yarn"), "install"
+ commandLine npmCommand("yarn"), "install"
}
tasks.register('yarnInstallForce', Exec) {
- commandLine npmCommand("yarn"), "install", "--force"
+ commandLine npmCommand("yarn"), "install", "--force"
}
tasks.register('npmClean', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "clean"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "clean"
}
tasks.register('npmBuild', Exec) {
- mustRunAfter npmClean
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "build"
+ mustRunAfter npmClean
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "build"
}
tasks.register('npmServe', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "serve"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "serve"
}
tasks.register('npmPrepare', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "prepublishOnly"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "prepublishOnly"
}
tasks.register('npmPublish', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "publish"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "publish"
}
tasks.register('npmServeProduction', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "serveProduction"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "serveProduction"
}
// Add typescript tasks
tasks.register('tscWatch', Exec) {
- commandLine npmCommand("npx"), "tsc", "-b", "--watch"
+ commandLine npmCommand("npx"), "tsc", "-b", "--watch"
}
// Configure Java build
plugins.withType(JavaPlugin).whenPluginAdded {
- // Use Java 21
- tasks.withType(JavaCompile) {
- sourceCompatibility = JavaVersion.VERSION_21
- targetCompatibility = JavaVersion.VERSION_21
- def warnLogFile = file("$buildDir/${name}Warnings.log")
- logging.addStandardErrorListener(new StandardOutputListener() {
- void onOutput(CharSequence output) {
+ // Use Java 21
+ tasks.withType(JavaCompile) {
+ sourceCompatibility = JavaVersion.VERSION_21
+ targetCompatibility = JavaVersion.VERSION_21
+ def warnLogFile = file("$buildDir/${name}Warnings.log")
+ logging.addStandardErrorListener(new StandardOutputListener() {
+ void onOutput(CharSequence output) {
warnLogFile << output
- }
- })
- options.compilerArgs += ["-Xlint:unchecked", "-Xlint:deprecation", "-parameters"]
- options.encoding = 'UTF-8'
- }
-
- // Allow dependencyInsight checks across all projects
- tasks.register('allDependencyInsight', DependencyInsightReportTask) {}
+ }
+ })
+ options.compilerArgs += ["-Xlint:unchecked", "-Xlint:deprecation", "-parameters"]
+ options.encoding = 'UTF-8'
+ }
+
+ // Allow dependencyInsight checks across all projects
+ tasks.register('allDependencyInsight', DependencyInsightReportTask) {}
+
+ base {
+ // JAR/ZIP base name is the fully qualified subproject name
+ archivesName = "${rootProject.name}${path.replaceAll(":", "-")}"
+ }
+}
- base {
- // JAR/ZIP base name is the fully qualified subproject name
- archivesName = "${rootProject.name}${path.replaceAll(":", "-")}"
- }
+// Configure Spotless
+if (project == rootProject) {
+ apply from: "${rootDir}/spotless.gradle"
}
// POM generator
diff --git a/settings.gradle b/settings.gradle
index 3bcd72eb2..56a022a2c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,21 +1,21 @@
pluginManagement {
- resolutionStrategy {
- eachPlugin {
- if (requested.id.id ==
- 'io.openremote.com.cherryperry.gradle-file-encrypt') {
- useModule(
- "com.github.openremote:GradleFileEncrypt:${requested.version}"
+ resolutionStrategy {
+ eachPlugin {
+ if (requested.id.id ==
+ 'io.openremote.com.cherryperry.gradle-file-encrypt') {
+ useModule(
+ "com.github.openremote:GradleFileEncrypt:${requested.version}"
)
- }
- }
+ }
}
+ }
- repositories {
- gradlePluginPortal()
- maven {
- url = uri('https://jitpack.io')
- }
- }
+ repositories {
+ gradlePluginPortal()
+ maven {
+ url = uri('https://jitpack.io')
+ }
+ }
}
// TODO: These plugins remain here because project.gradle imports their classes,
@@ -23,8 +23,9 @@ pluginManagement {
// configuration into convention plugins/build logic so their versions can be
// managed through libs.versions.toml.
plugins {
- id 'cz.habarta.typescript-generator' version "$typescriptGeneratorVersion" apply false
- id 'org.jetbrains.gradle.plugin.idea-ext' version "$ideaExtPluginVersion" apply false
+ id 'com.diffplug.spotless' version "$spotlessVersion" apply false
+ id 'cz.habarta.typescript-generator' version "$typescriptGeneratorVersion" apply false
+ id 'org.jetbrains.gradle.plugin.idea-ext' version "$ideaExtPluginVersion" apply false
}
rootProject.name = "$projectName"
@@ -34,22 +35,22 @@ fileTree(
dir: rootDir,
include: "**/build.gradle",
excludes: [
- ".git/**",
- "**/.git/**",
- ".gradle/**",
- "**/.gradle/**",
- "**/build/**",
- "**/node_modules/**",
- "**/generic_app/**",
- "**/dist/**",
- "**/lib/**"
+ ".git/**",
+ "**/.git/**",
+ ".gradle/**",
+ "**/.gradle/**",
+ "**/build/**",
+ "**/node_modules/**",
+ "**/generic_app/**",
+ "**/dist/**",
+ "**/lib/**"
]
-)
+ )
.filter { it.parentFile != rootDir }
.filter { !new File(it.parentFile, ".buildignore").exists() }
.each {
- include it.parentFile.canonicalPath
- .replace(rootDir.canonicalPath, "")
- .replace("\\", ":")
- .replace("/", ":")
+ include it.parentFile.canonicalPath
+ .replace(rootDir.canonicalPath, "")
+ .replace("\\", ":")
+ .replace("/", ":")
}
diff --git a/setup/build.gradle b/setup/build.gradle
index e1031d3dc..99ee790f5 100644
--- a/setup/build.gradle
+++ b/setup/build.gradle
@@ -1,22 +1,22 @@
plugins {
- id 'java-library'
+ id 'java-library'
}
configurations {
- developmentRuntime
+ developmentRuntime
}
dependencies {
- api project(":agent")
- api project(":model")
- api project(":manager")
+ api project(":agent")
+ api project(":model")
+ api project(":manager")
- developmentRuntime libs.openremote.ui.insights
- developmentRuntime libs.openremote.ui.manager
- developmentRuntime libs.openremote.ui.shared
- developmentRuntime libs.openremote.ui.swagger
+ developmentRuntime libs.openremote.ui.insights
+ developmentRuntime libs.openremote.ui.manager
+ developmentRuntime libs.openremote.ui.shared
+ developmentRuntime libs.openremote.ui.swagger
}
tasks.register('installDist') {
- dependsOn jar
+ dependsOn jar
}
diff --git a/setup/src/main/java/org/openremote/manager/setup/custom/CustomKeycloakSetup.java b/setup/src/main/java/org/openremote/manager/setup/custom/CustomKeycloakSetup.java
index ddf391359..37883d905 100644
--- a/setup/src/main/java/org/openremote/manager/setup/custom/CustomKeycloakSetup.java
+++ b/setup/src/main/java/org/openremote/manager/setup/custom/CustomKeycloakSetup.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,43 +12,51 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.manager.setup.custom;
+import static org.openremote.model.util.MapAccess.getString;
+
import org.openremote.manager.setup.AbstractKeycloakSetup;
import org.openremote.model.Container;
import org.openremote.model.security.ClientRole;
import org.openremote.model.security.Realm;
import org.openremote.model.util.TextUtil;
-import static org.openremote.model.util.MapAccess.getString;
-
public class CustomKeycloakSetup extends AbstractKeycloakSetup {
- public static final String CUSTOM_USER_PASSWORD = "CUSTOM_USER_PASSWORD";
- public static final String CUSTOM_USER_PASSWORD_DEFAULT = "custom";
- protected final String customUserPassword;
-
- public CustomKeycloakSetup(Container container, boolean isProduction) {
- super(container);
+ public static final String CUSTOM_USER_PASSWORD = "CUSTOM_USER_PASSWORD";
+ public static final String CUSTOM_USER_PASSWORD_DEFAULT = "custom";
+ protected final String customUserPassword;
- customUserPassword = getString(container.getConfig(), CUSTOM_USER_PASSWORD, CUSTOM_USER_PASSWORD_DEFAULT);
-
- if (isProduction && TextUtil.isNullOrEmpty(customUserPassword)) {
- throw new IllegalStateException("Custom user password must be supplied in production");
- }
- }
+ public CustomKeycloakSetup(Container container, boolean isProduction) {
+ super(container);
- @Override
- public void onStart() throws Exception {
- // Create custom realm
- Realm customRealm = createRealm("custom", "Custom", true);
+ customUserPassword =
+ getString(container.getConfig(), CUSTOM_USER_PASSWORD, CUSTOM_USER_PASSWORD_DEFAULT);
- // Create user(s) for custom realm
- createUser("custom", "custom", customUserPassword, "First", "Last", null, true, new ClientRole[] {
- ClientRole.READ,
- ClientRole.WRITE
- });
+ if (isProduction && TextUtil.isNullOrEmpty(customUserPassword)) {
+ throw new IllegalStateException("Custom user password must be supplied in production");
}
+ }
+
+ @Override
+ public void onStart() throws Exception {
+ // Create custom realm
+ Realm customRealm = createRealm("custom", "Custom", true);
+
+ // Create user(s) for custom realm
+ createUser(
+ "custom",
+ "custom",
+ customUserPassword,
+ "First",
+ "Last",
+ null,
+ true,
+ new ClientRole[] {ClientRole.READ, ClientRole.WRITE});
+ }
}
diff --git a/setup/src/main/java/org/openremote/manager/setup/custom/CustomManagerSetup.java b/setup/src/main/java/org/openremote/manager/setup/custom/CustomManagerSetup.java
index 186b8127d..f5c9c1a58 100644
--- a/setup/src/main/java/org/openremote/manager/setup/custom/CustomManagerSetup.java
+++ b/setup/src/main/java/org/openremote/manager/setup/custom/CustomManagerSetup.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,7 +12,9 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.manager.setup.custom;
@@ -26,15 +25,15 @@
public class CustomManagerSetup extends ManagerSetup {
- public CustomManagerSetup(Container container) {
- super(container);
- }
+ public CustomManagerSetup(Container container) {
+ super(container);
+ }
- @Override
- public void onStart() throws Exception {
- super.onStart();
+ @Override
+ public void onStart() throws Exception {
+ super.onStart();
- ThingAsset thing = new ThingAsset("Custom Thing").setRealm(Constants.MASTER_REALM);
- thing = assetStorageService.merge(thing);
- }
+ ThingAsset thing = new ThingAsset("Custom Thing").setRealm(Constants.MASTER_REALM);
+ thing = assetStorageService.merge(thing);
+ }
}
diff --git a/setup/src/main/java/org/openremote/manager/setup/custom/CustomSetupTasks.java b/setup/src/main/java/org/openremote/manager/setup/custom/CustomSetupTasks.java
index 478045003..551721a66 100644
--- a/setup/src/main/java/org/openremote/manager/setup/custom/CustomSetupTasks.java
+++ b/setup/src/main/java/org/openremote/manager/setup/custom/CustomSetupTasks.java
@@ -1,9 +1,6 @@
/*
* Copyright 2021, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,30 +12,29 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.manager.setup.custom;
+import java.util.Arrays;
+import java.util.List;
import org.openremote.model.Container;
import org.openremote.model.setup.Setup;
import org.openremote.model.setup.SetupTasks;
-import java.util.Arrays;
-import java.util.List;
-
public class CustomSetupTasks implements SetupTasks {
- public static final String PRODUCTION = "production";
+ public static final String PRODUCTION = "production";
- @Override
- public List createTasks(Container container, String setupType, boolean keycloakEnabled) {
+ @Override
+ public List createTasks(Container container, String setupType, boolean keycloakEnabled) {
- boolean isProduction = PRODUCTION.equalsIgnoreCase(setupType);
+ boolean isProduction = PRODUCTION.equalsIgnoreCase(setupType);
- // Add custom Setup task implementations here with tasks optionally dependent on setupType
- return Arrays.asList(
- new CustomKeycloakSetup(container, isProduction),
- new CustomManagerSetup(container)
- );
- }
+ // Add custom Setup task implementations here with tasks optionally dependent on setupType
+ return Arrays.asList(
+ new CustomKeycloakSetup(container, isProduction), new CustomManagerSetup(container));
+ }
}
diff --git a/spotless.gradle b/spotless.gradle
new file mode 100644
index 000000000..529bd8de1
--- /dev/null
+++ b/spotless.gradle
@@ -0,0 +1,232 @@
+import static java.time.Year.now
+import com.diffplug.gradle.spotless.GroovyGradleExtension
+import groovy.json.JsonSlurper
+
+if (project != rootProject) {
+ throw new GradleException('The Spotless configuration must only be applied to the root project.')
+}
+
+pluginManager.apply('com.diffplug.spotless')
+
+Map packageJson = new JsonSlurper().parse(rootProject.file('package.json')) as Map
+Map devDependencies =
+ (packageJson.devDependencies as Map)
+ .collectEntries { String name, Object version ->
+ [(name): version.toString()]
+ }
+Map eslintDependencies = devDependencies.findAll { String name, String ignored ->
+ name == 'eslint' ||
+ name == 'typescript' ||
+ name.startsWith('@typescript-eslint/') ||
+ name.startsWith('eslint-config-') ||
+ name.startsWith('eslint-plugin-')
+}
+['eslint', 'prettier', 'typescript'].each { String dependency ->
+ if (!devDependencies.containsKey(dependency)) {
+ throw new GradleException(
+ "Missing required formatter dependency '${dependency}' in package.json"
+ )
+ }
+}
+String prettierVersion = devDependencies.prettier
+
+def readIgnorePatterns = { File ignoreFile ->
+ List patterns = ignoreFile
+ .readLines('UTF-8')
+ .collect { it.trim() }
+ .findAll { it && !it.startsWith('#') }
+ List unsupported = patterns.findAll {
+ it.startsWith('!') || it.startsWith('/') || it.contains('\\')
+ }
+ if (!unsupported.isEmpty()) {
+ throw new GradleException(
+ ".prettierignore contains patterns that cannot safely be reused as Gradle Ant patterns: ${unsupported}"
+ )
+ }
+ return patterns
+}
+def prettierIgnorePatterns = readIgnorePatterns(rootProject.file('ui/.prettierignore'))
+def backendTarget = { Collection includes, Collection excludes = [] ->
+ rootProject.fileTree(rootProject.projectDir) {
+ includes.each { include it }
+ (prettierIgnorePatterns + excludes + ['ui/**'] + ['openremote/**']).each { exclude it }
+ }
+}
+def uiTarget = { Collection includes, Collection excludes = [] ->
+ rootProject.fileTree(rootProject.file('ui')) {
+ includes.each { include it }
+ prettierIgnorePatterns.each { exclude it }
+ excludes.each { exclude it }
+ }
+}
+
+String blockCommentLicenseDelimiter = '(?![ \\t]*/\\*(?:[ \\t]+|\\R[ \\t]*\\*[ \\t]+)Copyright\\b)(?![ \\t]*\\*)[ \\t]*\\S'
+
+spotless {
+ format('css') {
+ target uiTarget(['**/*.css'])
+ toggleOffOn()
+ licenseHeaderFile(rootProject.file('tools/spotless/openremote-license-header.txt'), blockCommentLicenseDelimiter).skipLinesMatching('^@charset\\s+.*$')
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ endWithNewline()
+ }
+
+ format 'gradleBackend', GroovyGradleExtension, {
+ target backendTarget(['*/**/*.gradle'])
+ toggleOffOn()
+ greclipse().configFile(rootProject.file('tools/spotless/greclipse-google.properties'))
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ format 'gradleRoot', GroovyGradleExtension, {
+ target '*.gradle'
+ toggleOffOn()
+ greclipse().configFile(rootProject.file('tools/spotless/greclipse-google.properties'))
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ format 'gradleUi', GroovyGradleExtension, {
+ target uiTarget(['**/*.gradle'])
+ toggleOffOn()
+ greclipse().configFile(rootProject.file('tools/spotless/greclipse-google.properties'))
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ // Groovy files are not yet formatted because some use '::' operations which greclipse currently cannot handle
+ groovy {
+ target backendTarget(['**/*.groovy'])
+ licenseHeaderFile(rootProject.file('tools/spotless/openremote-license-header.txt'), blockCommentLicenseDelimiter)
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ java {
+ target backendTarget(['**/src/*/java/**/*.java'], [
+ // Exclude third-party files that must remain unchanged, including their original license headers
+ '**/org/openremote/container/security/basic/PasswordStorage.java',
+ '**/org/openremote/model/util/TimeUtil.java',
+ '**/org/openremote/model/util/ValueUtil.java'
+ ])
+ toggleOffOn()
+ googleJavaFormat()
+ licenseHeaderFile rootProject.file('tools/spotless/openremote-license-header.txt')
+ removeUnusedImports()
+ formatAnnotations()
+ trimTrailingWhitespace()
+ endWithNewline()
+ }
+
+ javascript {
+ target uiTarget(['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'])
+ toggleOffOn()
+ eslint(eslintDependencies).npmInstallCache().configFile(rootProject.file('ui/.eslintrc.json'))
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ // Delimiter selects the first meaningful nonblank line while avoiding an existing copyright header
+ licenseHeaderFile(rootProject.file('tools/spotless/openremote-license-header.txt'), '(?![ \\t]*/\\*(?:[ \\t]+|\\R[ \\t]*\\*[ \\t]+)Copyright\\b)(?![ \\t]*\\*)[ \\t]*\\S').skipLinesMatching('^#!.*$')
+ endWithNewline()
+ }
+
+ format('markdownBackend') {
+ target backendTarget(['*/**/*.md'])
+ toggleOffOn()
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ endWithNewline()
+ }
+
+ format('markdownRoot') {
+ target '*.md'
+ toggleOffOn()
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ endWithNewline()
+ }
+
+ format('markdownUi') {
+ target uiTarget(['**/*.md'])
+ toggleOffOn()
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ endWithNewline()
+ }
+
+ format('miscUi') {
+ target uiTarget(['**/*.html', '**/*.json', '**/*.jsonc'])
+ toggleOffOn()
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ endWithNewline()
+ }
+
+ typescript {
+ target uiTarget(['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'])
+ // ESLint ignores directories starting with a dot so exclude these to prevent errors
+ targetExclude('**/.storybook/**')
+ toggleOffOn()
+ eslint(eslintDependencies).npmInstallCache().configFile(rootProject.file('ui/.eslintrc.json')).tsconfigFile(rootProject.file('ui/tsconfig.json'))
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ licenseHeaderFile rootProject.file('tools/spotless/openremote-license-header.txt'), '(import|export|declare|const|let|var|type|interface|class|enum|function) '
+ endWithNewline()
+ }
+
+ format('xml') {
+ target backendTarget(['**/*.xml', '**/*.xsd'])
+ toggleOffOn()
+ eclipseWtp('xml').configFile(rootProject.file('tools/spotless/eclipse-wtp-xml.properties'))
+ endWithNewline()
+ }
+
+ format('yaml') {
+ target backendTarget(['**/*.yaml', '**/*.yml'])
+ toggleOffOn()
+ prettier(prettierVersion).npmInstallCache().configFile(rootProject.file('ui/.prettierrc.json'))
+ endWithNewline()
+ }
+}
+
+def spotlessFormatGroups = [
+ Backend: [
+ 'GradleRoot',
+ 'GradleBackend',
+ 'Groovy',
+ 'Java',
+ 'MarkdownBackend',
+ 'MarkdownRoot',
+ 'Xml',
+ 'Yaml'
+ ],
+ Ui: [
+ 'Css',
+ 'GradleRoot',
+ 'GradleUi',
+ 'Javascript',
+ 'MarkdownRoot',
+ 'MarkdownUi',
+ 'MiscUi',
+ 'Typescript'
+ ]
+]
+
+def spotlessOperations = [
+ Apply : 'Apply formatting to',
+ Check : 'Check formatting of',
+ Diagnose: 'Diagnose formatting of'
+]
+
+spotlessFormatGroups.each { groupName, formats ->
+ spotlessOperations.each { operation, operationDescription ->
+ tasks.register("spotless${groupName}${operation}") { task ->
+ task.group = LifecycleBasePlugin.VERIFICATION_GROUP
+ task.description =
+ "${operationDescription} ${groupName.toLowerCase()} sources."
+
+ task.dependsOn formats.collect { format ->
+ "spotless${format}${operation}"
+ }
+ }
+ }
+}
+
+def yarnInstallTask = getYarnInstallTask()
+tasks.matching { it.name.startsWith("spotless") }.configureEach {
+ dependsOn yarnInstallTask
+}
diff --git a/test/build.gradle b/test/build.gradle
index d316670de..1a9cf1ee5 100644
--- a/test/build.gradle
+++ b/test/build.gradle
@@ -2,50 +2,46 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
- id 'java-library'
- id 'groovy'
- alias libs.plugins.testLogger
+ id 'java-library'
+ id 'groovy'
+ alias libs.plugins.testLogger
}
dependencies {
- api project(":manager")
- api project(":model")
- api project(":agent")
- testImplementation libs.openremote.test
+ api project(":manager")
+ api project(":model")
+ api project(":agent")
+ testImplementation libs.openremote.test
}
tasks.withType(Test).configureEach {
- environment("LOGGING_CONFIG_FILE", "test/src/logging-test.properties")
- addTestListener(new TestSummaryListener())
+ environment("LOGGING_CONFIG_FILE", "test/src/logging-test.properties")
+ addTestListener(new TestSummaryListener())
}
test {
- workingDir = findProject(":openremote") != null ? project(":openremote").projectDir : rootProject.projectDir
- useJUnitPlatform()
- testLogging {
- // set options for log level LIFECYCLE
- events = [
- TestLogEvent.FAILED,
- TestLogEvent.PASSED,
- TestLogEvent.SKIPPED
- ]
- exceptionFormat = TestExceptionFormat.FULL
- showExceptions = true
- showCauses = true
- showStackTraces = true
+ workingDir = findProject(":openremote") != null ? project(":openremote").projectDir : rootProject.projectDir
+ useJUnitPlatform()
+ testLogging {
+ // set options for log level LIFECYCLE
+ events = [TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED]
+ exceptionFormat = TestExceptionFormat.FULL
+ showExceptions = true
+ showCauses = true
+ showStackTraces = true
- // set options for log level DEBUG and INFO
- debug {
- events = [
- TestLogEvent.STARTED,
- TestLogEvent.FAILED,
- TestLogEvent.PASSED,
- TestLogEvent.SKIPPED,
- TestLogEvent.STANDARD_ERROR
- ]
- exceptionFormat = TestExceptionFormat.FULL
- }
- info.events = debug.events
- info.exceptionFormat = debug.exceptionFormat
+ // set options for log level DEBUG and INFO
+ debug {
+ events = [
+ TestLogEvent.STARTED,
+ TestLogEvent.FAILED,
+ TestLogEvent.PASSED,
+ TestLogEvent.SKIPPED,
+ TestLogEvent.STANDARD_ERROR
+ ]
+ exceptionFormat = TestExceptionFormat.FULL
}
+ info.events = debug.events
+ info.exceptionFormat = debug.exceptionFormat
+ }
}
diff --git a/test/src/test/groovy/org/openremote/test/custom/CustomTest.groovy b/test/src/test/groovy/org/openremote/test/custom/CustomTest.groovy
index 71b6d20ce..bfe87458a 100644
--- a/test/src/test/groovy/org/openremote/test/custom/CustomTest.groovy
+++ b/test/src/test/groovy/org/openremote/test/custom/CustomTest.groovy
@@ -1,9 +1,6 @@
/*
* Copyright 2019, OpenRemote Inc.
*
- * See the CONTRIBUTORS.txt file in the distribution for a
- * full listing of individual contributors.
- *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,7 +12,9 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
package org.openremote.test.custom
diff --git a/tools/spotless/eclipse-wtp-xml.properties b/tools/spotless/eclipse-wtp-xml.properties
new file mode 100644
index 000000000..0c4ffdca9
--- /dev/null
+++ b/tools/spotless/eclipse-wtp-xml.properties
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+indentationChar=space
+indentationSize=2
+lineWidth=120
+splitMultiAttrs=false
+clearAllBlankLines=false
diff --git a/tools/spotless/greclipse-google.properties b/tools/spotless/greclipse-google.properties
new file mode 100644
index 000000000..8168e6b79
--- /dev/null
+++ b/tools/spotless/greclipse-google.properties
@@ -0,0 +1,28 @@
+# Greclipse configuration approximating Google Java Format conventions for Groovy and Gradle files.
+# Greclipse uses a different formatting engine, so formatting may not match google-java-format exactly.
+
+# Use spaces rather than tabs.
+org.eclipse.jdt.core.formatter.tabulation.char=space
+
+# Google Java Style uses two spaces for block indentation.
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.indentation.size=2
+
+# Do not insert whitespace on otherwise-empty lines.
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+
+# Google Java Style uses at least four spaces for continuation lines.
+groovy.formatter.multiline.indentation=4
+
+# K&R-style braces.
+groovy.formatter.braces.start=same
+groovy.formatter.braces.end=next
+
+# Google Java Style uses a 100-character column limit.
+groovy.formatter.line.maxlength=100
+
+# Wrap long Groovy list literals at approximately the same limit.
+groovy.formatter.longListLength=100
+
+# Idiomatic for Groovy/Gradle scripts. Change to false to preserve semicolons.
+groovy.formatter.remove.unnecessary.semicolons=true
diff --git a/tools/spotless/openremote-license-header.txt b/tools/spotless/openremote-license-header.txt
new file mode 100644
index 000000000..2a336e1ba
--- /dev/null
+++ b/tools/spotless/openremote-license-header.txt
@@ -0,0 +1,18 @@
+/*
+ * Copyright $YEAR, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json
index a5fe3da74..110d38ffe 100644
--- a/ui/.eslintrc.json
+++ b/ui/.eslintrc.json
@@ -1,18 +1,33 @@
{
+ "root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
- "project": "tsconfig.json",
"sourceType": "module"
},
"extends": [
"eslint:recommended",
"standard",
"plugin:@typescript-eslint/recommended",
+ "plugin:wc/recommended",
+ "plugin:lit/recommended",
"prettier"
],
"plugins": ["@typescript-eslint"],
"env": {
- "es6": true
+ "browser": true,
+ "es2021": true
},
- "ignorePatterns": ["dist", "lib", "node_modules"]
+ "ignorePatterns": ["build", "dist", "lib", "node_modules"],
+ "rules": {
+ // Required because inline type imports may otherwise appear duplicated
+ "import/no-duplicates": "off",
+ "@typescript-eslint/consistent-type-imports": [
+ "error",
+ {
+ "prefer": "type-imports",
+ "fixStyle": "inline-type-imports"
+ }
+ ],
+ "@typescript-eslint/no-import-type-side-effects": "error"
+ }
}
diff --git a/ui/.prettierignore b/ui/.prettierignore
new file mode 100644
index 000000000..218558174
--- /dev/null
+++ b/ui/.prettierignore
@@ -0,0 +1,44 @@
+# Keep these patterns compatible with Gradle Ant patterns.
+# Do not use negation, root-anchored patterns, or gitignore escaping.
+
+# Dependencies
+**/node_modules/**
+
+# Gradle and Spotless output
+**/build/**
+
+# Compiled output
+**/dist/**
+**/lib/**
+
+# Test and coverage output
+**/coverage/**
+**/test-results/**
+**/playwright-report/**
+
+# Generated files
+**/generated/**
+**/*.generated.*
+**/custom-elements.json
+**/src/model.ts
+**/src/restclient.ts
+
+# Caches
+**/.cache/**
+**/.gradle/**
+**/.yarn/**
+
+# IDE metadata
+**/.idea/**
+**/.settings/**
+**/.vscode/**
+
+# Minified and bundled files
+**/*.bundle.js
+**/*.min.js
+**/*.min.css
+
+# Lock files
+package-lock.json
+pnpm-lock.yaml
+yarn.lock
diff --git a/ui/.prettierrc.json b/ui/.prettierrc.json
index 3e919d3df..6b8d5f573 100644
--- a/ui/.prettierrc.json
+++ b/ui/.prettierrc.json
@@ -1,5 +1,8 @@
{
"trailingComma": "es5",
"printWidth": 120,
- "singleQuote": false
+ "singleQuote": false,
+ "tabWidth": 2,
+ "useTabs": false,
+ "embeddedLanguageFormatting": "auto"
}
diff --git a/ui/app/README.md b/ui/app/README.md
index 4f97f3b6a..11928280c 100644
--- a/ui/app/README.md
+++ b/ui/app/README.md
@@ -1,7 +1,7 @@
# Custom UI apps
In this folder, you can add custom (web) applications that will be shipped along with OpenRemote.
-For example, special mobile apps for end users, or apps for less-technical consumers are widespread.
+For example, special mobile apps for end users, or apps for less-technical consumers are widespread.
Developing these custom apps is pretty straightforward, thanks to the built-in packages we provide.
These make communicating with OpenRemote easier, and allows developers to quickly set up a user interface.
@@ -13,10 +13,12 @@ All apps can be run using `npm run serve`, and visited at `http://localhost:9000
Here's a list of the apps, and what they do;
### /custom
+
This is an example web application built with [Lit Web Components](https://lit.dev) and [Webpack](https://webpack.js.org).
Apps in our main OpenRemote [repository](https://github.com/openremote/openremote) are built with these technologies as well.
It can be used as a template to add your own pages on top of it.
### /custom-react
+
This is an example web application built with [React 19](https://react.dev) and [RSPack](https://rspack.rs).
-*(more information soon)*
\ No newline at end of file
+_(more information soon)_
diff --git a/ui/app/custom-react/build.gradle b/ui/app/custom-react/build.gradle
index 00cd7b116..6a0114162 100644
--- a/ui/app/custom-react/build.gradle
+++ b/ui/app/custom-react/build.gradle
@@ -1,12 +1,12 @@
buildDir = "dist"
tasks.register('clean') {
- dependsOn npmClean
+ dependsOn npmClean
}
tasks.register('installDist', Copy) {
- dependsOn npmBuild
- mustRunAfter ":manager:installDist"
- from project.buildDir
- into "${project(':deployment').buildDir}/image/manager/app/${projectDir.name}"
+ dependsOn npmBuild
+ mustRunAfter ":manager:installDist"
+ from project.buildDir
+ into "${project(':deployment').buildDir}/image/manager/app/${projectDir.name}"
}
diff --git a/ui/app/custom-react/index.html b/ui/app/custom-react/index.html
index fddf53e04..deec68fe4 100644
--- a/ui/app/custom-react/index.html
+++ b/ui/app/custom-react/index.html
@@ -1,12 +1,12 @@
-
-
-
-
- Custom app example using React
-
-
-
-
+
+
+
+
+ Custom app example using React
+
+
+
+
diff --git a/ui/app/custom-react/package.json b/ui/app/custom-react/package.json
index 4951124dc..36980ed15 100644
--- a/ui/app/custom-react/package.json
+++ b/ui/app/custom-react/package.json
@@ -22,7 +22,18 @@
"@rspack/core": "^1.7.10",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"cross-env": "^10.1.0",
+ "eslint": "8.57.1",
+ "eslint-config-prettier": "10.1.8",
+ "eslint-config-standard": "17.1.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-lit": "2.3.1",
+ "eslint-plugin-n": "16.6.2",
+ "eslint-plugin-promise": "6.6.0",
+ "eslint-plugin-wc": "3.1.0",
+ "prettier": "3.9.6",
"react-refresh": "^0.18.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
diff --git a/ui/app/custom-react/rspack.config.ts b/ui/app/custom-react/rspack.config.ts
index fa3fe84e8..d108ee183 100644
--- a/ui/app/custom-react/rspack.config.ts
+++ b/ui/app/custom-react/rspack.config.ts
@@ -1,5 +1,23 @@
-import {defineConfig} from "@rspack/cli";
-import {rspack} from "@rspack/core";
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { defineConfig } from "@rspack/cli";
+import { rspack } from "@rspack/core";
const isDev = process.env.NODE_ENV === "development";
@@ -7,79 +25,77 @@ const isDev = process.env.NODE_ENV === "development";
const targets = ["chrome >= 87", "edge >= 88", "firefox >= 78", "safari >= 14"];
export default defineConfig({
- context: __dirname,
- devServer: {
- host: "0.0.0.0",
- port: 9000,
- open: false
+ context: __dirname,
+ devServer: {
+ host: "0.0.0.0",
+ port: 9000,
+ open: false,
+ },
+ entry: {
+ main: "./src/main.tsx",
+ },
+ resolve: {
+ extensions: ["...", ".ts", ".tsx", ".jsx"],
+ },
+ module: {
+ parser: {
+ "css/auto": {
+ namedExports: false,
+ },
},
- entry: {
- main: "./src/main.tsx"
- },
- resolve: {
- extensions: ["...", ".ts", ".tsx", ".jsx"]
- },
- module: {
- parser: {
- "css/auto": {
- namedExports: false,
- },
- },
- rules: [
- {
- test: /\.svg$/,
- type: "asset"
+ rules: [
+ {
+ test: /\.svg$/,
+ type: "asset",
+ },
+ {
+ test: /(maplibre|mapbox|@material|gridstack|@mdi).*\.css$/, // output css as strings
+ type: "asset/source",
+ },
+ {
+ test: /\.tsx$/,
+ type: "javascript/auto",
+ use: [
+ {
+ loader: "builtin:swc-loader",
+ options: {
+ jsc: {
+ parser: {
+ syntax: "typescript",
+ tsx: true,
+ },
+ },
},
- {
- test: /(maplibre|mapbox|@material|gridstack|@mdi).*\.css$/, //output css as strings
- type: "asset/source"
- },
- {
- test: /\.tsx$/,
- type: "javascript/auto",
- use: [
- {
- loader: "builtin:swc-loader",
- options: {
- jsc: {
- parser: {
- syntax: "typescript",
- tsx: true
- }
- }
- }
- }
- ]
- }
- ]
- },
- plugins: [
- new rspack.HtmlRspackPlugin({
- template: "./index.html"
- }),
- // Define MANAGER_URL as a global variable
- new rspack.DefinePlugin({
- MANAGER_URL: JSON.stringify(process.env.MANAGER_URL ?? (isDev ? "http://localhost:8080" : undefined))
- }),
- // Import assets
- new rspack.CopyRspackPlugin({
- patterns: [
- {from: "./assets", to: "assets"}
- ]
- })
+ },
+ ],
+ },
],
- optimization: {
- minimizer: [
- new rspack.SwcJsMinimizerRspackPlugin(),
- new rspack.LightningCssMinimizerRspackPlugin({
- minimizerOptions: {targets}
- })
- ]
- },
- output: {
- publicPath: isDev ? "/custom-react/" : "/",
- },
- experiments: {
- css: true
- }
+ },
+ plugins: [
+ new rspack.HtmlRspackPlugin({
+ template: "./index.html",
+ }),
+ // Define MANAGER_URL as a global variable
+ new rspack.DefinePlugin({
+ MANAGER_URL: JSON.stringify(process.env.MANAGER_URL ?? (isDev ? "http://localhost:8080" : undefined)),
+ }),
+ // Import assets
+ new rspack.CopyRspackPlugin({
+ patterns: [{ from: "./assets", to: "assets" }],
+ }),
+ ],
+ optimization: {
+ minimizer: [
+ new rspack.SwcJsMinimizerRspackPlugin(),
+ new rspack.LightningCssMinimizerRspackPlugin({
+ minimizerOptions: { targets },
+ }),
+ ],
+ },
+ output: {
+ publicPath: isDev ? "/custom-react/" : "/",
+ },
+ experiments: {
+ css: true,
+ },
});
diff --git a/ui/app/custom-react/src/App.css b/ui/app/custom-react/src/App.css
index 6f883249c..964de547a 100644
--- a/ui/app/custom-react/src/App.css
+++ b/ui/app/custom-react/src/App.css
@@ -1,10 +1,28 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
}
.logo {
- height: 12em;
+ height: 12em;
}
diff --git a/ui/app/custom-react/src/App.tsx b/ui/app/custom-react/src/App.tsx
index 803ba97a1..366bc648e 100644
--- a/ui/app/custom-react/src/App.tsx
+++ b/ui/app/custom-react/src/App.tsx
@@ -1,23 +1,41 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
import React from "react";
import openremoteLogo from "../assets/openremote.svg";
-import {InputType} from "@openremote/or-mwc-components/or-mwc-input";
+import { InputType } from "@openremote/or-mwc-components/or-mwc-input";
import type {} from "@openremote/or-mwc-components/jsx";
import "./App.css";
function App() {
- return (
-
-
-
-
-
"A React template for your custom app."
-
-
- );
+ return (
+
+
+
+
+
"A React template for your custom app."
+
+
+ );
}
-export default App;
\ No newline at end of file
+export default App;
diff --git a/ui/app/custom-react/src/index.css b/ui/app/custom-react/src/index.css
index c5795d5bb..18b984f99 100644
--- a/ui/app/custom-react/src/index.css
+++ b/ui/app/custom-react/src/index.css
@@ -1,31 +1,49 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
:root {
- font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
- font-size: 16px;
- line-height: 24px;
- font-weight: 400;
+ font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 400;
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- -webkit-text-size-adjust: 100%;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
}
body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
}
@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
}
diff --git a/ui/app/custom-react/src/main.tsx b/ui/app/custom-react/src/main.tsx
index c905efc99..fe50711ae 100644
--- a/ui/app/custom-react/src/main.tsx
+++ b/ui/app/custom-react/src/main.tsx
@@ -1,8 +1,26 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
-import {manager} from "@openremote/core";
-import {ManagerConfig} from "@openremote/model";
+import { manager } from "@openremote/core";
+import type { ManagerConfig } from "@openremote/model";
import "./index.css";
declare const MANAGER_URL: string | undefined;
@@ -13,9 +31,9 @@ declare const MANAGER_URL: string | undefined;
* We also enable autoLogin to prompt a Keycloak login before the app appears.
*/
const managerConfig: ManagerConfig = {
- realm: "master",
- managerUrl: MANAGER_URL ?? "",
- autoLogin: true
+ realm: "master",
+ managerUrl: MANAGER_URL ?? "",
+ autoLogin: true,
};
/**
@@ -23,13 +41,12 @@ const managerConfig: ManagerConfig = {
* Afterward, we can start rendering the React DOM UI.
*/
manager.init(managerConfig).then(() => {
-
- /**
- * Render your React application to the DOM.
- */
- ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
-
-
-
- );
+ /**
+ * Render your React application to the DOM.
+ */
+ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
+
+
+
+ );
});
diff --git a/ui/app/custom-react/src/react-env.d.ts b/ui/app/custom-react/src/react-env.d.ts
index 49c347bae..e6d951f2a 100644
--- a/ui/app/custom-react/src/react-env.d.ts
+++ b/ui/app/custom-react/src/react-env.d.ts
@@ -1,213 +1,230 @@
-// CSS modules
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
type CSSModuleClasses = { readonly [key: string]: string };
declare module "*.module.css" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.scss" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.sass" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.less" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.styl" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.stylus" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.pcss" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
declare module "*.module.sss" {
- const classes: CSSModuleClasses;
- export default classes;
+ const classes: CSSModuleClasses;
+ export default classes;
}
// CSS
declare module "*.css" {
- /**
- * @deprecated Use `import style from './style.css?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.css?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.scss" {
- /**
- * @deprecated Use `import style from './style.scss?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.scss?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.sass" {
- /**
- * @deprecated Use `import style from './style.sass?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.sass?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.less" {
- /**
- * @deprecated Use `import style from './style.less?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.less?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.styl" {
- /**
- * @deprecated Use `import style from './style.styl?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.styl?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.stylus" {
- /**
- * @deprecated Use `import style from './style.stylus?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.stylus?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.pcss" {
- /**
- * @deprecated Use `import style from './style.pcss?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.pcss?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
declare module "*.sss" {
- /**
- * @deprecated Use `import style from './style.sss?inline'` instead.
- */
- const css: string;
- export default css;
+ /**
+ * @deprecated Use `import style from './style.sss?inline'` instead.
+ */
+ const css: string;
+ export default css;
}
// images
declare module "*.png" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.jpg" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.jpeg" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.jfif" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.pjpeg" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.pjp" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.gif" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.svg" {
- const ReactComponent: React.FC>;
- const content: string;
+ const ReactComponent: React.FC>;
+ const content: string;
- export { ReactComponent };
- export default content;
+ export { ReactComponent };
+ export default content;
}
declare module "*.ico" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.webp" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.avif" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
// media
declare module "*.mp4" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.webm" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.ogg" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.mp3" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.wav" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.flac" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.aac" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.opus" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
// fonts
declare module "*.woff" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.woff2" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.eot" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.ttf" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.otf" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
// other
declare module "*.webmanifest" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.pdf" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
declare module "*.txt" {
- const src: string;
- export default src;
+ const src: string;
+ export default src;
}
diff --git a/ui/app/custom/build.gradle b/ui/app/custom/build.gradle
index 00cd7b116..6a0114162 100644
--- a/ui/app/custom/build.gradle
+++ b/ui/app/custom/build.gradle
@@ -1,12 +1,12 @@
buildDir = "dist"
tasks.register('clean') {
- dependsOn npmClean
+ dependsOn npmClean
}
tasks.register('installDist', Copy) {
- dependsOn npmBuild
- mustRunAfter ":manager:installDist"
- from project.buildDir
- into "${project(':deployment').buildDir}/image/manager/app/${projectDir.name}"
+ dependsOn npmBuild
+ mustRunAfter ":manager:installDist"
+ from project.buildDir
+ into "${project(':deployment').buildDir}/image/manager/app/${projectDir.name}"
}
diff --git a/ui/app/custom/index.html b/ui/app/custom/index.html
index a1af67ad7..c3e921563 100644
--- a/ui/app/custom/index.html
+++ b/ui/app/custom/index.html
@@ -1,42 +1,42 @@
-
-
-
+
+
+
OpenRemote Custom App
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
-
-
-
-
-
+ },
+ false
+ );
+ })();
+
+
+
diff --git a/ui/app/custom/package.json b/ui/app/custom/package.json
index 7aad7b5f5..62a082626 100644
--- a/ui/app/custom/package.json
+++ b/ui/app/custom/package.json
@@ -28,8 +28,19 @@
"@openremote/util": "~1.27.0",
"@rspack/cli": "^1.7.10",
"@rspack/core": "^1.7.10",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
"cross-env": "*",
"css-loader": "*",
+ "eslint": "8.57.1",
+ "eslint-config-prettier": "10.1.8",
+ "eslint-config-standard": "17.1.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-lit": "2.3.1",
+ "eslint-plugin-n": "16.6.2",
+ "eslint-plugin-promise": "6.6.0",
+ "eslint-plugin-wc": "3.1.0",
+ "prettier": "3.9.6",
"shx": "*",
"ts-loader": "*",
"typescript": "^5.9.3"
diff --git a/ui/app/custom/rspack.config.js b/ui/app/custom/rspack.config.js
index 60b126879..ae5ff0689 100644
--- a/ui/app/custom/rspack.config.js
+++ b/ui/app/custom/rspack.config.js
@@ -1,38 +1,57 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
const util = require("@openremote/util");
-const {rspack} = require("@rspack/core");
+const { rspack } = require("@rspack/core");
module.exports = (env, argv) => {
+ const customConfigDir = env.config;
+ const managerUrl = env.managerUrl;
+ const keycloakUrl = env.keycloakUrl;
+ const IS_DEV_SERVER = process.argv.find((arg) => arg.includes("serve"));
+ const config = util.getAppConfig(argv.mode, IS_DEV_SERVER, __dirname, managerUrl, keycloakUrl);
- const customConfigDir = env.config;
- const managerUrl = env.managerUrl;
- const keycloakUrl = env.keycloakUrl;
- const IS_DEV_SERVER = process.argv.find(arg => arg.includes("serve"));
- const config = util.getAppConfig(argv.mode, IS_DEV_SERVER, __dirname, managerUrl, keycloakUrl);
-
- if (IS_DEV_SERVER && customConfigDir) {
- console.log("CUSTOM_CONFIG_DIR: " + customConfigDir);
- // Try and include the static files in the specified config dir if we're in dev server mode
- config.plugins.push(new rspack.CopyRspackPlugin({
- patterns: [
- {
- from: customConfigDir
- },
- ]
- }));
- }
-
- config.plugins.push(new rspack.CopyRspackPlugin({
- patterns: [
- { from: 'locales', to: 'locales' }
- ]
- }));
-
- // Add a custom base URL to resolve the config dir to the path of the dev server not root
+ if (IS_DEV_SERVER && customConfigDir) {
+ console.log("CUSTOM_CONFIG_DIR: " + customConfigDir);
+ // Try and include the static files in the specified config dir if we're in dev server mode
config.plugins.push(
- new rspack.DefinePlugin({
- CONFIG_URL_PREFIX: JSON.stringify(IS_DEV_SERVER && customConfigDir ? "/manager" : "")
- })
+ new rspack.CopyRspackPlugin({
+ patterns: [
+ {
+ from: customConfigDir,
+ },
+ ],
+ })
);
+ }
+
+ config.plugins.push(
+ new rspack.CopyRspackPlugin({
+ patterns: [{ from: "locales", to: "locales" }],
+ })
+ );
+
+ // Add a custom base URL to resolve the config dir to the path of the dev server not root
+ config.plugins.push(
+ new rspack.DefinePlugin({
+ CONFIG_URL_PREFIX: JSON.stringify(IS_DEV_SERVER && customConfigDir ? "/manager" : ""),
+ })
+ );
- return config;
+ return config;
};
diff --git a/ui/app/custom/src/index.ts b/ui/app/custom/src/index.ts
index c0c814417..b1ee160ff 100644
--- a/ui/app/custom/src/index.ts
+++ b/ui/app/custom/src/index.ts
@@ -1,83 +1,116 @@
-// Declare require method which we'll use for importing webpack resources (using ES6 imports will confuse typescript parser)
-import {combineReducers, configureStore} from "@reduxjs/toolkit";
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { combineReducers, configureStore } from "@reduxjs/toolkit";
import "@openremote/or-app";
import themeCss from "@openremote/theme";
-import {AnyAction, appReducer, AppStateKeyed, HeaderConfig, HeaderItem, OrApp, PageProvider, RealmAppConfig} from "@openremote/or-app";
-import {headerItemAccount, headerItemLanguage, headerItemLogout, headerItemMap, headerItemAssets} from "@openremote/manager/headers";
-import {pageAssetsReducer, pageAssetsProvider} from "@openremote/manager/pages/page-assets";
-import {pageMapReducer, pageMapProvider} from "@openremote/manager/pages/page-map";
+import {
+ type AnyAction,
+ appReducer,
+ type AppStateKeyed,
+ type HeaderConfig,
+ type HeaderItem,
+ OrApp,
+ type PageProvider,
+ type RealmAppConfig,
+} from "@openremote/or-app";
+import {
+ headerItemAccount,
+ headerItemLanguage,
+ headerItemLogout,
+ headerItemMap,
+ headerItemAssets,
+} from "@openremote/manager/headers";
+import { pageAssetsReducer, pageAssetsProvider } from "@openremote/manager/pages/page-assets";
+import { pageMapReducer, pageMapProvider } from "@openremote/manager/pages/page-map";
import "./pages/page-custom";
-import {pageCustomProvider} from "./pages/page-custom";
+import { pageCustomProvider } from "./pages/page-custom";
const rootReducer = combineReducers({
- app: appReducer,
- map: pageMapReducer,
- assets: pageAssetsReducer
+ app: appReducer,
+ map: pageMapReducer,
+ assets: pageAssetsReducer,
});
type RootState = ReturnType;
export const store = configureStore({
- reducer: rootReducer
+ reducer: rootReducer,
});
const orApp = new OrApp(store);
export const DefaultPagesConfig: PageProvider[] = [
- pageMapProvider(store), // Standard manager map page
- pageAssetsProvider(store), // Standard manager asset page
- pageCustomProvider(store) // Custom page
+ pageMapProvider(store), // Standard manager map page
+ pageAssetsProvider(store), // Standard manager asset page
+ pageCustomProvider(store), // Custom page
];
// A new header for our custom page
export function headerItemCustom(orApp: OrApp): HeaderItem {
- return {
- icon: "rhombus-split",
- href: "custom1",
- text: "app:customPage",
- };
+ return {
+ icon: "rhombus-split",
+ href: "custom1",
+ text: "app:customPage",
+ };
}
-export const DefaultHeaderMainMenu: {[name: string]: HeaderItem} = {
- map: headerItemMap(orApp),
- assets: headerItemAssets(orApp),
- custom: headerItemCustom(orApp)
+export const DefaultHeaderMainMenu: { [name: string]: HeaderItem } = {
+ map: headerItemMap(orApp),
+ assets: headerItemAssets(orApp),
+ custom: headerItemCustom(orApp),
};
-export const DefaultHeaderSecondaryMenu: {[name: string]: HeaderItem} = {
- language: headerItemLanguage(orApp),
- account: headerItemAccount(orApp),
- logout: headerItemLogout(orApp)
+export const DefaultHeaderSecondaryMenu: { [name: string]: HeaderItem } = {
+ language: headerItemLanguage(orApp),
+ account: headerItemAccount(orApp),
+ logout: headerItemLogout(orApp),
};
export const DefaultHeaderConfig: HeaderConfig = {
- mainMenu: Object.values(DefaultHeaderMainMenu),
- secondaryMenu: Object.values(DefaultHeaderSecondaryMenu)
+ mainMenu: Object.values(DefaultHeaderMainMenu),
+ secondaryMenu: Object.values(DefaultHeaderSecondaryMenu),
};
export const DefaultRealmConfig: RealmAppConfig = {
- appTitle: "Custom App",
- header: DefaultHeaderConfig,
- styles: ":host > * {--or-app-color2: #F0F0F0; --or-app-color3: #22211f; --or-app-color4: #e3000a; --or-app-color5: #CCCCCC;}",
+ appTitle: "Custom App",
+ header: DefaultHeaderConfig,
+ styles:
+ ":host > * {--or-app-color2: #F0F0F0; --or-app-color3: #22211f; --or-app-color4: #e3000a; --or-app-color5: #CCCCCC;}",
};
// Configure manager connection and i18next settings
orApp.managerConfig = {
- realm: "custom",
- loadTranslations: ["app", "or"]
+ realm: "custom",
+ loadTranslations: ["app", "or"],
};
// Configure app pages and per realm styling/settings
orApp.appConfig = {
- pages: [...DefaultPagesConfig],
- languages: {
- en: "english",
- nl: "dutch"
- },
- superUserHeader: DefaultHeaderConfig,
- realms: {
- default: {...DefaultRealmConfig, header: DefaultHeaderConfig}
- }
+ pages: [...DefaultPagesConfig],
+ languages: {
+ en: "english",
+ nl: "dutch",
+ },
+ superUserHeader: DefaultHeaderConfig,
+ realms: {
+ default: { ...DefaultRealmConfig, header: DefaultHeaderConfig },
+ },
};
// Apply theme to the Manager app
diff --git a/ui/app/custom/src/pages/page-custom.ts b/ui/app/custom/src/pages/page-custom.ts
index fb050d1df..134a17244 100644
--- a/ui/app/custom/src/pages/page-custom.ts
+++ b/ui/app/custom/src/pages/page-custom.ts
@@ -1,68 +1,80 @@
-import {css, html, TemplateResult} from "lit";
-import {customElement} from "lit/decorators.js";
-import {AppStateKeyed, Page, PageProvider} from "@openremote/or-app";
-import {EnhancedStore} from "@reduxjs/toolkit";
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { css, html, type TemplateResult } from "lit";
+import { customElement } from "lit/decorators.js";
+import { type AppStateKeyed, Page, type PageProvider } from "@openremote/or-app";
+import type { EnhancedStore } from "@reduxjs/toolkit";
import rest from "rest";
export function pageCustomProvider(store: EnhancedStore): PageProvider {
- return {
- name: "customPage",
- routes: [
- "custom1"
- ],
- pageCreator: () => {
- const page = new PageCustom(store);
- return page;
- }
- };
+ return {
+ name: "customPage",
+ routes: ["custom1"],
+ pageCreator: () => {
+ const page = new PageCustom(store);
+ return page;
+ },
+ };
}
-
@customElement("page-custom")
-export class PageCustom extends Page {
-
- static get styles() {
- // language=CSS
- return css`
- :host {
- display: flex;
- align-items: start;
- flex-direction: column;
- width: 100%;
- --or-icon-fill: var(--or-app-color4);
- }
- `;
- }
+export class PageCustom extends Page {
+ static get styles() {
+ // language=CSS
+ return css`
+ :host {
+ display: flex;
+ align-items: start;
+ flex-direction: column;
+ width: 100%;
+ --or-icon-fill: var(--or-app-color4);
+ }
+ `;
+ }
- get name(): string {
- return "custom";
- }
+ get name(): string {
+ return "custom";
+ }
- constructor(store: EnhancedStore) {
- super(store);
- }
+ constructor(store: EnhancedStore) {
+ super(store);
+ }
- protected render(): TemplateResult | void {
- return html`
+ protected render(): TemplateResult | void {
+ return html`
This is an example custom page with a custom app translation for 'customString'
- `
- }
-
- protected firstUpdated(changedProperties: Map) {
- let submitter = async() => {
- // Crude custom REST API call example
- return await rest.api.CustomEndpointResource.submitData({
- name: "Commander Data",
- age: 1234
- });
- }
+ `;
+ }
- submitter()
- .then(() => console.log("Successfully posted to custom endpoint"))
- .catch((reason) => console.log("Failed to post to custom endpoint"));
- }
+ protected firstUpdated(changedProperties: Map) {
+ const submitter = async () => {
+ // Crude custom REST API call example
+ return await rest.api.CustomEndpointResource.submitData({
+ name: "Commander Data",
+ age: 1234,
+ });
+ };
- stateChanged(state: AppStateKeyed): void {
+ submitter()
+ .then(() => console.log("Successfully posted to custom endpoint"))
+ .catch((reason) => console.log("Failed to post to custom endpoint"));
+ }
- }
+ stateChanged(state: AppStateKeyed): void {}
}
diff --git a/ui/app/custom/tsconfig.json b/ui/app/custom/tsconfig.json
index a7da966ba..eeef3fff4 100644
--- a/ui/app/custom/tsconfig.json
+++ b/ui/app/custom/tsconfig.json
@@ -5,11 +5,6 @@
"rootDir": "src",
"strict": false
},
- "include": [
- "./src"
- ],
- "references": [
- { "path": "../../component/model" },
- { "path": "../../component/rest" }
- ]
+ "include": ["./src"],
+ "references": [{ "path": "../../component/model" }, { "path": "../../component/rest" }]
}
diff --git a/ui/build.gradle b/ui/build.gradle
index b5a6748ed..6b5908279 100644
--- a/ui/build.gradle
+++ b/ui/build.gradle
@@ -1,16 +1,16 @@
afterEvaluate {
- // Add dependencies on model and rest typescript generation
- rootProject.getTasksByName('prepareUi', true).forEach {
- it.dependsOn ":ui:component:model:generateTypeScript", ":ui:component:rest:generateTypeScript"
- }
- rootProject.getTasksByName('publishUi', true).forEach {
- it.dependsOn ":ui:component:model:generateTypeScript", ":ui:component:rest:generateTypeScript"
- }
- rootProject.getTasksByName('npmBuild', true).forEach {
- it.dependsOn ":ui:component:model:generateTypeScript", ":ui:component:rest:generateTypeScript"
- }
+ // Add dependencies on model and rest typescript generation
+ rootProject.getTasksByName('prepareUi', true).forEach {
+ it.dependsOn ":ui:component:model:generateTypeScript", ":ui:component:rest:generateTypeScript"
+ }
+ rootProject.getTasksByName('publishUi', true).forEach {
+ it.dependsOn ":ui:component:model:generateTypeScript", ":ui:component:rest:generateTypeScript"
+ }
+ rootProject.getTasksByName('npmBuild', true).forEach {
+ it.dependsOn ":ui:component:model:generateTypeScript", ":ui:component:rest:generateTypeScript"
+ }
}
tasks.register('modelWatch') {
- dependsOn ":ui:component:model:build", ":ui:component:rest:build"
+ dependsOn ":ui:component:model:build", ":ui:component:rest:build"
}
diff --git a/ui/component/model/build.gradle b/ui/component/model/build.gradle
index b5455977e..f922bbefa 100644
--- a/ui/component/model/build.gradle
+++ b/ui/component/model/build.gradle
@@ -1,46 +1,46 @@
plugins {
- id 'groovy'
- id 'cz.habarta.typescript-generator'
+ id 'groovy'
+ id 'cz.habarta.typescript-generator'
}
dependencies {
- compileOnly libs.openremote.model.util
- implementation project(":agent")
- implementation project(":model")
- implementation libs.jackson.datatype.jdk8
- implementation libs.jackson.datatype.jsr310
- implementation libs.jackson.module.parameter.names
- implementation libs.typescript.generator.core
+ compileOnly libs.openremote.model.util
+ implementation project(":agent")
+ implementation project(":model")
+ implementation libs.jackson.datatype.jdk8
+ implementation libs.jackson.datatype.jsr310
+ implementation libs.jackson.module.parameter.names
+ implementation libs.typescript.generator.core
}
generateTypeScript createTSGeneratorConfigForModel("src/model.ts", findProject(":model"))
def generateTypeScriptTask = tasks.named("generateTypeScript") {
- // Work around typescript-generator exposing non-serializable configuration objects as Gradle task inputs.
- doNotTrackState("typescript-generator uses non-serializable task input types")
+ // Work around typescript-generator exposing non-serializable configuration objects as Gradle task inputs.
+ doNotTrackState("typescript-generator uses non-serializable task input types")
}
tasks.named("build") {
- dependsOn generateTypeScriptTask, tasks.named("npmBuild")
+ dependsOn generateTypeScriptTask, tasks.named("npmBuild")
}
tasks.named("npmBuild") {
- dependsOn generateTypeScriptTask
+ dependsOn generateTypeScriptTask
}
tasks.named('clean', Delete) {
- delete layout.projectDirectory.dir('dist')
+ delete layout.projectDirectory.dir('dist')
}
tasks.register('npmTest', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "test"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "test"
}
tasks.register('prepareUi') {
- dependsOn clean, npmPrepare
+ dependsOn clean, npmPrepare
}
tasks.register('publishUi') {
- dependsOn clean, npmPublish
+ dependsOn clean, npmPublish
}
diff --git a/ui/component/model/package.json b/ui/component/model/package.json
index 645c79f5d..98ccaf99f 100644
--- a/ui/component/model/package.json
+++ b/ui/component/model/package.json
@@ -22,7 +22,18 @@
"author": "OpenRemote",
"license": "AGPL-3.0-or-later",
"devDependencies": {
- "@openremote/util": "~1.27.0"
+ "@openremote/util": "~1.27.0",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
+ "eslint": "8.57.1",
+ "eslint-config-prettier": "10.1.8",
+ "eslint-config-standard": "17.1.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-lit": "2.3.1",
+ "eslint-plugin-n": "16.6.2",
+ "eslint-plugin-promise": "6.6.0",
+ "eslint-plugin-wc": "3.1.0",
+ "prettier": "3.9.6"
},
"publishConfig": {
"access": "restricted"
diff --git a/ui/component/model/src/index.ts b/ui/component/model/src/index.ts
index e7d1b6224..af081e3c1 100644
--- a/ui/component/model/src/index.ts
+++ b/ui/component/model/src/index.ts
@@ -1,2 +1,20 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
export * from "./model";
-export {AssetModelUtil} from "./util";
+export { AssetModelUtil } from "./util";
diff --git a/ui/component/model/src/util.ts b/ui/component/model/src/util.ts
index 540c929a4..3e20b8dd4 100644
--- a/ui/component/model/src/util.ts
+++ b/ui/component/model/src/util.ts
@@ -1,222 +1,260 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
import {
- AgentDescriptor,
- AssetDescriptor,
- AssetTypeInfo, Attribute,
- AttributeDescriptor,
- MetaItemDescriptor,
- ValueDescriptor, ValueDescriptorHolder, ValueHolder, WellknownAssets, WellknownValueTypes
+ type AgentDescriptor,
+ type AssetDescriptor,
+ type AssetTypeInfo,
+ type Attribute,
+ type AttributeDescriptor,
+ type MetaItemDescriptor,
+ type ValueDescriptor,
+ type ValueDescriptorHolder,
+ type ValueHolder,
+ WellknownAssets,
+ WellknownValueTypes,
} from "./index";
export class AssetModelUtil {
+ static _assetTypeInfos: AssetTypeInfo[] = [];
+ static _metaItemDescriptors: MetaItemDescriptor[] = [];
+ static _valueDescriptors: ValueDescriptor[] = [];
- static _assetTypeInfos: AssetTypeInfo[] = [];
- static _metaItemDescriptors: MetaItemDescriptor[] = [];
- static _valueDescriptors: ValueDescriptor[] = [];
+ public static getAssetDescriptors(): AssetDescriptor[] {
+ return AssetModelUtil._assetTypeInfos.map((info) => info.assetDescriptor as AgentDescriptor);
+ }
- public static getAssetDescriptors(): AssetDescriptor[] {
- return AssetModelUtil._assetTypeInfos.map(info => info.assetDescriptor as AgentDescriptor);
- }
+ public static getMetaItemDescriptors(): MetaItemDescriptor[] {
+ return [...this._metaItemDescriptors];
+ }
- public static getMetaItemDescriptors(): MetaItemDescriptor[] {
- return [...this._metaItemDescriptors];
- }
+ public static getValueDescriptors(): ValueDescriptor[] {
+ return [...this._valueDescriptors];
+ }
- public static getValueDescriptors(): ValueDescriptor[] {
- return [...this._valueDescriptors];
- }
+ public static getAssetTypeInfos(): AssetTypeInfo[] {
+ return [...this._assetTypeInfos];
+ }
- public static getAssetTypeInfos(): AssetTypeInfo[] {
- return [...this._assetTypeInfos];
+ public static getAssetTypeInfo(type: string | AssetDescriptor | AssetTypeInfo): AssetTypeInfo | undefined {
+ if (!type) {
+ return;
}
- public static getAssetTypeInfo(type: string | AssetDescriptor | AssetTypeInfo): AssetTypeInfo | undefined {
- if (!type) {
- return;
- }
-
- if ((type as AssetTypeInfo).assetDescriptor) {
- return type as AssetTypeInfo;
- }
-
- if (typeof(type) !== "string") {
- type = (type as AssetDescriptor).name!;
- }
-
- return this._assetTypeInfos.find((assetTypeInfo) => {
- return assetTypeInfo.assetDescriptor!.name === type;
- });
+ if ((type as AssetTypeInfo).assetDescriptor) {
+ return type as AssetTypeInfo;
}
- public static getAssetDescriptor(type?: string | AssetDescriptor | AssetTypeInfo): AssetDescriptor | undefined {
- if (!type) {
- return;
- }
+ if (typeof type !== "string") {
+ type = (type as AssetDescriptor).name!;
+ }
- if ((type as AssetTypeInfo).assetDescriptor) {
- return (type as AssetTypeInfo).assetDescriptor;
- }
+ return this._assetTypeInfos.find((assetTypeInfo) => {
+ return assetTypeInfo.assetDescriptor!.name === type;
+ });
+ }
- if (typeof(type) !== "string") {
- return type as AssetDescriptor;
- }
+ public static getAssetDescriptor(type?: string | AssetDescriptor | AssetTypeInfo): AssetDescriptor | undefined {
+ if (!type) {
+ return;
+ }
- const match = this._assetTypeInfos.find((assetTypeInfo) => {
- return assetTypeInfo.assetDescriptor!.name === type;
- });
- return match ? match.assetDescriptor : undefined;
+ if ((type as AssetTypeInfo).assetDescriptor) {
+ return (type as AssetTypeInfo).assetDescriptor;
}
- public static getAttributeDescriptor(attributeName: string, assetTypeOrDescriptor: string | AssetDescriptor | AssetTypeInfo): AttributeDescriptor | undefined {
- if (!attributeName) {
- return;
- }
+ if (typeof type !== "string") {
+ return type as AssetDescriptor;
+ }
- const assetTypeInfo = this.getAssetTypeInfo(assetTypeOrDescriptor || WellknownAssets.THINGASSET);
+ const match = this._assetTypeInfos.find((assetTypeInfo) => {
+ return assetTypeInfo.assetDescriptor!.name === type;
+ });
+ return match ? match.assetDescriptor : undefined;
+ }
+
+ public static getAttributeDescriptor(
+ attributeName: string,
+ assetTypeOrDescriptor: string | AssetDescriptor | AssetTypeInfo
+ ): AttributeDescriptor | undefined {
+ if (!attributeName) {
+ return;
+ }
- if (!assetTypeInfo || !assetTypeInfo.attributeDescriptors) {
- return;
- }
+ const assetTypeInfo = this.getAssetTypeInfo(assetTypeOrDescriptor || WellknownAssets.THINGASSET);
- return assetTypeInfo.attributeDescriptors.find((attributeDescriptor) => attributeDescriptor.name === attributeName);
+ if (!assetTypeInfo || !assetTypeInfo.attributeDescriptors) {
+ return;
}
- public static getValueDescriptor(name?: string): ValueDescriptor | undefined {
- if (!name) {
- return;
- }
+ return assetTypeInfo.attributeDescriptors.find((attributeDescriptor) => attributeDescriptor.name === attributeName);
+ }
- // If name ends with [] then it's an array value type so lookup the base type and then convert to array
- let arrayDimensions: number | undefined;
+ public static getValueDescriptor(name?: string): ValueDescriptor | undefined {
+ if (!name) {
+ return;
+ }
- if (name.endsWith("[]")) {
- arrayDimensions = 0;
- while(name.endsWith("[]")) {
- name = name.substring(0, name.length - 2);
- arrayDimensions++;
- }
- }
+ // If name ends with [] then it's an array value type so lookup the base type and then convert to array
+ let arrayDimensions: number | undefined;
- // Value descriptor names are globally unique
- let valueDescriptor = this._valueDescriptors.find((valueDescriptor) => valueDescriptor.name === name);
- if (valueDescriptor && arrayDimensions) {
- valueDescriptor = {...valueDescriptor, arrayDimensions: arrayDimensions};
- }
- return valueDescriptor;
+ if (name.endsWith("[]")) {
+ arrayDimensions = 0;
+ while (name.endsWith("[]")) {
+ name = name.substring(0, name.length - 2);
+ arrayDimensions++;
+ }
}
- public static resolveValueDescriptor(valueHolder: ValueHolder | undefined, descriptorOrValueType: ValueDescriptorHolder | ValueDescriptor | string | undefined): ValueDescriptor | undefined {
- let valueDescriptor: ValueDescriptor | undefined;
+ // Value descriptor names are globally unique
+ let valueDescriptor = this._valueDescriptors.find((valueDescriptor) => valueDescriptor.name === name);
+ if (valueDescriptor && arrayDimensions) {
+ valueDescriptor = { ...valueDescriptor, arrayDimensions };
+ }
+ return valueDescriptor;
+ }
+
+ public static resolveValueDescriptor(
+ valueHolder: ValueHolder | undefined,
+ descriptorOrValueType: ValueDescriptorHolder | ValueDescriptor | string | undefined
+ ): ValueDescriptor | undefined {
+ let valueDescriptor: ValueDescriptor | undefined;
+
+ if (descriptorOrValueType) {
+ if (typeof descriptorOrValueType === "string") {
+ valueDescriptor = AssetModelUtil.getValueDescriptor(descriptorOrValueType);
+ }
+ if ((descriptorOrValueType as ValueDescriptor).jsonType) {
+ valueDescriptor = descriptorOrValueType as ValueDescriptor;
+ } else {
+ // Must be a value descriptor holder or value holder
+ valueDescriptor = AssetModelUtil.getValueDescriptor((descriptorOrValueType as ValueDescriptorHolder).type);
+ }
+ }
- if (descriptorOrValueType) {
- if (typeof(descriptorOrValueType) === "string") {
- valueDescriptor = AssetModelUtil.getValueDescriptor(descriptorOrValueType);
- }
- if ((descriptorOrValueType as ValueDescriptor).jsonType) {
- valueDescriptor = descriptorOrValueType as ValueDescriptor;
- } else {
- // Must be a value descriptor holder or value holder
- valueDescriptor = AssetModelUtil.getValueDescriptor((descriptorOrValueType as ValueDescriptorHolder).type);
- }
- }
+ if (!valueDescriptor && valueHolder) {
+ // Try and determine the value descriptor based on the value type
+ valueDescriptor = AssetModelUtil.getValueDescriptor(WellknownValueTypes.JSON);
+ }
- if (!valueDescriptor && valueHolder) {
- // Try and determine the value descriptor based on the value type
- valueDescriptor = AssetModelUtil.getValueDescriptor(WellknownValueTypes.JSON);
- }
+ return valueDescriptor;
+ }
- return valueDescriptor;
+ public static resolveValueTypeFromValue(value: any): string | undefined {
+ if (value === null || value === undefined) {
+ return undefined;
}
- public static resolveValueTypeFromValue(value: any): string | undefined {
- if (value === null || value === undefined) {
- return undefined;
- }
+ if (typeof value === "number") {
+ return WellknownValueTypes.NUMBER;
+ }
+ if (typeof value === "string") {
+ return WellknownValueTypes.TEXT;
+ }
+ if (typeof value === "boolean") {
+ return WellknownValueTypes.BOOLEAN;
+ }
+ if (Array.isArray(value)) {
+ let dimensions = 1;
+ let v = (value as any[]).find((v) => v !== undefined && v !== null);
- if (typeof value === "number") {
- return WellknownValueTypes.NUMBER;
- }
- if (typeof value === "string") {
- return WellknownValueTypes.TEXT;
- }
- if (typeof value === "boolean") {
- return WellknownValueTypes.BOOLEAN;
- }
- if (Array.isArray(value)) {
- let dimensions = 1;
- let v = (value as any[]).find(v => v !== undefined && v !== null);
+ while (Array.isArray(v)) {
+ v = (v as any[]).find((v) => v !== undefined && v !== null);
+ dimensions++;
+ }
- while (Array.isArray(v)) {
- v = (v as any[]).find(v => v !== undefined && v !== null);
- dimensions++;
- }
+ let valueType = this.resolveValueTypeFromValue(v);
- let valueType = this.resolveValueTypeFromValue(v);
+ if (!valueType) {
+ return;
+ }
- if (!valueType) {
- return;
- }
+ while (dimensions > 0) {
+ valueType += "[]";
+ dimensions--;
+ }
- while (dimensions > 0) {
- valueType += "[]";
- dimensions--;
- }
+ return valueType;
+ }
+ if (value instanceof Date) {
+ return WellknownValueTypes.DATEANDTIME;
+ }
+ }
+
+ public static getAttributeAndValueDescriptors(
+ assetType: string | undefined,
+ attributeNameOrDescriptor: string | AttributeDescriptor | undefined,
+ attribute?: Attribute
+ ): [AttributeDescriptor | undefined, ValueDescriptor | undefined] {
+ let attributeDescriptor: AttributeDescriptor | undefined;
+ let valueDescriptor: ValueDescriptor | undefined;
+
+ if (attributeNameOrDescriptor && typeof attributeNameOrDescriptor !== "string") {
+ attributeDescriptor = attributeNameOrDescriptor as AttributeDescriptor;
+ } else {
+ const assetTypeInfo = this.getAssetTypeInfo(assetType || WellknownAssets.THINGASSET);
+
+ if (!assetTypeInfo) {
+ return [undefined, undefined];
+ }
+
+ if (typeof attributeNameOrDescriptor === "string") {
+ attributeDescriptor = this.getAttributeDescriptor(attributeNameOrDescriptor as string, assetTypeInfo);
+ }
+
+ if (!attributeDescriptor && attribute) {
+ attributeDescriptor = {
+ type: attribute.type,
+ name: attribute.name,
+ meta: attribute.meta,
+ };
+ }
+ }
- return valueType;
- }
- if (value instanceof Date) {
- return WellknownValueTypes.DATEANDTIME;
- }
- }
-
- public static getAttributeAndValueDescriptors(assetType: string | undefined, attributeNameOrDescriptor: string | AttributeDescriptor | undefined, attribute?: Attribute): [AttributeDescriptor | undefined, ValueDescriptor | undefined] {
- let attributeDescriptor: AttributeDescriptor | undefined;
- let valueDescriptor: ValueDescriptor | undefined;
-
- if (attributeNameOrDescriptor && typeof attributeNameOrDescriptor !== "string") {
- attributeDescriptor = attributeNameOrDescriptor as AttributeDescriptor;
- } else {
- const assetTypeInfo = this.getAssetTypeInfo(assetType || WellknownAssets.THINGASSET);
+ if (attributeDescriptor) {
+ valueDescriptor = this.getValueDescriptor(attributeDescriptor.type);
+ }
- if (!assetTypeInfo) {
- return [undefined, undefined];
- }
-
- if (typeof (attributeNameOrDescriptor) === "string") {
- attributeDescriptor = this.getAttributeDescriptor(attributeNameOrDescriptor as string, assetTypeInfo);
- }
-
- if (!attributeDescriptor && attribute) {
- attributeDescriptor = {
- type: attribute.type,
- name: attribute.name,
- meta: attribute.meta
- };
- }
- }
-
- if (attributeDescriptor) {
- valueDescriptor = this.getValueDescriptor(attributeDescriptor.type);
- }
-
- return [attributeDescriptor, valueDescriptor];
- }
+ return [attributeDescriptor, valueDescriptor];
+ }
- public static getMetaItemDescriptor(name?: string): MetaItemDescriptor | undefined {
- if (!name) {
- return;
- }
-
- // Meta item descriptor names are globally unique
- return this._metaItemDescriptors.find((metaItemDescriptor) => metaItemDescriptor.name === name);
- }
-
- public static getAssetDescriptorColour(typeOrDescriptor: string | AssetTypeInfo | AssetDescriptor | undefined, fallbackColor?: string): string | undefined {
- const assetDescriptor = this.getAssetDescriptor(typeOrDescriptor);
- return assetDescriptor && assetDescriptor.colour ? assetDescriptor.colour : fallbackColor;
+ public static getMetaItemDescriptor(name?: string): MetaItemDescriptor | undefined {
+ if (!name) {
+ return;
}
- public static getAssetDescriptorIcon(typeOrDescriptor: string | AssetTypeInfo | AssetDescriptor | undefined, fallbackIcon?: string): string | undefined {
- const assetDescriptor = this.getAssetDescriptor(typeOrDescriptor);
- return assetDescriptor && assetDescriptor.icon ? assetDescriptor.icon : fallbackIcon;
- }
+ // Meta item descriptor names are globally unique
+ return this._metaItemDescriptors.find((metaItemDescriptor) => metaItemDescriptor.name === name);
+ }
+
+ public static getAssetDescriptorColour(
+ typeOrDescriptor: string | AssetTypeInfo | AssetDescriptor | undefined,
+ fallbackColor?: string
+ ): string | undefined {
+ const assetDescriptor = this.getAssetDescriptor(typeOrDescriptor);
+ return assetDescriptor && assetDescriptor.colour ? assetDescriptor.colour : fallbackColor;
+ }
+
+ public static getAssetDescriptorIcon(
+ typeOrDescriptor: string | AssetTypeInfo | AssetDescriptor | undefined,
+ fallbackIcon?: string
+ ): string | undefined {
+ const assetDescriptor = this.getAssetDescriptor(typeOrDescriptor);
+ return assetDescriptor && assetDescriptor.icon ? assetDescriptor.icon : fallbackIcon;
+ }
}
diff --git a/ui/component/model/tsconfig.json b/ui/component/model/tsconfig.json
index 8a0e9a1dd..c3294f18e 100644
--- a/ui/component/model/tsconfig.json
+++ b/ui/component/model/tsconfig.json
@@ -5,7 +5,5 @@
"rootDir": "src",
"preserveConstEnums": true
},
- "include": [
- "./src"
- ]
+ "include": ["./src"]
}
diff --git a/ui/component/rest/build.gradle b/ui/component/rest/build.gradle
index 3b6b3b104..705ff4da9 100644
--- a/ui/component/rest/build.gradle
+++ b/ui/component/rest/build.gradle
@@ -1,16 +1,16 @@
plugins {
- id 'groovy'
- id 'cz.habarta.typescript-generator'
+ id 'groovy'
+ id 'cz.habarta.typescript-generator'
}
dependencies {
- compileOnly libs.openremote.model.util
- implementation project(":agent")
- implementation project(":model")
- implementation libs.typescript.generator.core
- implementation libs.jackson.datatype.jdk8
- implementation libs.jackson.datatype.jsr310
- implementation libs.jackson.module.parameter.names
+ compileOnly libs.openremote.model.util
+ implementation project(":agent")
+ implementation project(":model")
+ implementation libs.typescript.generator.core
+ implementation libs.jackson.datatype.jdk8
+ implementation libs.jackson.datatype.jsr310
+ implementation libs.jackson.module.parameter.names
}
generateTypeScript createTSGeneratorConfigForClient("src/restclient.ts", new File("${project(":ui:component:model").projectDir}/src/typescript-generator-info.json"), project(":model"))
@@ -18,53 +18,53 @@ generateTypeScript createTSGeneratorConfigForClient("src/restclient.ts", new Fil
def resourceJavaFilesCount = countResourceJavaFiles()
def generateTypeScriptTask = tasks.named("generateTypeScript") {
- // Work around typescript-generator exposing non-serializable configuration objects as Gradle task inputs.
- doNotTrackState("typescript-generator uses non-serializable task input types")
- dependsOn ":ui:component:model:generateTypeScript"
- onlyIf { resourceJavaFilesCount > 0 }
- finalizedBy dummyRestClient
+ // Work around typescript-generator exposing non-serializable configuration objects as Gradle task inputs.
+ doNotTrackState("typescript-generator uses non-serializable task input types")
+ dependsOn ":ui:component:model:generateTypeScript"
+ onlyIf { resourceJavaFilesCount> 0 }
+ finalizedBy dummyRestClient
}
tasks.register('dummyRestClient') {
- onlyIf { resourceJavaFilesCount == 0 }
- doLast {
- file("${projectDir}/src/restclient.ts").text = 'export class ApiClient {}'
- }
+ onlyIf { resourceJavaFilesCount == 0 }
+ doLast {
+ file("${projectDir}/src/restclient.ts").text = 'export class ApiClient {}'
+ }
}
def countResourceJavaFiles() {
- def modelProject = project(":model")
- def sourceDir = modelProject.layout.projectDirectory.dir("src/main/java")
- def resourceFiles = modelProject.fileTree(sourceDir) {
- include "**/*Resource.java"
- }
- def count = resourceFiles.files.size()
- println "Total Java source files ending with 'Resource': $count"
- return count
+ def modelProject = project(":model")
+ def sourceDir = modelProject.layout.projectDirectory.dir("src/main/java")
+ def resourceFiles = modelProject.fileTree(sourceDir) {
+ include "**/*Resource.java"
+ }
+ def count = resourceFiles.files.size()
+ println "Total Java source files ending with 'Resource': $count"
+ return count
}
tasks.named('clean', Delete) {
- delete layout.projectDirectory.dir('dist')
+ delete layout.projectDirectory.dir('dist')
}
tasks.named("build") {
- dependsOn generateTypeScriptTask, tasks.named("npmBuild")
+ dependsOn generateTypeScriptTask, tasks.named("npmBuild")
}
tasks.named("npmBuild") {
- dependsOn generateTypeScriptTask
- onlyIf { resourceJavaFilesCount > 0 }
+ dependsOn generateTypeScriptTask
+ onlyIf { resourceJavaFilesCount> 0 }
}
tasks.register('npmTest', Exec) {
- dependsOn getYarnInstallTask()
- commandLine npmCommand("yarn"), "run", "test"
+ dependsOn getYarnInstallTask()
+ commandLine npmCommand("yarn"), "run", "test"
}
tasks.register('prepareUi') {
- dependsOn clean, build, npmPrepare
+ dependsOn clean, build, npmPrepare
}
tasks.register('publishUi') {
- dependsOn clean, build, npmPublish
+ dependsOn clean, build, npmPublish
}
diff --git a/ui/component/rest/package.json b/ui/component/rest/package.json
index 0ac56bc36..fec7f0a7f 100644
--- a/ui/component/rest/package.json
+++ b/ui/component/rest/package.json
@@ -28,6 +28,17 @@
"devDependencies": {
"@openremote/util": "~1.27.0",
"@rspack/core": "^1.7.10",
+ "@typescript-eslint/eslint-plugin": "8.40.0",
+ "@typescript-eslint/parser": "8.40.0",
+ "eslint": "8.57.1",
+ "eslint-config-prettier": "10.1.8",
+ "eslint-config-standard": "17.1.0",
+ "eslint-plugin-import": "2.32.0",
+ "eslint-plugin-lit": "2.3.1",
+ "eslint-plugin-n": "16.6.2",
+ "eslint-plugin-promise": "6.6.0",
+ "eslint-plugin-wc": "3.1.0",
+ "prettier": "3.9.6",
"typescript": "^5.9.3"
},
"publishConfig": {
diff --git a/ui/component/rest/rspack.config.js b/ui/component/rest/rspack.config.js
index 470cffad5..8043399e8 100644
--- a/ui/component/rest/rspack.config.js
+++ b/ui/component/rest/rspack.config.js
@@ -1,12 +1,30 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
const util = require("@openremote/util");
bundles = {
- "index": {
- excludeOr: true
- },
- "index.bundle": {
- excludeOr: true,
- }
+ index: {
+ excludeOr: true,
+ },
+ "index.bundle": {
+ excludeOr: true,
+ },
};
module.exports = util.generateExports(__dirname);
diff --git a/ui/component/rest/src/index.ts b/ui/component/rest/src/index.ts
index 9c5788385..8cfac0715 100644
--- a/ui/component/rest/src/index.ts
+++ b/ui/component/rest/src/index.ts
@@ -1,18 +1,33 @@
-import {ApiClient} from "./restclient";
-
-
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { ApiClient } from "./restclient";
export class RestApi {
+ protected _apiClient: ApiClient;
- protected _apiClient: ApiClient;
-
- constructor(apiClient: ApiClient) {
- this._apiClient = apiClient;
- }
+ constructor(apiClient: ApiClient) {
+ this._apiClient = apiClient;
+ }
- get api() {
- return this._apiClient;
- }
+ get api() {
+ return this._apiClient;
+ }
}
export default new RestApi(new ApiClient());
diff --git a/ui/component/rest/tsconfig.json b/ui/component/rest/tsconfig.json
index 3436fc50f..361c00b5e 100644
--- a/ui/component/rest/tsconfig.json
+++ b/ui/component/rest/tsconfig.json
@@ -4,10 +4,6 @@
"outDir": "./lib",
"rootDir": "src"
},
- "include": [
- "./src"
- ],
- "references": [
- { "path": "../model" }
- ]
+ "include": ["./src"],
+ "references": [{ "path": "../model" }]
}
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
index 007bcbc88..a7dffb7a4 100644
--- a/ui/tsconfig.json
+++ b/ui/tsconfig.json
@@ -4,14 +4,11 @@
"target": "es6",
"module": "esNext",
"moduleResolution": "node",
- "lib": [
- "dom",
- "es2017",
- "es2019"
- ],
+ "lib": ["dom", "es2017", "es2019", "es2021", "es2022"],
"strict": true,
"declaration": true,
"sourceMap": true,
+ "declarationMap": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"composite": true,
diff --git a/ui/typedoc.js b/ui/typedoc.js
index d297ede8a..17cbf3505 100644
--- a/ui/typedoc.js
+++ b/ui/typedoc.js
@@ -1,18 +1,36 @@
+/*
+ * Copyright 2026, OpenRemote Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
module.exports = {
- "mode": "modules",
- "out": "docs",
- "exclude": "test",
- "theme": "minimal",
- "ignoreCompilerErrors": true,
- "excludePrivate": true,
- "excludeProtected": true,
- "excludeNotExported": true,
- "target": "es6",
- "moduleResolution": "node",
- "preserveConstEnums": true,
- "stripInternal": true,
- "suppressExcessPropertyErrors": true,
- "suppressImplicitAnyIndexErrors": true,
- "module": "esNext",
- "external-modulemap": ".*node_modules\/(@openremote\/[^\/]+)\/.*"
-}
+ mode: "modules",
+ out: "docs",
+ exclude: "test",
+ theme: "minimal",
+ ignoreCompilerErrors: true,
+ excludePrivate: true,
+ excludeProtected: true,
+ excludeNotExported: true,
+ target: "es6",
+ moduleResolution: "node",
+ preserveConstEnums: true,
+ stripInternal: true,
+ suppressExcessPropertyErrors: true,
+ suppressImplicitAnyIndexErrors: true,
+ module: "esNext",
+ "external-modulemap": ".*node_modules\/(@openremote\/[^\/]+)\/.*",
+};
diff --git a/yarn.lock b/yarn.lock
index 046ac2585..79b84fca1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -115,7 +115,7 @@ __metadata:
languageName: node
linkType: hard
-"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1":
+"@eslint-community/eslint-utils@npm:^4.1.2, @eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1":
version: 4.9.1
resolution: "@eslint-community/eslint-utils@npm:4.9.1"
dependencies:
@@ -126,7 +126,7 @@ __metadata:
languageName: node
linkType: hard
-"@eslint-community/regexpp@npm:^4.12.2":
+"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.11.0, @eslint-community/regexpp@npm:^4.12.2, @eslint-community/regexpp@npm:^4.6.1":
version: 4.12.2
resolution: "@eslint-community/regexpp@npm:4.12.2"
checksum: 10/049b280fddf71dd325514e0a520024969431dc3a8b02fa77476e6820e9122f28ab4c9168c11821f91a27982d2453bcd7a66193356ea84e84fb7c8d793be1ba0c
@@ -162,6 +162,30 @@ __metadata:
languageName: node
linkType: hard
+"@eslint/eslintrc@npm:^2.1.4":
+ version: 2.1.4
+ resolution: "@eslint/eslintrc@npm:2.1.4"
+ dependencies:
+ ajv: "npm:^6.12.4"
+ debug: "npm:^4.3.2"
+ espree: "npm:^9.6.0"
+ globals: "npm:^13.19.0"
+ ignore: "npm:^5.2.0"
+ import-fresh: "npm:^3.2.1"
+ js-yaml: "npm:^4.1.0"
+ minimatch: "npm:^3.1.2"
+ strip-json-comments: "npm:^3.1.1"
+ checksum: 10/7a3b14f4b40fc1a22624c3f84d9f467a3d9ea1ca6e9a372116cb92507e485260359465b58e25bcb6c9981b155416b98c9973ad9b796053fd7b3f776a6946bce8
+ languageName: node
+ linkType: hard
+
+"@eslint/js@npm:8.57.1":
+ version: 8.57.1
+ resolution: "@eslint/js@npm:8.57.1"
+ checksum: 10/7562b21be10c2adbfa4aa5bb2eccec2cb9ac649a3569560742202c8d1cb6c931ce634937a2f0f551e078403a1c1285d6c2c0aa345dafc986149665cd69fe8b59
+ languageName: node
+ linkType: hard
+
"@eslint/object-schema@npm:^3.0.5":
version: 3.0.5
resolution: "@eslint/object-schema@npm:3.0.5"
@@ -213,6 +237,17 @@ __metadata:
languageName: node
linkType: hard
+"@humanwhocodes/config-array@npm:^0.13.0":
+ version: 0.13.0
+ resolution: "@humanwhocodes/config-array@npm:0.13.0"
+ dependencies:
+ "@humanwhocodes/object-schema": "npm:^2.0.3"
+ debug: "npm:^4.3.1"
+ minimatch: "npm:^3.0.5"
+ checksum: 10/524df31e61a85392a2433bf5d03164e03da26c03d009f27852e7dcfdafbc4a23f17f021dacf88e0a7a9fe04ca032017945d19b57a16e2676d9114c22a53a9d11
+ languageName: node
+ linkType: hard
+
"@humanwhocodes/module-importer@npm:^1.0.1":
version: 1.0.1
resolution: "@humanwhocodes/module-importer@npm:1.0.1"
@@ -220,6 +255,13 @@ __metadata:
languageName: node
linkType: hard
+"@humanwhocodes/object-schema@npm:^2.0.3":
+ version: 2.0.3
+ resolution: "@humanwhocodes/object-schema@npm:2.0.3"
+ checksum: 10/05bb99ed06c16408a45a833f03a732f59bf6184795d4efadd33238ff8699190a8c871ad1121241bb6501589a9598dc83bf25b99dcbcf41e155cdf36e35e937a3
+ languageName: node
+ linkType: hard
+
"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2":
version: 0.4.3
resolution: "@humanwhocodes/retry@npm:0.4.3"
@@ -1228,7 +1270,7 @@ __metadata:
languageName: node
linkType: hard
-"@nodelib/fs.walk@npm:^1.2.3":
+"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8":
version: 1.2.8
resolution: "@nodelib/fs.walk@npm:1.2.8"
dependencies:
@@ -1298,7 +1340,18 @@ __metadata:
"@rspack/core": "npm:^1.7.10"
"@types/react": "npm:^19.2.2"
"@types/react-dom": "npm:^19.2.2"
+ "@typescript-eslint/eslint-plugin": "npm:8.40.0"
+ "@typescript-eslint/parser": "npm:8.40.0"
cross-env: "npm:^10.1.0"
+ eslint: "npm:8.57.1"
+ eslint-config-prettier: "npm:10.1.8"
+ eslint-config-standard: "npm:17.1.0"
+ eslint-plugin-import: "npm:2.32.0"
+ eslint-plugin-lit: "npm:2.3.1"
+ eslint-plugin-n: "npm:16.6.2"
+ eslint-plugin-promise: "npm:6.6.0"
+ eslint-plugin-wc: "npm:3.1.0"
+ prettier: "npm:3.9.6"
react: "npm:^19.2.0"
react-dom: "npm:^19.2.0"
react-refresh: "npm:^0.18.0"
@@ -1316,10 +1369,21 @@ __metadata:
"@openremote/util": "npm:~1.27.0"
"@rspack/cli": "npm:^1.7.10"
"@rspack/core": "npm:^1.7.10"
+ "@typescript-eslint/eslint-plugin": "npm:8.40.0"
+ "@typescript-eslint/parser": "npm:8.40.0"
cross-env: "npm:*"
css-loader: "npm:*"
+ eslint: "npm:8.57.1"
+ eslint-config-prettier: "npm:10.1.8"
+ eslint-config-standard: "npm:17.1.0"
+ eslint-plugin-import: "npm:2.32.0"
+ eslint-plugin-lit: "npm:2.3.1"
+ eslint-plugin-n: "npm:16.6.2"
+ eslint-plugin-promise: "npm:6.6.0"
+ eslint-plugin-wc: "npm:3.1.0"
lit: "npm:^3.3.1"
model: "workspace:*"
+ prettier: "npm:3.9.6"
rest: "workspace:*"
shx: "npm:*"
ts-loader: "npm:*"
@@ -2611,6 +2675,27 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/eslint-plugin@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/eslint-plugin@npm:8.40.0"
+ dependencies:
+ "@eslint-community/regexpp": "npm:^4.10.0"
+ "@typescript-eslint/scope-manager": "npm:8.40.0"
+ "@typescript-eslint/type-utils": "npm:8.40.0"
+ "@typescript-eslint/utils": "npm:8.40.0"
+ "@typescript-eslint/visitor-keys": "npm:8.40.0"
+ graphemer: "npm:^1.4.0"
+ ignore: "npm:^7.0.0"
+ natural-compare: "npm:^1.4.0"
+ ts-api-utils: "npm:^2.1.0"
+ peerDependencies:
+ "@typescript-eslint/parser": ^8.40.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/9df4d4ac58734a34964b791622dcb94ffc6c49c1d0f4fd0480b3fc0e026527df7167ff78a4f8bbd29089d605756c28c1a90b2f0653df34b40ac8b969bc6c92e9
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/eslint-plugin@npm:^8.61.1":
version: 8.64.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.64.0"
@@ -2631,6 +2716,22 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/parser@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/parser@npm:8.40.0"
+ dependencies:
+ "@typescript-eslint/scope-manager": "npm:8.40.0"
+ "@typescript-eslint/types": "npm:8.40.0"
+ "@typescript-eslint/typescript-estree": "npm:8.40.0"
+ "@typescript-eslint/visitor-keys": "npm:8.40.0"
+ debug: "npm:^4.3.4"
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/1e60f70e9d02f930553db7f4684c27c376fadf345db155414a22d1a32cd21def7d36496bd63c1acbf3afbec9fb8794947e880f88c1143b83e1d3c45146cec41a
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/parser@npm:^8.61.1":
version: 8.64.0
resolution: "@typescript-eslint/parser@npm:8.64.0"
@@ -2647,6 +2748,19 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/project-service@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/project-service@npm:8.40.0"
+ dependencies:
+ "@typescript-eslint/tsconfig-utils": "npm:^8.40.0"
+ "@typescript-eslint/types": "npm:^8.40.0"
+ debug: "npm:^4.3.4"
+ peerDependencies:
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/86491aa65c1dd78c9784dddd8467601aef8be652c5fb3a901e8b1995cf07c1dbe11d0ab4610d770e3f4063c0c254a6c6aa5fb7cf724bf12fa4ee56f47f3a2955
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/project-service@npm:8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/project-service@npm:8.64.0"
@@ -2660,6 +2774,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/scope-manager@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/scope-manager@npm:8.40.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.40.0"
+ "@typescript-eslint/visitor-keys": "npm:8.40.0"
+ checksum: 10/0c5aa10208bfbb506bf3925a420c3de667298064bde400f03ee52c19cd0785dd05c2c820e05724d005737e2920d925aff0318ec3308156f9b81c84736a1fe46b
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/scope-manager@npm:8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/scope-manager@npm:8.64.0"
@@ -2670,6 +2794,15 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/tsconfig-utils@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/tsconfig-utils@npm:8.40.0"
+ peerDependencies:
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/c5a557cc83d194902140af0ddfa10b2776db3625e2c9bb609d0d720aa78a0735ff71df8bffd5c2a1b90cdada8242543c5421ad4dcd58cf2ff12717b733bcfca9
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/tsconfig-utils@npm:8.64.0, @typescript-eslint/tsconfig-utils@npm:^8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.64.0"
@@ -2679,6 +2812,31 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/tsconfig-utils@npm:^8.40.0":
+ version: 8.63.0
+ resolution: "@typescript-eslint/tsconfig-utils@npm:8.63.0"
+ peerDependencies:
+ typescript: ">=4.8.4 <6.1.0"
+ checksum: 10/7c23671159f8593b676d53bc52b4a3bd4c3ad62abe2ad99b379b70298abf3b784cdcf18ca2b10f2aa62a9f8e0232e6efb87364940a3bd4a32126a357f39ac3c8
+ languageName: node
+ linkType: hard
+
+"@typescript-eslint/type-utils@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/type-utils@npm:8.40.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.40.0"
+ "@typescript-eslint/typescript-estree": "npm:8.40.0"
+ "@typescript-eslint/utils": "npm:8.40.0"
+ debug: "npm:^4.3.4"
+ ts-api-utils: "npm:^2.1.0"
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/296c718330b2ac4408840258c30c01072de01ffe1c009be00c5049be1b19a71cbb2e258363ae349150760bcd2d34799df305b4cfc4d7f3b2fa9760ac8ffb3f75
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/type-utils@npm:8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/type-utils@npm:8.64.0"
@@ -2695,6 +2853,13 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/types@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/types@npm:8.40.0"
+ checksum: 10/f3931d0920a42b3bc69e9cdeb67a0c710597271cdd9d7c736302bdc52d21df1c962082df7cd712eeabd2c47658415d0a4b7d72f819cb38f82f4e234b48dbaa57
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/types@npm:8.64.0, @typescript-eslint/types@npm:^8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/types@npm:8.64.0"
@@ -2702,6 +2867,33 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/types@npm:^8.40.0":
+ version: 8.63.0
+ resolution: "@typescript-eslint/types@npm:8.63.0"
+ checksum: 10/f72eb114970cae0da8e53f68cd8dca1b51ac410f4438141a2851655fa07aacb3090e24a2ae53462cf0970e4d5b52cabf9693aa6f07abdff5c210874806427e5b
+ languageName: node
+ linkType: hard
+
+"@typescript-eslint/typescript-estree@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/typescript-estree@npm:8.40.0"
+ dependencies:
+ "@typescript-eslint/project-service": "npm:8.40.0"
+ "@typescript-eslint/tsconfig-utils": "npm:8.40.0"
+ "@typescript-eslint/types": "npm:8.40.0"
+ "@typescript-eslint/visitor-keys": "npm:8.40.0"
+ debug: "npm:^4.3.4"
+ fast-glob: "npm:^3.3.2"
+ is-glob: "npm:^4.0.3"
+ minimatch: "npm:^9.0.4"
+ semver: "npm:^7.6.0"
+ ts-api-utils: "npm:^2.1.0"
+ peerDependencies:
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/2e61ecfecb933f644799a7c11e4c7a730df57290c8d0482082cff7739b2401b0cf3b1ebef7b08a54a90285978957a49850d1a53061e8770164da651172ebee32
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/typescript-estree@npm:8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/typescript-estree@npm:8.64.0"
@@ -2721,6 +2913,21 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/utils@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/utils@npm:8.40.0"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.7.0"
+ "@typescript-eslint/scope-manager": "npm:8.40.0"
+ "@typescript-eslint/types": "npm:8.40.0"
+ "@typescript-eslint/typescript-estree": "npm:8.40.0"
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: ">=4.8.4 <6.0.0"
+ checksum: 10/b4cd1e6a4f55cc6475189de12e6bd418080a227e794745a2af304ab21655b031c28dae6387d4e9b54dd2f420696cec4f77cca9c66db405ed2281e0e09c95ba1c
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/utils@npm:8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/utils@npm:8.64.0"
@@ -2736,6 +2943,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/visitor-keys@npm:8.40.0":
+ version: 8.40.0
+ resolution: "@typescript-eslint/visitor-keys@npm:8.40.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.40.0"
+ eslint-visitor-keys: "npm:^4.2.1"
+ checksum: 10/191f47998001a5e9cdde7491b0215d9c6c45c637aedd7d32cafd35ce2a4a0f4b8582edab015e09238f48e025a788b99efd8e70e4e3200e32143f91c95112abcd
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/visitor-keys@npm:8.64.0":
version: 8.64.0
resolution: "@typescript-eslint/visitor-keys@npm:8.64.0"
@@ -2746,6 +2963,13 @@ __metadata:
languageName: node
linkType: hard
+"@ungap/structured-clone@npm:^1.2.0":
+ version: 1.3.0
+ resolution: "@ungap/structured-clone@npm:1.3.0"
+ checksum: 10/80d6910946f2b1552a2406650051c91bbd1f24a6bf854354203d84fe2714b3e8ce4618f49cc3410494173a1c1e8e9777372fe68dce74bd45faf0a7a1a6ccf448
+ languageName: node
+ linkType: hard
+
"@vaadin/a11y-base@npm:~25.2.4":
version: 25.2.4
resolution: "@vaadin/a11y-base@npm:25.2.4"
@@ -3522,7 +3746,7 @@ __metadata:
languageName: node
linkType: hard
-"acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.4.1":
+"acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.4.1, acorn@npm:^8.9.0":
version: 8.15.0
resolution: "acorn@npm:8.15.0"
bin:
@@ -3593,6 +3817,18 @@ __metadata:
languageName: node
linkType: hard
+"ajv@npm:^6.12.4":
+ version: 6.12.6
+ resolution: "ajv@npm:6.12.6"
+ dependencies:
+ fast-deep-equal: "npm:^3.1.1"
+ fast-json-stable-stringify: "npm:^2.0.0"
+ json-schema-traverse: "npm:^0.4.1"
+ uri-js: "npm:^4.2.2"
+ checksum: 10/48d6ad21138d12eb4d16d878d630079a2bda25a04e745c07846a4ad768319533031e28872a9b3c5790fa1ec41aabdf2abed30a56e5a03ebc2cf92184b8ee306c
+ languageName: node
+ linkType: hard
+
"ajv@npm:^6.14.0":
version: 6.15.0
resolution: "ajv@npm:6.15.0"
@@ -3697,6 +3933,13 @@ __metadata:
languageName: node
linkType: hard
+"argparse@npm:^2.0.1":
+ version: 2.0.1
+ resolution: "argparse@npm:2.0.1"
+ checksum: 10/18640244e641a417ec75a9bd38b0b2b6b95af5199aa241b131d4b2fb206f334d7ecc600bd194861610a5579084978bfcbb02baa399dbe442d56d0ae5e60dbaef
+ languageName: node
+ linkType: hard
+
"array-back@npm:^3.0.1":
version: 3.1.0
resolution: "array-back@npm:3.1.0"
@@ -3940,6 +4183,15 @@ __metadata:
languageName: node
linkType: hard
+"brace-expansion@npm:^2.0.2":
+ version: 2.1.2
+ resolution: "brace-expansion@npm:2.1.2"
+ dependencies:
+ balanced-match: "npm:^1.0.0"
+ checksum: 10/0e0f9b0df1f0809e9c326470e022eeb24334ddb54c43b1ac39f1d38f3cbaeb940794de1db826f5491843ac00d7932c43f10350a65ccd51fe7d05da627152f204
+ languageName: node
+ linkType: hard
+
"brace-expansion@npm:^5.0.5":
version: 5.0.7
resolution: "brace-expansion@npm:5.0.7"
@@ -3989,6 +4241,22 @@ __metadata:
languageName: node
linkType: hard
+"builtin-modules@npm:^3.3.0":
+ version: 3.3.0
+ resolution: "builtin-modules@npm:3.3.0"
+ checksum: 10/62e063ab40c0c1efccbfa9ffa31873e4f9d57408cb396a2649981a0ecbce56aabc93c28feaccbc5658c95aab2703ad1d11980e62ec2e5e72637404e1eb60f39e
+ languageName: node
+ linkType: hard
+
+"builtins@npm:^5.0.1":
+ version: 5.1.0
+ resolution: "builtins@npm:5.1.0"
+ dependencies:
+ semver: "npm:^7.0.0"
+ checksum: 10/60aa9969f69656bf6eab82cd74b23ab805f112ae46a54b912bccc1533875760f2d2ce95e0a7d13144e35ada9f0386f17ed4961908bc9434b5a5e21375b1902b2
+ languageName: node
+ linkType: hard
+
"bundle-name@npm:^4.1.0":
version: 4.1.0
resolution: "bundle-name@npm:4.1.0"
@@ -4077,6 +4345,13 @@ __metadata:
languageName: node
linkType: hard
+"callsites@npm:^3.0.0":
+ version: 3.1.0
+ resolution: "callsites@npm:3.1.0"
+ checksum: 10/072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3
+ languageName: node
+ linkType: hard
+
"camel-case@npm:^4.1.2":
version: 4.1.2
resolution: "camel-case@npm:4.1.2"
@@ -4094,7 +4369,7 @@ __metadata:
languageName: node
linkType: hard
-"chalk@npm:^4.1.0":
+"chalk@npm:^4.0.0, chalk@npm:^4.1.0":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
dependencies:
@@ -4380,7 +4655,7 @@ __metadata:
languageName: node
linkType: hard
-"cross-spawn@npm:^7.0.6":
+"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
dependencies:
@@ -4693,6 +4968,15 @@ __metadata:
languageName: node
linkType: hard
+"doctrine@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "doctrine@npm:3.0.0"
+ dependencies:
+ esutils: "npm:^2.0.2"
+ checksum: 10/b4b28f1df5c563f7d876e7461254a4597b8cabe915abe94d7c5d1633fed263fcf9a85e8d3836591fc2d040108e822b0d32758e5ec1fe31c590dc7e08086e3e48
+ languageName: node
+ linkType: hard
+
"dom-converter@npm:^0.2.0":
version: 0.2.0
resolution: "dom-converter@npm:0.2.0"
@@ -4720,6 +5004,13 @@ __metadata:
languageName: node
linkType: hard
+"domelementtype@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "domelementtype@npm:3.0.0"
+ checksum: 10/6b5120222f7e8b3491ad2f00036a1a14965bccab91014bfa54fa7789081b71f88141bf3b5b264c75ca4916b08e16ac1233a4f3b38067acb3fca9a7b21327cec1
+ languageName: node
+ linkType: hard
+
"domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1":
version: 4.3.1
resolution: "domhandler@npm:4.3.1"
@@ -4729,6 +5020,15 @@ __metadata:
languageName: node
linkType: hard
+"domhandler@npm:^6.0.1":
+ version: 6.0.1
+ resolution: "domhandler@npm:6.0.1"
+ dependencies:
+ domelementtype: "npm:^3.0.0"
+ checksum: 10/370e7c121cf3223a5aa21ce03666189eebac8d5b16af83efc221a93ed4f6c7b9f2cf2cc92ef7d50cbbd05787e1fbad42ec3c4717f6eb578667280443b94ffe10
+ languageName: node
+ linkType: hard
+
"domutils@npm:^2.5.2, domutils@npm:^2.8.0":
version: 2.8.0
resolution: "domutils@npm:2.8.0"
@@ -4879,6 +5179,13 @@ __metadata:
languageName: node
linkType: hard
+"entities@npm:^8.0.0":
+ version: 8.0.0
+ resolution: "entities@npm:8.0.0"
+ checksum: 10/d6e2ba75e444fb101ee2fbb07c839e687306c8a509426b75186619c19196f97c1db9932ca083f823c03e4a20e7407b654aa34de8cbb7770468e20fb2d4573a0e
+ languageName: node
+ linkType: hard
+
"env-paths@npm:^2.2.0":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
@@ -5056,7 +5363,18 @@ __metadata:
languageName: node
linkType: hard
-"eslint-config-prettier@npm:^10.1.8":
+"eslint-compat-utils@npm:^0.5.1":
+ version: 0.5.1
+ resolution: "eslint-compat-utils@npm:0.5.1"
+ dependencies:
+ semver: "npm:^7.5.4"
+ peerDependencies:
+ eslint: ">=6.0.0"
+ checksum: 10/ac65ac1c6107cf19f63f5fc17cea361c9cb1336be7356f23dbb0fac10979974b4622e13e950be43cbf431801f2c07f7dab448573181ccf6edc0b86d5b5304511
+ languageName: node
+ linkType: hard
+
+"eslint-config-prettier@npm:10.1.8, eslint-config-prettier@npm:^10.1.8":
version: 10.1.8
resolution: "eslint-config-prettier@npm:10.1.8"
peerDependencies:
@@ -5067,7 +5385,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-config-standard@npm:^17.1.0":
+"eslint-config-standard@npm:17.1.0, eslint-config-standard@npm:^17.1.0":
version: 17.1.0
resolution: "eslint-config-standard@npm:17.1.0"
peerDependencies:
@@ -5102,6 +5420,19 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-es-x@npm:^7.5.0":
+ version: 7.8.0
+ resolution: "eslint-plugin-es-x@npm:7.8.0"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.1.2"
+ "@eslint-community/regexpp": "npm:^4.11.0"
+ eslint-compat-utils: "npm:^0.5.1"
+ peerDependencies:
+ eslint: ">=8"
+ checksum: 10/1df8d52c4fadc06854ce801af05b05f2642aa2deb918fb7d37738596eabd70b7f21a22b150b78ec9104bac6a1b6b4fb796adea2364ede91b01d20964849ce5f7
+ languageName: node
+ linkType: hard
+
"eslint-plugin-es@npm:^3.0.0":
version: 3.0.1
resolution: "eslint-plugin-es@npm:3.0.1"
@@ -5114,7 +5445,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-plugin-import@npm:^2.32.0":
+"eslint-plugin-import@npm:2.32.0, eslint-plugin-import@npm:^2.32.0":
version: 2.32.0
resolution: "eslint-plugin-import@npm:2.32.0"
dependencies:
@@ -5143,6 +5474,39 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-lit@npm:2.3.1":
+ version: 2.3.1
+ resolution: "eslint-plugin-lit@npm:2.3.1"
+ dependencies:
+ parse5: "npm:^8.0.1"
+ parse5-htmlparser2-tree-adapter: "npm:^8.0.1"
+ peerDependencies:
+ eslint: ">= 8"
+ checksum: 10/e1de53d3e8dc4d7e43dba67ad66cbc150025a52e6c30877874f162dfc5625b323c6f0d04beb727d7b97ff8e33a9a5a0a224b0711e7817b9ab8ef2bc77bed6cce
+ languageName: node
+ linkType: hard
+
+"eslint-plugin-n@npm:16.6.2":
+ version: 16.6.2
+ resolution: "eslint-plugin-n@npm:16.6.2"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.4.0"
+ builtins: "npm:^5.0.1"
+ eslint-plugin-es-x: "npm:^7.5.0"
+ get-tsconfig: "npm:^4.7.0"
+ globals: "npm:^13.24.0"
+ ignore: "npm:^5.2.4"
+ is-builtin-module: "npm:^3.2.1"
+ is-core-module: "npm:^2.12.1"
+ minimatch: "npm:^3.1.2"
+ resolve: "npm:^1.22.2"
+ semver: "npm:^7.5.3"
+ peerDependencies:
+ eslint: ">=7.0.0"
+ checksum: 10/e0f600d03d3a3df57e9a811648b1b534a6d67c90ea9406340ddf3763c2b87cf5ef910b390f787ca5cb27c8d8ff36aad42d70209b54e2a1cb4cc2507ca417229a
+ languageName: node
+ linkType: hard
+
"eslint-plugin-node@npm:^11.1.0":
version: 11.1.0
resolution: "eslint-plugin-node@npm:11.1.0"
@@ -5159,6 +5523,15 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-promise@npm:6.6.0":
+ version: 6.6.0
+ resolution: "eslint-plugin-promise@npm:6.6.0"
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+ checksum: 10/c2b5604efd7e1390c132fcbf06cb2f072c956ffa65c14a991cb74ba1e2327357797239cb5b9b292d5e4010301bb897bd85a6273d7873fb157edc46aa2d95cbd9
+ languageName: node
+ linkType: hard
+
"eslint-plugin-promise@npm:^7.3.0":
version: 7.3.0
resolution: "eslint-plugin-promise@npm:7.3.0"
@@ -5170,6 +5543,18 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-wc@npm:3.1.0":
+ version: 3.1.0
+ resolution: "eslint-plugin-wc@npm:3.1.0"
+ dependencies:
+ is-valid-element-name: "npm:^1.0.0"
+ js-levenshtein-esm: "npm:^2.0.0"
+ peerDependencies:
+ eslint: ">=8.40.0"
+ checksum: 10/00797dcbf3f50d88ce1036555bc8940d7796015a77c2a5e9953638ae9d847f8a81027342232909924f46a1b834be957ed5e08168932482b9c21e6a0ef02f7543
+ languageName: node
+ linkType: hard
+
"eslint-scope@npm:*, eslint-scope@npm:^9.1.2":
version: 9.1.2
resolution: "eslint-scope@npm:9.1.2"
@@ -5192,6 +5577,16 @@ __metadata:
languageName: node
linkType: hard
+"eslint-scope@npm:^7.2.2":
+ version: 7.2.2
+ resolution: "eslint-scope@npm:7.2.2"
+ dependencies:
+ esrecurse: "npm:^4.3.0"
+ estraverse: "npm:^5.2.0"
+ checksum: 10/5c660fb905d5883ad018a6fea2b49f3cb5b1cbf2cd4bd08e98646e9864f9bc2c74c0839bed2d292e90a4a328833accc197c8f0baed89cbe8d605d6f918465491
+ languageName: node
+ linkType: hard
+
"eslint-utils@npm:^2.0.0":
version: 2.1.0
resolution: "eslint-utils@npm:2.1.0"
@@ -5208,13 +5603,20 @@ __metadata:
languageName: node
linkType: hard
-"eslint-visitor-keys@npm:^3.4.3":
+"eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3":
version: 3.4.3
resolution: "eslint-visitor-keys@npm:3.4.3"
checksum: 10/3f357c554a9ea794b094a09bd4187e5eacd1bc0d0653c3adeb87962c548e6a1ab8f982b86963ae1337f5d976004146536dcee5d0e2806665b193fbfbf1a9231b
languageName: node
linkType: hard
+"eslint-visitor-keys@npm:^4.2.1":
+ version: 4.2.1
+ resolution: "eslint-visitor-keys@npm:4.2.1"
+ checksum: 10/3ee00fc6a7002d4b0ffd9dc99e13a6a7882c557329e6c25ab254220d71e5c9c4f89dca4695352949ea678eb1f3ba912a18ef8aac0a7fe094196fd92f441bfce2
+ languageName: node
+ linkType: hard
+
"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1":
version: 5.0.1
resolution: "eslint-visitor-keys@npm:5.0.1"
@@ -5222,6 +5624,54 @@ __metadata:
languageName: node
linkType: hard
+"eslint@npm:8.57.1":
+ version: 8.57.1
+ resolution: "eslint@npm:8.57.1"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.2.0"
+ "@eslint-community/regexpp": "npm:^4.6.1"
+ "@eslint/eslintrc": "npm:^2.1.4"
+ "@eslint/js": "npm:8.57.1"
+ "@humanwhocodes/config-array": "npm:^0.13.0"
+ "@humanwhocodes/module-importer": "npm:^1.0.1"
+ "@nodelib/fs.walk": "npm:^1.2.8"
+ "@ungap/structured-clone": "npm:^1.2.0"
+ ajv: "npm:^6.12.4"
+ chalk: "npm:^4.0.0"
+ cross-spawn: "npm:^7.0.2"
+ debug: "npm:^4.3.2"
+ doctrine: "npm:^3.0.0"
+ escape-string-regexp: "npm:^4.0.0"
+ eslint-scope: "npm:^7.2.2"
+ eslint-visitor-keys: "npm:^3.4.3"
+ espree: "npm:^9.6.1"
+ esquery: "npm:^1.4.2"
+ esutils: "npm:^2.0.2"
+ fast-deep-equal: "npm:^3.1.3"
+ file-entry-cache: "npm:^6.0.1"
+ find-up: "npm:^5.0.0"
+ glob-parent: "npm:^6.0.2"
+ globals: "npm:^13.19.0"
+ graphemer: "npm:^1.4.0"
+ ignore: "npm:^5.2.0"
+ imurmurhash: "npm:^0.1.4"
+ is-glob: "npm:^4.0.0"
+ is-path-inside: "npm:^3.0.3"
+ js-yaml: "npm:^4.1.0"
+ json-stable-stringify-without-jsonify: "npm:^1.0.1"
+ levn: "npm:^0.4.1"
+ lodash.merge: "npm:^4.6.2"
+ minimatch: "npm:^3.1.2"
+ natural-compare: "npm:^1.4.0"
+ optionator: "npm:^0.9.3"
+ strip-ansi: "npm:^6.0.1"
+ text-table: "npm:^0.2.0"
+ bin:
+ eslint: bin/eslint.js
+ checksum: 10/5504fa24879afdd9f9929b2fbfc2ee9b9441a3d464efd9790fbda5f05738858530182029f13323add68d19fec749d3ab4a70320ded091ca4432b1e9cc4ed104c
+ languageName: node
+ linkType: hard
+
"eslint@npm:^10.5.0":
version: 10.7.0
resolution: "eslint@npm:10.7.0"
@@ -5278,7 +5728,18 @@ __metadata:
languageName: node
linkType: hard
-"esquery@npm:^1.7.0":
+"espree@npm:^9.6.0, espree@npm:^9.6.1":
+ version: 9.6.1
+ resolution: "espree@npm:9.6.1"
+ dependencies:
+ acorn: "npm:^8.9.0"
+ acorn-jsx: "npm:^5.3.2"
+ eslint-visitor-keys: "npm:^3.4.1"
+ checksum: 10/255ab260f0d711a54096bdeda93adff0eadf02a6f9b92f02b323e83a2b7fc258797919437ad331efec3930475feb0142c5ecaaf3cdab4befebd336d47d3f3134
+ languageName: node
+ linkType: hard
+
+"esquery@npm:^1.4.2, esquery@npm:^1.7.0":
version: 1.7.0
resolution: "esquery@npm:1.7.0"
dependencies:
@@ -5477,6 +5938,15 @@ __metadata:
languageName: node
linkType: hard
+"file-entry-cache@npm:^6.0.1":
+ version: 6.0.1
+ resolution: "file-entry-cache@npm:6.0.1"
+ dependencies:
+ flat-cache: "npm:^3.0.4"
+ checksum: 10/099bb9d4ab332cb93c48b14807a6918a1da87c45dce91d4b61fd40e6505d56d0697da060cb901c729c90487067d93c9243f5da3dc9c41f0358483bfdebca736b
+ languageName: node
+ linkType: hard
+
"file-entry-cache@npm:^8.0.0":
version: 8.0.0
resolution: "file-entry-cache@npm:8.0.0"
@@ -5538,6 +6008,17 @@ __metadata:
languageName: node
linkType: hard
+"flat-cache@npm:^3.0.4":
+ version: 3.2.0
+ resolution: "flat-cache@npm:3.2.0"
+ dependencies:
+ flatted: "npm:^3.2.9"
+ keyv: "npm:^4.5.3"
+ rimraf: "npm:^3.0.2"
+ checksum: 10/02381c6ece5e9fa5b826c9bbea481d7fd77645d96e4b0b1395238124d581d10e56f17f723d897b6d133970f7a57f0fab9148cbbb67237a0a0ffe794ba60c0c70
+ languageName: node
+ linkType: hard
+
"flat-cache@npm:^4.0.0":
version: 4.0.1
resolution: "flat-cache@npm:4.0.1"
@@ -5639,6 +6120,13 @@ __metadata:
languageName: node
linkType: hard
+"fs.realpath@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "fs.realpath@npm:1.0.0"
+ checksum: 10/e703107c28e362d8d7b910bbcbfd371e640a3bb45ae157a362b5952c0030c0b6d4981140ec319b347bce7adc025dd7813da1ff908a945ac214d64f5402a51b96
+ languageName: node
+ linkType: hard
+
"fsevents@npm:~2.3.2":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
@@ -5771,6 +6259,15 @@ __metadata:
languageName: node
linkType: hard
+"get-tsconfig@npm:^4.7.0":
+ version: 4.14.0
+ resolution: "get-tsconfig@npm:4.14.0"
+ dependencies:
+ resolve-pkg-maps: "npm:^1.0.0"
+ checksum: 10/f5626971905ca386c9ddeb302504e8a2301b9c05641803267223ebd50b7c81aaf9324d29cf9f4e4ff3f245632c3392aa83719dc6cb3e98b4e4a1702a27c5cc5d
+ languageName: node
+ linkType: hard
+
"gl-matrix@npm:^3.4.4":
version: 3.4.4
resolution: "gl-matrix@npm:3.4.4"
@@ -5811,6 +6308,29 @@ __metadata:
languageName: node
linkType: hard
+"glob@npm:^7.1.3":
+ version: 7.2.3
+ resolution: "glob@npm:7.2.3"
+ dependencies:
+ fs.realpath: "npm:^1.0.0"
+ inflight: "npm:^1.0.4"
+ inherits: "npm:2"
+ minimatch: "npm:^3.1.1"
+ once: "npm:^1.3.0"
+ path-is-absolute: "npm:^1.0.0"
+ checksum: 10/59452a9202c81d4508a43b8af7082ca5c76452b9fcc4a9ab17655822e6ce9b21d4f8fbadabe4fe3faef448294cec249af305e2cd824b7e9aaf689240e5e96a7b
+ languageName: node
+ linkType: hard
+
+"globals@npm:^13.19.0, globals@npm:^13.24.0":
+ version: 13.24.0
+ resolution: "globals@npm:13.24.0"
+ dependencies:
+ type-fest: "npm:^0.20.2"
+ checksum: 10/62c5b1997d06674fc7191d3e01e324d3eda4d65ac9cc4e78329fa3b5c4fd42a0e1c8722822497a6964eee075255ce21ccf1eec2d83f92ef3f06653af4d0ee28e
+ languageName: node
+ linkType: hard
+
"globalthis@npm:^1.0.4":
version: 1.0.4
resolution: "globalthis@npm:1.0.4"
@@ -5858,6 +6378,13 @@ __metadata:
languageName: node
linkType: hard
+"graphemer@npm:^1.4.0":
+ version: 1.4.0
+ resolution: "graphemer@npm:1.4.0"
+ checksum: 10/6dd60dba97007b21e3a829fab3f771803cc1292977fe610e240ea72afd67e5690ac9eeaafc4a99710e78962e5936ab5a460787c2a1180f1cb0ccfac37d29f897
+ languageName: node
+ linkType: hard
+
"gridstack@npm:^12.4.2":
version: 12.4.2
resolution: "gridstack@npm:12.4.2"
@@ -5952,7 +6479,7 @@ __metadata:
languageName: node
linkType: hard
-"hasown@npm:^2.0.4":
+"hasown@npm:^2.0.3, hasown@npm:^2.0.4":
version: 2.0.4
resolution: "hasown@npm:2.0.4"
dependencies:
@@ -6201,13 +6728,20 @@ __metadata:
languageName: node
linkType: hard
-"ignore@npm:^5.1.1, ignore@npm:^5.1.4, ignore@npm:^5.2.0":
+"ignore@npm:^5.1.1, ignore@npm:^5.1.4, ignore@npm:^5.2.0, ignore@npm:^5.2.4":
version: 5.3.2
resolution: "ignore@npm:5.3.2"
checksum: 10/cceb6a457000f8f6a50e1196429750d782afce5680dd878aa4221bd79972d68b3a55b4b1458fc682be978f4d3c6a249046aa0880637367216444ab7b014cfc98
languageName: node
linkType: hard
+"ignore@npm:^7.0.0":
+ version: 7.0.5
+ resolution: "ignore@npm:7.0.5"
+ checksum: 10/f134b96a4de0af419196f52c529d5c6120c4456ff8a6b5a14ceaaa399f883e15d58d2ce651c9b69b9388491d4669dda47285d307e827de9304a53a1824801bc6
+ languageName: node
+ linkType: hard
+
"ignore@npm:^7.0.5":
version: 7.0.6
resolution: "ignore@npm:7.0.6"
@@ -6222,6 +6756,16 @@ __metadata:
languageName: node
linkType: hard
+"import-fresh@npm:^3.2.1":
+ version: 3.3.1
+ resolution: "import-fresh@npm:3.3.1"
+ dependencies:
+ parent-module: "npm:^1.0.0"
+ resolve-from: "npm:^4.0.0"
+ checksum: 10/a06b19461b4879cc654d46f8a6244eb55eb053437afd4cbb6613cad6be203811849ed3e4ea038783092879487299fda24af932b86bdfff67c9055ba3612b8c87
+ languageName: node
+ linkType: hard
+
"imurmurhash@npm:^0.1.4":
version: 0.1.4
resolution: "imurmurhash@npm:0.1.4"
@@ -6236,20 +6780,30 @@ __metadata:
languageName: node
linkType: hard
-"inherits@npm:2.0.3":
- version: 2.0.3
- resolution: "inherits@npm:2.0.3"
- checksum: 10/8771303d66c51be433b564427c16011a8e3fbc3449f1f11ea50efb30a4369495f1d0e89f0fc12bdec0bd7e49102ced5d137e031d39ea09821cb3c717fcf21e69
+"inflight@npm:^1.0.4":
+ version: 1.0.6
+ resolution: "inflight@npm:1.0.6"
+ dependencies:
+ once: "npm:^1.3.0"
+ wrappy: "npm:1"
+ checksum: 10/d2ebd65441a38c8336c223d1b80b921b9fa737e37ea466fd7e253cb000c64ae1f17fa59e68130ef5bda92cfd8d36b83d37dab0eb0a4558bcfec8e8cdfd2dcb67
languageName: node
linkType: hard
-"inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:~2.0.3":
+"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:~2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
languageName: node
linkType: hard
+"inherits@npm:2.0.3":
+ version: 2.0.3
+ resolution: "inherits@npm:2.0.3"
+ checksum: 10/8771303d66c51be433b564427c16011a8e3fbc3449f1f11ea50efb30a4369495f1d0e89f0fc12bdec0bd7e49102ced5d137e031d39ea09821cb3c717fcf21e69
+ languageName: node
+ linkType: hard
+
"inter-ui@npm:^4.1.1":
version: 4.1.1
resolution: "inter-ui@npm:4.1.1"
@@ -6351,6 +6905,15 @@ __metadata:
languageName: node
linkType: hard
+"is-builtin-module@npm:^3.2.1":
+ version: 3.2.1
+ resolution: "is-builtin-module@npm:3.2.1"
+ dependencies:
+ builtin-modules: "npm:^3.3.0"
+ checksum: 10/e8f0ffc19a98240bda9c7ada84d846486365af88d14616e737d280d378695c8c448a621dcafc8332dbf0fcd0a17b0763b845400709963fa9151ddffece90ae88
+ languageName: node
+ linkType: hard
+
"is-callable@npm:^1.2.7":
version: 1.2.7
resolution: "is-callable@npm:1.2.7"
@@ -6358,6 +6921,15 @@ __metadata:
languageName: node
linkType: hard
+"is-core-module@npm:^2.12.1":
+ version: 2.16.2
+ resolution: "is-core-module@npm:2.16.2"
+ dependencies:
+ hasown: "npm:^2.0.3"
+ checksum: 10/6ee7535d82bbe457685799c5f145daf4b7c6be3afbd8e90788429d557f663d6dee72a8e4b9a45d0d756c243fcb5028095999243df090e5f04c02b153786bc8c6
+ languageName: node
+ linkType: hard
+
"is-core-module@npm:^2.13.0, is-core-module@npm:^2.16.1":
version: 2.16.1
resolution: "is-core-module@npm:2.16.1"
@@ -6498,6 +7070,13 @@ __metadata:
languageName: node
linkType: hard
+"is-path-inside@npm:^3.0.3":
+ version: 3.0.3
+ resolution: "is-path-inside@npm:3.0.3"
+ checksum: 10/abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9
+ languageName: node
+ linkType: hard
+
"is-plain-obj@npm:^3.0.0":
version: 3.0.0
resolution: "is-plain-obj@npm:3.0.0"
@@ -6505,6 +7084,13 @@ __metadata:
languageName: node
linkType: hard
+"is-potential-custom-element-name@npm:^1.0.0":
+ version: 1.0.1
+ resolution: "is-potential-custom-element-name@npm:1.0.1"
+ checksum: 10/ced7bbbb6433a5b684af581872afe0e1767e2d1146b2207ca0068a648fb5cab9d898495d1ac0583524faaf24ca98176a7d9876363097c2d14fee6dd324f3a1ab
+ languageName: node
+ linkType: hard
+
"is-regex@npm:^1.2.1":
version: 1.2.1
resolution: "is-regex@npm:1.2.1"
@@ -6570,6 +7156,15 @@ __metadata:
languageName: node
linkType: hard
+"is-valid-element-name@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "is-valid-element-name@npm:1.0.0"
+ dependencies:
+ is-potential-custom-element-name: "npm:^1.0.0"
+ checksum: 10/67f1c2b44f57e761c56562c08e764aa11b1f23c91773089302536e45337e771ec17520874ee7939ca8d48bb71d7b7f3b13fc5a6678a71b5b6829ae5490f961a2
+ languageName: node
+ linkType: hard
+
"is-weakmap@npm:^2.0.2":
version: 2.0.2
resolution: "is-weakmap@npm:2.0.2"
@@ -6664,6 +7259,13 @@ __metadata:
languageName: node
linkType: hard
+"js-levenshtein-esm@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "js-levenshtein-esm@npm:2.0.0"
+ checksum: 10/9c6b1ace700743fc0010532a2736c78cf011c179b51c9d0218720488aebd3f1cefa943ab69b1ca087c1dbbfd407e56ee8f0fb308db2e5f689da4768b290776a8
+ languageName: node
+ linkType: hard
+
"js-sha256@npm:^0.11.0":
version: 0.11.0
resolution: "js-sha256@npm:0.11.0"
@@ -6671,6 +7273,17 @@ __metadata:
languageName: node
linkType: hard
+"js-yaml@npm:^4.1.0":
+ version: 4.1.1
+ resolution: "js-yaml@npm:4.1.1"
+ dependencies:
+ argparse: "npm:^2.0.1"
+ bin:
+ js-yaml: bin/js-yaml.js
+ checksum: 10/a52d0519f0f4ef5b4adc1cde466cb54c50d56e2b4a983b9d5c9c0f2f99462047007a6274d7e95617a21d3c91fde3ee6115536ed70991cd645ba8521058b78f77
+ languageName: node
+ linkType: hard
+
"jsbn@npm:1.1.0":
version: 1.1.0
resolution: "jsbn@npm:1.1.0"
@@ -6769,7 +7382,7 @@ __metadata:
languageName: node
linkType: hard
-"keyv@npm:^4.5.4":
+"keyv@npm:^4.5.3, keyv@npm:^4.5.4":
version: 4.5.4
resolution: "keyv@npm:4.5.4"
dependencies:
@@ -6893,6 +7506,13 @@ __metadata:
languageName: node
linkType: hard
+"lodash.merge@npm:^4.6.2":
+ version: 4.6.2
+ resolution: "lodash.merge@npm:4.6.2"
+ checksum: 10/d0ea2dd0097e6201be083865d50c3fb54fbfbdb247d9cc5950e086c991f448b7ab0cdab0d57eacccb43473d3f2acd21e134db39f22dac2d6c9ba6bf26978e3d6
+ languageName: node
+ linkType: hard
+
"lodash.throttle@npm:^4.1.1":
version: 4.1.1
resolution: "lodash.throttle@npm:4.1.1"
@@ -7124,7 +7744,7 @@ __metadata:
languageName: node
linkType: hard
-"minimatch@npm:^3.0.4, minimatch@npm:^3.1.2":
+"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
dependencies:
@@ -7142,6 +7762,15 @@ __metadata:
languageName: node
linkType: hard
+"minimatch@npm:^9.0.4":
+ version: 9.0.9
+ resolution: "minimatch@npm:9.0.9"
+ dependencies:
+ brace-expansion: "npm:^2.0.2"
+ checksum: 10/b91fad937deaffb68a45a2cb731ff3cff1c3baf9b6469c879477ed16f15c8f4ce39d63a3f75c2455107c2fdff0f3ab597d97dc09e2e93b883aafcf926ef0c8f9
+ languageName: node
+ linkType: hard
+
"minimist@npm:^1.2.0, minimist@npm:^1.2.6, minimist@npm:^1.2.8":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
@@ -7286,6 +7915,17 @@ __metadata:
resolution: "model@workspace:ui/component/model"
dependencies:
"@openremote/util": "npm:~1.27.0"
+ "@typescript-eslint/eslint-plugin": "npm:8.40.0"
+ "@typescript-eslint/parser": "npm:8.40.0"
+ eslint: "npm:8.57.1"
+ eslint-config-prettier: "npm:10.1.8"
+ eslint-config-standard: "npm:17.1.0"
+ eslint-plugin-import: "npm:2.32.0"
+ eslint-plugin-lit: "npm:2.3.1"
+ eslint-plugin-n: "npm:16.6.2"
+ eslint-plugin-promise: "npm:6.6.0"
+ eslint-plugin-wc: "npm:3.1.0"
+ prettier: "npm:3.9.6"
languageName: unknown
linkType: soft
@@ -7572,7 +8212,7 @@ __metadata:
languageName: node
linkType: hard
-"once@npm:^1.3.1, once@npm:^1.4.0":
+"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
@@ -7751,6 +8391,34 @@ __metadata:
languageName: node
linkType: hard
+"parent-module@npm:^1.0.0":
+ version: 1.0.1
+ resolution: "parent-module@npm:1.0.1"
+ dependencies:
+ callsites: "npm:^3.0.0"
+ checksum: 10/6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff
+ languageName: node
+ linkType: hard
+
+"parse5-htmlparser2-tree-adapter@npm:^8.0.1":
+ version: 8.0.1
+ resolution: "parse5-htmlparser2-tree-adapter@npm:8.0.1"
+ dependencies:
+ domhandler: "npm:^6.0.1"
+ parse5: "npm:^8.0.0"
+ checksum: 10/6d7590d247752f6259d6f8d6c5e44e074ec3b7665ed9ea95f877a5b341f756b61af2aeadc5b773942420c052123915a26d59bada9c794071ad7e8da8997fb984
+ languageName: node
+ linkType: hard
+
+"parse5@npm:^8.0.0, parse5@npm:^8.0.1":
+ version: 8.0.1
+ resolution: "parse5@npm:8.0.1"
+ dependencies:
+ entities: "npm:^8.0.0"
+ checksum: 10/671dedfe7cbf4714414317bc8c6b2a14c61ef44f8fd90c983b5b1870653af5aa2e3b4e25e38e9538a7120ea2b688c50908830da2bd0930d8fd4bce34aed024eb
+ languageName: node
+ linkType: hard
+
"parseurl@npm:~1.3.2, parseurl@npm:~1.3.3":
version: 1.3.3
resolution: "parseurl@npm:1.3.3"
@@ -7775,6 +8443,13 @@ __metadata:
languageName: node
linkType: hard
+"path-is-absolute@npm:^1.0.0":
+ version: 1.0.1
+ resolution: "path-is-absolute@npm:1.0.1"
+ checksum: 10/060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8
+ languageName: node
+ linkType: hard
+
"path-key@npm:^2.0.0, path-key@npm:^2.0.1":
version: 2.0.1
resolution: "path-key@npm:2.0.1"
@@ -7959,6 +8634,15 @@ __metadata:
languageName: node
linkType: hard
+"prettier@npm:3.9.6":
+ version: 3.9.6
+ resolution: "prettier@npm:3.9.6"
+ bin:
+ prettier: bin/prettier.cjs
+ checksum: 10/1dd1a1e0e40ec3b91cda9d294c4a95022aef61880ca3f441aad99b1252279f58a766c4cece81132c01550dcff1fd445efbd8812ea4a2374313e7fc6c8136f11f
+ languageName: node
+ linkType: hard
+
"prettier@npm:^2.5.1":
version: 2.8.8
resolution: "prettier@npm:2.8.8"
@@ -8299,6 +8983,20 @@ __metadata:
languageName: node
linkType: hard
+"resolve-from@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "resolve-from@npm:4.0.0"
+ checksum: 10/91eb76ce83621eea7bbdd9b55121a5c1c4a39e54a9ce04a9ad4517f102f8b5131c2cf07622c738a6683991bf54f2ce178f5a42803ecbd527ddc5105f362cc9e3
+ languageName: node
+ linkType: hard
+
+"resolve-pkg-maps@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "resolve-pkg-maps@npm:1.0.0"
+ checksum: 10/0763150adf303040c304009231314d1e84c6e5ebfa2d82b7d94e96a6e82bacd1dcc0b58ae257315f3c8adb89a91d8d0f12928241cba2df1680fbe6f60bf99b0e
+ languageName: node
+ linkType: hard
+
"resolve-protobuf-schema@npm:^2.1.0":
version: 2.1.0
resolution: "resolve-protobuf-schema@npm:2.1.0"
@@ -8321,6 +9019,20 @@ __metadata:
languageName: node
linkType: hard
+"resolve@npm:^1.22.2":
+ version: 1.22.12
+ resolution: "resolve@npm:1.22.12"
+ dependencies:
+ es-errors: "npm:^1.3.0"
+ is-core-module: "npm:^2.16.1"
+ path-parse: "npm:^1.0.7"
+ supports-preserve-symlinks-flag: "npm:^1.0.0"
+ bin:
+ resolve: bin/resolve
+ checksum: 10/1d2a081e4b7198e2a70abd7bbbf8aea5380c2d074b6c870035aab50ebfb7312b6492b3588e752faef83a75147862a3d3e09b222bc9afd536804181fd3a515ef9
+ languageName: node
+ linkType: hard
+
"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin":
version: 1.22.11
resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=9bd1a5"
@@ -8334,6 +9046,20 @@ __metadata:
languageName: node
linkType: hard
+"resolve@patch:resolve@npm%3A^1.22.2#optional!builtin":
+ version: 1.22.12
+ resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin::version=1.22.12&hash=9bd1a5"
+ dependencies:
+ es-errors: "npm:^1.3.0"
+ is-core-module: "npm:^2.16.1"
+ path-parse: "npm:^1.0.7"
+ supports-preserve-symlinks-flag: "npm:^1.0.0"
+ bin:
+ resolve: bin/resolve
+ checksum: 10/7c0ee8a1bd6126d7adde06b8cbba7578f6b791560200cd235b696106b9803038fcf680f0273b350a4e9476de1a5194a9a77827cb08e10d80bea38f4d63b1d254
+ languageName: node
+ linkType: hard
+
"rest@workspace:*, rest@workspace:ui/component/rest":
version: 0.0.0-use.local
resolution: "rest@workspace:ui/component/rest"
@@ -8341,7 +9067,18 @@ __metadata:
"@openremote/rest": "npm:~1.27.0"
"@openremote/util": "npm:~1.27.0"
"@rspack/core": "npm:^1.7.10"
+ "@typescript-eslint/eslint-plugin": "npm:8.40.0"
+ "@typescript-eslint/parser": "npm:8.40.0"
+ eslint: "npm:8.57.1"
+ eslint-config-prettier: "npm:10.1.8"
+ eslint-config-standard: "npm:17.1.0"
+ eslint-plugin-import: "npm:2.32.0"
+ eslint-plugin-lit: "npm:2.3.1"
+ eslint-plugin-n: "npm:16.6.2"
+ eslint-plugin-promise: "npm:6.6.0"
+ eslint-plugin-wc: "npm:3.1.0"
model: "workspace:*"
+ prettier: "npm:3.9.6"
typescript: "npm:^5.9.3"
languageName: unknown
linkType: soft
@@ -8367,9 +9104,33 @@ __metadata:
languageName: node
linkType: hard
+"rimraf@npm:^3.0.2":
+ version: 3.0.2
+ resolution: "rimraf@npm:3.0.2"
+ dependencies:
+ glob: "npm:^7.1.3"
+ bin:
+ rimraf: bin.js
+ checksum: 10/063ffaccaaaca2cfd0ef3beafb12d6a03dd7ff1260d752d62a6077b5dfff6ae81bea571f655bb6b589d366930ec1bdd285d40d560c0dae9b12f125e54eb743d5
+ languageName: node
+ linkType: hard
+
"root-workspace-0b6124@workspace:.":
version: 0.0.0-use.local
resolution: "root-workspace-0b6124@workspace:."
+ dependencies:
+ "@typescript-eslint/eslint-plugin": "npm:8.40.0"
+ "@typescript-eslint/parser": "npm:8.40.0"
+ eslint: "npm:8.57.1"
+ eslint-config-prettier: "npm:10.1.8"
+ eslint-config-standard: "npm:17.1.0"
+ eslint-plugin-import: "npm:2.32.0"
+ eslint-plugin-lit: "npm:2.3.1"
+ eslint-plugin-n: "npm:16.6.2"
+ eslint-plugin-promise: "npm:6.6.0"
+ eslint-plugin-wc: "npm:3.1.0"
+ prettier: "npm:3.9.6"
+ typescript: "npm:5.8.3"
languageName: unknown
linkType: soft
@@ -8574,6 +9335,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:^7.0.0, semver@npm:^7.5.3, semver@npm:^7.6.0, semver@npm:^7.7.3":
+ version: 7.8.5
+ resolution: "semver@npm:7.8.5"
+ bin:
+ semver: bin/semver.js
+ checksum: 10/9b01d2ff11e6e4a4539b7ca3c5f280c8704cb397a28504469f2ed4f00ad2194748d756647362a9712fff30984d15772ab7f083108c2fb508e2096ae9e708f22c
+ languageName: node
+ linkType: hard
+
"semver@npm:^7.3.4, semver@npm:^7.3.5":
version: 7.6.2
resolution: "semver@npm:7.6.2"
@@ -8601,15 +9371,6 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:^7.7.3":
- version: 7.8.5
- resolution: "semver@npm:7.8.5"
- bin:
- semver: bin/semver.js
- checksum: 10/9b01d2ff11e6e4a4539b7ca3c5f280c8704cb397a28504469f2ed4f00ad2194748d756647362a9712fff30984d15772ab7f083108c2fb508e2096ae9e708f22c
- languageName: node
- linkType: hard
-
"send@npm:0.19.0":
version: 0.19.0
resolution: "send@npm:0.19.0"
@@ -9134,6 +9895,13 @@ __metadata:
languageName: node
linkType: hard
+"strip-json-comments@npm:^3.1.1":
+ version: 3.1.1
+ resolution: "strip-json-comments@npm:3.1.1"
+ checksum: 10/492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443
+ languageName: node
+ linkType: hard
+
"supercluster@npm:^8.0.1":
version: 8.0.1
resolution: "supercluster@npm:8.0.1"
@@ -9217,6 +9985,13 @@ __metadata:
languageName: node
linkType: hard
+"text-table@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "text-table@npm:0.2.0"
+ checksum: 10/4383b5baaeffa9bb4cda2ac33a4aa2e6d1f8aaf811848bf73513a9b88fd76372dc461f6fd6d2e9cb5100f48b473be32c6f95bd983509b7d92bb4d92c10747452
+ languageName: node
+ linkType: hard
+
"thingies@npm:^2.5.0":
version: 2.5.0
resolution: "thingies@npm:2.5.0"
@@ -9282,7 +10057,7 @@ __metadata:
languageName: node
linkType: hard
-"ts-api-utils@npm:^2.5.0":
+"ts-api-utils@npm:^2.1.0, ts-api-utils@npm:^2.5.0":
version: 2.5.0
resolution: "ts-api-utils@npm:2.5.0"
peerDependencies:
@@ -9394,6 +10169,13 @@ __metadata:
languageName: node
linkType: hard
+"type-fest@npm:^0.20.2":
+ version: 0.20.2
+ resolution: "type-fest@npm:0.20.2"
+ checksum: 10/8907e16284b2d6cfa4f4817e93520121941baba36b39219ea36acfe64c86b9dbc10c9941af450bd60832c8f43464974d51c0957f9858bc66b952b66b6914cbb9
+ languageName: node
+ linkType: hard
+
"type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"