Repository: evcc-io/evcc
Pull request #10606 (2023-11-01 22:13:02)

Tapo: Enable KLAP protocol + P100 devices
This PR enables evcc to use TAPO plugs having a newer firmware that supports only the new KLAP protocol.
Refer to discussion #10461

Additionally the complete tapo go module structure was redesigned, based on the top work of @insomniacslk.
Refer to https://github.com/insomniacslk/tapo

Test-runs
Passthrough-Protocol (deprecated):
```
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_tapotest.yaml -l debug charger
[main  ] INFO 2023/11/07 23:37:51 evcc 0.121.5 (d98c2b72)
[main  ] INFO 2023/11/07 23:37:51 using config file: ./evcc_tapotest.yaml
[db    ] INFO 2023/11/07 23:37:51 using sqlite database: /home/thierolm/.evcc/evcc.db
[tapo  ] DEBUG 2023/11/07 23:37:51 SMART.TAPOPLUG P110 connected (fw:1.2.3 Build 230425 Rel.142542,hw:1.0,mac:54-AF-97-1D-5E-98)
[tapo  ] DEBUG 2023/11/07 23:37:51 SMART.TAPOPLUG P100 connected (fw:1.1.0 Build 220720 Rel.163239,hw:2.0,mac:34-60-F9-9C-B5-E1)
wb-Tapo-P110
------------
Power:         0W
Charge status: B
Enabled:       false
Charged:       0.0kWh
Features:      []

wb-Tapo-P100
------------
[tapo  ] WARN 2023/11/07 23:37:52 meter not available
Power:         300W
Charge status: C
Enabled:       true
Charged:       0.0kWh
Features:      []
```

KLAP-Protocol (newer fw versions - P100 fw:1.2.1 Build 230804 Rel.190922):
```
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_tapotest.yaml -l debug charger
[main  ] INFO 2023/11/12 00:49:23 evcc 0.122.0 (b4b154e4)
[main  ] INFO 2023/11/12 00:49:23 using config file: ./evcc_tapotest.yaml
[db    ] INFO 2023/11/12 00:49:23 using sqlite database: /home/thierolm/.evcc/evcc.db
[tapo  ] DEBUG 2023/11/12 00:49:23 SMART.TAPOPLUG P100 connected (fw:1.2.1 Build 230804 Rel.190922,hw:2.0,mac:34-60-F9-9C-B5-E1)
[tapo  ] DEBUG 2023/11/12 00:49:23 meter not available
Power:         300W
Charge status: C
Enabled:       true
Charged:       0.0kWh
Features:      []
```

thierolm: @AndySchoe , ist deine Tapo Dose schon zu mir unterwegs? Ich denke die Chance stehen gut, dass ich sie erfolgreich testen kann. :-)
thierolm: Die P100 von @AndySchoe ist bei mir angekommen :-) Vielen Dank!! Ich habe sie eingerichtet und auf den aktuellsten Firmwarestand gebracht. Beim Testen hat sich herausgestellt, dass die Probleme der Dosen nicht durch das KLAP Protokoll ausgelöst worden sind, sondern durch das fehlende Meter Feature bei der P100 zum Fehler-Code `{"error_code":-1001}` führt.
Ich habe diesen jetzt behandelt und schon funktionierts:

```
thierolm@LAPTOP-KQMMVN3H:~/git-repos/evcc$ ./evcc -c ./evcc_tapotest.yaml -l debug charger
[main  ] INFO 2023/11/07 23:37:51 evcc 0.121.5 (d98c2b72)
[main  ] INFO 2023/11/07 23:37:51 using config file: ./evcc_tapotest.yaml
[db    ] INFO 2023/11/07 23:37:51 using sqlite database: /home/thierolm/.evcc/evcc.db
[tapo  ] DEBUG 2023/11/07 23:37:51 SMART.TAPOPLUG P110 connected (fw:1.2.3 Build 230425 Rel.142542,hw:1.0,mac:54-AF-97-1D-5E-98)
[tapo  ] DEBUG 2023/11/07 23:37:51 SMART.TAPOPLUG P100 connected (fw:1.1.0 Build 220720 Rel.163239,hw:2.0,mac:34-60-F9-9C-B5-E1)
wb-Tapo-P110
------------
Power:         0W
Charge status: B
Enabled:       false
Charged:       0.0kWh
Features:      []

wb-Tapo-P100
------------
[tapo  ] WARN 2023/11/07 23:37:52 meter not available
Power:         300W
Charge status: C
Enabled:       true
Charged:       0.0kWh
Features:      []
```


thierolm: @andig Leider hab' ich jetzt keine Dose mit KLAP Protokoll zum Testen.
Allerdings ist m.E. durch den Switch auf das Repo von @insomniacslk mit hoher Wahrscheinlichkeit sicher gestellt, das solche Devices zukünftig funktionieren.
Ich würde für mergen voten :-)
Was meinst du? 
thierolm: Hier noch das charger Trace Log der P100 von @AndySchoe:
```
[main  ] INFO 2023/11/08 00:00:04 evcc 0.121.5 (d98c2b72)
[main  ] INFO 2023/11/08 00:00:04 using config file: ./evcc_tapotest.yaml
...
[tapo  ] TRACE 2023/11/08 00:00:04 KLAP handshake failed, trying passthrough handshake
[tapo  ] TRACE 2023/11/08 00:00:04 Handshake request: {"method":"handshake","requestTimeMils":1699398004982,"params":{"key":"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDqwhwaiJiQwM7VQnasJdS05q8V\npw7HJPut0IGj9bIMe3rJaV0clmuOnjM6sXRUmGSIpSstju09sFfXqSHDQGov95sn\nTf9U+692b23Z39xHRZBVwTj4Z8TAddfbjob5Xw3NZwUQkjw5j7UqnqyhFDMMyqpL\nb0AfM5U6o4ntqx694QIDAQAB\n-----END PUBLIC KEY-----\n"}}
[tapo  ] TRACE 2023/11/08 00:00:04 Handshake response: {"error_code":0,"result":{"key":"2XoP6TOne4Ta3PXFIQ0tJK3TiXenPjPmk9WZ51Jox22oZGvk84+1+PN0UaBMaZNchaDLxNjGpLajlEQkpjLwZ3T+9d/Q+FW0Hcp0qtIx2Ai2DaY3TA5skwofAF1efKy0fO6Nkp0wKfRWap2LRIYIRpplmEYtGvwBiHvkBxNDfho="}}
[tapo  ] TRACE 2023/11/08 00:00:05 Login request: {"method":"login_device","requestTimeMils":1699398005000,"params":{"username":"MWYzYzU4N2JmMjRhYzUwY2RiY2RmZjUyZjAxMmI1MWQ0MzM0OGY1Mw==","password":"dGFwbzEyMzQ="}}
[tapo  ] TRACE 2023/11/08 00:00:05 Passthrough request: {"method":"securePassthrough","params":{"request":"4zAmKoA8WrpHkPmCUPxlRyxWv+eBUgfT/WUK75CixW8LtMAZ1S5as4HIoKN6GHezEmHSi04nymH1ueALfyDhMchwv9EFt97KvSPUS/bx5Q0defjn3VIynZf314uydn5rNmBU8oHcx71svXv/2Vxqre5vTAkf3RvkciM2gfxGzKOa9jZD35dFLivoTtW0gApFsuyeqtT2PY5OuhgoVkd92Whdz9rnUaY2dqBSTBfbOkE="}}
[tapo  ] TRACE 2023/11/08 00:00:05 Passthrough response: {"error_code":0,"result":{"response":"CRP3Jqq8SL3PXU8LP0MDqlVrxL8YvK+buIT3hb3mV9n6pNS0W5ZdPu6SFnKnzXFUVMjDIPCZ8J5lRaFuRjkgPEDTXxjiwvOzO5HzZhiy4w4="}}
[tapo  ] TRACE 2023/11/08 00:00:05 Login response: {"error_code":0,"result":{"token":"F16F1792853A1791D00014916A2A9490"}}
[tapo  ] TRACE 2023/11/08 00:00:05 Session: &{log:0xc001506180 Key:[238 227 16 146 137 245 145 146 31 70 17 146 172 141 17 146] IV:[231 212 17 146 62 241 17 146 155 157 17 146 119 207 17 146] ID:TP_SESSIONID=CA3D14B357E790900D299392C3041193 addr:{addr:{hi:0 lo:281473914024589} z:0xc000128078} username:m.thierolf@googlemail.com password:tapo1234 token:F16F1792853A1791D00014916A2A9490 privateKey:0xc000bb8000 publicKey:0xc000bb8000 timeout:0}
[tapo  ] TRACE 2023/11/08 00:00:05 GetDeviceInfo request: {"method":"get_device_info","requestTimeMils":1699398005008}
[tapo  ] TRACE 2023/11/08 00:00:05 Passthrough request: {"method":"securePassthrough","params":{"request":"Q/LSM87CJN1NBYUN3rplAniDturL/EEYjLkEllD6iytwvnLhprcGYeOun1nh8L07qdtITHnx5LSwPzC/q2yWpw=="}}
[tapo  ] TRACE 2023/11/08 00:00:05 Passthrough response: {"error_code":0,"result":{"response":"1dnP/SaqUTbrf3lREE65STsm/W1dpjbA6hrrvUd13zV4NQk3qTiouX+y8E7OMdubdb6/48jLBUzuzz7hRr2In0IaVy+S0PZk5kqgtAgBTf6rZPrHock42ao93mrQcLEVFAazoWibBpaZ5AAY2e7aCMHB5g9Z4SzBQA1dmW8Ma1u+D4QW4Yb5Sav2gZ7TeMVGw4xTz7rJtZyjMKPVXjGc8puJ84VHv4g6GwtJ+Ufu4KUkGb7iY5UkAGYmDJNUyFJmQn/ZqmSQW/korZaLWVJIJ47NQKsfRw2AMkoRnKoM+GdYClkMjIbe1+dzW+5Xhx6CB0Gu2XHRsMa5CMxIibvmYHfaG+eeUQddMsDWJ3Vb2z0L7n2d9E3c6sof5pRcmuaDJ4swz0kHnFwhOC0QfRpBkQJ/EBXGOuR+nXxuftGqsqfhUt9RQPUeyt08nFrlhl4DfXeewSi752jWLs7Mo7DM6htYpdT7A7RNP+QX1t7uXa9LYEMvqbND7LH9kL/mWTg9WuyvHgWlisDXqUxrGjVhD4UMN4N0bdK104rcYAGIV7IDPZ6B+wjnIldLEaO4U8ZRNmRmEjB/mfwYLQCWR2uvzN3uhJjbtrJobTUSU+kB5APxokO1nmq5dhnG2na+dxPmdootk8cGtHUjw8oP5cXiNwha9Sf00rjOLB3E+DnRC2+toK2gSwJ5L8Ik7Op+EAzMTMbxun0YemuOpmQsX0VaGYUbfTbBan9nd/IP8uvtwcAatF4syQHcRpDHgrBna5qaDgNoS3dTyI7/qgNR4qOzDNGXnu9f11mTiep1StMg9nPzWLULBVnlyAnYawFp/bScwXoBRARqMnoPAxIJdV0klQHA+edzIxsZnOnjC5SwRV/ZFTIcI5CP3kUOdBdGchkGajXZW9aKqUp+wemaUkZBfg=="}}
[tapo  ] TRACE 2023/11/08 00:00:05 GetDeviceInfo response: {"result":{"device_id":"8022787193A97B73E48B699466A2F0D520076842","fw_ver":"1.1.0 Build 220720 Rel.163239","hw_ver":"2.0","type":"SMART.TAPOPLUG","model":"P100","mac":"34-60-F9-9C-B5-E1","hw_id":"4D44B233807D5BDA45BEC69DC18AAE09","fw_id":"00000000000000000000000000000000","oem_id":"6A0E161B05B8FB93E53988B66CE51E30","ip":"192.168.178.141","time_diff":60,"ssid":"VEhJRVJPTEYtV0xBTg==","rssi":-68,"signal_level":2,"latitude":498164,"longitude":90563,"lang":"","avatar":"plug","region":"Europe/Berlin","specs":"","nickname":"VGFwbyBQMTAw","has_set_location_info":true,"device_on":true,"on_time":1339,"default_states":{"type":"last_states","state":{}},"overheated":false},"error_code":0}
[tapo  ] DEBUG 2023/11/08 00:00:05 SMART.TAPOPLUG P100 connected (fw:1.1.0 Build 220720 Rel.163239,hw:2.0,mac:34-60-F9-9C-B5-E1)
...
[tapo  ] TRACE 2023/11/08 00:00:05 GetEnergyUsage request: {"method":"get_energy_usage","requestTimeMils":1699398005105}
[tapo  ] TRACE 2023/11/08 00:00:05 Passthrough request: {"method":"securePassthrough","params":{"request":"pxJ7b32YfmU68J7aI+sSnOYB/9LgepEIvjEGnxlxwRjNZO8qbfPc6umO0i4Qqkn11cu+OJZzRW4h9lPLzIRHMw=="}}
[tapo  ] TRACE 2023/11/08 00:00:05 Passthrough response: {"error_code":0,"result":{"response":"gXMYxo00iIwwrh9Cn0+9Ur83sfBfc1sDdbvKwfty5Qc="}}
[tapo  ] TRACE 2023/11/08 00:00:05 GetEnergyUsage response: {"error_code":-1001}
[tapo  ] WARN 2023/11/08 00:00:05 meter not available
Power:         300W
Charge status: C
Enabled:       true
Charged:       0.0kWh
Features:      []
```
thierolm: @andig ich habe versucht den go.mod Konflikt mit `go mod tidy` aufzulösen. Hat leider nicht funktioniert. Hast du einen Tip?
andig: User/password sollten noch redacted werden ;)
andig: @insomniacslk wäre es nicht auch eine gute Idee, wenn man sich pro Connection merken würde, mit welchem Verfahren die Authentifizierung erfolgreich war? Nutzen sicher davon abhängig, wie oft man das machen muss.

Patch:
diff --git a/api/api.go b/api/api.go
index 4d502922..2ef44502 100644
--- a/api/api.go
+++ b/api/api.go
@@ -9,7 +9,7 @@ import (
 	"time"
 )
 
-//go:generate mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,PhaseSwitcher,Identifier,Meter,MeterEnergy,Vehicle,ChargeRater,Battery,Tariff
+//go:generate mockgen -package api -destination mock.go github.com/evcc-io/evcc/api Charger,ChargeState,PhaseSwitcher,Identifier,Meter,MeterEnergy,Vehicle,ChargeRater,Battery,Tariff,BatteryController
 
 // ChargeMode is the charge operation mode. Valid values are off, now, minpv and pv
 type ChargeMode string
@@ -134,6 +134,12 @@ type CurrentGetter interface {
 	GetMaxCurrent() (float64, error)
 }
 
+// BatteryController optionally allows to control home battery (dis)charging behaviour
+type BatteryController interface {
+	SetBatteryMode(BatteryMode) error
+	GetBatteryMode() BatteryMode
+}
+
 // Charger provides current charging status and enable/disable charging
 type Charger interface {
 	ChargeState
diff --git a/api/batterymode.go b/api/batterymode.go
new file mode 100644
index 00000000..511fa5f0
--- /dev/null
+++ b/api/batterymode.go
@@ -0,0 +1,12 @@
+package api
+
+// BatteryMode is the home battery operation mode. Valid values are normal, locked and charge
+type BatteryMode int
+
+//go:generate enumer -type BatteryMode
+const (
+	BatteryUnknown BatteryMode = iota
+	BatteryNormal
+	BatteryLocked
+	BatteryCharge
+)
diff --git a/api/batterymode_enumer.go b/api/batterymode_enumer.go
new file mode 100644
index 00000000..d78db37a
--- /dev/null
+++ b/api/batterymode_enumer.go
@@ -0,0 +1,86 @@
+// Code generated by "enumer -type BatteryMode"; DO NOT EDIT.
+
+package api
+
+import (
+	"fmt"
+	"strings"
+)
+
+const _BatteryModeName = "BatteryUnknownBatteryNormalBatteryLockedBatteryCharge"
+
+var _BatteryModeIndex = [...]uint8{0, 14, 27, 40, 53}
+
+const _BatteryModeLowerName = "batteryunknownbatterynormalbatterylockedbatterycharge"
+
+func (i BatteryMode) String() string {
+	if i < 0 || i >= BatteryMode(len(_BatteryModeIndex)-1) {
+		return fmt.Sprintf("BatteryMode(%d)", i)
+	}
+	return _BatteryModeName[_BatteryModeIndex[i]:_BatteryModeIndex[i+1]]
+}
+
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _BatteryModeNoOp() {
+	var x [1]struct{}
+	_ = x[BatteryUnknown-(0)]
+	_ = x[BatteryNormal-(1)]
+	_ = x[BatteryLocked-(2)]
+	_ = x[BatteryCharge-(3)]
+}
+
+var _BatteryModeValues = []BatteryMode{BatteryUnknown, BatteryNormal, BatteryLocked, BatteryCharge}
+
+var _BatteryModeNameToValueMap = map[string]BatteryMode{
+	_BatteryModeName[0:14]:       BatteryUnknown,
+	_BatteryModeLowerName[0:14]:  BatteryUnknown,
+	_BatteryModeName[14:27]:      BatteryNormal,
+	_BatteryModeLowerName[14:27]: BatteryNormal,
+	_BatteryModeName[27:40]:      BatteryLocked,
+	_BatteryModeLowerName[27:40]: BatteryLocked,
+	_BatteryModeName[40:53]:      BatteryCharge,
+	_BatteryModeLowerName[40:53]: BatteryCharge,
+}
+
+var _BatteryModeNames = []string{
+	_BatteryModeName[0:14],
+	_BatteryModeName[14:27],
+	_BatteryModeName[27:40],
+	_BatteryModeName[40:53],
+}
+
+// BatteryModeString retrieves an enum value from the enum constants string name.
+// Throws an error if the param is not part of the enum.
+func BatteryModeString(s string) (BatteryMode, error) {
+	if val, ok := _BatteryModeNameToValueMap[s]; ok {
+		return val, nil
+	}
+
+	if val, ok := _BatteryModeNameToValueMap[strings.ToLower(s)]; ok {
+		return val, nil
+	}
+	return 0, fmt.Errorf("%s does not belong to BatteryMode values", s)
+}
+
+// BatteryModeValues returns all values of the enum
+func BatteryModeValues() []BatteryMode {
+	return _BatteryModeValues
+}
+
+// BatteryModeStrings returns a slice of all String values of the enum
+func BatteryModeStrings() []string {
+	strs := make([]string, len(_BatteryModeNames))
+	copy(strs, _BatteryModeNames)
+	return strs
+}
+
+// IsABatteryMode returns "true" if the value is listed in the enum definition. "false" otherwise
+func (i BatteryMode) IsABatteryMode() bool {
+	for _, v := range _BatteryModeValues {
+		if i == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/api/mock.go b/api/mock.go
index c46974f9..78bdeed3 100644
--- a/api/mock.go
+++ b/api/mock.go
@@ -1,5 +1,5 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/evcc-io/evcc/api (interfaces: Charger,ChargeState,PhaseSwitcher,Identifier,Meter,MeterEnergy,Vehicle,ChargeRater,Battery,Tariff)
+// Source: github.com/evcc-io/evcc/api (interfaces: Charger,ChargeState,PhaseSwitcher,Identifier,Meter,MeterEnergy,Vehicle,ChargeRater,Battery,Tariff,BatteryController)
 
 // Package api is a generated GoMock package.
 package api
@@ -541,3 +541,54 @@ func (mr *MockTariffMockRecorder) Type() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*MockTariff)(nil).Type))
 }
+
+// MockBatteryController is a mock of BatteryController interface.
+type MockBatteryController struct {
+	ctrl     *gomock.Controller
+	recorder *MockBatteryControllerMockRecorder
+}
+
+// MockBatteryControllerMockRecorder is the mock recorder for MockBatteryController.
+type MockBatteryControllerMockRecorder struct {
+	mock *MockBatteryController
+}
+
+// NewMockBatteryController creates a new mock instance.
+func NewMockBatteryController(ctrl *gomock.Controller) *MockBatteryController {
+	mock := &MockBatteryController{ctrl: ctrl}
+	mock.recorder = &MockBatteryControllerMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockBatteryController) EXPECT() *MockBatteryControllerMockRecorder {
+	return m.recorder
+}
+
+// GetBatteryMode mocks base method.
+func (m *MockBatteryController) GetBatteryMode() BatteryMode {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetBatteryMode")
+	ret0, _ := ret[0].(BatteryMode)
+	return ret0
+}
+
+// GetBatteryMode indicates an expected call of GetBatteryMode.
+func (mr *MockBatteryControllerMockRecorder) GetBatteryMode() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBatteryMode", reflect.TypeOf((*MockBatteryController)(nil).GetBatteryMode))
+}
+
+// SetBatteryMode mocks base method.
+func (m *MockBatteryController) SetBatteryMode(arg0 BatteryMode) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "SetBatteryMode", arg0)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// SetBatteryMode indicates an expected call of SetBatteryMode.
+func (mr *MockBatteryControllerMockRecorder) SetBatteryMode(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBatteryMode", reflect.TypeOf((*MockBatteryController)(nil).SetBatteryMode), arg0)
+}
diff --git a/charger/tapo.go b/charger/tapo.go
index dcc9a539..2aa503f7 100644
--- a/charger/tapo.go
+++ b/charger/tapo.go
@@ -55,17 +55,12 @@ func NewTapo(embed embed, uri, user, password string, standbypower float64) (*Ta
 
 // Enabled implements the api.Charger interface
 func (c *Tapo) Enabled() (bool, error) {
-	resp, err := c.conn.ExecCmd("get_device_info", false)
-	if err != nil {
-		return false, err
-	}
-	return resp.Result.DeviceON, nil
+	return c.conn.Enabled()
 }
 
 // Enable implements the api.Charger interface
 func (c *Tapo) Enable(enable bool) error {
-	_, err := c.conn.ExecCmd("set_device_info", enable)
-	return err
+	return c.conn.Enable(enable)
 }
 
 var _ api.ChargeRater = (*Tapo)(nil)
diff --git a/cmd/root.go b/cmd/root.go
index 65d9ee18..3afe6195 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -179,7 +179,13 @@ func runRoot(cmd *cobra.Command, args []string) {
 	// setup modbus proxy
 	if err == nil {
 		for _, cfg := range conf.ModbusProxy {
-			if err = modbus.StartProxy(cfg.Port, cfg.Settings, cfg.ReadOnly); err != nil {
+			var mode modbus.ReadOnlyMode
+			mode, err = modbus.ReadOnlyModeString(cfg.ReadOnly)
+			if err != nil {
+				break
+			}
+
+			if err = modbus.StartProxy(cfg.Port, cfg.Settings, mode); err != nil {
 				break
 			}
 		}
diff --git a/cmd/setup.go b/cmd/setup.go
index 134b4344..3e218ab2 100644
--- a/cmd/setup.go
+++ b/cmd/setup.go
@@ -113,7 +113,7 @@ type goConfig struct {
 
 type proxyConfig struct {
 	Port            int
-	ReadOnly        bool
+	ReadOnly        string
 	modbus.Settings `mapstructure:",squash"`
 }
 
diff --git a/core/loadpoint.go b/core/loadpoint.go
index c7079a7c..8651113d 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -1384,7 +1384,7 @@ func (lp *Loadpoint) publishSocAndRange() {
 	soc, err := lp.chargerSoc()
 
 	// guard for socEstimator removed by api
-	if lp.socEstimator == nil || !lp.vehicleHasSoc() {
+	if lp.socEstimator == nil || (!lp.vehicleHasSoc() && err != nil) {
 		// This is a workaround for heaters. Without vehicle, the soc estimator is not initialized.
 		// We need to check if the charger can provide soc and use it if available.
 		if err == nil {
diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go
index 226f505b..f7c698c8 100644
--- a/core/loadpoint/api.go
+++ b/core/loadpoint/api.go
@@ -100,6 +100,8 @@ type API interface {
 	// charge progress
 	//
 
+	// GetPlanActive returns the active state of the planner
+	GetPlanActive() bool
 	// GetRemainingDuration is the estimated remaining charging duration
 	GetRemainingDuration() time.Duration
 	// GetRemainingEnergy is the remaining charge energy in Wh
diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go
index a2c2addb..64a1dd8c 100644
--- a/core/loadpoint/mock.go
+++ b/core/loadpoint/mock.go
@@ -205,6 +205,20 @@ func (mr *MockAPIMockRecorder) GetPlan(arg0, arg1 interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPlan", reflect.TypeOf((*MockAPI)(nil).GetPlan), arg0, arg1)
 }
 
+// GetPlanActive mocks base method.
+func (m *MockAPI) GetPlanActive() bool {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetPlanActive")
+	ret0, _ := ret[0].(bool)
+	return ret0
+}
+
+// GetPlanActive indicates an expected call of GetPlanActive.
+func (mr *MockAPIMockRecorder) GetPlanActive() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPlanActive", reflect.TypeOf((*MockAPI)(nil).GetPlanActive))
+}
+
 // GetPriority mocks base method.
 func (m *MockAPI) GetPriority() int {
 	m.ctrl.T.Helper()
diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go
index a5faaa13..2c633d80 100644
--- a/core/loadpoint_api.go
+++ b/core/loadpoint_api.go
@@ -377,6 +377,13 @@ func (lp *Loadpoint) GetMaxPower() float64 {
 	return Voltage * lp.GetMaxCurrent() * float64(lp.maxActivePhases())
 }
 
+// GetPlanActive returns the active state of the planner
+func (lp *Loadpoint) GetPlanActive() bool {
+	lp.Lock()
+	defer lp.Unlock()
+	return lp.planActive
+}
+
 // SetRemainingDuration sets the estimated remaining charging duration
 func (lp *Loadpoint) SetRemainingDuration(chargeRemainingDuration time.Duration) {
 	lp.Lock()
diff --git a/core/planner/planner.go b/core/planner/planner.go
index 5377c73b..43193054 100644
--- a/core/planner/planner.go
+++ b/core/planner/planner.go
@@ -7,7 +7,6 @@ import (
 	"github.com/benbjohnson/clock"
 	"github.com/evcc-io/evcc/api"
 	"github.com/evcc-io/evcc/util"
-	"github.com/jinzhu/copier"
 )
 
 // Planner plans a series of charging slots for a given (variable) tariff
@@ -39,12 +38,8 @@ func (t *Planner) plan(rates api.Rates, requiredDuration time.Duration, targetTi
 			continue
 		}
 
-		var slot api.Rate
-		if err := copier.Copy(&slot, source); err != nil {
-			panic(err)
-		}
-
 		// adjust slot start and end
+		slot := source
 		if slot.Start.Before(t.clock.Now()) {
 			slot.Start = t.clock.Now()
 		}
diff --git a/core/site.go b/core/site.go
index 78607574..f69a0dcc 100644
--- a/core/site.go
+++ b/core/site.go
@@ -67,6 +67,7 @@ type Site struct {
 	BufferStartSoc                    float64      `mapstructure:"bufferStartSoc"`                    // start charging on battery above this Soc
 	MaxGridSupplyWhileBatteryCharging float64      `mapstructure:"maxGridSupplyWhileBatteryCharging"` // ignore battery charging if AC consumption is above this value
 	SmartCostLimit                    float64      `mapstructure:"smartCostLimit"`                    // always charge if cost is below this value
+	BatteryDischargeControl           bool         `mapstructure:"batteryDischargeControl"`           // shall discharge of home battery be adjusted
 
 	// meters
 	gridMeter     api.Meter   // Grid usage meter
@@ -81,10 +82,11 @@ type Site struct {
 	stats       *Stats                   // Stats
 
 	// cached state
-	gridPower    float64 // Grid power
-	pvPower      float64 // PV power
-	batteryPower float64 // Battery charge power
-	batterySoc   float64 // Battery soc
+	gridPower    float64         // Grid power
+	pvPower      float64         // PV power
+	batteryPower float64         // Battery charge power
+	batterySoc   float64         // Battery soc
+	batteryMode  api.BatteryMode // Battery discharge currently enabled
 
 	publishCache map[string]any // store last published values to avoid unnecessary republishing
 }
@@ -224,6 +226,7 @@ func NewSite() *Site {
 		log:          util.NewLogger("site"),
 		publishCache: make(map[string]any),
 		Voltage:      230, // V
+		batteryMode:  api.BatteryNormal,
 	}
 
 	return lp
@@ -251,6 +254,9 @@ func (site *Site) restoreSettings() {
 	if v, err := settings.Float("site.smartCostLimit"); err == nil {
 		site.SmartCostLimit = v
 	}
+	if v, err := settings.Bool("site.batteryDischargeControl"); err == nil {
+		site.BatteryDischargeControl = v
+	}
 }
 
 func meterCapabilities(name string, meter interface{}) string {
@@ -769,7 +775,6 @@ func (site *Site) update(lp Updater) {
 	}
 
 	if sitePower, batteryBuffered, batteryStart, err := site.sitePower(totalChargePower, flexiblePower); err == nil {
-
 		// ignore negative pvPower values as that means it is not an energy source but consumption
 		homePower := site.gridPower + max(0, site.pvPower) + site.batteryPower - totalChargePower
 		homePower = max(homePower, 0)
@@ -791,6 +796,10 @@ func (site *Site) update(lp Updater) {
 		site.log.ERROR.Println(err)
 	}
 
+	if site.BatteryDischargeControl {
+		site.updateBatteryMode(site.Loadpoints())
+	}
+
 	site.stats.Update(site)
 }
 
@@ -814,6 +823,8 @@ func (site *Site) prepare() {
 	site.publish("currency", site.tariffs.Currency.String())
 
 	site.publish("vehicles", vehicleTitles(site.GetVehicles()))
+	site.publish("batteryDischargeControl", site.BatteryDischargeControl)
+	site.publish("batteryMode", site.batteryMode)
 }
 
 // Prepare attaches communication channels to site and loadpoints
diff --git a/core/site_battery.go b/core/site_battery.go
new file mode 100644
index 00000000..ca46ccfe
--- /dev/null
+++ b/core/site_battery.go
@@ -0,0 +1,49 @@
+package core
+
+import (
+	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/loadpoint"
+)
+
+// getBatteryMode returns the battery mode
+func (site *Site) getBatteryMode() api.BatteryMode {
+	site.Lock()
+	defer site.Unlock()
+	return site.batteryMode
+}
+
+// setBatteryMode sets the battery mode
+func (site *Site) setBatteryMode(batMode api.BatteryMode) {
+	site.Lock()
+	defer site.Unlock()
+	site.batteryMode = batMode
+}
+
+func (site *Site) updateBatteryMode(loadpoints []loadpoint.API) {
+	// determine expected state
+	batMode := api.BatteryNormal
+	for _, lp := range loadpoints {
+		if lp.GetStatus() == api.StatusC && (lp.GetMode() == api.ModeNow || lp.GetPlanActive()) {
+			batMode = api.BatteryLocked
+			break
+		}
+	}
+
+	if batMode == site.getBatteryMode() {
+		return
+	}
+
+	// update batteries
+	for _, meter := range site.batteryMeters {
+		if batCtrl, ok := meter.(api.BatteryController); ok {
+			if err := batCtrl.SetBatteryMode(batMode); err != nil {
+				site.log.ERROR.Println("battery mode:", err)
+				return
+			}
+		}
+	}
+
+	// update state and publish
+	site.setBatteryMode(batMode)
+	site.publish("batteryMode", batMode)
+}
diff --git a/evcc.dist.yaml b/evcc.dist.yaml
index 5ed71c87..6ecbef10 100644
--- a/evcc.dist.yaml
+++ b/evcc.dist.yaml
@@ -49,7 +49,7 @@ modbusproxy:
   #  - port: 5200
   #    uri: solar-edge:502
   #    # rtu: true
-  #    # readonly: true
+  #    # readonly: true # use `deny` to raise modbus errors
 
 # meter definitions
 # name can be freely chosen and is used as reference when assigning meters to site and loadpoints
diff --git a/go.mod b/go.mod
index 95af0e54..6536412a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,8 @@
 module github.com/evcc-io/evcc
 
-go 1.21
+go 1.21.1
+
+toolchain go1.21.3
 
 require (
 	dario.cat/mergo v1.0.0
@@ -47,6 +49,7 @@ require (
 	github.com/hashicorp/go-version v1.6.0
 	github.com/hasura/go-graphql-client v0.10.0
 	github.com/influxdata/influxdb-client-go/v2 v2.12.5-0.20231103130105-12eadbdbefaf
+	github.com/insomniacslk/tapo v0.0.0-20231102105048-52dbe7a83ca7
 	github.com/itchyny/gojq v0.12.13
 	github.com/jeremywohl/flatten v1.0.1
 	github.com/jinzhu/copier v0.4.0
@@ -63,7 +66,6 @@ require (
 	github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
 	github.com/mabunixda/wattpilot v1.6.2
 	github.com/manifoldco/promptui v0.9.0
-	github.com/mergermarket/go-pkcs7 v0.0.0-20170926155232-153b18ea13c9
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/mlnoga/rct v0.1.2-0.20230731074838-03eacb926f99
@@ -140,6 +142,7 @@ require (
 	github.com/imdario/mergo v1.0.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
+	github.com/insomniacslk/xjson v0.0.0-20231023101448-2249e546a131 // indirect
 	github.com/itchyny/timefmt-go v0.1.5 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -151,6 +154,7 @@ require (
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
+	github.com/mergermarket/go-pkcs7 v0.0.0-20170926155232-153b18ea13c9 // indirect
 	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 	github.com/miekg/dns v1.1.56 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index 442fb6a2..a37ee528 100644
--- a/go.sum
+++ b/go.sum
@@ -424,6 +424,10 @@ github.com/influxdata/influxdb-client-go/v2 v2.12.5-0.20231103130105-12eadbdbefa
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
 github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
+github.com/insomniacslk/tapo v0.0.0-20231102105048-52dbe7a83ca7 h1:HqhTgNBPK6hxSV2Ib+7bZUWNdsIlydgc3Q0T06pEbJc=
+github.com/insomniacslk/tapo v0.0.0-20231102105048-52dbe7a83ca7/go.mod h1:Ac/4L4hAA14H3yYtFk/ZfgpYZ7knJhNuNKmGl/+YDik=
+github.com/insomniacslk/xjson v0.0.0-20231023101448-2249e546a131 h1:bVGPuMhjgFtxVdQGfYnFq+EnCqArOAjLNciow/nArwE=
+github.com/insomniacslk/xjson v0.0.0-20231023101448-2249e546a131/go.mod h1:Z4EVr4bVv9LZbbje9xyZEyOLpdCOmCvr5S9BJtrdTfw=
 github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
 github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
 github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
diff --git a/meter/tapo/connection.go b/meter/tapo/connection.go
index 6a635b9d..d4a0a43c 100644
--- a/meter/tapo/connection.go
+++ b/meter/tapo/connection.go
@@ -1,49 +1,19 @@
 package tapo
 
 import (
-	"bytes"
-	"crypto/aes"
-	"crypto/cipher"
-	"crypto/rand"
-	"crypto/rsa"
-	"crypto/sha1"
-	"crypto/x509"
-	"encoding/base64"
-	"encoding/hex"
-	"encoding/json"
-	"encoding/pem"
-	"errors"
 	"fmt"
-	"net/http"
+	"net/netip"
+	"net/url"
 	"strings"
-	"time"
 
 	"github.com/evcc-io/evcc/util"
-	"github.com/evcc-io/evcc/util/request"
-	"github.com/mergermarket/go-pkcs7"
+	"github.com/insomniacslk/tapo"
 )
 
-// Tapo homepage + api reverse engineering results
-// https://www.tapo.com/de/
-// Credits to & inspired by:
-// https://k4czp3r.xyz/reverse-engineering/tp-link/tapo/2020/10/15/reverse-engineering-tp-link-tapo.html
-// https://github.com/fishbigger/TapoP100
-// https://github.com/artemvang/p100-go
-
-const Timeout = time.Second * 15
-
 // Connection is the Tapo connection
 type Connection struct {
-	*request.Helper
 	log             *util.Logger
-	URI             string
-	EncodedUser     string
-	EncodedPassword string
-	Cipher          *ConnectionCipher
-	SessionID       string
-	Token           string
-	TerminalUUID    string
-	updated         time.Time
+	plug            tapo.Plug
 	lasttodayenergy int64
 	energy          int64
 }
@@ -52,365 +22,87 @@ type Connection struct {
 // User is encoded by using MessageDigest of SHA1 which is afterwards B64 encoded.
 // Password is directly B64 encoded.
 func NewConnection(uri, user, password string) (*Connection, error) {
-	if uri == "" {
-		return nil, errors.New("missing uri")
-	}
-
-	if user == "" || password == "" {
-		return nil, fmt.Errorf("missing user or password")
-	}
-
-	for _, suffix := range []string{"/", "/app"} {
-		uri = strings.TrimSuffix(uri, suffix)
-	}
-
-	log := util.NewLogger("tapo")
-
-	// nosemgrep:go.lang.security.audit.crypto.use_of_weak_crypto.use-of-sha1
-	h := sha1.New()
-	_, err := h.Write([]byte(user))
-	userhash := hex.EncodeToString(h.Sum(nil))
-
-	conn := &Connection{
-		log:             log,
-		Helper:          request.NewHelper(log),
-		URI:             fmt.Sprintf("%s/app", util.DefaultScheme(uri, "http")),
-		EncodedUser:     base64.StdEncoding.EncodeToString([]byte(userhash)),
-		EncodedPassword: base64.StdEncoding.EncodeToString([]byte(password)),
-	}
-
-	conn.Client.Timeout = Timeout
-
-	return conn, err
-}
-
-// Login provides the Tapo device session token and MAC address (TerminalUUID).
-func (d *Connection) Login() error {
-	err := d.Handshake()
-	if err != nil {
-		return err
-	}
-
-	req := map[string]interface{}{
-		"method": "login_device",
-		"params": map[string]interface{}{
-			"username": d.EncodedUser,
-			"password": d.EncodedPassword,
-		},
-	}
-
-	res, err := d.DoSecureRequest(d.URI, req)
-	if err != nil {
-		return err
-	}
-
-	if err := d.CheckErrorCode(res.ErrorCode); err != nil {
-		return err
-	}
-
-	d.Token = res.Result.Token
-
-	deviceResponse, err := d.ExecMethod("get_device_info", false)
-	if err != nil {
-		return err
-	}
-
-	d.TerminalUUID = deviceResponse.Result.MAC
-
-	return nil
-}
-
-// Handshake provides the Tapo device session cookie and encryption cipher.
-func (d *Connection) Handshake() error {
-	privKey, pubKey, err := GenerateRSAKeys()
-	if err != nil {
-		return err
-	}
-
-	pubPEM, err := DumpRSAPEM(pubKey)
+	url, err := url.Parse(uri)
 	if err != nil {
-		return err
+		return nil, fmt.Errorf("invalid url: %s", uri)
 	}
 
-	req, err := json.Marshal(map[string]interface{}{
-		"method": "handshake",
-		"params": map[string]interface{}{
-			"key":             string(pubPEM),
-			"requestTimeMils": 0,
-		},
-	})
+	addr, err := netip.ParseAddr(url.Hostname())
 	if err != nil {
-		return err
-	}
-
-	resp, err := http.Post(d.URI, "application/json", bytes.NewBuffer(req))
-	if err != nil {
-		return err
-	}
-	defer resp.Body.Close()
-
-	var res DeviceResponse
-	if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
-		return err
-	}
-
-	if err = d.CheckErrorCode(res.ErrorCode); err != nil {
-		return err
+		return nil, fmt.Errorf("invalid ip address: %s", uri)
 	}
 
-	encryptedEncryptionKey, err := base64.StdEncoding.DecodeString(res.Result.Key)
-	if err != nil {
-		return err
-	}
-
-	encryptionKey, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, encryptedEncryptionKey)
-	if err != nil {
-		return err
+	if user == "" || password == "" {
+		return nil, fmt.Errorf("missing user or password")
 	}
 
-	d.Cipher = &ConnectionCipher{
-		Key: encryptionKey[:16],
-		Iv:  encryptionKey[16:],
-	}
+	log := util.NewLogger("tapo").Redact(user, password)
 
-	cookie := strings.Split(resp.Header.Get("Set-Cookie"), ";")
-	if len(cookie) == 0 {
-		return errors.New("missing session cookie")
+	plug := tapo.NewPlug(addr, nil)
+	if err := plug.Handshake(user, password); err != nil {
+		return nil, fmt.Errorf("login failed: %w", err)
 	}
 
-	d.SessionID = cookie[0]
-
-	return nil
-}
-
-// ExecMethod executes a Tapo device command method and provides the corresponding response.
-func (d *Connection) ExecMethod(method string, deviceOn bool) (*DeviceResponse, error) {
-	var req map[string]interface{}
-
-	switch method {
-	case "set_device_info":
-		req = map[string]interface{}{
-			"method": method,
-			"params": map[string]interface{}{
-				"device_on": deviceOn,
-			},
-			"requestTimeMils": int(time.Now().Unix() * 1000),
-			"terminalUUID":    d.TerminalUUID,
-		}
-	default:
-		req = map[string]interface{}{
-			"method":          method,
-			"requestTimeMils": int(time.Now().Unix() * 1000),
-		}
+	conn := &Connection{
+		log:  log,
+		plug: *plug,
 	}
 
-	res, err := d.DoSecureRequest(fmt.Sprintf("%s?token=%s", d.URI, d.Token), req)
+	res, err := conn.plug.GetDeviceInfo()
 	if err != nil {
 		return nil, err
 	}
+	conn.log.DEBUG.Printf("%s %s connected (fw:%s,hw:%s,mac:%s)", res.Type, res.Model, res.FWVersion, res.HWVersion, res.MAC)
 
-	if method == "get_device_info" {
-		res.Result.Nickname, err = base64Decode(res.Result.Nickname)
-		if err != nil {
-			return nil, err
-		}
-
-		res.Result.SSID, err = base64Decode(res.Result.SSID)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return res, nil
-}
-
-// ExecCmd executes a Tapo api command and provides the response
-func (d *Connection) ExecCmd(method string, enable bool) (*DeviceResponse, error) {
-	// refresh session id
-	if time.Since(d.updated) >= 600*time.Minute {
-		if err := d.Login(); err != nil {
-			return nil, err
-		}
-
-		d.updated = time.Now()
-	}
-
-	return d.ExecMethod(method, enable)
+	return conn, err
 }
 
-// CurrentPower provides current power consuption
-func (d *Connection) CurrentPower() (float64, error) {
-	resp, err := d.ExecCmd("get_energy_usage", false)
-	if err != nil {
-		return 0, err
-	}
-
-	return float64(resp.Result.Current_Power) / 1e3, nil
+// Enable implements the api.Charger interface
+func (c *Connection) Enable(enable bool) error {
+	return c.plug.SetDeviceInfo(enable)
 }
 
-// ChargedEnergy collects the daily charged energy
-func (d *Connection) ChargedEnergy() (float64, error) {
-	resp, err := d.ExecCmd("get_energy_usage", false)
+// Enabled implements the api.Charger interface
+func (c *Connection) Enabled() (bool, error) {
+	resp, err := c.plug.GetDeviceInfo()
 	if err != nil {
-		return 0, err
-	}
-
-	if resp.Result.Today_Energy > d.lasttodayenergy {
-		d.energy = d.energy + (resp.Result.Today_Energy - d.lasttodayenergy)
+		return false, err
 	}
-	d.lasttodayenergy = resp.Result.Today_Energy
 
-	return float64(d.energy) / 1000, nil
+	return resp.DeviceON, nil
 }
 
-// DoSecureRequest executes a Tapo device request by encding the request and decoding its response.
-func (d *Connection) DoSecureRequest(uri string, taporequest map[string]interface{}) (*DeviceResponse, error) {
-	payload, err := json.Marshal(taporequest)
-	if err != nil {
-		return nil, err
-	}
-
-	d.log.TRACE.Printf("request: %s", string(payload))
-
-	encryptedRequest, err := d.Cipher.Encrypt(payload)
-	if err != nil {
-		return nil, err
-	}
-
-	data := map[string]interface{}{
-		"method": "securePassthrough",
-		"params": map[string]interface{}{
-			"request": base64.StdEncoding.EncodeToString(encryptedRequest),
-		},
-	}
-
-	req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), map[string]string{
-		"Cookie": d.SessionID,
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	var res *DeviceResponse
-	if err := d.DoJSON(req, &res); err != nil {
-		return nil, err
-	}
-
-	// Login atempt in case of tapo switch connection hicups
-	if res.ErrorCode == 9999 {
-		if err := d.Login(); err != nil {
-			return nil, err
-		}
-
-		if err := d.DoJSON(req, &res); err != nil {
-			return nil, err
+// CurrentPower provides current power consuption
+func (c *Connection) CurrentPower() (float64, error) {
+	resp, err := c.plug.GetEnergyUsage()
+	if err != nil {
+		if strings.Contains(err.Error(), "-1001") {
+			c.log.DEBUG.Printf("meter not available")
+			return 0, nil
+		} else {
+			return 0, err
 		}
 	}
 
-	if err := d.CheckErrorCode(res.ErrorCode); err != nil {
-		return nil, err
-	}
-
-	decodedResponse, err := base64.StdEncoding.DecodeString(res.Result.Response)
-	if err != nil {
-		return nil, err
-	}
-
-	decryptedResponse, err := d.Cipher.Decrypt(decodedResponse)
-	if err != nil {
-		return nil, err
-	}
-
-	d.log.TRACE.Printf("decrypted result: %v", string(decryptedResponse))
-
-	var deviceResp *DeviceResponse
-	err = json.Unmarshal(decryptedResponse, &deviceResp)
-
-	return deviceResp, err
-}
-
-// Tapo helper functions
-
-func (d *Connection) CheckErrorCode(errorCode int) error {
-	errorDesc := map[int]string{
-		0:     "Success",
-		9999:  "Login failed, invalid user or password",
-		-1002: "Incorrect Request/Method",
-		-1003: "JSON formatting error ",
-		-1010: "Invalid Public Key Length",
-		-1012: "Invalid terminalUUID",
-		-1501: "Invalid Request or Credentials",
-	}
-
-	if errorCode != 0 {
-		return fmt.Errorf("tapo error %d: %s", errorCode, errorDesc[errorCode])
-	}
-
-	return nil
-}
-
-func (c *ConnectionCipher) Encrypt(payload []byte) ([]byte, error) {
-	paddedPayload, err := pkcs7.Pad(payload, aes.BlockSize)
-	if err != nil {
-		return nil, err
-	}
-
-	block, err := aes.NewCipher(c.Key)
-	if err != nil {
-		return nil, err
-	}
-
-	encrypter := cipher.NewCBCEncrypter(block, c.Iv)
-	encryptedPayload := make([]byte, len(paddedPayload))
-	encrypter.CryptBlocks(encryptedPayload, paddedPayload)
-
-	return encryptedPayload, nil
-}
-
-func (c *ConnectionCipher) Decrypt(payload []byte) ([]byte, error) {
-	block, err := aes.NewCipher(c.Key)
-	if err != nil {
-		return nil, err
-	}
-
-	encrypter := cipher.NewCBCDecrypter(block, c.Iv)
-	decryptedPayload := make([]byte, len(payload))
-
-	encrypter.CryptBlocks(decryptedPayload, payload)
-
-	return pkcs7.Unpad(decryptedPayload, aes.BlockSize)
+	return float64(resp.CurrentPower) / 1e3, nil
 }
 
-func DumpRSAPEM(pubKey *rsa.PublicKey) ([]byte, error) {
-	pubKeyPKIX, err := x509.MarshalPKIXPublicKey(pubKey)
-	if err != nil {
-		return nil, err
-	}
-
-	pubPEM := pem.EncodeToMemory(
-		&pem.Block{
-			Type:  "PUBLIC KEY",
-			Bytes: pubKeyPKIX,
-		},
-	)
-
-	return pubPEM, nil
-}
-
-func GenerateRSAKeys() (*rsa.PrivateKey, *rsa.PublicKey, error) {
-	key, err := rsa.GenerateKey(rand.Reader, 1024)
-	if err != nil {
-		return nil, nil, err
+// ChargedEnergy collects the daily charged energy
+func (c *Connection) ChargedEnergy() (float64, error) {
+	resp, err := c.plug.GetEnergyUsage()
+	if err != nil {
+		if strings.Contains(err.Error(), "-1001") {
+			c.log.DEBUG.Printf("meter not available")
+			return 0, nil
+		} else {
+			return 0, err
+		}
 	}
 
-	return key, key.Public().(*rsa.PublicKey), nil
-}
-
-func base64Decode(base64String string) (string, error) {
-	decodedString, err := base64.StdEncoding.DecodeString(base64String)
-	if err != nil {
-		return "", err
+	if int64(resp.TodayEnergy) > c.lasttodayenergy {
+		c.energy = c.energy + (int64(resp.TodayEnergy) - c.lasttodayenergy)
 	}
+	c.lasttodayenergy = int64(resp.TodayEnergy)
 
-	return string(decodedString), nil
+	return float64(c.energy) / 1000, nil
 }
diff --git a/meter/tapo/types.go b/meter/tapo/types.go
deleted file mode 100644
index 4a8027ed..00000000
--- a/meter/tapo/types.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package tapo
-
-// Tapo homepage + api reverse engineering results
-// https://www.tapo.com/de/
-// Credits to & inspired by:
-// https://k4czp3r.xyz/reverse-engineering/tp-link/tapo/2020/10/15/reverse-engineering-tp-link-tapo.html
-// https://github.com/fishbigger/TapoP100
-// https://github.com/artemvang/p100-go
-
-// Tapo connection cipher
-type ConnectionCipher struct {
-	Key []byte
-	Iv  []byte
-}
-
-// Tapo device response
-type DeviceResponse struct {
-	Result struct {
-		DeviceID           string `json:"device_id"`
-		FWVersion          string `json:"fw_ver"`
-		HWVersion          string `json:"hw_ver"`
-		Type               string `json:"type"`
-		Model              string `json:"model"`
-		MAC                string `json:"mac"`
-		HWID               string `json:"hw_id"`
-		FWID               string `json:"fw_id"`
-		OEMID              string `json:"oem_id"`
-		Specs              string `json:"specs"`
-		DeviceON           bool   `json:"device_on"`
-		OnTime             int64  `json:"on_time"`
-		OverHeated         bool   `json:"overheated"`
-		Nickname           string `json:"nickname"`
-		Location           string `json:"location"`
-		Avatar             string `json:"avatar"`
-		Longitude          int64  `json:"longitude"`
-		Latitude           int64  `json:"latitude"`
-		HasSetLocationInfo bool   `json:"has_set_location_info"`
-		IP                 string `json:"ip"`
-		SSID               string `json:"ssid"`
-		SignalLevel        int64  `json:"signal_level"`
-		RSSI               int64  `json:"rssi"`
-		Region             string `json:"Europe/Kiev"`
-		TimeDiff           int64  `json:"time_diff"`
-		Lang               string `json:"lang"`
-		Key                string `json:"key"`
-		Response           string `json:"response"`
-		Token              string `json:"token"`
-		Current_Power      int64  `json:"current_power"`
-		Today_Energy       int64  `json:"today_energy"`
-	} `json:"result"`
-	ErrorCode int `json:"error_code"`
-}
diff --git a/package-lock.json b/package-lock.json
index 4ac5d965..a5d6f5f6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,7 +36,7 @@
         "rollup-plugin-visualizer": "^5.8.3",
         "smoothscroll-polyfill": "^0.4.4",
         "vite": "^4.2.1",
-        "vite-plugin-toml": "^0.5.0",
+        "vite-plugin-toml": "^0.6.0",
         "vitest": "^0.34.4",
         "vue": "^3.2.29",
         "vue-eslint-parser": "^9.1.0",
@@ -92,28 +92,28 @@
       }
     },
     "node_modules/@babel/compat-data": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz",
-      "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz",
+      "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==",
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/core": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz",
-      "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz",
+      "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==",
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
         "@babel/code-frame": "^7.22.13",
-        "@babel/generator": "^7.23.0",
+        "@babel/generator": "^7.23.3",
         "@babel/helper-compilation-targets": "^7.22.15",
-        "@babel/helper-module-transforms": "^7.23.0",
-        "@babel/helpers": "^7.23.0",
-        "@babel/parser": "^7.23.0",
+        "@babel/helper-module-transforms": "^7.23.3",
+        "@babel/helpers": "^7.23.2",
+        "@babel/parser": "^7.23.3",
         "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.0",
-        "@babel/types": "^7.23.0",
+        "@babel/traverse": "^7.23.3",
+        "@babel/types": "^7.23.3",
         "convert-source-map": "^2.0.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
@@ -129,11 +129,11 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
-      "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz",
+      "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==",
       "dependencies": {
-        "@babel/types": "^7.23.0",
+        "@babel/types": "^7.23.3",
         "@jridgewell/gen-mapping": "^0.3.2",
         "@jridgewell/trace-mapping": "^0.3.17",
         "jsesc": "^2.5.1"
@@ -218,9 +218,9 @@
       }
     },
     "node_modules/@babel/helper-define-polyfill-provider": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz",
-      "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==",
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz",
+      "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==",
       "dependencies": {
         "@babel/helper-compilation-targets": "^7.22.6",
         "@babel/helper-plugin-utils": "^7.22.5",
@@ -286,9 +286,9 @@
       }
     },
     "node_modules/@babel/helper-module-transforms": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz",
-      "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
+      "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
       "dependencies": {
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-module-imports": "^7.22.15",
@@ -425,12 +425,12 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.23.1",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz",
-      "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==",
+      "version": "7.23.2",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz",
+      "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==",
       "dependencies": {
         "@babel/template": "^7.22.15",
-        "@babel/traverse": "^7.23.0",
+        "@babel/traverse": "^7.23.2",
         "@babel/types": "^7.23.0"
       },
       "engines": {
@@ -451,9 +451,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
-      "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
+      "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -462,9 +462,9 @@
       }
     },
     "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz",
-      "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz",
+      "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -476,13 +476,13 @@
       }
     },
     "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz",
-      "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz",
+      "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
-        "@babel/plugin-transform-optional-chaining": "^7.22.15"
+        "@babel/plugin-transform-optional-chaining": "^7.23.3"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -491,6 +491,21 @@
         "@babel/core": "^7.13.0"
       }
     },
+    "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz",
+      "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==",
+      "dependencies": {
+        "@babel/helper-environment-visitor": "^7.22.20",
+        "@babel/helper-plugin-utils": "^7.22.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
     "node_modules/@babel/plugin-proposal-private-property-in-object": {
       "version": "7.21.0-placeholder-for-preset-env.2",
       "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
@@ -561,9 +576,9 @@
       }
     },
     "node_modules/@babel/plugin-syntax-import-assertions": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz",
-      "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz",
+      "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -575,9 +590,9 @@
       }
     },
     "node_modules/@babel/plugin-syntax-import-attributes": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz",
-      "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz",
+      "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -720,9 +735,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-arrow-functions": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz",
-      "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz",
+      "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -734,13 +749,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-async-generator-functions": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz",
-      "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.3.tgz",
+      "integrity": "sha512-59GsVNavGxAXCDDbakWSMJhajASb4kBCqDjqJsv+p5nKdbz7istmZ3HrX3L2LuiI80+zsOADCvooqQH3qGCucQ==",
       "dependencies": {
-        "@babel/helper-environment-visitor": "^7.22.5",
+        "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-plugin-utils": "^7.22.5",
-        "@babel/helper-remap-async-to-generator": "^7.22.9",
+        "@babel/helper-remap-async-to-generator": "^7.22.20",
         "@babel/plugin-syntax-async-generators": "^7.8.4"
       },
       "engines": {
@@ -751,13 +766,13 @@
       }
     },
     "node_modules/@babel/plugin-transform-async-to-generator": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz",
-      "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz",
+      "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==",
       "dependencies": {
-        "@babel/helper-module-imports": "^7.22.5",
+        "@babel/helper-module-imports": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5",
-        "@babel/helper-remap-async-to-generator": "^7.22.5"
+        "@babel/helper-remap-async-to-generator": "^7.22.20"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -767,9 +782,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-block-scoped-functions": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz",
-      "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz",
+      "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -781,9 +796,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-block-scoping": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz",
-      "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.3.tgz",
+      "integrity": "sha512-QPZxHrThbQia7UdvfpaRRlq/J9ciz1J4go0k+lPBXbgaNeY7IQrBj/9ceWjvMMI07/ZBzHl/F0R/2K0qH7jCVw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -795,11 +810,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-class-properties": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz",
-      "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz",
+      "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==",
       "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.22.5",
+        "@babel/helper-create-class-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -810,11 +825,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-class-static-block": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz",
-      "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.3.tgz",
+      "integrity": "sha512-PENDVxdr7ZxKPyi5Ffc0LjXdnJyrJxyqF5T5YjlVg4a0VFfQHW0r8iAtRiDXkfHlu1wwcvdtnndGYIeJLSuRMQ==",
       "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.22.11",
+        "@babel/helper-create-class-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-class-static-block": "^7.14.5"
       },
@@ -826,17 +841,17 @@
       }
     },
     "node_modules/@babel/plugin-transform-classes": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz",
-      "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz",
+      "integrity": "sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w==",
       "dependencies": {
         "@babel/helper-annotate-as-pure": "^7.22.5",
         "@babel/helper-compilation-targets": "^7.22.15",
-        "@babel/helper-environment-visitor": "^7.22.5",
-        "@babel/helper-function-name": "^7.22.5",
+        "@babel/helper-environment-visitor": "^7.22.20",
+        "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-optimise-call-expression": "^7.22.5",
         "@babel/helper-plugin-utils": "^7.22.5",
-        "@babel/helper-replace-supers": "^7.22.9",
+        "@babel/helper-replace-supers": "^7.22.20",
         "@babel/helper-split-export-declaration": "^7.22.6",
         "globals": "^11.1.0"
       },
@@ -848,12 +863,12 @@
       }
     },
     "node_modules/@babel/plugin-transform-computed-properties": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz",
-      "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz",
+      "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
-        "@babel/template": "^7.22.5"
+        "@babel/template": "^7.22.15"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -863,9 +878,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-destructuring": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz",
-      "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz",
+      "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -877,11 +892,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-dotall-regex": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz",
-      "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz",
+      "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==",
       "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.22.5",
+        "@babel/helper-create-regexp-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -892,9 +907,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-duplicate-keys": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz",
-      "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz",
+      "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -906,9 +921,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-dynamic-import": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz",
-      "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.3.tgz",
+      "integrity": "sha512-vTG+cTGxPFou12Rj7ll+eD5yWeNl5/8xvQvF08y5Gv3v4mZQoyFf8/n9zg4q5vvCWt5jmgymfzMAldO7orBn7A==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-dynamic-import": "^7.8.3"
@@ -921,11 +936,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-exponentiation-operator": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz",
-      "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz",
+      "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==",
       "dependencies": {
-        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5",
+        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -936,9 +951,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-export-namespace-from": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz",
-      "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.3.tgz",
+      "integrity": "sha512-yCLhW34wpJWRdTxxWtFZASJisihrfyMOTOQexhVzA78jlU+dH7Dw+zQgcPepQ5F3C6bAIiblZZ+qBggJdHiBAg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
@@ -951,9 +966,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-for-of": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz",
-      "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz",
+      "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -965,12 +980,12 @@
       }
     },
     "node_modules/@babel/plugin-transform-function-name": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz",
-      "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz",
+      "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==",
       "dependencies": {
-        "@babel/helper-compilation-targets": "^7.22.5",
-        "@babel/helper-function-name": "^7.22.5",
+        "@babel/helper-compilation-targets": "^7.22.15",
+        "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -981,9 +996,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-json-strings": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz",
-      "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.3.tgz",
+      "integrity": "sha512-H9Ej2OiISIZowZHaBwF0tsJOih1PftXJtE8EWqlEIwpc7LMTGq0rPOrywKLQ4nefzx8/HMR0D3JGXoMHYvhi0A==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-json-strings": "^7.8.3"
@@ -996,9 +1011,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-literals": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz",
-      "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz",
+      "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1010,9 +1025,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-logical-assignment-operators": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz",
-      "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.3.tgz",
+      "integrity": "sha512-+pD5ZbxofyOygEp+zZAfujY2ShNCXRpDRIPOiBmTO693hhyOEteZgl876Xs9SAHPQpcV0vz8LvA/T+w8AzyX8A==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
@@ -1025,9 +1040,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-member-expression-literals": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz",
-      "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz",
+      "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1039,11 +1054,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-modules-amd": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz",
-      "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz",
+      "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==",
       "dependencies": {
-        "@babel/helper-module-transforms": "^7.23.0",
+        "@babel/helper-module-transforms": "^7.23.3",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -1054,11 +1069,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-modules-commonjs": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz",
-      "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz",
+      "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==",
       "dependencies": {
-        "@babel/helper-module-transforms": "^7.23.0",
+        "@babel/helper-module-transforms": "^7.23.3",
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/helper-simple-access": "^7.22.5"
       },
@@ -1070,12 +1085,12 @@
       }
     },
     "node_modules/@babel/plugin-transform-modules-systemjs": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz",
-      "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz",
+      "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==",
       "dependencies": {
         "@babel/helper-hoist-variables": "^7.22.5",
-        "@babel/helper-module-transforms": "^7.23.0",
+        "@babel/helper-module-transforms": "^7.23.3",
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/helper-validator-identifier": "^7.22.20"
       },
@@ -1087,11 +1102,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-modules-umd": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz",
-      "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz",
+      "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==",
       "dependencies": {
-        "@babel/helper-module-transforms": "^7.22.5",
+        "@babel/helper-module-transforms": "^7.23.3",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -1117,9 +1132,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-new-target": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz",
-      "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz",
+      "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1131,9 +1146,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz",
-      "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.3.tgz",
+      "integrity": "sha512-xzg24Lnld4DYIdysyf07zJ1P+iIfJpxtVFOzX4g+bsJ3Ng5Le7rXx9KwqKzuyaUeRnt+I1EICwQITqc0E2PmpA==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
@@ -1146,9 +1161,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-numeric-separator": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz",
-      "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.3.tgz",
+      "integrity": "sha512-s9GO7fIBi/BLsZ0v3Rftr6Oe4t0ctJ8h4CCXfPoEJwmvAPMyNrfkOOJzm6b9PX9YXcCJWWQd/sBF/N26eBiMVw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-numeric-separator": "^7.10.4"
@@ -1161,15 +1176,15 @@
       }
     },
     "node_modules/@babel/plugin-transform-object-rest-spread": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz",
-      "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.3.tgz",
+      "integrity": "sha512-VxHt0ANkDmu8TANdE9Kc0rndo/ccsmfe2Cx2y5sI4hu3AukHQ5wAu4cM7j3ba8B9548ijVyclBU+nuDQftZsog==",
       "dependencies": {
-        "@babel/compat-data": "^7.22.9",
+        "@babel/compat-data": "^7.23.3",
         "@babel/helper-compilation-targets": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-transform-parameters": "^7.22.15"
+        "@babel/plugin-transform-parameters": "^7.23.3"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1179,12 +1194,12 @@
       }
     },
     "node_modules/@babel/plugin-transform-object-super": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz",
-      "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz",
+      "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
-        "@babel/helper-replace-supers": "^7.22.5"
+        "@babel/helper-replace-supers": "^7.22.20"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1194,9 +1209,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-optional-catch-binding": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz",
-      "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.3.tgz",
+      "integrity": "sha512-LxYSb0iLjUamfm7f1D7GpiS4j0UAC8AOiehnsGAP8BEsIX8EOi3qV6bbctw8M7ZvLtcoZfZX5Z7rN9PlWk0m5A==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
@@ -1209,9 +1224,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-optional-chaining": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz",
-      "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.3.tgz",
+      "integrity": "sha512-zvL8vIfIUgMccIAK1lxjvNv572JHFJIKb4MWBz5OGdBQA0fB0Xluix5rmOby48exiJc987neOmP/m9Fnpkz3Tg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
@@ -1225,9 +1240,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-parameters": {
-      "version": "7.22.15",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz",
-      "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz",
+      "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1239,11 +1254,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-private-methods": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz",
-      "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz",
+      "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==",
       "dependencies": {
-        "@babel/helper-create-class-features-plugin": "^7.22.5",
+        "@babel/helper-create-class-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -1254,12 +1269,12 @@
       }
     },
     "node_modules/@babel/plugin-transform-private-property-in-object": {
-      "version": "7.22.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz",
-      "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.3.tgz",
+      "integrity": "sha512-a5m2oLNFyje2e/rGKjVfAELTVI5mbA0FeZpBnkOWWV7eSmKQ+T/XW0Vf+29ScLzSxX+rnsarvU0oie/4m6hkxA==",
       "dependencies": {
         "@babel/helper-annotate-as-pure": "^7.22.5",
-        "@babel/helper-create-class-features-plugin": "^7.22.11",
+        "@babel/helper-create-class-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
       },
@@ -1271,9 +1286,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-property-literals": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz",
-      "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz",
+      "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1285,9 +1300,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-regenerator": {
-      "version": "7.22.10",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz",
-      "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz",
+      "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "regenerator-transform": "^0.15.2"
@@ -1300,9 +1315,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-reserved-words": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz",
-      "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz",
+      "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1314,9 +1329,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-shorthand-properties": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz",
-      "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz",
+      "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1328,9 +1343,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-spread": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz",
-      "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz",
+      "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5"
@@ -1343,9 +1358,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-sticky-regex": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz",
-      "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz",
+      "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1357,9 +1372,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-template-literals": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz",
-      "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz",
+      "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1371,9 +1386,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-typeof-symbol": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz",
-      "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz",
+      "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1385,9 +1400,9 @@
       }
     },
     "node_modules/@babel/plugin-transform-unicode-escapes": {
-      "version": "7.22.10",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz",
-      "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz",
+      "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==",
       "dependencies": {
         "@babel/helper-plugin-utils": "^7.22.5"
       },
@@ -1399,11 +1414,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-unicode-property-regex": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz",
-      "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz",
+      "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==",
       "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.22.5",
+        "@babel/helper-create-regexp-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -1414,11 +1429,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-unicode-regex": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz",
-      "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz",
+      "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==",
       "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.22.5",
+        "@babel/helper-create-regexp-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -1429,11 +1444,11 @@
       }
     },
     "node_modules/@babel/plugin-transform-unicode-sets-regex": {
-      "version": "7.22.5",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz",
-      "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz",
+      "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==",
       "dependencies": {
-        "@babel/helper-create-regexp-features-plugin": "^7.22.5",
+        "@babel/helper-create-regexp-features-plugin": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5"
       },
       "engines": {
@@ -1444,24 +1459,25 @@
       }
     },
     "node_modules/@babel/preset-env": {
-      "version": "7.22.20",
-      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz",
-      "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.3.tgz",
+      "integrity": "sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q==",
       "dependencies": {
-        "@babel/compat-data": "^7.22.20",
+        "@babel/compat-data": "^7.23.3",
         "@babel/helper-compilation-targets": "^7.22.15",
         "@babel/helper-plugin-utils": "^7.22.5",
         "@babel/helper-validator-option": "^7.22.15",
-        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15",
-        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15",
+        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3",
+        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3",
+        "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3",
         "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
         "@babel/plugin-syntax-async-generators": "^7.8.4",
         "@babel/plugin-syntax-class-properties": "^7.12.13",
         "@babel/plugin-syntax-class-static-block": "^7.14.5",
         "@babel/plugin-syntax-dynamic-import": "^7.8.3",
         "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
-        "@babel/plugin-syntax-import-assertions": "^7.22.5",
-        "@babel/plugin-syntax-import-attributes": "^7.22.5",
+        "@babel/plugin-syntax-import-assertions": "^7.23.3",
+        "@babel/plugin-syntax-import-attributes": "^7.23.3",
         "@babel/plugin-syntax-import-meta": "^7.10.4",
         "@babel/plugin-syntax-json-strings": "^7.8.3",
         "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
@@ -1473,59 +1489,58 @@
         "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
         "@babel/plugin-syntax-top-level-await": "^7.14.5",
         "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
-        "@babel/plugin-transform-arrow-functions": "^7.22.5",
-        "@babel/plugin-transform-async-generator-functions": "^7.22.15",
-        "@babel/plugin-transform-async-to-generator": "^7.22.5",
-        "@babel/plugin-transform-block-scoped-functions": "^7.22.5",
-        "@babel/plugin-transform-block-scoping": "^7.22.15",
-        "@babel/plugin-transform-class-properties": "^7.22.5",
-        "@babel/plugin-transform-class-static-block": "^7.22.11",
-        "@babel/plugin-transform-classes": "^7.22.15",
-        "@babel/plugin-transform-computed-properties": "^7.22.5",
-        "@babel/plugin-transform-destructuring": "^7.22.15",
-        "@babel/plugin-transform-dotall-regex": "^7.22.5",
-        "@babel/plugin-transform-duplicate-keys": "^7.22.5",
-        "@babel/plugin-transform-dynamic-import": "^7.22.11",
-        "@babel/plugin-transform-exponentiation-operator": "^7.22.5",
-        "@babel/plugin-transform-export-namespace-from": "^7.22.11",
-        "@babel/plugin-transform-for-of": "^7.22.15",
-        "@babel/plugin-transform-function-name": "^7.22.5",
-        "@babel/plugin-transform-json-strings": "^7.22.11",
-        "@babel/plugin-transform-literals": "^7.22.5",
-        "@babel/plugin-transform-logical-assignment-operators": "^7.22.11",
-        "@babel/plugin-transform-member-expression-literals": "^7.22.5",
-        "@babel/plugin-transform-modules-amd": "^7.22.5",
-        "@babel/plugin-transform-modules-commonjs": "^7.22.15",
-        "@babel/plugin-transform-modules-systemjs": "^7.22.11",
-        "@babel/plugin-transform-modules-umd": "^7.22.5",
+        "@babel/plugin-transform-arrow-functions": "^7.23.3",
+        "@babel/plugin-transform-async-generator-functions": "^7.23.3",
+        "@babel/plugin-transform-async-to-generator": "^7.23.3",
+        "@babel/plugin-transform-block-scoped-functions": "^7.23.3",
+        "@babel/plugin-transform-block-scoping": "^7.23.3",
+        "@babel/plugin-transform-class-properties": "^7.23.3",
+        "@babel/plugin-transform-class-static-block": "^7.23.3",
+        "@babel/plugin-transform-classes": "^7.23.3",
+        "@babel/plugin-transform-computed-properties": "^7.23.3",
+        "@babel/plugin-transform-destructuring": "^7.23.3",
+        "@babel/plugin-transform-dotall-regex": "^7.23.3",
+        "@babel/plugin-transform-duplicate-keys": "^7.23.3",
+        "@babel/plugin-transform-dynamic-import": "^7.23.3",
+        "@babel/plugin-transform-exponentiation-operator": "^7.23.3",
+        "@babel/plugin-transform-export-namespace-from": "^7.23.3",
+        "@babel/plugin-transform-for-of": "^7.23.3",
+        "@babel/plugin-transform-function-name": "^7.23.3",
+        "@babel/plugin-transform-json-strings": "^7.23.3",
+        "@babel/plugin-transform-literals": "^7.23.3",
+        "@babel/plugin-transform-logical-assignment-operators": "^7.23.3",
+        "@babel/plugin-transform-member-expression-literals": "^7.23.3",
+        "@babel/plugin-transform-modules-amd": "^7.23.3",
+        "@babel/plugin-transform-modules-commonjs": "^7.23.3",
+        "@babel/plugin-transform-modules-systemjs": "^7.23.3",
+        "@babel/plugin-transform-modules-umd": "^7.23.3",
         "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5",
-        "@babel/plugin-transform-new-target": "^7.22.5",
-        "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11",
-        "@babel/plugin-transform-numeric-separator": "^7.22.11",
-        "@babel/plugin-transform-object-rest-spread": "^7.22.15",
-        "@babel/plugin-transform-object-super": "^7.22.5",
-        "@babel/plugin-transform-optional-catch-binding": "^7.22.11",
-        "@babel/plugin-transform-optional-chaining": "^7.22.15",
-        "@babel/plugin-transform-parameters": "^7.22.15",
-        "@babel/plugin-transform-private-methods": "^7.22.5",
-        "@babel/plugin-transform-private-property-in-object": "^7.22.11",
-        "@babel/plugin-transform-property-literals": "^7.22.5",
-        "@babel/plugin-transform-regenerator": "^7.22.10",
-        "@babel/plugin-transform-reserved-words": "^7.22.5",
-        "@babel/plugin-transform-shorthand-properties": "^7.22.5",
-        "@babel/plugin-transform-spread": "^7.22.5",
-        "@babel/plugin-transform-sticky-regex": "^7.22.5",
-        "@babel/plugin-transform-template-literals": "^7.22.5",
-        "@babel/plugin-transform-typeof-symbol": "^7.22.5",
-        "@babel/plugin-transform-unicode-escapes": "^7.22.10",
-        "@babel/plugin-transform-unicode-property-regex": "^7.22.5",
-        "@babel/plugin-transform-unicode-regex": "^7.22.5",
-        "@babel/plugin-transform-unicode-sets-regex": "^7.22.5",
+        "@babel/plugin-transform-new-target": "^7.23.3",
+        "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.3",
+        "@babel/plugin-transform-numeric-separator": "^7.23.3",
+        "@babel/plugin-transform-object-rest-spread": "^7.23.3",
+        "@babel/plugin-transform-object-super": "^7.23.3",
+        "@babel/plugin-transform-optional-catch-binding": "^7.23.3",
+        "@babel/plugin-transform-optional-chaining": "^7.23.3",
+        "@babel/plugin-transform-parameters": "^7.23.3",
+        "@babel/plugin-transform-private-methods": "^7.23.3",
+        "@babel/plugin-transform-private-property-in-object": "^7.23.3",
+        "@babel/plugin-transform-property-literals": "^7.23.3",
+        "@babel/plugin-transform-regenerator": "^7.23.3",
+        "@babel/plugin-transform-reserved-words": "^7.23.3",
+        "@babel/plugin-transform-shorthand-properties": "^7.23.3",
+        "@babel/plugin-transform-spread": "^7.23.3",
+        "@babel/plugin-transform-sticky-regex": "^7.23.3",
+        "@babel/plugin-transform-template-literals": "^7.23.3",
+        "@babel/plugin-transform-typeof-symbol": "^7.23.3",
+        "@babel/plugin-transform-unicode-escapes": "^7.23.3",
+        "@babel/plugin-transform-unicode-property-regex": "^7.23.3",
+        "@babel/plugin-transform-unicode-regex": "^7.23.3",
+        "@babel/plugin-transform-unicode-sets-regex": "^7.23.3",
         "@babel/preset-modules": "0.1.6-no-external-plugins",
-        "@babel/types": "^7.22.19",
-        "babel-plugin-polyfill-corejs2": "^0.4.5",
-        "babel-plugin-polyfill-corejs3": "^0.8.3",
-        "babel-plugin-polyfill-regenerator": "^0.5.2",
+        "babel-plugin-polyfill-corejs2": "^0.4.6",
+        "babel-plugin-polyfill-corejs3": "^0.8.5",
+        "babel-plugin-polyfill-regenerator": "^0.5.3",
         "core-js-compat": "^3.31.0",
         "semver": "^6.3.1"
       },
@@ -1555,9 +1570,9 @@
       "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
     },
     "node_modules/@babel/runtime": {
-      "version": "7.23.1",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz",
-      "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==",
+      "version": "7.23.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
+      "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -1584,18 +1599,18 @@
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.23.2",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
-      "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz",
+      "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==",
       "dependencies": {
         "@babel/code-frame": "^7.22.13",
-        "@babel/generator": "^7.23.0",
+        "@babel/generator": "^7.23.3",
         "@babel/helper-environment-visitor": "^7.22.20",
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.23.0",
-        "@babel/types": "^7.23.0",
+        "@babel/parser": "^7.23.3",
+        "@babel/types": "^7.23.3",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
       },
@@ -1604,9 +1619,9 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.23.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
-      "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz",
+      "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==",
       "dependencies": {
         "@babel/helper-string-parser": "^7.22.5",
         "@babel/helper-validator-identifier": "^7.22.20",
@@ -1671,9 +1686,9 @@
       }
     },
     "node_modules/@codemirror/language": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.1.tgz",
-      "integrity": "sha512-lWRP3Y9IUdOms6DXuBpoWwjkR7yRmnS0hKYCbSfPz9v6Em1A1UCRujAkDiCrdYfs1Z0Eu4dGtwovNPStIfkgNA==",
+      "version": "6.9.2",
+      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.2.tgz",
+      "integrity": "sha512-QGTQXSpAKDIzaSE96zNK1UfIUhPgkT1CLjh1N5qVzZuxgsEOhz5RqaN8QCIdyOQklGLx3MgHd9YrE3X3+Pl1ow==",
       "dependencies": {
         "@codemirror/state": "^6.0.0",
         "@codemirror/view": "^6.0.0",
@@ -1694,9 +1709,9 @@
       }
     },
     "node_modules/@codemirror/state": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.1.tgz",
-      "integrity": "sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw=="
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.1.tgz",
+      "integrity": "sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA=="
     },
     "node_modules/@codemirror/theme-one-dark": {
       "version": "6.1.2",
@@ -1710,9 +1725,9 @@
       }
     },
     "node_modules/@codemirror/view": {
-      "version": "6.21.3",
-      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.21.3.tgz",
-      "integrity": "sha512-8l1aSQ6MygzL4Nx7GVYhucSXvW4jQd0F6Zm3v9Dg+6nZEfwzJVqi4C2zHfDljID+73gsQrWp9TgHc81xU15O4A==",
+      "version": "6.22.0",
+      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.0.tgz",
+      "integrity": "sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==",
       "dependencies": {
         "@codemirror/state": "^6.1.4",
         "style-mod": "^4.1.0",
@@ -2064,17 +2079,17 @@
       }
     },
     "node_modules/@eslint-community/regexpp": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz",
-      "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==",
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+      "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
       "engines": {
         "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
       }
     },
     "node_modules/@eslint/eslintrc": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
-      "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
+      "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
       "dependencies": {
         "ajv": "^6.12.4",
         "debug": "^4.3.2",
@@ -2108,9 +2123,9 @@
       }
     },
     "node_modules/@eslint/js": {
-      "version": "8.51.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
-      "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
+      "version": "8.53.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz",
+      "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==",
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
@@ -2134,22 +2149,22 @@
       }
     },
     "node_modules/@histoire/app": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@histoire/app/-/app-0.17.0.tgz",
-      "integrity": "sha512-rZyuIw3Ck6RqwBSkCfIAZAzKZTclzWmLcCNtSCbUHF/wcEUbFdfAhGTW56+NhzNeURq+QxNuklHhSnE+x0PiEQ==",
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/@histoire/app/-/app-0.17.4.tgz",
+      "integrity": "sha512-I1FmkPvxL76YaUIyruW2TI0F+16TGSOGFBxfMzt0lcHOSJqyf2VYaDp0Z0FKkiQOEtR7JTWlEkYhKTALZaH0Rw==",
       "dependencies": {
-        "@histoire/controls": "^0.17.0",
-        "@histoire/shared": "^0.17.0",
-        "@histoire/vendors": "^0.17.0",
+        "@histoire/controls": "^0.17.4",
+        "@histoire/shared": "^0.17.4",
+        "@histoire/vendors": "^0.17.4",
         "@types/flexsearch": "^0.7.3",
         "flexsearch": "0.7.21",
         "shiki-es": "^0.2.0"
       }
     },
     "node_modules/@histoire/controls": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@histoire/controls/-/controls-0.17.0.tgz",
-      "integrity": "sha512-zdZB4kLthWIZxe/GxUdYM18y7lXiF13g8BMomE9d/YudY1KFkfmMKSbrNlDyM9yKiTd6ycSRJIWEXlxOSuyNrw==",
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/@histoire/controls/-/controls-0.17.4.tgz",
+      "integrity": "sha512-eHon0NF7NpvQt1azE83laK+rX2elCm5wp45Oijv1viBwD+1KfXuxlU1xQoFqeNHPyD2F2c3NYLuKUOW/tzTKDw==",
       "dependencies": {
         "@codemirror/commands": "^6.2.4",
         "@codemirror/lang-json": "^6.0.1",
@@ -2158,34 +2173,34 @@
         "@codemirror/state": "^6.2.1",
         "@codemirror/theme-one-dark": "^6.1.2",
         "@codemirror/view": "^6.16.0",
-        "@histoire/shared": "^0.17.0",
-        "@histoire/vendors": "^0.17.0"
+        "@histoire/shared": "^0.17.4",
+        "@histoire/vendors": "^0.17.4"
       }
     },
     "node_modules/@histoire/plugin-vue": {
-      "version": "0.17.1",
-      "resolved": "https://registry.npmjs.org/@histoire/plugin-vue/-/plugin-vue-0.17.1.tgz",
-      "integrity": "sha512-p9MkCy1Ohe0RkAwpTB6XcOoAhlSsONBSWorhVukwi4HnIMUsPbTjOVkLfKhALW9QtVux+7reWXRocJdLECNwGA==",
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/@histoire/plugin-vue/-/plugin-vue-0.17.4.tgz",
+      "integrity": "sha512-j0gI8dlW2xdpNDvndEkZeanvARYhZTDaCDy1FtB5GixWqoa6Z1pXKhp9urAQs1PixjxRudMGjc/V0fS00d2B1w==",
       "dependencies": {
-        "@histoire/controls": "^0.17.0",
-        "@histoire/shared": "^0.17.0",
-        "@histoire/vendors": "^0.17.0",
+        "@histoire/controls": "^0.17.4",
+        "@histoire/shared": "^0.17.4",
+        "@histoire/vendors": "^0.17.4",
         "change-case": "^4.1.2",
         "globby": "^13.2.2",
         "launch-editor": "^2.6.0",
         "pathe": "^0.2.0"
       },
       "peerDependencies": {
-        "histoire": "^0.17.0",
+        "histoire": "^0.17.4",
         "vue": "^3.2.47"
       }
     },
     "node_modules/@histoire/shared": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@histoire/shared/-/shared-0.17.0.tgz",
-      "integrity": "sha512-itdEqD+V+WNkCqhSsgoSRuoHeXBH3/bNjY98vHz/89prKeqEVMGbBbGPUpgm1IpKEYrNcLhfDAREJHnwsKxKCg==",
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/@histoire/shared/-/shared-0.17.4.tgz",
+      "integrity": "sha512-S2UDTare+ZZi+RBe+fhSJ8qbpulLQvtfeAdun87CQqycuS3lemjqZaDcNxYGeFhmNT17c0HEu6fisRrB3Vk1tw==",
       "dependencies": {
-        "@histoire/vendors": "^0.17.0",
+        "@histoire/vendors": "^0.17.4",
         "@types/fs-extra": "^9.0.13",
         "@types/markdown-it": "^12.2.3",
         "chokidar": "^3.5.3",
@@ -2197,16 +2212,16 @@
       }
     },
     "node_modules/@histoire/vendors": {
-      "version": "0.17.0",
-      "resolved": "https://registry.npmjs.org/@histoire/vendors/-/vendors-0.17.0.tgz",
-      "integrity": "sha512-HI2hkai3p5+gAoWZAGWoAdDLlu0ujfYtGhykGChTQyKAroocizRG21l/2w71cN1K+oigbAccqtxP8BkpqX74cQ=="
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/@histoire/vendors/-/vendors-0.17.4.tgz",
+      "integrity": "sha512-Tz9bGp3onic5cc1x+5NErS+Y19NxNEAPpYKab7UnPbx8iEsWqNGsbgZAAEL8CJKhOX/DiKfD7NUEQMC4AW/Kbg=="
     },
     "node_modules/@humanwhocodes/config-array": {
-      "version": "0.11.11",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
-      "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
+      "version": "0.11.13",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+      "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
       "dependencies": {
-        "@humanwhocodes/object-schema": "^1.2.1",
+        "@humanwhocodes/object-schema": "^2.0.1",
         "debug": "^4.1.1",
         "minimatch": "^3.0.5"
       },
@@ -2227,17 +2242,17 @@
       }
     },
     "node_modules/@humanwhocodes/object-schema": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
-      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+      "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw=="
     },
     "node_modules/@intlify/core-base": {
-      "version": "9.5.0",
-      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.5.0.tgz",
-      "integrity": "sha512-y3ufM1RJbI/DSmJf3lYs9ACq3S/iRvaSsE3rPIk0MGH7fp+JxU6rdryv/EYcwfcr3Y1aHFlCBir6S391hRZ57w==",
+      "version": "9.6.5",
+      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.6.5.tgz",
+      "integrity": "sha512-LzbGXiZkMWPIHnHI0g6q554S87Cmh2mmCmjytK/3pDQfjI84l+dgGoeQuKj02q7EbULRuUUgYVZVqAwEUawXGg==",
       "dependencies": {
-        "@intlify/message-compiler": "9.5.0",
-        "@intlify/shared": "9.5.0"
+        "@intlify/message-compiler": "9.6.5",
+        "@intlify/shared": "9.6.5"
       },
       "engines": {
         "node": ">= 16"
@@ -2247,11 +2262,11 @@
       }
     },
     "node_modules/@intlify/message-compiler": {
-      "version": "9.5.0",
-      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.5.0.tgz",
-      "integrity": "sha512-CAhVNfEZcOVFg0/5MNyt+OFjvs4J/ARjCj2b+54/FvFP0EDJI5lIqMTSDBE7k0atMROSP0SvWCkwu/AZ5xkK1g==",
+      "version": "9.6.5",
+      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.6.5.tgz",
+      "integrity": "sha512-WeJ499thIj0p7JaIO1V3JaJbqdqfBykS5R8fElFs5hNeotHtPAMBs4IiA+8/KGFkAbjJusgFefCq6ajP7F7+4Q==",
       "dependencies": {
-        "@intlify/shared": "9.5.0",
+        "@intlify/shared": "9.6.5",
         "source-map-js": "^1.0.2"
       },
       "engines": {
@@ -2262,9 +2277,9 @@
       }
     },
     "node_modules/@intlify/shared": {
-      "version": "9.5.0",
-      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.5.0.tgz",
-      "integrity": "sha512-tAxV14LMXZDZbu32XzLMTsowNlgJNmLwWHYzvMUl6L8gvQeoYiZONjY7AUsqZW8TOZDX9lfvF6adPkk9FSRdDA==",
+      "version": "9.6.5",
+      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.6.5.tgz",
+      "integrity": "sha512-gD7Ey47Xi4h/t6P+S04ymMSoA3wVRxGqjxuIMglwRO8POki9h164Epu2N8wk/GHXM/hR6ZGcsx2HArCCENjqSQ==",
       "engines": {
         "node": ">= 16"
       },
@@ -2328,18 +2343,18 @@
       "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.19",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
-      "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
+      "version": "0.3.20",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
+      "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
     "node_modules/@lezer/common": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz",
-      "integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw=="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.1.tgz",
+      "integrity": "sha512-aAPB9YbvZHqAW+bIwiuuTDGB4DG0sYNRObGLxud8cW7osw1ZQxfDuTZ8KQiqfZ0QJGcR34CvpTMDXEyo/+Htgg=="
     },
     "node_modules/@lezer/highlight": {
       "version": "1.1.6",
@@ -2359,9 +2374,9 @@
       }
     },
     "node_modules/@lezer/lr": {
-      "version": "1.3.13",
-      "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.13.tgz",
-      "integrity": "sha512-RLAbau/4uSzKgIKj96mI5WUtG1qtiR0Frn0Ei9zhPj8YOkHM+1Bb8SgdVvmR/aWJCFIzjo2KFnDiRZ75Xf5NdQ==",
+      "version": "1.3.14",
+      "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz",
+      "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==",
       "dependencies": {
         "@lezer/common": "^1.0.0"
       }
@@ -2451,11 +2466,11 @@
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.38.1",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
-      "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
+      "version": "1.39.0",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz",
+      "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==",
       "dependencies": {
-        "playwright": "1.38.1"
+        "playwright": "1.39.0"
       },
       "bin": {
         "playwright": "cli.js"
@@ -2479,15 +2494,24 @@
       }
     },
     "node_modules/@rollup/pluginutils": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
-      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz",
+      "integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==",
       "dependencies": {
-        "estree-walker": "^2.0.1",
-        "picomatch": "^2.2.2"
+        "@types/estree": "^1.0.0",
+        "estree-walker": "^2.0.2",
+        "picomatch": "^2.3.1"
       },
       "engines": {
-        "node": ">= 8.0.0"
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
       }
     },
     "node_modules/@sideway/address": {
@@ -2539,22 +2563,27 @@
       }
     },
     "node_modules/@types/chai": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.7.tgz",
-      "integrity": "sha512-/k+vesl92vMvMygmQrFe9Aimxi6oQXFUX9mA5HanTrKUSAMoLauSi6PNFOdRw0oeqilaW600GNx2vSaT2f8aIQ=="
+      "version": "4.3.10",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.10.tgz",
+      "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg=="
     },
     "node_modules/@types/chai-subset": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
-      "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz",
+      "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==",
       "dependencies": {
         "@types/chai": "*"
       }
     },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+    },
     "node_modules/@types/flexsearch": {
-      "version": "0.7.4",
-      "resolved": "https://registry.npmjs.org/@types/flexsearch/-/flexsearch-0.7.4.tgz",
-      "integrity": "sha512-R5ZZmhxOBquntySny8dWYLDGdPJlX9K6p6vcj1w5NWGQ33X4V8MEtdQXK5KFFR1j6o0RhbPMdLSVg5XULVn3oA=="
+      "version": "0.7.6",
+      "resolved": "https://registry.npmjs.org/@types/flexsearch/-/flexsearch-0.7.6.tgz",
+      "integrity": "sha512-H5IXcRn96/gaDmo+rDl2aJuIJsob8dgOXDqf8K0t8rWZd1AFNaaspmRsElESiU+EWE33qfbFPgI0OC/B1g9FCA=="
     },
     "node_modules/@types/fs-extra": {
       "version": "9.0.13",
@@ -2570,9 +2599,9 @@
       "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
     },
     "node_modules/@types/linkify-it": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.3.tgz",
-      "integrity": "sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g=="
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz",
+      "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw=="
     },
     "node_modules/@types/markdown-it": {
       "version": "12.2.3",
@@ -2584,18 +2613,23 @@
       }
     },
     "node_modules/@types/mdurl": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.3.tgz",
-      "integrity": "sha512-T5k6kTXak79gwmIOaDF2UUQXFbnBE0zBUzF20pz7wDYu0RQMzWg+Ml/Pz50214NsFHBITkoi5VtdjFZnJ2ijjA=="
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz",
+      "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA=="
     },
     "node_modules/@types/node": {
-      "version": "20.8.4",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
-      "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
+      "version": "20.9.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
+      "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
       "dependencies": {
-        "undici-types": "~5.25.1"
+        "undici-types": "~5.26.4"
       }
     },
+    "node_modules/@ungap/structured-clone": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+    },
     "node_modules/@vitejs/plugin-legacy": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-4.1.1.tgz",
@@ -2621,9 +2655,9 @@
       }
     },
     "node_modules/@vitejs/plugin-vue": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
-      "integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.1.tgz",
+      "integrity": "sha512-HCQG8VDFDM7YDAdcj5QI5DvUi+r6xvo9LgvYdk7LSkUNwdpempdB5horkMSZsbdey9Ywsf5aaU8kEPw9M5kREA==",
       "engines": {
         "node": "^14.18.0 || >=16.0.0"
       },
@@ -2731,49 +2765,49 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
-      "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
+      "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
       "dependencies": {
-        "@babel/parser": "^7.21.3",
-        "@vue/shared": "3.3.4",
+        "@babel/parser": "^7.23.0",
+        "@vue/shared": "3.3.8",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.0.2"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
-      "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
+      "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
       "dependencies": {
-        "@vue/compiler-core": "3.3.4",
-        "@vue/shared": "3.3.4"
+        "@vue/compiler-core": "3.3.8",
+        "@vue/shared": "3.3.8"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
-      "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
-      "dependencies": {
-        "@babel/parser": "^7.20.15",
-        "@vue/compiler-core": "3.3.4",
-        "@vue/compiler-dom": "3.3.4",
-        "@vue/compiler-ssr": "3.3.4",
-        "@vue/reactivity-transform": "3.3.4",
-        "@vue/shared": "3.3.4",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
+      "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
+      "dependencies": {
+        "@babel/parser": "^7.23.0",
+        "@vue/compiler-core": "3.3.8",
+        "@vue/compiler-dom": "3.3.8",
+        "@vue/compiler-ssr": "3.3.8",
+        "@vue/reactivity-transform": "3.3.8",
+        "@vue/shared": "3.3.8",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.0",
-        "postcss": "^8.1.10",
+        "magic-string": "^0.30.5",
+        "postcss": "^8.4.31",
         "source-map-js": "^1.0.2"
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
-      "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
+      "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
       "dependencies": {
-        "@vue/compiler-dom": "3.3.4",
-        "@vue/shared": "3.3.4"
+        "@vue/compiler-dom": "3.3.8",
+        "@vue/shared": "3.3.8"
       }
     },
     "node_modules/@vue/devtools-api": {
@@ -2782,60 +2816,60 @@
       "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
-      "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz",
+      "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
       "dependencies": {
-        "@vue/shared": "3.3.4"
+        "@vue/shared": "3.3.8"
       }
     },
     "node_modules/@vue/reactivity-transform": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
-      "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
+      "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
       "dependencies": {
-        "@babel/parser": "^7.20.15",
-        "@vue/compiler-core": "3.3.4",
-        "@vue/shared": "3.3.4",
+        "@babel/parser": "^7.23.0",
+        "@vue/compiler-core": "3.3.8",
+        "@vue/shared": "3.3.8",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.0"
+        "magic-string": "^0.30.5"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
-      "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
+      "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
       "dependencies": {
-        "@vue/reactivity": "3.3.4",
-        "@vue/shared": "3.3.4"
+        "@vue/reactivity": "3.3.8",
+        "@vue/shared": "3.3.8"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
-      "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
+      "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
       "dependencies": {
-        "@vue/runtime-core": "3.3.4",
-        "@vue/shared": "3.3.4",
-        "csstype": "^3.1.1"
+        "@vue/runtime-core": "3.3.8",
+        "@vue/shared": "3.3.8",
+        "csstype": "^3.1.2"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
-      "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
+      "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
       "dependencies": {
-        "@vue/compiler-ssr": "3.3.4",
-        "@vue/shared": "3.3.4"
+        "@vue/compiler-ssr": "3.3.8",
+        "@vue/shared": "3.3.8"
       },
       "peerDependencies": {
-        "vue": "3.3.4"
+        "vue": "3.3.8"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
-      "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
+      "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
     },
     "node_modules/@vue/test-utils": {
       "version": "2.4.1",
@@ -2866,9 +2900,9 @@
       "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
     },
     "node_modules/acorn": {
-      "version": "8.10.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
-      "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+      "version": "8.11.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
+      "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
       "bin": {
         "acorn": "bin/acorn"
       },
@@ -2894,9 +2928,9 @@
       }
     },
     "node_modules/acorn-walk": {
-      "version": "8.2.0",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
-      "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz",
+      "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==",
       "engines": {
         "node": ">=0.4.0"
       }
@@ -3090,9 +3124,9 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
-      "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz",
+      "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==",
       "dependencies": {
         "follow-redirects": "^1.15.0",
         "form-data": "^4.0.0",
@@ -3100,12 +3134,12 @@
       }
     },
     "node_modules/babel-plugin-polyfill-corejs2": {
-      "version": "0.4.5",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz",
-      "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==",
+      "version": "0.4.6",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz",
+      "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==",
       "dependencies": {
         "@babel/compat-data": "^7.22.6",
-        "@babel/helper-define-polyfill-provider": "^0.4.2",
+        "@babel/helper-define-polyfill-provider": "^0.4.3",
         "semver": "^6.3.1"
       },
       "peerDependencies": {
@@ -3113,23 +3147,23 @@
       }
     },
     "node_modules/babel-plugin-polyfill-corejs3": {
-      "version": "0.8.4",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz",
-      "integrity": "sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==",
+      "version": "0.8.6",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz",
+      "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==",
       "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.4.2",
-        "core-js-compat": "^3.32.2"
+        "@babel/helper-define-polyfill-provider": "^0.4.3",
+        "core-js-compat": "^3.33.1"
       },
       "peerDependencies": {
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
     "node_modules/babel-plugin-polyfill-regenerator": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz",
-      "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==",
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz",
+      "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==",
       "dependencies": {
-        "@babel/helper-define-polyfill-provider": "^0.4.2"
+        "@babel/helper-define-polyfill-provider": "^0.4.3"
       },
       "peerDependencies": {
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -3322,12 +3356,13 @@
       }
     },
     "node_modules/call-bind": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
-      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
+      "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
       "dependencies": {
-        "function-bind": "^1.1.1",
-        "get-intrinsic": "^1.0.2"
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.1",
+        "set-function-length": "^1.1.1"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -3351,9 +3386,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001547",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz",
-      "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==",
+      "version": "1.0.30001561",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz",
+      "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==",
       "funding": [
         {
           "type": "opencollective",
@@ -3597,9 +3632,9 @@
       "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
     },
     "node_modules/core-js": {
-      "version": "3.33.0",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.0.tgz",
-      "integrity": "sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw==",
+      "version": "3.33.2",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz",
+      "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==",
       "hasInstallScript": true,
       "funding": {
         "type": "opencollective",
@@ -3607,9 +3642,9 @@
       }
     },
     "node_modules/core-js-compat": {
-      "version": "3.33.0",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.0.tgz",
-      "integrity": "sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw==",
+      "version": "3.33.2",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.2.tgz",
+      "integrity": "sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw==",
       "dependencies": {
         "browserslist": "^4.22.1"
       },
@@ -3766,9 +3801,9 @@
       }
     },
     "node_modules/define-data-property": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz",
-      "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
+      "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
       "dependencies": {
         "get-intrinsic": "^1.2.1",
         "gopd": "^1.0.1",
@@ -3803,9 +3838,9 @@
       }
     },
     "node_modules/defu": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.2.tgz",
-      "integrity": "sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ=="
+      "version": "6.1.3",
+      "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.3.tgz",
+      "integrity": "sha512-Vy2wmG3NTkmHNg/kzpuvHhkqeIx3ODWqasgCRbKtbXEN0G+HpEEv9BtJLp7ZG1CZloFaC41Ah3ZFbq7aqCqMeQ=="
     },
     "node_modules/delayed-stream": {
       "version": "1.0.0",
@@ -3962,9 +3997,9 @@
       "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.549",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.549.tgz",
-      "integrity": "sha512-gpXfJslSi4hYDkA0mTLEpYKRv9siAgSUgZ+UWyk+J5Cttpd1ThCVwdclzIwQSclz3hYn049+M2fgrP1WpvF8xg=="
+      "version": "1.4.581",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz",
+      "integrity": "sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw=="
     },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
@@ -3991,25 +4026,25 @@
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.22.2",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz",
-      "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==",
+      "version": "1.22.3",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
+      "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==",
       "dependencies": {
         "array-buffer-byte-length": "^1.0.0",
         "arraybuffer.prototype.slice": "^1.0.2",
         "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.2",
+        "call-bind": "^1.0.5",
         "es-set-tostringtag": "^2.0.1",
         "es-to-primitive": "^1.2.1",
         "function.prototype.name": "^1.1.6",
-        "get-intrinsic": "^1.2.1",
+        "get-intrinsic": "^1.2.2",
         "get-symbol-description": "^1.0.0",
         "globalthis": "^1.0.3",
         "gopd": "^1.0.1",
-        "has": "^1.0.3",
         "has-property-descriptors": "^1.0.0",
         "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
+        "hasown": "^2.0.0",
         "internal-slot": "^1.0.5",
         "is-array-buffer": "^3.0.2",
         "is-callable": "^1.2.7",
@@ -4019,7 +4054,7 @@
         "is-string": "^1.0.7",
         "is-typed-array": "^1.1.12",
         "is-weakref": "^1.0.2",
-        "object-inspect": "^1.12.3",
+        "object-inspect": "^1.13.1",
         "object-keys": "^1.1.1",
         "object.assign": "^4.1.4",
         "regexp.prototype.flags": "^1.5.1",
@@ -4033,7 +4068,7 @@
         "typed-array-byte-offset": "^1.0.0",
         "typed-array-length": "^1.0.4",
         "unbox-primitive": "^1.0.2",
-        "which-typed-array": "^1.1.11"
+        "which-typed-array": "^1.1.13"
       },
       "engines": {
         "node": ">= 0.4"
@@ -4043,24 +4078,24 @@
       }
     },
     "node_modules/es-set-tostringtag": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
-      "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz",
+      "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==",
       "dependencies": {
-        "get-intrinsic": "^1.1.3",
-        "has": "^1.0.3",
-        "has-tostringtag": "^1.0.0"
+        "get-intrinsic": "^1.2.2",
+        "has-tostringtag": "^1.0.0",
+        "hasown": "^2.0.0"
       },
       "engines": {
         "node": ">= 0.4"
       }
     },
     "node_modules/es-shim-unscopables": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
-      "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+      "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
       "dependencies": {
-        "has": "^1.0.3"
+        "hasown": "^2.0.0"
       }
     },
     "node_modules/es-to-primitive": {
@@ -4157,17 +4192,18 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.51.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
-      "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
+      "version": "8.53.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz",
+      "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.2",
-        "@eslint/js": "8.51.0",
-        "@humanwhocodes/config-array": "^0.11.11",
+        "@eslint/eslintrc": "^2.1.3",
+        "@eslint/js": "8.53.0",
+        "@humanwhocodes/config-array": "^0.11.13",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@nodelib/fs.walk": "^1.2.8",
+        "@ungap/structured-clone": "^1.2.0",
         "ajv": "^6.12.4",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
@@ -4281,25 +4317,25 @@
       }
     },
     "node_modules/eslint-plugin-import": {
-      "version": "2.28.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz",
-      "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==",
-      "dependencies": {
-        "array-includes": "^3.1.6",
-        "array.prototype.findlastindex": "^1.2.2",
-        "array.prototype.flat": "^1.3.1",
-        "array.prototype.flatmap": "^1.3.1",
+      "version": "2.29.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz",
+      "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==",
+      "dependencies": {
+        "array-includes": "^3.1.7",
+        "array.prototype.findlastindex": "^1.2.3",
+        "array.prototype.flat": "^1.3.2",
+        "array.prototype.flatmap": "^1.3.2",
         "debug": "^3.2.7",
         "doctrine": "^2.1.0",
-        "eslint-import-resolver-node": "^0.3.7",
+        "eslint-import-resolver-node": "^0.3.9",
         "eslint-module-utils": "^2.8.0",
-        "has": "^1.0.3",
-        "is-core-module": "^2.13.0",
+        "hasown": "^2.0.0",
+        "is-core-module": "^2.13.1",
         "is-glob": "^4.0.3",
         "minimatch": "^3.1.2",
-        "object.fromentries": "^2.0.6",
-        "object.groupby": "^1.0.0",
-        "object.values": "^1.1.6",
+        "object.fromentries": "^2.0.7",
+        "object.groupby": "^1.0.1",
+        "object.values": "^1.1.7",
         "semver": "^6.3.1",
         "tsconfig-paths": "^3.14.2"
       },
@@ -4349,9 +4385,9 @@
       }
     },
     "node_modules/eslint-plugin-prettier": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz",
-      "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==",
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
+      "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
       "dependencies": {
         "prettier-linter-helpers": "^1.0.0",
         "synckit": "^0.8.5"
@@ -4388,9 +4424,9 @@
       }
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "9.17.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.17.0.tgz",
-      "integrity": "sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==",
+      "version": "9.18.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.18.1.tgz",
+      "integrity": "sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "natural-compare": "^1.4.0",
@@ -4700,9 +4736,9 @@
       "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
     },
     "node_modules/fast-glob": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
-      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
         "@nodelib/fs.walk": "^1.2.3",
@@ -4905,9 +4941,12 @@
       }
     },
     "node_modules/function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
     "node_modules/function.prototype.name": {
       "version": "1.1.6",
@@ -4959,14 +4998,14 @@
       }
     },
     "node_modules/get-intrinsic": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
-      "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
+      "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
       "dependencies": {
-        "function-bind": "^1.1.1",
-        "has": "^1.0.3",
+        "function-bind": "^1.1.2",
         "has-proto": "^1.0.1",
-        "has-symbols": "^1.0.3"
+        "has-symbols": "^1.0.3",
+        "hasown": "^2.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -5142,9 +5181,9 @@
       }
     },
     "node_modules/happy-dom": {
-      "version": "12.9.1",
-      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-12.9.1.tgz",
-      "integrity": "sha512-UvQ3IwKn1G3iiNCdTrhijdLGqf8Vj7d3OpmYcPwlKakjFy83oYbW6TmOKDLMTVLO9whmOC1HIpS09wf/14k7cA==",
+      "version": "12.10.3",
+      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-12.10.3.tgz",
+      "integrity": "sha512-JzUXOh0wdNGY54oKng5hliuBkq/+aT1V3YpTM+lrN/GoLQTANZsMaIvmHiHe612rauHvPJnDZkZ+5GZR++1Abg==",
       "dependencies": {
         "css.escape": "^1.5.1",
         "entities": "^4.5.0",
@@ -5165,14 +5204,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/has": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
-      "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==",
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
     "node_modules/has-bigints": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -5190,11 +5221,11 @@
       }
     },
     "node_modules/has-property-descriptors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
-      "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
+      "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
       "dependencies": {
-        "get-intrinsic": "^1.1.1"
+        "get-intrinsic": "^1.2.2"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -5236,6 +5267,17 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/hasown": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
+      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/header-case": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz",
@@ -5246,15 +5288,15 @@
       }
     },
     "node_modules/histoire": {
-      "version": "0.17.2",
-      "resolved": "https://registry.npmjs.org/histoire/-/histoire-0.17.2.tgz",
-      "integrity": "sha512-Mz+4AWBs+P2TeopXJ0qG4ZsrpuLggNPNF3yG4Q8yg8kXcd6a9DXUWuEElYbglod/xP5XB8efaDy2nibLTybJzg==",
+      "version": "0.17.4",
+      "resolved": "https://registry.npmjs.org/histoire/-/histoire-0.17.4.tgz",
+      "integrity": "sha512-B0cRe4HlsBLziI1DaPfySvRAJQR0x0TAdEeX1gxTLXYIwq7jFoD1OrXmqHjfwCJQtJzML7WaT851RewtMLCXZw==",
       "dependencies": {
         "@akryum/tinypool": "^0.3.1",
-        "@histoire/app": "^0.17.0",
-        "@histoire/controls": "^0.17.0",
-        "@histoire/shared": "^0.17.0",
-        "@histoire/vendors": "^0.17.0",
+        "@histoire/app": "^0.17.4",
+        "@histoire/controls": "^0.17.4",
+        "@histoire/shared": "^0.17.4",
+        "@histoire/vendors": "^0.17.4",
         "@types/flexsearch": "^0.7.3",
         "@types/markdown-it": "^12.2.3",
         "birpc": "^0.1.1",
@@ -5275,7 +5317,7 @@
         "markdown-it-emoji": "^2.0.2",
         "micromatch": "^4.0.5",
         "mrmime": "^1.0.1",
-        "pathe": "^0.2.0",
+        "pathe": "^1.1.1",
         "picocolors": "^1.0.0",
         "sade": "^1.8.1",
         "shiki-es": "^0.2.0",
@@ -5289,6 +5331,11 @@
         "vite": "^2.9.0 || ^3.0.0 || ^4.0.0"
       }
     },
+    "node_modules/histoire/node_modules/pathe": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz",
+      "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q=="
+    },
     "node_modules/html-encoding-sniffer": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
@@ -5418,12 +5465,12 @@
       "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
     },
     "node_modules/internal-slot": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
-      "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
+      "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
       "dependencies": {
-        "get-intrinsic": "^1.2.0",
-        "has": "^1.0.3",
+        "get-intrinsic": "^1.2.2",
+        "hasown": "^2.0.0",
         "side-channel": "^1.0.4"
       },
       "engines": {
@@ -5492,11 +5539,11 @@
       }
     },
     "node_modules/is-core-module": {
-      "version": "2.13.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
-      "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+      "version": "2.13.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+      "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
       "dependencies": {
-        "has": "^1.0.3"
+        "hasown": "^2.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -5754,9 +5801,9 @@
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
     },
     "node_modules/jiti": {
-      "version": "1.20.0",
-      "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz",
-      "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==",
+      "version": "1.21.0",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
+      "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
       "bin": {
         "jiti": "bin/jiti.js"
       }
@@ -5996,11 +6043,11 @@
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
     },
     "node_modules/loupe": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
-      "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
+      "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
       "dependencies": {
-        "get-func-name": "^2.0.0"
+        "get-func-name": "^2.0.1"
       }
     },
     "node_modules/lower-case": {
@@ -6020,9 +6067,9 @@
       }
     },
     "node_modules/magic-string": {
-      "version": "0.30.4",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz",
-      "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==",
+      "version": "0.30.5",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
+      "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
       "dependencies": {
         "@jridgewell/sourcemap-codec": "^1.4.15"
       },
@@ -6216,9 +6263,9 @@
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "node_modules/nanoid": {
-      "version": "3.3.6",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
-      "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
       "funding": [
         {
           "type": "github",
@@ -6315,9 +6362,9 @@
       "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ=="
     },
     "node_modules/object-inspect": {
-      "version": "1.12.3",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
-      "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+      "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -6622,11 +6669,11 @@
       "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q=="
     },
     "node_modules/playwright": {
-      "version": "1.38.1",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
-      "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
+      "version": "1.39.0",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz",
+      "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==",
       "dependencies": {
-        "playwright-core": "1.38.1"
+        "playwright-core": "1.39.0"
       },
       "bin": {
         "playwright": "cli.js"
@@ -6639,9 +6686,9 @@
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.38.1",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
-      "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
+      "version": "1.39.0",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
+      "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
       "bin": {
         "playwright-core": "cli.js"
       },
@@ -6786,9 +6833,9 @@
       "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
     },
     "node_modules/punycode": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
-      "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
       "engines": {
         "node": ">=6"
       }
@@ -6966,9 +7013,9 @@
       "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
     },
     "node_modules/resolve": {
-      "version": "1.22.6",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
-      "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==",
+      "version": "1.22.8",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
       "dependencies": {
         "is-core-module": "^2.13.0",
         "path-parse": "^1.0.7",
@@ -7292,6 +7339,20 @@
         "upper-case-first": "^2.0.2"
       }
     },
+    "node_modules/set-function-length": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
+      "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+      "dependencies": {
+        "define-data-property": "^1.1.1",
+        "get-intrinsic": "^1.2.1",
+        "gopd": "^1.0.1",
+        "has-property-descriptors": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/set-function-name": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
@@ -7620,9 +7681,9 @@
       "integrity": "sha512-1TlOwvKWdXxAY9vba+huLu99zrQURDWA8pUTYsRIYDZYQbGyK+pyEP4h4dlySsqo7ozyJBmYD20F+iUHhAltEg=="
     },
     "node_modules/terser": {
-      "version": "5.21.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz",
-      "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==",
+      "version": "5.24.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz",
+      "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==",
       "peer": true,
       "dependencies": {
         "@jridgewell/source-map": "^0.3.3",
@@ -7708,9 +7769,9 @@
       }
     },
     "node_modules/toml-eslint-parser": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.4.0.tgz",
-      "integrity": "sha512-lwMC8RPBoAksoMFpQnB4c3zz+o9JPv1sxsduVH9AHb2o4kxQl7mtP5YaYoS33+6IdVrzHAXxi4JgowJDERoynw==",
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.6.1.tgz",
+      "integrity": "sha512-7xjjVOdu0c6GpaP2AmA48ZcjesBL7KB2qeMNz93gMG76yV/lHVzQiSlD6HqwAdMJiL9hM44fung0NzhjTfihtw==",
       "dependencies": {
         "eslint-visitor-keys": "^3.0.0"
       },
@@ -7917,9 +7978,9 @@
       }
     },
     "node_modules/undici-types": {
-      "version": "5.25.3",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
-      "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA=="
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
     },
     "node_modules/unicode-canonical-property-names-ecmascript": {
       "version": "2.0.0",
@@ -7958,9 +8019,9 @@
       }
     },
     "node_modules/universalify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
-      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
       "engines": {
         "node": ">= 10.0.0"
       }
@@ -8057,9 +8118,9 @@
       }
     },
     "node_modules/vite": {
-      "version": "4.4.11",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
-      "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
+      "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
       "dependencies": {
         "esbuild": "^0.18.10",
         "postcss": "^8.4.27",
@@ -8138,18 +8199,18 @@
       "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q=="
     },
     "node_modules/vite-plugin-toml": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/vite-plugin-toml/-/vite-plugin-toml-0.5.0.tgz",
-      "integrity": "sha512-68KbihVlcXzw7iUpQkuedjjrmHrEB3U6kfnW2dhFossK20wAt4nruNfI1Sn16DXnCmaA1McX+NNFlBW2ydwMnw==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/vite-plugin-toml/-/vite-plugin-toml-0.6.0.tgz",
+      "integrity": "sha512-q/SkISnZPwN5DB+Y6mw4hl5NkYkAZcmn3DbXGTW4JnFIrSN8ZznZR1r/P1N3eylY/XibYDR0oKw0YfHp4vLlOg==",
       "dependencies": {
-        "@rollup/pluginutils": "^4.2.1",
-        "toml-eslint-parser": "^0.4.0"
+        "@rollup/pluginutils": "^5.0.5",
+        "toml-eslint-parser": "^0.6.0"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^14.17.0 || >=16.0.0"
       },
       "peerDependencies": {
-        "rollup": "^2.77.0 || ^3.0.0"
+        "rollup": "^2.77.0 || ^3.0.0 || ^4.0.0"
       }
     },
     "node_modules/vitest": {
@@ -8234,15 +8295,23 @@
       "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q=="
     },
     "node_modules/vue": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
-      "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz",
+      "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
       "dependencies": {
-        "@vue/compiler-dom": "3.3.4",
-        "@vue/compiler-sfc": "3.3.4",
-        "@vue/runtime-dom": "3.3.4",
-        "@vue/server-renderer": "3.3.4",
-        "@vue/shared": "3.3.4"
+        "@vue/compiler-dom": "3.3.8",
+        "@vue/compiler-sfc": "3.3.8",
+        "@vue/runtime-dom": "3.3.8",
+        "@vue/server-renderer": "3.3.8",
+        "@vue/shared": "3.3.8"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
       }
     },
     "node_modules/vue-component-type-helpers": {
@@ -8309,12 +8378,12 @@
       "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog=="
     },
     "node_modules/vue-i18n": {
-      "version": "9.5.0",
-      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.5.0.tgz",
-      "integrity": "sha512-NiI3Ph1qMstNf7uhYh8trQBOBFLxeJgcOxBq51pCcZ28Vs18Y7BDS58r8HGDKCYgXdLUYqPDXdKatIF4bvBVZg==",
+      "version": "9.6.5",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.6.5.tgz",
+      "integrity": "sha512-dpUEjKHg7pEsaS7ZPPxp1CflaR7bGmsvZJEhnszHPKl9OTNyno5j/DvMtMSo41kpddq4felLA7GK2prjpnXVlw==",
       "dependencies": {
-        "@intlify/core-base": "9.5.0",
-        "@intlify/shared": "9.5.0",
+        "@intlify/core-base": "9.6.5",
+        "@intlify/shared": "9.6.5",
         "@vue/devtools-api": "^6.5.0"
       },
       "engines": {
@@ -8366,15 +8435,15 @@
       }
     },
     "node_modules/wait-on": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz",
-      "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.1.0.tgz",
+      "integrity": "sha512-U7TF/OYYzAg+OoiT/B8opvN48UHt0QYMi4aD3PjRFpybQ+o6czQF8Ig3SKCCMJdxpBrCalIJ4O00FBof27Fu9Q==",
       "dependencies": {
         "axios": "^0.27.2",
-        "joi": "^17.7.0",
+        "joi": "^17.11.0",
         "lodash": "^4.17.21",
-        "minimist": "^1.2.7",
-        "rxjs": "^7.8.0"
+        "minimist": "^1.2.8",
+        "rxjs": "^7.8.1"
       },
       "bin": {
         "wait-on": "bin/wait-on"
@@ -8472,12 +8541,12 @@
       }
     },
     "node_modules/which-typed-array": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz",
-      "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==",
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
+      "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
       "dependencies": {
         "available-typed-arrays": "^1.0.5",
-        "call-bind": "^1.0.2",
+        "call-bind": "^1.0.4",
         "for-each": "^0.3.3",
         "gopd": "^1.0.1",
         "has-tostringtag": "^1.0.0"
diff --git a/package.json b/package.json
index cf45edc7..bf23ae51 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "rollup-plugin-visualizer": "^5.8.3",
     "smoothscroll-polyfill": "^0.4.4",
     "vite": "^4.2.1",
-    "vite-plugin-toml": "^0.5.0",
+    "vite-plugin-toml": "^0.6.0",
     "vitest": "^0.34.4",
     "vue": "^3.2.29",
     "vue-eslint-parser": "^9.1.0",
diff --git a/server/modbus/handler.go b/server/modbus/handler.go
index 035ef9fa..caae01aa 100644
--- a/server/modbus/handler.go
+++ b/server/modbus/handler.go
@@ -13,7 +13,7 @@ import (
 
 type handler struct {
 	log      *util.Logger
-	readOnly bool
+	readOnly ReadOnlyMode
 	conn     *modbus.Connection
 }
 
@@ -104,8 +104,13 @@ func (h *handler) HandleDiscreteInputs(req *mbserver.DiscreteInputsRequest) ([]b
 
 func (h *handler) HandleCoils(req *mbserver.CoilsRequest) ([]bool, error) {
 	if req.IsWrite {
-		if h.readOnly {
+		switch h.readOnly {
+		case ReadOnlyDeny:
+			h.log.TRACE.Printf("deny: write coils: id %d addr %d qty %d val %v", req.UnitId, req.Addr, req.Quantity, req.Args)
 			return nil, mbserver.ErrIllegalFunction
+		case ReadOnlyTrue:
+			h.log.TRACE.Printf("ignore: write coils: id %d addr %d qty %d val %v", req.UnitId, req.Addr, req.Quantity, req.Args)
+			return req.Args, nil
 		}
 
 		if req.WriteFuncCode == gridx.FuncCodeWriteSingleCoil {
@@ -138,8 +143,13 @@ func (h *handler) HandleInputRegisters(req *mbserver.InputRegistersRequest) ([]u
 
 func (h *handler) HandleHoldingRegisters(req *mbserver.HoldingRegistersRequest) ([]uint16, error) {
 	if req.IsWrite {
-		if h.readOnly {
+		switch h.readOnly {
+		case ReadOnlyDeny:
+			h.log.TRACE.Printf("deny: write holdings: id %d addr %d qty %d val %0x", req.UnitId, req.Addr, req.Quantity, asBytes(req.Args))
 			return nil, mbserver.ErrIllegalFunction
+		case ReadOnlyTrue:
+			h.log.TRACE.Printf("ignore: write holdings: id %d addr %d qty %d val %0x", req.UnitId, req.Addr, req.Quantity, asBytes(req.Args))
+			return req.Args, nil
 		}
 
 		if req.WriteFuncCode == gridx.FuncCodeWriteSingleRegister {
diff --git a/server/modbus/proxy.go b/server/modbus/proxy.go
index dc964bad..096b6d7a 100644
--- a/server/modbus/proxy.go
+++ b/server/modbus/proxy.go
@@ -11,7 +11,7 @@ import (
 	"github.com/evcc-io/evcc/util/sponsor"
 )
 
-func StartProxy(port int, config modbus.Settings, readOnly bool) error {
+func StartProxy(port int, config modbus.Settings, readOnly ReadOnlyMode) error {
 	conn, err := modbus.NewConnection(config.URI, config.Device, config.Comset, config.Baudrate, modbus.ProtocolFromRTU(config.RTU), config.ID)
 	if err != nil {
 		return err
diff --git a/server/modbus/readonlymode.go b/server/modbus/readonlymode.go
new file mode 100644
index 00000000..c7b5d732
--- /dev/null
+++ b/server/modbus/readonlymode.go
@@ -0,0 +1,11 @@
+package modbus
+
+// go:generate enumer -type ReadOnlyMode -trimprefix ReadOnly -transform=lower
+
+type ReadOnlyMode int
+
+const (
+	ReadOnlyFalse ReadOnlyMode = iota
+	ReadOnlyDeny
+	ReadOnlyTrue
+)
diff --git a/server/modbus/readonlymode_enumer.go b/server/modbus/readonlymode_enumer.go
new file mode 100644
index 00000000..f19ea5ed
--- /dev/null
+++ b/server/modbus/readonlymode_enumer.go
@@ -0,0 +1,90 @@
+// Code generated by "enumer -type ReadOnlyMode -trimprefix ReadOnly -transform=lower"; DO NOT EDIT.
+
+package modbus
+
+import (
+	"fmt"
+	"strings"
+)
+
+const _ReadOnlyModeName = "falsedenytrue"
+
+var _ReadOnlyModeIndex = [...]uint8{0, 5, 9, 13}
+
+const _ReadOnlyModeLowerName = "falsedenytrue"
+
+func (i ReadOnlyMode) String() string {
+	if i < 0 || i >= ReadOnlyMode(len(_ReadOnlyModeIndex)-1) {
+		return fmt.Sprintf("ReadOnlyMode(%d)", i)
+	}
+	return _ReadOnlyModeName[_ReadOnlyModeIndex[i]:_ReadOnlyModeIndex[i+1]]
+}
+
+// An "invalid array index" compiler error signifies that the constant values have changed.
+// Re-run the stringer command to generate them again.
+func _ReadOnlyModeNoOp() {
+	var x [1]struct{}
+	_ = x[ReadOnlyFalse-(0)]
+	_ = x[ReadOnlyDeny-(1)]
+	_ = x[ReadOnlyTrue-(2)]
+}
+
+var _ReadOnlyModeValues = []ReadOnlyMode{ReadOnlyFalse, ReadOnlyDeny, ReadOnlyTrue}
+
+var _ReadOnlyModeNameToValueMap = map[string]ReadOnlyMode{
+	_ReadOnlyModeName[0:5]:       ReadOnlyFalse,
+	_ReadOnlyModeLowerName[0:5]:  ReadOnlyFalse,
+	_ReadOnlyModeName[5:9]:       ReadOnlyDeny,
+	_ReadOnlyModeLowerName[5:9]:  ReadOnlyDeny,
+	_ReadOnlyModeName[9:13]:      ReadOnlyTrue,
+	_ReadOnlyModeLowerName[9:13]: ReadOnlyTrue,
+}
+
+var _ReadOnlyModeNames = []string{
+	_ReadOnlyModeName[0:5],
+	_ReadOnlyModeName[5:9],
+	_ReadOnlyModeName[9:13],
+}
+
+// ReadOnlyModeString retrieves an enum value from the enum constants string name.
+// Throws an error if the param is not part of the enum.
+func ReadOnlyModeString(s string) (ReadOnlyMode, error) {
+	if val, ok := _ReadOnlyModeNameToValueMap[s]; ok {
+		return val, nil
+	}
+
+	if val, ok := _ReadOnlyModeNameToValueMap[strings.ToLower(s)]; ok {
+		return val, nil
+	}
+
+	switch s {
+	case "", "0":
+		return ReadOnlyFalse, nil
+	case "1":
+		return ReadOnlyTrue, nil
+	}
+
+	return 0, fmt.Errorf("%s does not belong to ReadOnlyMode values", s)
+}
+
+// ReadOnlyModeValues returns all values of the enum
+func ReadOnlyModeValues() []ReadOnlyMode {
+	return _ReadOnlyModeValues
+}
+
+// ReadOnlyModeStrings returns a slice of all String values of the enum
+func ReadOnlyModeStrings() []string {
+	strs := make([]string, len(_ReadOnlyModeNames))
+	copy(strs, _ReadOnlyModeNames)
+	return strs
+}
+
+// IsAReadOnlyMode returns "true" if the value is listed in the enum definition. "false" otherwise
+func (i ReadOnlyMode) IsAReadOnlyMode() bool {
+	for _, v := range _ReadOnlyModeValues {
+		if i == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/templates/definition/meter/deye-hybrid.yaml b/templates/definition/meter/deye-hybrid-3p.yaml
similarity index 71%
rename from templates/definition/meter/deye-hybrid.yaml
rename to templates/definition/meter/deye-hybrid-3p.yaml
index d5fdffd1..950763a5 100644
--- a/templates/definition/meter/deye-hybrid.yaml
+++ b/templates/definition/meter/deye-hybrid-3p.yaml
@@ -1,11 +1,12 @@
-template: deye-hybrid
+template: deye-hybrid-3p
+covers: ["deye-hybrid"]
 products:
   - brand: Deye
     description:
-      generic: SUN-(5/6/8/10/12)K-SG04LP3-EU series hybrid inverter
+      generic: 3p hybrid inverter
   - brand: Sunsynk
     description:
-      generic: SYNK-(8/10/12)K-SG04LP3 *untested*
+      generic: 3p hybrid inverter
 params:
   - name: usage
     choice: ["grid", "pv", "battery"]
@@ -22,14 +23,14 @@ render: |
   power:
     source: modbus
     {{- include "modbus" . | indent 2 }}
-    register: # manual non-sunspec register configuration
+    register:
       address: 625 # Grid side total power
       type: holding
       decode: int16
   energy:
     source: modbus
     {{- include "modbus" . | indent 2 }}
-    register: # manual non-sunspec register configuration
+    register:
       address: 522 # "Total_GridBuy_Power Wh"
       type: holding
       decode: uint32
@@ -37,21 +38,21 @@ render: |
   currents:
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 613 # "Out-of-grid - current A"
         type: holding
         decode: int16
       scale: 0.01
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 614 # "Out-of-grid - current B"
         type: holding
         decode: int16
       scale: 0.01
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 615 # "Out-of-grid - current C"
         type: holding
         decode: int16
@@ -63,32 +64,32 @@ render: |
     add:
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 672 # "PV1 input power"
         type: holding
-        decode: int16
+        decode: uint16
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 673 # "PV2 input power"
         type: holding
-        decode: int16
+        decode: uint16
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 674 # "PV3 input power"
         type: holding
-        decode: int16
+        decode: uint16
     - source: modbus
       {{- include "modbus" . | indent 4 }}
-      register: # manual non-sunspec register configuration
+      register:
         address: 675 # "PV4 input power"
         type: holding
-        decode: int16
+        decode: uint16
   energy:
     source: modbus
     {{- include "modbus" . | indent 2 }}
-    register: # manual non-sunspec register configuration
+    register:
       address: 534 # "Total_PV_Power_Wh"
       type: holding
       decode: uint32
@@ -98,24 +99,24 @@ render: |
   power:
     source: modbus
     {{- include "modbus" . | indent 2 }}
-    register: # manual non-sunspec register configuration
+    register:
       address: 590 # "Battery output power"
       type: holding
       decode: int16
   soc:
     source: modbus
     {{- include "modbus" . | indent 2 }}
-    register: # manual non-sunspec register configuration
+    register:
       address: 588 # "battery capacity"
       type: holding
-      decode: int16
+      decode: uint16
   {{- if .capacity }}
   capacity: {{ .capacity }} # kWh
   {{- end }}
   energy:
     source: modbus
     {{- include "modbus" . | indent 2 }}
-    register: # manual non-sunspec register configuration
+    register:
       address: 518 # "Total discharge of the battery (Wh)"
       type: holding
       decode: uint32
diff --git a/templates/definition/meter/deye-mi.yaml b/templates/definition/meter/deye-mi.yaml
new file mode 100644
index 00000000..00f9ee00
--- /dev/null
+++ b/templates/definition/meter/deye-mi.yaml
@@ -0,0 +1,42 @@
+template: deye-mi
+products:
+  - brand: Deye
+    description:
+      generic: Micro inverter
+  - brand: Bosswerk
+    description:
+      generic: Micro inverter
+  - brand: Anker
+    description:
+      generic: Micro inverter
+  - brand: Sunsynk
+    description:
+      generic: Micro inverter
+params:
+  - name: usage
+    choice: ["pv"]
+  - name: modbus
+    choice: ["rs485", "tcpip"]
+    baudrate: 9600
+    id: 1
+    advanced: true
+render: |
+  type: custom
+  {{- if eq .usage "pv" }}
+  power:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 86 # "Output active power"
+      type: holding
+      decode: uint32
+    scale: 0.1
+  energy:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 63 # "Total_Active_PowerWh"
+      type: holding
+      decode: uint32
+    scale: 0.1
+  {{- end }}
diff --git a/templates/definition/meter/deye-storage.yaml b/templates/definition/meter/deye-storage.yaml
new file mode 100644
index 00000000..6a90dee7
--- /dev/null
+++ b/templates/definition/meter/deye-storage.yaml
@@ -0,0 +1,109 @@
+template: deye-storage
+products:
+  - brand: Deye
+    description:
+      generic: Storage (hybrid) inverter
+  - brand: Sunsynk
+    description:
+      generic: Storage (hybrid) inverter
+params:
+  - name: usage
+    choice: ["pv", "battery", "grid"]
+  - name: modbus
+    choice: ["rs485", "tcpip"]
+    baudrate: 9600
+    id: 1
+  - name: capacity
+    advanced: true
+render: |
+  type: custom
+  {{- if eq .usage "pv" }}
+  power:
+    source: calc
+    add:
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 186 # "PV1 input power"
+        type: holding
+        decode: uint16
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 187 # "PV2 input power"
+        type: holding
+        decode: uint16
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 188 # "PV3 input power"
+        type: holding
+        decode: uint16
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 189 # "PV4 input power"
+        type: holding
+        decode: uint16
+  energy:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 96 # "historyPV PowerWh"
+      type: holding
+      decode: uint32
+    scale: 0.1
+  {{- end }}
+  {{- if eq .usage "battery" }}
+  power:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 190 # "Battery output power"
+      type: holding
+      decode: int16
+  energy:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 74 # "Battery cumulative discharge"
+      type: holding
+      decode: uint32
+    scale: 0.1
+  soc:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 184 # "battery capacity"
+      type: holding
+      decode: uint16
+  {{- if .capacity }}
+  capacity: {{ .capacity }} # kWh
+  {{- end }}
+  {{- end }}
+  {{- if eq .usage "grid" }}
+  power:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 169 # "Total grid power"
+      type: holding
+      decode: int16
+  energy:
+    source: calc
+    add:
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 78 # "Total_GridBuy_PowerWh_low"
+        type: holding
+        decode: uint16
+      scale: 0.1
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 80 # "Total_GridBuy_PowerWh"
+        type: holding
+        decode: uint16
+      scale: 6553.6
+  {{- end }}
diff --git a/templates/definition/meter/deye-string.yaml b/templates/definition/meter/deye-string.yaml
new file mode 100644
index 00000000..393e8759
--- /dev/null
+++ b/templates/definition/meter/deye-string.yaml
@@ -0,0 +1,35 @@
+template: deye-string
+products:
+  - brand: Deye
+    description:
+      generic: String inverter
+  - brand: Sunsynk
+    description:
+      generic: String inverter
+params:
+  - name: usage
+    choice: ["pv"]
+  - name: modbus
+    choice: ["rs485", "tcpip"]
+    baudrate: 9600
+    id: 1
+render: |
+  type: custom
+  {{- if eq .usage "pv" }}
+  power:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 86 # "Output active power"
+      type: holding
+      decode: uint32
+    scale: 0.1
+  energy:
+    source: modbus
+    {{- include "modbus" . | indent 2 }}
+    register:
+      address: 63 # "Total_Active_PowerWh"
+      type: holding
+      decode: uint32
+    scale: 0.1
+  {{- end }}
diff --git a/templates/definition/meter/growatt-hybrid-tlxh.yaml b/templates/definition/meter/growatt-hybrid-tlxh.yaml
index 71bc44c5..b04db49e 100644
--- a/templates/definition/meter/growatt-hybrid-tlxh.yaml
+++ b/templates/definition/meter/growatt-hybrid-tlxh.yaml
@@ -43,13 +43,36 @@ render: |
   {{- end }}
   {{- if eq .usage "pv" }}
   power:
-    source: modbus
-    {{- include "modbus" . | indent 2 }}
-    register:
-      address: 3001 # PV total power
-      type: input
-      decode: uint32
-    scale: 0.1
+    source: calc
+    add:
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 3005 # PV1 power
+        type: input
+        decode: uint32
+      scale: 0.1
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 3009 # PV2 power
+        type: input
+        decode: uint32
+      scale: 0.1
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 3013 # PV3 power
+        type: input
+        decode: uint32
+      scale: 0.1
+    - source: modbus
+      {{- include "modbus" . | indent 4 }}
+      register:
+        address: 3017 # PV4 power
+        type: input
+        decode: uint32
+      scale: 0.1
   energy:
     source: modbus
     {{- include "modbus" . | indent 2 }}
diff --git a/templates/evcc.io/brands.json b/templates/evcc.io/brands.json
index 71e74228..c6e94279 100644
--- a/templates/evcc.io/brands.json
+++ b/templates/evcc.io/brands.json
@@ -151,6 +151,7 @@
   "Acrel",
   "Ads-tec",
   "Alpha ESS",
+  "Anker",
   "AVM",
   "Bosswerk",
   "Deye",


Test patch:
diff --git a/core/site_battery_test.go b/core/site_battery_test.go
new file mode 100644
index 00000000..1c356fe6
--- /dev/null
+++ b/core/site_battery_test.go
@@ -0,0 +1,96 @@
+package core
+
+import (
+	"testing"
+
+	"github.com/evcc-io/evcc/api"
+	"github.com/evcc-io/evcc/core/loadpoint"
+	"github.com/evcc-io/evcc/util"
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestBatteryDischarge(t *testing.T) {
+	ctrl := gomock.NewController(t)
+
+	tcs := []struct {
+		chargeStatus api.ChargeStatus
+		planActive   bool
+		expBatMode   api.BatteryMode
+		mode         api.ChargeMode
+	}{
+		{api.StatusB, false, api.BatteryNormal, api.ModeOff},   // mode off -> bat enabled
+		{api.StatusB, false, api.BatteryNormal, api.ModeNow},   // mode now, not charging -> bat enabled
+		{api.StatusC, false, api.BatteryLocked, api.ModeNow},   // mode now, charging -> bat disabled
+		{api.StatusB, false, api.BatteryNormal, api.ModeMinPV}, // mode minPV, not charging -> bat enabled
+		{api.StatusC, false, api.BatteryNormal, api.ModeMinPV}, // mode minPV, charging -> bat enabled
+		{api.StatusB, false, api.BatteryNormal, api.ModePV},    // mode PV, not charging -> bat enabled
+		{api.StatusC, false, api.BatteryNormal, api.ModePV},    // mode PV, charging, no planner -> bat enabled
+		{api.StatusC, true, api.BatteryLocked, api.ModePV},     // mode PV, charging, planner active -> bat disabled
+	}
+
+	log := util.NewLogger("foo")
+
+	for _, tc := range tcs {
+		batCtrl := struct {
+			*api.MockBatteryController
+			*api.MockMeter
+		}{
+			api.NewMockBatteryController(ctrl),
+			api.NewMockMeter(ctrl),
+		}
+		batCtrl.MockBatteryController.EXPECT().SetBatteryMode(tc.expBatMode).Times(1)
+
+		s := &Site{
+			log:                     log,
+			BatteryDischargeControl: true,
+			batteryMeters:           []api.Meter{batCtrl},
+		}
+
+		lp := loadpoint.NewMockAPI(ctrl)
+		lp.EXPECT().GetStatus().Return(tc.chargeStatus).AnyTimes()
+		lp.EXPECT().GetMode().Return(tc.mode).AnyTimes()
+		lp.EXPECT().GetPlanActive().Return(tc.planActive).AnyTimes()
+
+		loadpoints := []loadpoint.API{lp}
+		s.updateBatteryMode(loadpoints)
+		assert.Equal(t, tc.expBatMode, s.getBatteryMode(), tc)
+	}
+}
+
+// test that BatteryControllers are only called if batterymode changes
+func TestBatteryModeNoUpdate(t *testing.T) {
+	ctrl := gomock.NewController(t)
+
+	batCtrl := struct {
+		*api.MockBatteryController
+		*api.MockMeter
+	}{
+		api.NewMockBatteryController(ctrl),
+		api.NewMockMeter(ctrl),
+	}
+	batCtrl.MockBatteryController.EXPECT().SetBatteryMode(api.BatteryLocked).Times(1)
+
+	lp := loadpoint.NewMockAPI(ctrl)
+	lp.EXPECT().GetStatus().Return(api.StatusC).Times(2)
+	lp.EXPECT().GetMode().Return(api.ModeNow).Times(2)
+	lp.EXPECT().GetPlanActive().Times(0)
+	loadpoints := []loadpoint.API{lp}
+
+	s := &Site{
+		batteryMode:             api.BatteryNormal,
+		batteryMeters:           []api.Meter{batCtrl},
+		BatteryDischargeControl: true,
+		log:                     util.NewLogger("foo"),
+	}
+
+	s.updateBatteryMode(loadpoints) // first call should call BatteryController
+	s.updateBatteryMode(loadpoints) // this one should not
+
+	// adjust mocks to simulate charge stop, should cause batMode udpate
+	lp.EXPECT().GetStatus().Return(api.StatusB).Times(1)
+	lp.EXPECT().GetMode().Return(api.ModeNow).Times(0)
+	batCtrl.MockBatteryController.EXPECT().SetBatteryMode(api.BatteryNormal).Times(1)
+
+	s.updateBatteryMode(loadpoints) // this one should have updated again
+}
diff --git a/provider/pipeline/pipeline_test.go b/provider/pipeline/pipeline_test.go
index d3b3c26b..ba27c68c 100644
--- a/provider/pipeline/pipeline_test.go
+++ b/provider/pipeline/pipeline_test.go
@@ -1,40 +1,52 @@
 package pipeline
 
 import (
-	"bytes"
+	"fmt"
 	"testing"
+
+	"github.com/stretchr/testify/require"
 )
 
 func TestRegex(t *testing.T) {
 	for _, re := range []string{`([0-9.]+)`, `[0-9.]+`} {
 		p, err := new(Pipeline).WithRegex(re, "")
-		if err != nil {
-			t.Error(err)
-		}
+		require.NoError(t, err)
 
 		res, err := p.Process([]byte("12.3W"))
-		if err != nil {
-			t.Error(err)
-		}
+		require.NoError(t, err)
 
-		if exp := []byte("12.3"); !bytes.Equal(res, exp) {
-			t.Errorf("Expected %s, got %s", exp, res)
-		}
+		require.Equal(t, []byte("12.3"), res)
 	}
 }
 
 func TestRegexDefault(t *testing.T) {
 	p, err := new(Pipeline).WithRegex(`\d+`, "123")
-	if err != nil {
-		t.Error(err)
-	}
+	require.NoError(t, err)
 
 	res, err := p.Process([]byte("xxx"))
-	if err != nil {
-		t.Error(err)
-	}
+	require.NoError(t, err)
+
+	require.Equal(t, []byte("123"), res)
+}
+
+func TestJq(t *testing.T) {
+	for _, uuid := range []string{"a8232ee0-a4ab-11ec-8d36-211f6b082dc8", "08232ee0-a4ab-11ec-8d36-211f6b082dc8"} {
+
+		p, err := new(Pipeline).WithJq(fmt.Sprintf(`.data[] | select(.uuid=="%s") | .tuples[0][1]`, uuid))
+		require.NoError(t, err)
+
+		res, err := p.Process([]byte(`
+			{
+				"data": [
+					{
+						"uuid": "` + uuid + `",
+						"tuples": [[1,2,3]]
+					}
+				]
+			}
+		`))
+		require.NoError(t, err)
 
-	if exp := []byte("123"); !bytes.Equal(res, exp) {
-		t.Errorf("Expected %s, got %s", exp, res)
+		require.Equal(t, []byte("2"), res)
 	}
 }
diff --git a/util/templates/render_testing.go b/util/templates/render_testing.go
index 22baff0d..6766074c 100644
--- a/util/templates/render_testing.go
+++ b/util/templates/render_testing.go
@@ -1,11 +1,11 @@
 package templates
 
 import (
+	"maps"
 	"os"
 	"slices"
 	"testing"
 
-	"github.com/jinzhu/copier"
 	"gopkg.in/yaml.v3"
 )
 
@@ -70,10 +70,7 @@ func TestClass(t *testing.T, class Class, instantiate func(t *testing.T, values
 
 		for _, u := range usages {
 			// create a copy of the map for parallel execution
-			usageValues := make(map[string]interface{}, len(values)+1)
-			if err := copier.Copy(&usageValues, values); err != nil {
-				panic(err)
-			}
+			usageValues := maps.Clone(values)
 			usageValues[ParamUsage] = u
 
 			// subtest for each usage