Browse Source

Merge with lts-4.2

pull/15772/head
Igor Kulikov 4 months ago
parent
commit
4e4d2ef629
  1. 2
      application/pom.xml
  2. 2
      application/src/main/data/json/system/widget_types/html_container.json
  3. 12
      application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
  4. 2
      common/actor/pom.xml
  5. 2
      common/cache/pom.xml
  6. 2
      common/cluster-api/pom.xml
  7. 2
      common/coap-server/pom.xml
  8. 2
      common/dao-api/pom.xml
  9. 2
      common/data/pom.xml
  10. 2
      common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java
  11. 2
      common/discovery-api/pom.xml
  12. 2
      common/edge-api/pom.xml
  13. 1
      common/edge-api/src/main/proto/edge.proto
  14. 2
      common/edqs/pom.xml
  15. 2
      common/message/pom.xml
  16. 2
      common/pom.xml
  17. 2
      common/proto/pom.xml
  18. 2
      common/queue/pom.xml
  19. 2
      common/script/pom.xml
  20. 2
      common/script/remote-js-client/pom.xml
  21. 2
      common/script/script-api/pom.xml
  22. 2
      common/stats/pom.xml
  23. 2
      common/transport/coap/pom.xml
  24. 2
      common/transport/http/pom.xml
  25. 2
      common/transport/lwm2m/pom.xml
  26. 2
      common/transport/mqtt/pom.xml
  27. 2
      common/transport/pom.xml
  28. 2
      common/transport/snmp/pom.xml
  29. 2
      common/transport/transport-api/pom.xml
  30. 74
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java
  31. 64
      common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/service/CertificateReloadManagerTest.java
  32. 2
      common/util/pom.xml
  33. 2
      common/version-control/pom.xml
  34. 2
      dao/pom.xml
  35. 2
      edqs/pom.xml
  36. 2
      monitoring/pom.xml
  37. 2
      msa/black-box-tests/pom.xml
  38. 2
      msa/edqs/pom.xml
  39. 2
      msa/js-executor/package.json
  40. 2
      msa/js-executor/pom.xml
  41. 2
      msa/monitoring/pom.xml
  42. 2
      msa/pom.xml
  43. 2
      msa/tb-node/pom.xml
  44. 2
      msa/tb/pom.xml
  45. 2
      msa/transport/coap/pom.xml
  46. 2
      msa/transport/http/pom.xml
  47. 2
      msa/transport/lwm2m/pom.xml
  48. 2
      msa/transport/mqtt/pom.xml
  49. 2
      msa/transport/pom.xml
  50. 2
      msa/transport/snmp/pom.xml
  51. 2
      msa/vc-executor-docker/pom.xml
  52. 2
      msa/vc-executor/pom.xml
  53. 2
      msa/web-ui/package.json
  54. 2
      msa/web-ui/pom.xml
  55. 4
      netty-mqtt/pom.xml
  56. 4
      pom.xml
  57. 2
      rest-client/pom.xml
  58. 2
      rule-engine/pom.xml
  59. 2
      rule-engine/rule-engine-api/pom.xml
  60. 2
      rule-engine/rule-engine-components/pom.xml
  61. 2
      tools/pom.xml
  62. 2
      transport/coap/pom.xml
  63. 2
      transport/http/pom.xml
  64. 2
      transport/lwm2m/pom.xml
  65. 2
      transport/mqtt/pom.xml
  66. 2
      transport/pom.xml
  67. 2
      transport/snmp/pom.xml
  68. 2
      ui-ngx/package.json
  69. 2
      ui-ngx/pom.xml
  70. 1
      ui-ngx/src/app/core/api/widget-api.models.ts
  71. 3
      ui-ngx/src/app/core/auth/auth.models.ts
  72. 2
      ui-ngx/src/app/core/auth/auth.reducer.ts
  73. 3
      ui-ngx/src/app/core/auth/auth.service.ts
  74. 2
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.html
  75. 5
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts
  76. 4
      ui-ngx/src/app/modules/home/components/widget/config/basic/html/html-container-basic-config.component.html
  77. 4
      ui-ngx/src/app/modules/home/components/widget/config/basic/html/html-container-basic-config.component.ts
  78. 16
      ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts
  79. 15
      ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts
  80. 35
      ui-ngx/src/app/modules/home/components/widget/lib/html/html-container-widget.component.ts
  81. 11
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  82. 15
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  83. 33
      ui-ngx/src/app/shared/models/ace/widget-completion.models.ts
  84. 43
      ui-ngx/src/app/shared/models/page/page-link.ts
  85. 32
      ui-ngx/yarn.lock

2
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>application</artifactId> <artifactId>application</artifactId>

2
application/src/main/data/json/system/widget_types/html_container.json

@ -11,7 +11,7 @@
"resources": [], "resources": [],
"templateHtml": "<tb-html-container-widget \n [ctx]=\"ctx\">\n</tb-html-container-widget>", "templateHtml": "<tb-html-container-widget \n [ctx]=\"ctx\">\n</tb-html-container-widget>",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n \n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '100%',\n previewHeight: '100%',\n overflowVisible: true\n };\n};\n", "controllerScript": "self.onInit = function() {\n \n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '100%',\n previewHeight: '100%',\n overflowVisible: true\n };\n};\n\nself.actionSources = function() {\n return {\n 'javaScript': {\n name: 'JavaScript',\n multiple: true\n }\n };\n}",
"settingsDirective": "tb-html-container-widget-settings", "settingsDirective": "tb-html-container-widget-settings",
"hasBasicMode": true, "hasBasicMode": true,
"basicModeDirective": "tb-html-container-basic-config", "basicModeDirective": "tb-html-container-basic-config",

12
application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.common.data.settings.UserSettings;
import org.thingsboard.server.common.data.settings.UserSettingsType; import org.thingsboard.server.common.data.settings.UserSettingsType;
import org.thingsboard.server.common.msg.edqs.EdqsService;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.mobile.QrCodeSettingService; import org.thingsboard.server.dao.mobile.QrCodeSettingService;
import org.thingsboard.server.dao.trendz.TrendzSettingsService; import org.thingsboard.server.dao.trendz.TrendzSettingsService;
@ -52,6 +53,7 @@ import org.thingsboard.server.utils.DebugModeRateLimitsConfig;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Hidden @Hidden
@ -76,9 +78,14 @@ public class SystemInfoController extends BaseController {
@Value("${debug.settings.default_duration:15}") @Value("${debug.settings.default_duration:15}")
private int defaultDebugDurationMinutes; private int defaultDebugDurationMinutes;
@Value("${sql.entity_data_query_nulls_order_strategy:default}")
private String nullsOrderStrategy;
@Value("${iot-hub.base-url:https://iot-hub.thingsboard.io}") @Value("${iot-hub.base-url:https://iot-hub.thingsboard.io}")
private String iotHubBaseUrl; private String iotHubBaseUrl;
private static final Set<String> ACCEPTED_NULLS_ORDER_STRATEGIES = Set.of("default", "nulls_first", "nulls_last");
@Autowired(required = false) @Autowired(required = false)
private BuildProperties buildProperties; private BuildProperties buildProperties;
@ -94,6 +101,9 @@ public class SystemInfoController extends BaseController {
@Autowired @Autowired
private TrendzSettingsService trendzSettingsService; private TrendzSettingsService trendzSettingsService;
@Autowired
private EdqsService edqsService;
@PostConstruct @PostConstruct
public void init() { public void init() {
JsonNode info = buildInfoObject(); JsonNode info = buildInfoObject();
@ -153,6 +163,8 @@ public class SystemInfoController extends BaseController {
} }
systemParams.setUserSettings(userSettingsNode); systemParams.setUserSettings(userSettingsNode);
systemParams.setMaxDatapointsLimit(maxDatapointsLimit); systemParams.setMaxDatapointsLimit(maxDatapointsLimit);
systemParams.setNullsOrderStrategy(ACCEPTED_NULLS_ORDER_STRATEGIES.contains(nullsOrderStrategy) ? nullsOrderStrategy : "default");
systemParams.setEdqsEnabled(edqsService.isApiEnabled());
if (!currentUser.isSystemAdmin()) { if (!currentUser.isSystemAdmin()) {
DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration();
systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize()); systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize());

2
common/actor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/cache/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/cluster-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/coap-server/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/dao-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/data/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java

@ -39,5 +39,7 @@ public class SystemParams {
long maxArgumentsPerCF; long maxArgumentsPerCF;
long maxDataPointsPerRollingArg; long maxDataPointsPerRollingArg;
TrendzSettings trendzSettings; TrendzSettings trendzSettings;
String nullsOrderStrategy;
boolean edqsEnabled;
String iotHubBaseUrl; String iotHubBaseUrl;
} }

2
common/discovery-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/edge-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

1
common/edge-api/src/main/proto/edge.proto

@ -48,6 +48,7 @@ enum EdgeVersion {
V_4_2_2 = 4220; V_4_2_2 = 4220;
V_4_2_2_1 = 4221; V_4_2_2_1 = 4221;
V_4_2_2_2 = 4222; V_4_2_2_2 = 4222;
V_4_2_2_3 = 4223;
V_LATEST = 99999; V_LATEST = 99999;
} }

2
common/edqs/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/message/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>common</artifactId> <artifactId>common</artifactId>

2
common/proto/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/queue/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/script/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/script/remote-js-client/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>script</artifactId> <artifactId>script</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.script</groupId> <groupId>org.thingsboard.common.script</groupId>

2
common/script/script-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>script</artifactId> <artifactId>script</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.script</groupId> <groupId>org.thingsboard.common.script</groupId>

2
common/stats/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>

2
common/transport/transport-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

74
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/CertificateReloadManager.java

@ -27,7 +27,6 @@ import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig; import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
import org.thingsboard.server.queue.util.TbTransportComponent; import org.thingsboard.server.queue.util.TbTransportComponent;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -165,7 +164,6 @@ public class CertificateReloadManager implements SmartInitializingSingleton, Dis
static class CertificateWatcher { static class CertificateWatcher {
private final List<Path> paths; private final List<Path> paths;
private final Runnable reloadCallback; private final Runnable reloadCallback;
private final Map<Path, Long> lastModifiedMap;
private final Map<Path, String> lastChecksumMap; private final Map<Path, String> lastChecksumMap;
private int consecutiveFailures; private int consecutiveFailures;
private String failedCombinedChecksum; private String failedCombinedChecksum;
@ -173,60 +171,23 @@ public class CertificateReloadManager implements SmartInitializingSingleton, Dis
CertificateWatcher(List<Path> paths, Runnable reloadCallback) { CertificateWatcher(List<Path> paths, Runnable reloadCallback) {
this.paths = paths; this.paths = paths;
this.reloadCallback = reloadCallback; this.reloadCallback = reloadCallback;
this.lastModifiedMap = new HashMap<>();
this.lastChecksumMap = new HashMap<>(); this.lastChecksumMap = new HashMap<>();
for (Path path : paths) { for (Path path : paths) {
lastModifiedMap.put(path, getLastModifiedTime(path));
lastChecksumMap.put(path, calculateChecksum(path)); lastChecksumMap.put(path, calculateChecksum(path));
} }
this.consecutiveFailures = 0; this.consecutiveFailures = 0;
} }
synchronized void checkAndReload(String name) { synchronized void checkAndReload(String name) {
boolean anyModifiedChanged = false;
for (Path path : paths) {
long currentModified = getLastModifiedTime(path);
Long lastModified = lastModifiedMap.getOrDefault(path, 0L);
if (currentModified != lastModified) {
anyModifiedChanged = true;
break;
}
}
if (!anyModifiedChanged) {
return;
}
// Capture mtimes and checksums together before the callback runs.
// Pairing a post-callback mtime with a pre-callback checksum would let a write-during-reload be missed on the next poll.
Map<Path, Long> currentModifiedTimes = new HashMap<>();
Map<Path, String> currentChecksums = new HashMap<>(); Map<Path, String> currentChecksums = new HashMap<>();
StringBuilder combined = new StringBuilder();
for (Path path : paths) { for (Path path : paths) {
currentModifiedTimes.put(path, getLastModifiedTime(path)); currentChecksums.put(path, calculateChecksum(path));
String checksum = calculateChecksum(path);
currentChecksums.put(path, checksum);
if (!combined.isEmpty()) {
combined.append("|");
}
combined.append(path).append("=").append(checksum);
} }
String combinedChecksum = combined.toString(); String combinedChecksum = combinedChecksum(currentChecksums);
String oldCombinedChecksum = combinedChecksum(lastChecksumMap);
// Build old combined checksum for comparison
StringBuilder oldCombined = new StringBuilder();
for (Path path : paths) {
if (!oldCombined.isEmpty()) {
oldCombined.append("|");
}
oldCombined.append(path).append("=").append(lastChecksumMap.getOrDefault(path, ""));
}
String oldCombinedChecksum = oldCombined.toString();
if (combinedChecksum.equals(oldCombinedChecksum)) { if (combinedChecksum.equals(oldCombinedChecksum)) {
// Content unchanged, just update modification times // Content unchanged
for (Path path : paths) {
lastModifiedMap.put(path, currentModifiedTimes.get(path));
}
return; return;
} }
@ -237,41 +198,34 @@ public class CertificateReloadManager implements SmartInitializingSingleton, Dis
} }
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) { if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
// Update modification times to avoid re-checking mtime and re-computing checksums every poll cycle
for (Path path : paths) {
lastModifiedMap.put(path, currentModifiedTimes.get(path));
}
return; return;
} }
try { try {
log.info("Certificate change detected for: {}. Triggering reload...", name); log.info("Certificate change detected for: {}. Triggering reload...", name);
reloadCallback.run(); reloadCallback.run();
for (Path path : paths) { lastChecksumMap.putAll(currentChecksums);
lastModifiedMap.put(path, currentModifiedTimes.get(path));
lastChecksumMap.put(path, currentChecksums.get(path));
}
consecutiveFailures = 0; consecutiveFailures = 0;
failedCombinedChecksum = null; failedCombinedChecksum = null;
} catch (Exception e) { } catch (Exception e) {
consecutiveFailures++; consecutiveFailures++;
failedCombinedChecksum = combinedChecksum; failedCombinedChecksum = combinedChecksum;
// Deliberately NOT updating the lastModifiedMap here, so the next poll cycle retries // Deliberately NOT updating lastChecksumMap here, so the next poll cycle still sees a differing
// (mtime mismatch passes the early gate, checksum matches failedCombinedChecksum). // checksum, re-enters this method, and retries the same content.
log.error("Failed to reload certificate for {} (attempt {}/{}): {}", log.error("Failed to reload certificate for {} (attempt {}/{}): {}",
name, consecutiveFailures, MAX_CONSECUTIVE_FAILURES, e.getMessage(), e); name, consecutiveFailures, MAX_CONSECUTIVE_FAILURES, e.getMessage(), e);
} }
} }
private long getLastModifiedTime(Path path) { private String combinedChecksum(Map<Path, String> checksums) {
try { StringBuilder combined = new StringBuilder();
if (!Files.exists(path)) { for (Path path : paths) {
return 0; if (!combined.isEmpty()) {
combined.append("|");
} }
return Files.getLastModifiedTime(path).toMillis(); combined.append(path).append("=").append(checksums.getOrDefault(path, ""));
} catch (IOException e) {
return 0;
} }
return combined.toString();
} }
private String calculateChecksum(Path path) { private String calculateChecksum(Path path) {

64
common/transport/transport-api/src/test/java/org/thingsboard/server/common/transport/service/CertificateReloadManagerTest.java

@ -31,10 +31,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
public class CertificateReloadManagerTest { public class CertificateReloadManagerTest {
@ -59,11 +56,10 @@ public class CertificateReloadManagerTest {
} }
} }
private void writeFileAndAwaitMtimeChange(Path path, String content, long baselineMtime) throws IOException { private void writeFileAndBumpMtime(Path path, String content, long baselineMtime) throws IOException {
Files.writeString(path, content); Files.writeString(path, content);
await().atMost(2, SECONDS) // Force a strictly newer mtime: back-to-back writes can share a millisecond, hiding the change from the watcher.
.pollInterval(10, MILLISECONDS) Files.setLastModifiedTime(path, FileTime.fromMillis(baselineMtime + 1000));
.until(() -> Files.getLastModifiedTime(path).toMillis() != baselineMtime);
} }
private long mtime(Path path) throws IOException { private long mtime(Path path) throws IOException {
@ -77,7 +73,7 @@ public class CertificateReloadManagerTest {
certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet); certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet);
long baseline = mtime(certFile); long baseline = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nTEST_CERT_V2_MODIFIED\n-----END CERTIFICATE-----\n", baseline); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nTEST_CERT_V2_MODIFIED\n-----END CERTIFICATE-----\n", baseline);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -85,7 +81,7 @@ public class CertificateReloadManagerTest {
} }
@Test @Test
public void givenCertificateFileUnchanged_whenCheckForChanges_thenShouldNotTriggerReload() throws Exception { public void givenCertificateFileUnchanged_whenCheckForChanges_thenShouldNotTriggerReload() {
AtomicInteger reloadCount = new AtomicInteger(0); AtomicInteger reloadCount = new AtomicInteger(0);
certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet); certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet);
@ -147,7 +143,7 @@ public class CertificateReloadManagerTest {
certificateReloadManager.registerWatcher("test-key", keyFile, keyReloadCount::incrementAndGet); certificateReloadManager.registerWatcher("test-key", keyFile, keyReloadCount::incrementAndGet);
long baseline = mtime(keyFile); long baseline = mtime(keyFile);
writeFileAndAwaitMtimeChange(keyFile, "-----BEGIN PRIVATE KEY-----\nTEST_KEY_V2_MODIFIED\n-----END PRIVATE KEY-----\n", baseline); writeFileAndBumpMtime(keyFile, "-----BEGIN PRIVATE KEY-----\nTEST_KEY_V2_MODIFIED\n-----END PRIVATE KEY-----\n", baseline);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -168,8 +164,8 @@ public class CertificateReloadManagerTest {
long baseline1 = mtime(certFile); long baseline1 = mtime(certFile);
long baseline2 = mtime(cert2File); long baseline2 = mtime(cert2File);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nMODIFIED1\n-----END CERTIFICATE-----\n", baseline1); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nMODIFIED1\n-----END CERTIFICATE-----\n", baseline1);
writeFileAndAwaitMtimeChange(cert2File, "-----BEGIN CERTIFICATE-----\nMODIFIED2\n-----END CERTIFICATE-----\n", baseline2); writeFileAndBumpMtime(cert2File, "-----BEGIN CERTIFICATE-----\nMODIFIED2\n-----END CERTIFICATE-----\n", baseline2);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -191,8 +187,8 @@ public class CertificateReloadManagerTest {
long baseline1 = mtime(certFile); long baseline1 = mtime(certFile);
long baseline2 = mtime(cert2File); long baseline2 = mtime(cert2File);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nMODIFIED1\n-----END CERTIFICATE-----\n", baseline1); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nMODIFIED1\n-----END CERTIFICATE-----\n", baseline1);
writeFileAndAwaitMtimeChange(cert2File, "-----BEGIN CERTIFICATE-----\nMODIFIED2\n-----END CERTIFICATE-----\n", baseline2); writeFileAndBumpMtime(cert2File, "-----BEGIN CERTIFICATE-----\nMODIFIED2\n-----END CERTIFICATE-----\n", baseline2);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -224,9 +220,7 @@ public class CertificateReloadManagerTest {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
Files.writeString(certFile, "-----BEGIN CERTIFICATE-----\nCERT_VERSION_" + i + "\n-----END CERTIFICATE-----\n"); Files.writeString(certFile, "-----BEGIN CERTIFICATE-----\nCERT_VERSION_" + i + "\n-----END CERTIFICATE-----\n");
} }
await().atMost(2, SECONDS) Files.setLastModifiedTime(certFile, FileTime.fromMillis(baseline + 1000));
.pollInterval(10, MILLISECONDS)
.until(() -> mtime(certFile) != baseline);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -242,7 +236,7 @@ public class CertificateReloadManagerTest {
certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet); certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet);
long baseline = mtime(certFile); long baseline = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nMODIFIED\n-----END CERTIFICATE-----\n", baseline); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nMODIFIED\n-----END CERTIFICATE-----\n", baseline);
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
new Thread(() -> { new Thread(() -> {
@ -272,7 +266,7 @@ public class CertificateReloadManagerTest {
certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet); certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet);
long baseline = mtime(certFile); long baseline = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, originalContent, baseline); writeFileAndBumpMtime(certFile, originalContent, baseline);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -289,7 +283,7 @@ public class CertificateReloadManagerTest {
}); });
long baseline = mtime(certFile); long baseline = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nBAD_CERT\n-----END CERTIFICATE-----\n", baseline); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nBAD_CERT\n-----END CERTIFICATE-----\n", baseline);
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -311,19 +305,41 @@ public class CertificateReloadManagerTest {
}); });
long baseline = mtime(certFile); long baseline = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nBAD_CERT\n-----END CERTIFICATE-----\n", baseline); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nBAD_CERT\n-----END CERTIFICATE-----\n", baseline);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
assertThat(reloadAttempts.get()).isEqualTo(1); assertThat(reloadAttempts.get()).isEqualTo(1);
shouldFail.set(0); shouldFail.set(0);
long baseline2 = mtime(certFile); long baseline2 = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nGOOD_CERT\n-----END CERTIFICATE-----\n", baseline2); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nGOOD_CERT\n-----END CERTIFICATE-----\n", baseline2);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
assertThat(reloadAttempts.get()).isEqualTo(2); assertThat(reloadAttempts.get()).isEqualTo(2);
} }
@Test
public void givenContentChangedButMtimeUnchanged_whenCheckForChanges_thenShouldTriggerReload() throws Exception {
// Bug fingerprint: a cert-manager rotation that lands in the same wall-clock millisecond as the
// watcher's recorded baseline mtime. Files.getLastModifiedTime().toMillis() truncates to the ms,
// so the rotated content shares the baseline mtime and an mtime-only gate would never re-hash it.
AtomicInteger reloadCount = new AtomicInteger(0);
certificateReloadManager.registerWatcher("test-cert", certFile, reloadCount::incrementAndGet);
long baseline = mtime(certFile);
Files.writeString(certFile, "-----BEGIN CERTIFICATE-----\nROTATED_SAME_MS\n-----END CERTIFICATE-----\n");
// Force the mtime back to the exact baseline millisecond — content changed, timestamp did not.
Files.setLastModifiedTime(certFile, FileTime.fromMillis(baseline));
// Sanity guard: the watcher observes a timestamp identical to its recorded baseline.
assertThat(mtime(certFile)).isEqualTo(baseline);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
assertThat(reloadCount.get()).isEqualTo(1);
}
@Test @Test
public void givenCallbackHitMaxFailures_whenFileChangesToNewContent_thenShouldResetAndRetry() throws Exception { public void givenCallbackHitMaxFailures_whenFileChangesToNewContent_thenShouldResetAndRetry() throws Exception {
AtomicInteger reloadAttempts = new AtomicInteger(0); AtomicInteger reloadAttempts = new AtomicInteger(0);
@ -337,7 +353,7 @@ public class CertificateReloadManagerTest {
}); });
long baseline = mtime(certFile); long baseline = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nBAD_CERT\n-----END CERTIFICATE-----\n", baseline); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nBAD_CERT\n-----END CERTIFICATE-----\n", baseline);
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
@ -346,7 +362,7 @@ public class CertificateReloadManagerTest {
shouldFail.set(0); shouldFail.set(0);
long baseline2 = mtime(certFile); long baseline2 = mtime(certFile);
writeFileAndAwaitMtimeChange(certFile, "-----BEGIN CERTIFICATE-----\nFIXED_CERT\n-----END CERTIFICATE-----\n", baseline2); writeFileAndBumpMtime(certFile, "-----BEGIN CERTIFICATE-----\nFIXED_CERT\n-----END CERTIFICATE-----\n", baseline2);
ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates"); ReflectionTestUtils.invokeMethod(certificateReloadManager, "checkCertificates");
assertThat(reloadAttempts.get()).isEqualTo(11); assertThat(reloadAttempts.get()).isEqualTo(11);

2
common/util/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/version-control/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
dao/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>dao</artifactId> <artifactId>dao</artifactId>

2
edqs/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>edqs</artifactId> <artifactId>edqs</artifactId>

2
monitoring/pom.xml

@ -21,7 +21,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>

2
msa/black-box-tests/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/edqs/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/js-executor/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-js-executor", "name": "thingsboard-js-executor",
"private": true, "private": true,
"version": "4.2.2.2", "version": "4.2.2.3",
"description": "ThingsBoard JavaScript Executor Microservice", "description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.ts", "main": "server.ts",
"bin": "server.js", "bin": "server.js",

2
msa/js-executor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/monitoring/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>

2
msa/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>

2
msa/tb-node/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/tb/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/vc-executor-docker/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/vc-executor/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/web-ui/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-web-ui", "name": "thingsboard-web-ui",
"private": true, "private": true,
"version": "4.2.2.2", "version": "4.2.2.3",
"description": "ThingsBoard Web UI Microservice", "description": "ThingsBoard Web UI Microservice",
"main": "server.ts", "main": "server.ts",
"bin": "server.js", "bin": "server.js",

2
msa/web-ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

4
netty-mqtt/pom.xml

@ -19,11 +19,11 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>netty-mqtt</artifactId> <artifactId>netty-mqtt</artifactId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Netty MQTT Client</name> <name>Netty MQTT Client</name>

4
pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Thingsboard</name> <name>Thingsboard</name>
@ -69,7 +69,7 @@
<spring-boot-test.version>3.5.13</spring-boot-test.version> <spring-boot-test.version>3.5.13</spring-boot-test.version>
<commons-lang3.version>3.18.0</commons-lang3.version> <!-- to fix CVE-2025-48924. TODO: remove when fixed in spring-boot-dependencies --> <commons-lang3.version>3.18.0</commons-lang3.version> <!-- to fix CVE-2025-48924. TODO: remove when fixed in spring-boot-dependencies -->
<postgresql.version>42.7.11</postgresql.version> <!-- to fix CVE-2026-42198. TODO: remove when fixed in spring-boot-dependencies --> <postgresql.version>42.7.11</postgresql.version> <!-- to fix CVE-2026-42198. TODO: remove when fixed in spring-boot-dependencies -->
<netty.version>4.1.133.Final</netty.version> <!-- to fix CVE-2026-42579, CVE-2026-42583, CVE-2026-42584, CVE-2026-42587. TODO: remove when fixed in spring-boot-dependencies --> <netty.version>4.1.134.Final</netty.version> <!-- to fix CVE-2026-42579, CVE-2026-42583, CVE-2026-42584, CVE-2026-42587, and MQTT decoder regression introduced in 4.1.133 by the CVE-2026-44248 fix. TODO: remove when fixed in spring-boot-dependencies -->
<tomcat.version>10.1.55</tomcat.version> <!-- to fix CVE-2026-41284, CVE-2026-43512. TODO: remove when fixed in spring-boot-dependencies --> <tomcat.version>10.1.55</tomcat.version> <!-- to fix CVE-2026-41284, CVE-2026-43512. TODO: remove when fixed in spring-boot-dependencies -->
<javax.xml.bind-api.version>2.4.0-b180830.0359</javax.xml.bind-api.version> <javax.xml.bind-api.version>2.4.0-b180830.0359</javax.xml.bind-api.version>
<jjwt.version>0.12.5</jjwt.version> <jjwt.version>0.12.5</jjwt.version>

2
rest-client/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>rest-client</artifactId> <artifactId>rest-client</artifactId>

2
rule-engine/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>rule-engine</artifactId> <artifactId>rule-engine</artifactId>

2
rule-engine/rule-engine-api/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>rule-engine</artifactId> <artifactId>rule-engine</artifactId>
</parent> </parent>
<groupId>org.thingsboard.rule-engine</groupId> <groupId>org.thingsboard.rule-engine</groupId>

2
rule-engine/rule-engine-components/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>rule-engine</artifactId> <artifactId>rule-engine</artifactId>
</parent> </parent>
<groupId>org.thingsboard.rule-engine</groupId> <groupId>org.thingsboard.rule-engine</groupId>

2
tools/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>tools</artifactId> <artifactId>tools</artifactId>

2
transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.transport</groupId> <groupId>org.thingsboard.transport</groupId>

2
transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>

2
transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>

2
ui-ngx/package.json

@ -1,6 +1,6 @@
{ {
"name": "thingsboard", "name": "thingsboard",
"version": "4.2.2.2", "version": "4.2.2.3",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open",

2
ui-ngx/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.2.2.2-SNAPSHOT</version> <version>4.2.2.3-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>

1
ui-ngx/src/app/core/api/widget-api.models.ts

@ -110,6 +110,7 @@ export interface WidgetActionsApi {
elementClick: ($event: Event) => void; elementClick: ($event: Event) => void;
cardClick: ($event: Event) => void; cardClick: ($event: Event) => void;
click: ($event: Event) => void; click: ($event: Event) => void;
invokeAction: ($event: Event, actionName: string, additionalParams?: any) => void;
getActiveEntityInfo: () => SubscriptionEntityInfo; getActiveEntityInfo: () => SubscriptionEntityInfo;
openDashboardStateInSeparateDialog: (targetDashboardStateId: string, params?: StateParams, dialogTitle?: string, openDashboardStateInSeparateDialog: (targetDashboardStateId: string, params?: StateParams, dialogTitle?: string,
hideDashboardToolbar?: boolean, dialogWidth?: number, dialogHeight?: number) => MatDialogRef<any>; hideDashboardToolbar?: boolean, dialogWidth?: number, dialogHeight?: number) => MatDialogRef<any>;

3
ui-ngx/src/app/core/auth/auth.models.ts

@ -17,6 +17,7 @@
import { AuthUser, User } from '@shared/models/user.model'; import { AuthUser, User } from '@shared/models/user.model';
import { UserSettings } from '@shared/models/user-settings.models'; import { UserSettings } from '@shared/models/user-settings.models';
import { TrendzSettings } from '@shared/models/trendz-settings.models'; import { TrendzSettings } from '@shared/models/trendz-settings.models';
import { NullsOrderStrategy } from '@shared/models/page/page-link';
export interface SysParamsState { export interface SysParamsState {
userTokenAccessEnabled: boolean; userTokenAccessEnabled: boolean;
@ -34,6 +35,8 @@ export interface SysParamsState {
ruleChainDebugPerTenantLimitsConfiguration?: string; ruleChainDebugPerTenantLimitsConfiguration?: string;
calculatedFieldDebugPerTenantLimitsConfiguration?: string; calculatedFieldDebugPerTenantLimitsConfiguration?: string;
trendzSettings: TrendzSettings; trendzSettings: TrendzSettings;
nullsOrderStrategy: NullsOrderStrategy;
edqsEnabled: boolean;
iotHubBaseUrl: string; iotHubBaseUrl: string;
} }

2
ui-ngx/src/app/core/auth/auth.reducer.ts

@ -37,6 +37,8 @@ const emptyUserAuthState: AuthPayload = {
maxDebugModeDurationMinutes: 0, maxDebugModeDurationMinutes: 0,
userSettings: initialUserSettings, userSettings: initialUserSettings,
trendzSettings: initialTrendzSettings, trendzSettings: initialTrendzSettings,
nullsOrderStrategy: 'default',
edqsEnabled: false,
iotHubBaseUrl: '' iotHubBaseUrl: ''
}; };

3
ui-ngx/src/app/core/auth/auth.service.ts

@ -20,6 +20,7 @@ import { HttpClient } from '@angular/common/http';
import { Observable, of, ReplaySubject, throwError } from 'rxjs'; import { Observable, of, ReplaySubject, throwError } from 'rxjs';
import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { catchError, map, mergeMap, tap } from 'rxjs/operators';
import { setEdqsEnabled, setNullsOrderStrategy } from '@shared/models/page/page-link';
import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models'; import { LoginRequest, LoginResponse, PublicLoginRequest } from '@shared/models/login.models';
import { Router, UrlTree } from '@angular/router'; import { Router, UrlTree } from '@angular/router';
@ -441,6 +442,8 @@ export class AuthService {
return this.http.get<SysParams>('/api/system/params', defaultHttpOptions()).pipe( return this.http.get<SysParams>('/api/system/params', defaultHttpOptions()).pipe(
map((sysParams) => { map((sysParams) => {
this.timeService.setMaxDatapointsLimit(sysParams.maxDatapointsLimit); this.timeService.setMaxDatapointsLimit(sysParams.maxDatapointsLimit);
setNullsOrderStrategy(sysParams.nullsOrderStrategy);
setEdqsEnabled(sysParams.edqsEnabled);
return sysParams; return sysParams;
}), }),
catchError(() => of({} as SysParamsState)) catchError(() => of({} as SysParamsState))

2
ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<div class="tb-form-panel" (click)="manageWidgetActions()" style="cursor: pointer;"> <div class="tb-form-panel" [class.stroked]="strokedPanel" (click)="manageWidgetActions()" style="cursor: pointer;">
<div class="flex flex-row items-center justify-start gap-4"> <div class="flex flex-row items-center justify-start gap-4">
<div class="tb-form-panel-title" translate style="padding-right: 48px;">widget-config.actions</div> <div class="tb-form-panel-title" translate style="padding-right: 48px;">widget-config.actions</div>
<mat-chip-listbox class="flex-1"> <mat-chip-listbox class="flex-1">

5
ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts

@ -26,6 +26,7 @@ import {
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-widget-actions-panel', selector: 'tb-widget-actions-panel',
@ -45,6 +46,10 @@ export class WidgetActionsPanelComponent implements ControlValueAccessor, OnInit
@Input() @Input()
disabled: boolean; disabled: boolean;
@Input()
@coerceBoolean()
strokedPanel = false;
actionsFormGroup: UntypedFormGroup; actionsFormGroup: UntypedFormGroup;
private propagateChange = (_val: any) => {}; private propagateChange = (_val: any) => {};

4
ui-ngx/src/app/modules/home/components/widget/config/basic/html/html-container-basic-config.component.html

@ -17,4 +17,8 @@
--> -->
<ng-container [formGroup]="htmlContainerWidgetConfigForm"> <ng-container [formGroup]="htmlContainerWidgetConfigForm">
<tb-html-container-settings formControlName="settings"></tb-html-container-settings> <tb-html-container-settings formControlName="settings"></tb-html-container-settings>
<tb-widget-actions-panel
formControlName="actions"
strokedPanel>
</tb-widget-actions-panel>
</ng-container> </ng-container>

4
ui-ngx/src/app/modules/home/components/widget/config/basic/html/html-container-basic-config.component.ts

@ -51,12 +51,14 @@ export class HtmlContainerBasicConfigComponent extends BasicWidgetConfigComponen
protected onConfigSet(configData: WidgetConfigComponentData) { protected onConfigSet(configData: WidgetConfigComponentData) {
const settings: HtmlContainerWidgetSettings = {...htmlContainerDefaultSettings, ...(configData.config.settings || {})}; const settings: HtmlContainerWidgetSettings = {...htmlContainerDefaultSettings, ...(configData.config.settings || {})};
this.htmlContainerWidgetConfigForm = this.fb.group({ this.htmlContainerWidgetConfigForm = this.fb.group({
settings: [settings, []] settings: [settings, []],
actions: [configData.config.actions || {}, []]
}); });
} }
protected prepareOutputConfig(config: any): WidgetConfigComponentData { protected prepareOutputConfig(config: any): WidgetConfigComponentData {
this.widgetConfig.config.settings = {...(this.widgetConfig.config.settings || {}), ...config.settings}; this.widgetConfig.config.settings = {...(this.widgetConfig.config.settings || {}), ...config.settings};
this.widgetConfig.config.actions = config.actions;
return this.widgetConfig; return this.widgetConfig;
} }
} }

16
ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts

@ -47,7 +47,7 @@ import {
isUndefined isUndefined
} from '@core/utils'; } from '@core/utils';
import cssjs from '@core/css/css'; import cssjs from '@core/css/css';
import { sortItems } from '@shared/models/page/page-link'; import { SortColumnType, sortItems } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections';
import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs'; import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs';
@ -118,6 +118,7 @@ import {
dataKeyToEntityKey, dataKeyToEntityKey,
dataKeyTypeToEntityKeyType, dataKeyTypeToEntityKeyType,
entityDataPageLinkSortDirection, entityDataPageLinkSortDirection,
EntityKeyType,
KeyFilter KeyFilter
} from '@app/shared/models/query/query.models'; } from '@app/shared/models/query/query.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
@ -730,8 +731,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.pageLink.sortOrder = null; this.pageLink.sortOrder = null;
} }
const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns);
const sortColumnType: SortColumnType = key
? (key.type === EntityKeyType.ENTITY_FIELD || key.type === EntityKeyType.ALARM_FIELD ? 'entityField'
: key.type === EntityKeyType.TIME_SERIES ? 'timeseries' : 'attribute')
: 'entityField';
const keyFilters: KeyFilter[] = null; // TODO: const keyFilters: KeyFilter[] = null; // TODO:
this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, keyFilters); this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, sortColumnType, keyFilters);
this.ctx.detectChanges(); this.ctx.detectChanges();
} }
@ -1256,6 +1261,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
private appliedPageLink: AlarmDataPageLink; private appliedPageLink: AlarmDataPageLink;
private appliedSortOrderLabel: string; private appliedSortOrderLabel: string;
private appliedSortColumnType: SortColumnType = 'entityField';
private reserveSpaceForHiddenAction = true; private reserveSpaceForHiddenAction = true;
private cellButtonActions: TableCellButtonActionDescriptor[]; private cellButtonActions: TableCellButtonActionDescriptor[];
@ -1294,11 +1300,13 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
this.pageDataSubject.complete(); this.pageDataSubject.complete();
} }
loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string,
sortColumnType: SortColumnType, keyFilters: KeyFilter[]) {
this.dataLoading = true; this.dataLoading = true;
// this.clear(); // this.clear();
this.appliedPageLink = pageLink; this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel; this.appliedSortOrderLabel = sortOrderLabel;
this.appliedSortColumnType = sortColumnType;
this.subscription.subscribeForAlarms(pageLink, keyFilters); this.subscription.subscribeForAlarms(pageLink, keyFilters);
} }
@ -1330,7 +1338,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
} }
if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) {
const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC;
alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc, this.appliedSortColumnType));
} }
if (this.selection.hasValue()) { if (this.selection.hasValue()) {
const alarmIds = alarms.map((alarm) => alarm.id.id); const alarmIds = alarms.map((alarm) => alarm.id.id);

15
ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts

@ -100,7 +100,7 @@ import {
EntityKeyType, EntityKeyType,
KeyFilter KeyFilter
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { sortItems } from '@shared/models/page/page-link'; import { SortColumnType, sortItems } from '@shared/models/page/page-link';
import { entityFields } from '@shared/models/entity.models'; import { entityFields } from '@shared/models/entity.models';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
@ -617,8 +617,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
this.pageLink.sortOrder = null; this.pageLink.sortOrder = null;
} }
const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns);
const sortColumnType: SortColumnType = key
? (key.type === EntityKeyType.ENTITY_FIELD ? 'entityField'
: key.type === EntityKeyType.TIME_SERIES ? 'timeseries' : 'attribute')
: 'entityField';
const keyFilters: KeyFilter[] = null; // TODO: const keyFilters: KeyFilter[] = null; // TODO:
this.entityDatasource.loadEntities(this.pageLink, sortOrderLabel, keyFilters); this.entityDatasource.loadEntities(this.pageLink, sortOrderLabel, sortColumnType, keyFilters);
this.ctx.detectChanges(); this.ctx.detectChanges();
} }
@ -865,6 +869,7 @@ class EntityDatasource implements DataSource<EntityData> {
private appliedPageLink: EntityDataPageLink; private appliedPageLink: EntityDataPageLink;
private appliedSortOrderLabel: string; private appliedSortOrderLabel: string;
private appliedSortColumnType: SortColumnType = 'entityField';
private reserveSpaceForHiddenAction = true; private reserveSpaceForHiddenAction = true;
private cellButtonActions: TableCellButtonActionDescriptor[]; private cellButtonActions: TableCellButtonActionDescriptor[];
@ -905,11 +910,13 @@ class EntityDatasource implements DataSource<EntityData> {
this.pageDataSubject.complete(); this.pageDataSubject.complete();
} }
loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string,
sortColumnType: SortColumnType, keyFilters: KeyFilter[]) {
this.dataLoading = true; this.dataLoading = true;
// this.clear(); // this.clear();
this.appliedPageLink = pageLink; this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel; this.appliedSortOrderLabel = sortOrderLabel;
this.appliedSortColumnType = sortColumnType;
this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters);
} }
@ -934,7 +941,7 @@ class EntityDatasource implements DataSource<EntityData> {
}); });
if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) {
const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC;
entities = entities.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); entities = entities.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc, this.appliedSortColumnType));
} }
if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && entities.length) { if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && entities.length) {
maxCellButtonAction = entities[0].actionCellButtons.length; maxCellButtonAction = entities[0].actionCellButtons.length;

35
ui-ngx/src/app/modules/home/components/widget/lib/html/html-container-widget.component.ts

@ -15,8 +15,10 @@
/// ///
import { import {
Component, ChangeDetectorRef,
Component, ComponentRef,
ElementRef, ElementRef,
inject,
Inject, Inject,
Injector, Injector,
Input, Input,
@ -96,6 +98,7 @@ export class HtmlContainerWidgetComponent implements OnInit {
@Inject(HOME_COMPONENTS_MODULE_TOKEN) private homeComponentsModule: Type<any>, @Inject(HOME_COMPONENTS_MODULE_TOKEN) private homeComponentsModule: Type<any>,
private dynamicComponentFactoryService: DynamicComponentFactoryService, private dynamicComponentFactoryService: DynamicComponentFactoryService,
private utils: UtilsService, private utils: UtilsService,
private cdr: ChangeDetectorRef,
private resources: ResourcesService) {} private resources: ResourcesService) {}
ngOnInit(): void { ngOnInit(): void {
@ -160,11 +163,13 @@ export class HtmlContainerWidgetComponent implements OnInit {
this.compileAngularFunction().subscribe( this.compileAngularFunction().subscribe(
{ {
next: (containerFunction) => { next: (containerFunction) => {
try { setTimeout(() => {
this.initAngularComponent(imports, containerFunction); try {
} catch (e) { this.initAngularComponent(imports, containerFunction);
this.handleWidgetException(e); } catch (e) {
} this.handleWidgetException(e);
}
});
}, },
error: (e) => { error: (e) => {
this.handleWidgetException(e); this.handleWidgetException(e);
@ -200,9 +205,16 @@ export class HtmlContainerWidgetComponent implements OnInit {
compileModules = compileModules.concat(imports); compileModules = compileModules.concat(imports);
} }
const self = () => this; const self = () => this;
let containerRef: ComponentRef<any>;
this.dynamicComponentFactoryService.createDynamicComponent( this.dynamicComponentFactoryService.createDynamicComponent(
class TbContainerInstance { class TbContainerInstance {
private cdr = inject(ChangeDetectorRef);
ngOnInit(): void { ngOnInit(): void {
this.cdr.detach();
if (containerFunction) { if (containerFunction) {
const instance = self(); const instance = self();
try { try {
@ -212,6 +224,15 @@ export class HtmlContainerWidgetComponent implements OnInit {
} }
} }
} }
ngDoCheck(): void {
const instance = self();
try {
this.cdr.detectChanges()
} catch (error) {
containerRef.destroy();
instance.handleWidgetException(error)
}
}
ngOnDestroy(): void { ngOnDestroy(): void {
destroyContainerInstanceResources(); destroyContainerInstanceResources();
} }
@ -224,7 +245,7 @@ export class HtmlContainerWidgetComponent implements OnInit {
this.containerInstanceComponentType = componentType; this.containerInstanceComponentType = componentType;
const injector: Injector = Injector.create({providers: [], parent: this.angularContainer.viewContainerRef.injector}); const injector: Injector = Injector.create({providers: [], parent: this.angularContainer.viewContainerRef.injector});
try { try {
this.angularContainer.viewContainerRef.createComponent(this.containerInstanceComponentType, containerRef = this.angularContainer.viewContainerRef.createComponent(this.containerInstanceComponentType,
{index: 0, injector}); {index: 0, injector});
} catch (error) { } catch (error) {

11
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -276,6 +276,7 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
elementClick: this.elementClick.bind(this), elementClick: this.elementClick.bind(this),
cardClick: this.cardClick.bind(this), cardClick: this.cardClick.bind(this),
click: this.click.bind(this), click: this.click.bind(this),
invokeAction: this.invokeAction.bind(this),
getActiveEntityInfo: this.getActiveEntityInfo.bind(this), getActiveEntityInfo: this.getActiveEntityInfo.bind(this),
openDashboardStateInSeparateDialog: this.openDashboardStateInSeparateDialog.bind(this), openDashboardStateInSeparateDialog: this.openDashboardStateInSeparateDialog.bind(this),
openDashboardStateInPopover: this.openDashboardStateInPopover.bind(this), openDashboardStateInPopover: this.openDashboardStateInPopover.bind(this),
@ -1590,6 +1591,16 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
} }
} }
private invokeAction($event: Event, actionName: string, additionalParams?: any) {
const descriptors = this.getActionDescriptors('javaScript');
if (descriptors?.length) {
const found = descriptors.find(d => d.name === actionName);
if (found) {
this.handleWidgetAction($event, found, null, null, additionalParams);
}
}
}
private onWidgetAction($event: Event, action: WidgetAction) { private onWidgetAction($event: Event, action: WidgetAction) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();

15
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -149,6 +149,8 @@ export interface IDashboardWidget {
updateWidgetParams(): void; updateWidgetParams(): void;
} }
export type WidgetDestroyCallback = () => void;
export class WidgetContext { export class WidgetContext {
constructor(public dashboard: IDashboardComponent, constructor(public dashboard: IDashboardComponent,
@ -343,6 +345,8 @@ export class WidgetContext {
...RxJSOperators ...RxJSOperators
}; };
private destroyCallbacks: WidgetDestroyCallback[] = [];
registerPopoverComponent(popoverComponent: TbPopoverComponent) { registerPopoverComponent(popoverComponent: TbPopoverComponent) {
this.popoverComponents.push(popoverComponent); this.popoverComponents.push(popoverComponent);
popoverComponent.tbDestroy.subscribe(() => { popoverComponent.tbDestroy.subscribe(() => {
@ -382,6 +386,10 @@ export class WidgetContext {
} }
} }
registerDestroyCallback(destroyCallback: WidgetDestroyCallback) {
this.destroyCallbacks.push(destroyCallback);
}
showSuccessToast(message: string, duration: number = 1000, showSuccessToast(message: string, duration: number = 1000,
verticalPosition: NotificationVerticalPosition = 'bottom', verticalPosition: NotificationVerticalPosition = 'bottom',
horizontalPosition: NotificationHorizontalPosition = 'left', horizontalPosition: NotificationHorizontalPosition = 'left',
@ -494,6 +502,13 @@ export class WidgetContext {
labelPattern.destroy(); labelPattern.destroy();
} }
this.labelPatterns.clear(); this.labelPatterns.clear();
this.destroyCallbacks.forEach((destroyCallback) => {
try {
destroyCallback()
} catch (_ignoredError) { /* empty */ }
}
);
this.destroyCallbacks.length = 0;
this.width = undefined; this.width = undefined;
this.height = undefined; this.height = undefined;
this.destroyed = true; this.destroyed = true;

33
ui-ngx/src/app/shared/models/ace/widget-completion.models.ts

@ -632,6 +632,28 @@ export const widgetContextCompletionsWithSettings = (settingsCompletions?: TbEdi
optional: true optional: true
} }
] ]
},
invokeAction: {
description: 'Invoke action with JavaScript action source.',
meta: 'function',
args: [
{
name: '$event',
description: 'DOM event object associated with action.',
type: 'Event'
},
{
name: 'actionName',
description: 'Name of the configured action with JavaScript action source.',
type: 'string'
},
{
name: 'additionalParams',
description: 'Optional payload merged into the action context and forwarded to the configured JavaScript action function as its <code>additionalParams</code> argument. Use it to pass row data, button state, or any extra values the action handler should react to.',
type: 'object',
optional: true
}
]
} }
} }
}, },
@ -736,6 +758,17 @@ export const widgetContextCompletionsWithSettings = (settingsCompletions?: TbEdi
} }
} }
} }
},
registerDestroyCallback: {
description: 'Registers a teardown callback that will be invoked exactly once when the widget is about to be destroyed (dashboard navigation, edit/view switch, layout change, etc.). Use it to release resources acquired during widget setup so they don\'t leak across widget reloads — e.g. unsubscribe RxJS subscriptions, detach DOM/window event listeners, clear <code>setInterval</code> / <code>setTimeout</code> timers, abort outstanding HTTP requests, destroy third-party plugin instances. Multiple callbacks may be registered; they are executed in registration order.',
meta: 'function',
args: [
{
description: 'Zero-argument function executed when the widget is destroyed. Should be idempotent — synchronously dispose of one specific resource (e.g. one subscription or one listener) and avoid throwing; throwing here may prevent later cleanup callbacks from running.',
name: 'destroyCallback',
type: '() => void',
}
]
} }
}, },
...serviceCompletions ...serviceCompletions

43
ui-ngx/src/app/shared/models/page/page-link.ts

@ -22,6 +22,19 @@ import { EntitiesTableAction } from '@home/models/entity/entity-table-component.
export const MAX_SAFE_PAGE_SIZE = 2147483647; export const MAX_SAFE_PAGE_SIZE = 2147483647;
export type NullsOrderStrategy = 'default' | 'nulls_first' | 'nulls_last';
let nullsOrderStrategy: NullsOrderStrategy = 'default';
let edqsEnabled = false;
export function setNullsOrderStrategy(value: NullsOrderStrategy): void {
nullsOrderStrategy = value ?? 'default';
}
export function setEdqsEnabled(value: boolean): void {
edqsEnabled = !!value;
}
export type PageLinkSearchFunction<T> = (entity: T, textSearch: string, searchProperty?: string) => boolean; export type PageLinkSearchFunction<T> = (entity: T, textSearch: string, searchProperty?: string) => boolean;
export interface PageQueryParam extends Partial<SortOrder>{ export interface PageQueryParam extends Partial<SortOrder>{
@ -73,9 +86,37 @@ const defaultPageLinkSearch: PageLinkSearchFunction<any> =
return false; return false;
}; };
export function sortItems(item1: any, item2: any, property: string, asc: boolean): number { export type SortColumnType = 'entityField' | 'attribute' | 'timeseries';
export function sortItems(item1: any, item2: any, property: string, asc: boolean,
columnType: SortColumnType = 'entityField'): number {
const item1Value = getDescendantProp(item1, property); const item1Value = getDescendantProp(item1, property);
const item2Value = getDescendantProp(item2, property); const item2Value = getDescendantProp(item2, property);
const item1Empty = item1Value === null || item1Value === undefined || item1Value === '';
const item2Empty = item2Value === null || item2Value === undefined || item2Value === '';
// Mirror backend's nulls ordering. EDQS uses fixed NULLS FIRST regardless of strategy and
// naive compare below already matches it, so skip this branch when EDQS is on.
// For entityField columns the ORDER BY hits a real nullable DB column → strategy always applies.
// For attribute/timeseries the strategy only applies to numeric/boolean values; string/json
// are coalesced to '' on the backend, so naive compare below already matches its order.
if (!edqsEnabled && (item1Empty || item2Empty) && !(item1Empty && item2Empty)) {
let applyStrategy = columnType === 'entityField';
if (!applyStrategy) {
const other = item1Empty ? item2Value : item1Value;
applyStrategy =
typeof other === 'boolean' || other === 'true' || other === 'false' ||
(typeof other === 'number' && isFinite(other)) ||
(typeof other === 'string' && other.trim() !== '' && !isNaN(Number(other)));
}
if (applyStrategy) {
const nullsFirst = nullsOrderStrategy === 'nulls_first'
|| (nullsOrderStrategy === 'default' && !asc);
if (item1Empty) {
return nullsFirst ? -1 : 1;
}
return nullsFirst ? 1 : -1;
}
}
let result = 0; let result = 0;
if (item1Value !== item2Value) { if (item1Value !== item2Value) {
const item1Type = typeof item1Value; const item1Type = typeof item1Value;

32
ui-ngx/yarn.lock

@ -987,14 +987,14 @@
"@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-plugin-utils" "^7.28.6"
"@babel/plugin-transform-modules-systemjs@^7.27.1": "@babel/plugin-transform-modules-systemjs@^7.27.1":
version "7.28.5" version "7.29.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz#7439e592a92d7670dfcb95d0cbc04bd3e64801d2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz#f621105da99919c15cf4bde6fcc7346ef95e7b20"
integrity sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew== integrity sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==
dependencies: dependencies:
"@babel/helper-module-transforms" "^7.28.3" "@babel/helper-module-transforms" "^7.28.6"
"@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-plugin-utils" "^7.28.6"
"@babel/helper-validator-identifier" "^7.28.5" "@babel/helper-validator-identifier" "^7.28.5"
"@babel/traverse" "^7.28.5" "@babel/traverse" "^7.29.0"
"@babel/plugin-transform-modules-umd@^7.27.1": "@babel/plugin-transform-modules-umd@^7.27.1":
version "7.27.1" version "7.27.1"
@ -1298,7 +1298,7 @@
"@babel/parser" "^7.28.6" "@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6" "@babel/types" "^7.28.6"
"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6": "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0":
version "7.29.0" version "7.29.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a"
integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==
@ -4349,17 +4349,17 @@ boolbase@^1.0.0:
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.13" version "1.1.14"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.13.tgz#d37875c01dc9eff988dd49d112a57cb67b54efe6" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.14.tgz#d9de602370d91347cd9ddad1224d4fd701eb348b"
integrity sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w== integrity sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^5.0.2: brace-expansion@^5.0.2:
version "5.0.5" version "5.0.6"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285"
integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==
dependencies: dependencies:
balanced-match "^4.0.2" balanced-match "^4.0.2"
@ -6113,9 +6113,9 @@ fast-levenshtein@^2.0.6:
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-uri@^3.0.1: fast-uri@^3.0.1:
version "3.1.0" version "3.1.2"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.2.tgz#8af3d4fc9d3e71b11572cc2673b514a7d1a8c8ec"
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== integrity sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==
fastq@^1.6.0: fastq@^1.6.0:
version "1.17.1" version "1.17.1"

Loading…
Cancel
Save