node-switchbot 4.0.0-beta.1 → 4.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BLE.md +63 -0
- package/CHANGELOG.md +34 -0
- package/README.md +6 -0
- package/dist/devices/base.d.ts +59 -5
- package/dist/devices/base.d.ts.map +1 -1
- package/dist/devices/base.js +280 -28
- package/dist/devices/base.js.map +1 -1
- package/dist/devices/wo-hand.d.ts +35 -0
- package/dist/devices/wo-hand.d.ts.map +1 -1
- package/dist/devices/wo-hand.js +87 -0
- package/dist/devices/wo-hand.js.map +1 -1
- package/dist/settings.d.ts +6 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +6 -0
- package/dist/settings.js.map +1 -1
- package/dist/switchbot.d.ts.map +1 -1
- package/dist/switchbot.js +14 -0
- package/dist/switchbot.js.map +1 -1
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/bot-ble.d.ts +36 -0
- package/dist/utils/bot-ble.d.ts.map +1 -0
- package/dist/utils/bot-ble.js +107 -0
- package/dist/utils/bot-ble.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +98 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +187 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/connection-tracker.d.ts +66 -0
- package/dist/utils/connection-tracker.d.ts.map +1 -0
- package/dist/utils/connection-tracker.js +184 -0
- package/dist/utils/connection-tracker.js.map +1 -0
- package/dist/utils/fallback-handler.d.ts +68 -0
- package/dist/utils/fallback-handler.d.ts.map +1 -0
- package/dist/utils/fallback-handler.js +131 -0
- package/dist/utils/fallback-handler.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/retry.d.ts +55 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +95 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/APIError.html +2 -2
- package/docs/classes/APINotAvailableError.html +2 -2
- package/docs/classes/BLEConnection.html +9 -9
- package/docs/classes/BLENotAvailableError.html +2 -2
- package/docs/classes/BLEScanner.html +8 -8
- package/docs/classes/CommandFailedError.html +2 -2
- package/docs/classes/ConnectionTimeoutError.html +2 -2
- package/docs/classes/DeviceManager.html +13 -13
- package/docs/classes/DeviceNotFoundError.html +2 -2
- package/docs/classes/DiscoveryError.html +2 -2
- package/docs/classes/OpenAPIClient.html +24 -24
- package/docs/classes/SwitchBot.html +11 -11
- package/docs/classes/SwitchBotDevice.html +33 -15
- package/docs/classes/SwitchBotError.html +2 -2
- package/docs/classes/ValidationError.html +2 -2
- package/docs/classes/WoAirPurifier.html +36 -18
- package/docs/classes/WoAirPurifierTable.html +36 -18
- package/docs/classes/WoBlindTilt.html +38 -20
- package/docs/classes/WoBulb.html +37 -19
- package/docs/classes/WoCeilingLight.html +37 -19
- package/docs/classes/WoContact.html +32 -14
- package/docs/classes/WoCurtain.html +36 -18
- package/docs/classes/WoHand.html +45 -19
- package/docs/classes/WoHub2.html +32 -14
- package/docs/classes/WoHub3.html +32 -14
- package/docs/classes/WoHumi.html +36 -18
- package/docs/classes/WoHumi2.html +36 -18
- package/docs/classes/WoIOSensorTH.html +32 -14
- package/docs/classes/WoKeypad.html +32 -14
- package/docs/classes/WoLeak.html +32 -14
- package/docs/classes/WoPlugMiniJP.html +35 -17
- package/docs/classes/WoPlugMiniUS.html +35 -17
- package/docs/classes/WoPresence.html +32 -14
- package/docs/classes/WoRelaySwitch1.html +35 -17
- package/docs/classes/WoRelaySwitch1PM.html +35 -17
- package/docs/classes/WoRemote.html +32 -14
- package/docs/classes/WoSensorTH.html +32 -14
- package/docs/classes/WoSensorTHPlus.html +32 -14
- package/docs/classes/WoSensorTHPro.html +32 -14
- package/docs/classes/WoSensorTHProCO2.html +32 -14
- package/docs/classes/WoSmartLock.html +34 -16
- package/docs/classes/WoSmartLockPro.html +35 -17
- package/docs/classes/WoStrip.html +37 -19
- package/docs/enums/LogLevel.html +2 -2
- package/docs/enums/SwitchBotBLEModel.html +2 -2
- package/docs/enums/SwitchBotBLEModelName.html +2 -2
- package/docs/functions/updateBaseURL.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +1 -1
- package/docs/interfaces/APICommandRequest.html +2 -2
- package/docs/interfaces/APICommandResponse.html +2 -2
- package/docs/interfaces/APIDevice.html +2 -2
- package/docs/interfaces/APIDeviceStatus.html +2 -2
- package/docs/interfaces/APIErrorResponse.html +2 -2
- package/docs/interfaces/APIResponse.html +2 -2
- package/docs/interfaces/AirPurifierCommands.html +2 -2
- package/docs/interfaces/AirPurifierServiceData.html +5 -5
- package/docs/interfaces/AirPurifierStatus.html +7 -7
- package/docs/interfaces/BLEAdvertisement.html +2 -2
- package/docs/interfaces/BLEScanOptions.html +5 -5
- package/docs/interfaces/BLEServiceData.html +5 -5
- package/docs/interfaces/BlindTiltCommands.html +2 -2
- package/docs/interfaces/BlindTiltServiceData.html +5 -5
- package/docs/interfaces/BlindTiltStatus.html +6 -6
- package/docs/interfaces/BotCommands.html +2 -2
- package/docs/interfaces/BotServiceData.html +5 -5
- package/docs/interfaces/BotStatus.html +6 -6
- package/docs/interfaces/BulbCommands.html +2 -2
- package/docs/interfaces/BulbServiceData.html +5 -5
- package/docs/interfaces/BulbStatus.html +6 -6
- package/docs/interfaces/CeilingLightCommands.html +2 -2
- package/docs/interfaces/CeilingLightServiceData.html +5 -5
- package/docs/interfaces/CeilingLightStatus.html +6 -6
- package/docs/interfaces/CommandResult.html +6 -6
- package/docs/interfaces/ContactServiceData.html +5 -5
- package/docs/interfaces/ContactStatus.html +6 -6
- package/docs/interfaces/CurtainCommands.html +2 -2
- package/docs/interfaces/CurtainServiceData.html +5 -5
- package/docs/interfaces/CurtainStatus.html +6 -6
- package/docs/interfaces/DeviceInfo.html +13 -13
- package/docs/interfaces/DeviceListResponse.html +2 -2
- package/docs/interfaces/DeviceStatus.html +6 -6
- package/docs/interfaces/DiscoveryOptions.html +7 -7
- package/docs/interfaces/HubServiceData.html +5 -5
- package/docs/interfaces/HubStatus.html +6 -6
- package/docs/interfaces/HumidifierCommands.html +2 -2
- package/docs/interfaces/HumidifierServiceData.html +6 -6
- package/docs/interfaces/HumidifierStatus.html +6 -6
- package/docs/interfaces/KeypadStatus.html +6 -6
- package/docs/interfaces/LeakServiceData.html +5 -5
- package/docs/interfaces/LeakStatus.html +6 -6
- package/docs/interfaces/LockCommands.html +2 -2
- package/docs/interfaces/LockServiceData.html +6 -6
- package/docs/interfaces/LockStatus.html +6 -6
- package/docs/interfaces/MeterServiceData.html +5 -5
- package/docs/interfaces/MeterStatus.html +6 -6
- package/docs/interfaces/MotionServiceData.html +5 -5
- package/docs/interfaces/MotionStatus.html +6 -6
- package/docs/interfaces/PlugCommands.html +2 -2
- package/docs/interfaces/PlugServiceData.html +5 -5
- package/docs/interfaces/PlugStatus.html +6 -6
- package/docs/interfaces/PresenceServiceData.html +5 -5
- package/docs/interfaces/PresenceStatus.html +6 -6
- package/docs/interfaces/RelaySwitchCommands.html +2 -2
- package/docs/interfaces/RelaySwitchServiceData.html +5 -5
- package/docs/interfaces/RelaySwitchStatus.html +6 -6
- package/docs/interfaces/RemoteStatus.html +6 -6
- package/docs/interfaces/SceneListResponse.html +2 -2
- package/docs/interfaces/StripCommands.html +2 -2
- package/docs/interfaces/StripServiceData.html +5 -5
- package/docs/interfaces/StripStatus.html +6 -6
- package/docs/interfaces/SwitchBotConfig.html +21 -9
- package/docs/interfaces/WebhookConfig.html +2 -2
- package/docs/interfaces/WebhookDetails.html +2 -2
- package/docs/interfaces/WebhookQueryResponse.html +2 -2
- package/docs/interfaces/WebhookSetupResponse.html +2 -2
- package/docs/media/BLE.md +63 -0
- package/docs/types/ConnectionType.html +1 -1
- package/docs/types/PhysicalDeviceType.html +1 -1
- package/docs/types/VirtualDeviceType.html +1 -1
- package/docs/variables/urls.html +1 -1
- package/package.json +2 -2
- package/tsconfig.build.json +17 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit breaker states
|
|
3
|
+
*/
|
|
4
|
+
export declare enum CircuitBreakerState {
|
|
5
|
+
CLOSED = "CLOSED",// Normal operation
|
|
6
|
+
OPEN = "OPEN",// Failing, reject requests
|
|
7
|
+
HALF_OPEN = "HALF_OPEN"
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Circuit breaker configuration
|
|
11
|
+
*/
|
|
12
|
+
export interface CircuitBreakerConfig {
|
|
13
|
+
/** Failure threshold (0-1) to open circuit (default: 0.5) */
|
|
14
|
+
failureThreshold?: number;
|
|
15
|
+
/** Minimum number of requests before checking threshold (default: 5) */
|
|
16
|
+
minRequests?: number;
|
|
17
|
+
/** Time to wait before attempting recovery in milliseconds (default: 30000) */
|
|
18
|
+
resetTimeoutMs?: number;
|
|
19
|
+
/** Maximum number of requests in half-open state (default: 1) */
|
|
20
|
+
maxHalfOpenRequests?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Circuit breaker statistics
|
|
24
|
+
*/
|
|
25
|
+
export interface CircuitBreakerStats {
|
|
26
|
+
state: CircuitBreakerState;
|
|
27
|
+
successCount: number;
|
|
28
|
+
failureCount: number;
|
|
29
|
+
successRate: number;
|
|
30
|
+
totalRequests: number;
|
|
31
|
+
lastFailureTime?: Date;
|
|
32
|
+
lastRecoveryTime?: Date;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Circuit breaker for managing connection reliability
|
|
36
|
+
*/
|
|
37
|
+
export declare class CircuitBreaker {
|
|
38
|
+
private name;
|
|
39
|
+
private state;
|
|
40
|
+
private successCount;
|
|
41
|
+
private failureCount;
|
|
42
|
+
private halfOpenAttempts;
|
|
43
|
+
private lastFailureTime?;
|
|
44
|
+
private lastRecoveryTime?;
|
|
45
|
+
private resetTimer?;
|
|
46
|
+
private logger;
|
|
47
|
+
private config;
|
|
48
|
+
constructor(name: string, config?: CircuitBreakerConfig, logLevel?: number);
|
|
49
|
+
/**
|
|
50
|
+
* Record a successful operation
|
|
51
|
+
*/
|
|
52
|
+
recordSuccess(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Record a failed operation
|
|
55
|
+
*/
|
|
56
|
+
recordFailure(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Check if circuit should open based on failure rate
|
|
59
|
+
*/
|
|
60
|
+
private shouldOpen;
|
|
61
|
+
/**
|
|
62
|
+
* Transition to CLOSED state (recovered)
|
|
63
|
+
*/
|
|
64
|
+
private transitionToClosed;
|
|
65
|
+
/**
|
|
66
|
+
* Transition to OPEN state (failing)
|
|
67
|
+
*/
|
|
68
|
+
private transitionToOpen;
|
|
69
|
+
/**
|
|
70
|
+
* Check if the circuit allows operations
|
|
71
|
+
*/
|
|
72
|
+
canExecute(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Mark that we tried to execute in half-open state
|
|
75
|
+
*/
|
|
76
|
+
markHalfOpenAttempt(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Get current state
|
|
79
|
+
*/
|
|
80
|
+
getState(): CircuitBreakerState;
|
|
81
|
+
/**
|
|
82
|
+
* Get current failure rate (0-1)
|
|
83
|
+
*/
|
|
84
|
+
getFailureRate(): number;
|
|
85
|
+
/**
|
|
86
|
+
* Get statistics
|
|
87
|
+
*/
|
|
88
|
+
getStats(): CircuitBreakerStats;
|
|
89
|
+
/**
|
|
90
|
+
* Reset circuit breaker (for testing)
|
|
91
|
+
*/
|
|
92
|
+
reset(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Cleanup
|
|
95
|
+
*/
|
|
96
|
+
cleanup(): void;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/utils/circuit-breaker.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,oBAAY,mBAAmB;IAC7B,MAAM,WAAW,CAAE,mBAAmB;IACtC,IAAI,SAAS,CAAE,2BAA2B;IAC1C,SAAS,cAAc;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,mBAAmB,CAAA;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,IAAI,CAAA;IACtB,gBAAgB,CAAC,EAAE,IAAI,CAAA;CACxB;AAED;;GAEG;AACH,qBAAa,cAAc;IAYvB,OAAO,CAAC,IAAI;IAXd,OAAO,CAAC,KAAK,CAAkD;IAC/D,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,eAAe,CAAC,CAAM;IAC9B,OAAO,CAAC,gBAAgB,CAAC,CAAM;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAgB;IACnC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAAgC;gBAGpC,IAAI,EAAE,MAAM,EACpB,MAAM,GAAE,oBAAyB,EACjC,QAAQ,CAAC,EAAE,MAAM;IAYnB;;OAEG;IACH,aAAa,IAAI,IAAI;IAUrB;;OAEG;IACH,aAAa,IAAI,IAAI;IAmBrB;;OAEG;IACH,OAAO,CAAC,UAAU;IAWlB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;OAEG;IACH,UAAU,IAAI,OAAO;IAarB;;OAEG;IACH,mBAAmB,IAAI,IAAI;IAM3B;;OAEG;IACH,QAAQ,IAAI,mBAAmB;IAI/B;;OAEG;IACH,cAAc,IAAI,MAAM;IAKxB;;OAEG;IACH,QAAQ,IAAI,mBAAmB;IAc/B;;OAEG;IACH,KAAK,IAAI,IAAI;IAeb;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, donavanbecker (https://github.com/donavanbecker). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* utils/circuit-breaker.ts: SwitchBot v4.0.0 - Circuit Breaker Pattern
|
|
4
|
+
*/
|
|
5
|
+
import { Logger } from './index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Circuit breaker states
|
|
8
|
+
*/
|
|
9
|
+
export var CircuitBreakerState;
|
|
10
|
+
(function (CircuitBreakerState) {
|
|
11
|
+
CircuitBreakerState["CLOSED"] = "CLOSED";
|
|
12
|
+
CircuitBreakerState["OPEN"] = "OPEN";
|
|
13
|
+
CircuitBreakerState["HALF_OPEN"] = "HALF_OPEN";
|
|
14
|
+
})(CircuitBreakerState || (CircuitBreakerState = {}));
|
|
15
|
+
/**
|
|
16
|
+
* Circuit breaker for managing connection reliability
|
|
17
|
+
*/
|
|
18
|
+
export class CircuitBreaker {
|
|
19
|
+
name;
|
|
20
|
+
state = CircuitBreakerState.CLOSED;
|
|
21
|
+
successCount = 0;
|
|
22
|
+
failureCount = 0;
|
|
23
|
+
halfOpenAttempts = 0;
|
|
24
|
+
lastFailureTime;
|
|
25
|
+
lastRecoveryTime;
|
|
26
|
+
resetTimer;
|
|
27
|
+
logger;
|
|
28
|
+
config;
|
|
29
|
+
constructor(name, config = {}, logLevel) {
|
|
30
|
+
this.name = name;
|
|
31
|
+
this.config = {
|
|
32
|
+
failureThreshold: config.failureThreshold ?? 0.5,
|
|
33
|
+
minRequests: config.minRequests ?? 5,
|
|
34
|
+
resetTimeoutMs: config.resetTimeoutMs ?? 30000,
|
|
35
|
+
maxHalfOpenRequests: config.maxHalfOpenRequests ?? 1,
|
|
36
|
+
};
|
|
37
|
+
this.logger = new Logger(`CircuitBreaker:${name}`, logLevel);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Record a successful operation
|
|
41
|
+
*/
|
|
42
|
+
recordSuccess() {
|
|
43
|
+
this.successCount++;
|
|
44
|
+
if (this.state === CircuitBreakerState.HALF_OPEN) {
|
|
45
|
+
// Successful in Half-Open -> recover
|
|
46
|
+
this.logger.info(`${this.name}: circuit recovered (HALF_OPEN -> CLOSED)`);
|
|
47
|
+
this.transitionToClosed();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record a failed operation
|
|
52
|
+
*/
|
|
53
|
+
recordFailure() {
|
|
54
|
+
this.failureCount++;
|
|
55
|
+
this.lastFailureTime = new Date();
|
|
56
|
+
if (this.state === CircuitBreakerState.CLOSED) {
|
|
57
|
+
// Check if we should open the circuit
|
|
58
|
+
if (this.shouldOpen()) {
|
|
59
|
+
this.logger.warn(`${this.name}: circuit opened - failure rate ${this.getFailureRate().toFixed(2)}`);
|
|
60
|
+
this.transitionToOpen();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (this.state === CircuitBreakerState.HALF_OPEN) {
|
|
64
|
+
// Failed during recovery -> reopen
|
|
65
|
+
this.logger.warn(`${this.name}: circuit reopened (HALF_OPEN -> OPEN)`);
|
|
66
|
+
this.transitionToOpen();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if circuit should open based on failure rate
|
|
71
|
+
*/
|
|
72
|
+
shouldOpen() {
|
|
73
|
+
const totalRequests = this.successCount + this.failureCount;
|
|
74
|
+
if (totalRequests < this.config.minRequests) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const failureRate = this.failureCount / totalRequests;
|
|
78
|
+
return failureRate >= this.config.failureThreshold;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Transition to CLOSED state (recovered)
|
|
82
|
+
*/
|
|
83
|
+
transitionToClosed() {
|
|
84
|
+
this.state = CircuitBreakerState.CLOSED;
|
|
85
|
+
this.successCount = 0;
|
|
86
|
+
this.failureCount = 0;
|
|
87
|
+
this.halfOpenAttempts = 0;
|
|
88
|
+
this.lastRecoveryTime = new Date();
|
|
89
|
+
if (this.resetTimer) {
|
|
90
|
+
clearTimeout(this.resetTimer);
|
|
91
|
+
this.resetTimer = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Transition to OPEN state (failing)
|
|
96
|
+
*/
|
|
97
|
+
transitionToOpen() {
|
|
98
|
+
this.state = CircuitBreakerState.OPEN;
|
|
99
|
+
this.successCount = 0;
|
|
100
|
+
this.failureCount = 0;
|
|
101
|
+
this.halfOpenAttempts = 0;
|
|
102
|
+
// Schedule recovery attempt
|
|
103
|
+
if (this.resetTimer) {
|
|
104
|
+
clearTimeout(this.resetTimer);
|
|
105
|
+
}
|
|
106
|
+
this.resetTimer = setTimeout(() => {
|
|
107
|
+
this.logger.info(`${this.name}: attempting recovery (OPEN -> HALF_OPEN)`);
|
|
108
|
+
this.state = CircuitBreakerState.HALF_OPEN;
|
|
109
|
+
this.halfOpenAttempts = 0;
|
|
110
|
+
}, this.config.resetTimeoutMs);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if the circuit allows operations
|
|
114
|
+
*/
|
|
115
|
+
canExecute() {
|
|
116
|
+
if (this.state === CircuitBreakerState.CLOSED) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (this.state === CircuitBreakerState.HALF_OPEN) {
|
|
120
|
+
return this.halfOpenAttempts < this.config.maxHalfOpenRequests;
|
|
121
|
+
}
|
|
122
|
+
// OPEN state
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Mark that we tried to execute in half-open state
|
|
127
|
+
*/
|
|
128
|
+
markHalfOpenAttempt() {
|
|
129
|
+
if (this.state === CircuitBreakerState.HALF_OPEN) {
|
|
130
|
+
this.halfOpenAttempts++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get current state
|
|
135
|
+
*/
|
|
136
|
+
getState() {
|
|
137
|
+
return this.state;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get current failure rate (0-1)
|
|
141
|
+
*/
|
|
142
|
+
getFailureRate() {
|
|
143
|
+
const total = this.successCount + this.failureCount;
|
|
144
|
+
return total === 0 ? 0 : this.failureCount / total;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get statistics
|
|
148
|
+
*/
|
|
149
|
+
getStats() {
|
|
150
|
+
const totalRequests = this.successCount + this.failureCount;
|
|
151
|
+
return {
|
|
152
|
+
state: this.state,
|
|
153
|
+
successCount: this.successCount,
|
|
154
|
+
failureCount: this.failureCount,
|
|
155
|
+
successRate: totalRequests === 0 ? 1 : this.successCount / totalRequests,
|
|
156
|
+
totalRequests,
|
|
157
|
+
lastFailureTime: this.lastFailureTime,
|
|
158
|
+
lastRecoveryTime: this.lastRecoveryTime,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Reset circuit breaker (for testing)
|
|
163
|
+
*/
|
|
164
|
+
reset() {
|
|
165
|
+
this.logger.debug(`${this.name}: reset`);
|
|
166
|
+
this.state = CircuitBreakerState.CLOSED;
|
|
167
|
+
this.successCount = 0;
|
|
168
|
+
this.failureCount = 0;
|
|
169
|
+
this.halfOpenAttempts = 0;
|
|
170
|
+
this.lastFailureTime = undefined;
|
|
171
|
+
this.lastRecoveryTime = undefined;
|
|
172
|
+
if (this.resetTimer) {
|
|
173
|
+
clearTimeout(this.resetTimer);
|
|
174
|
+
this.resetTimer = undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Cleanup
|
|
179
|
+
*/
|
|
180
|
+
cleanup() {
|
|
181
|
+
if (this.resetTimer) {
|
|
182
|
+
clearTimeout(this.resetTimer);
|
|
183
|
+
this.resetTimer = undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/utils/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ConnectionType } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Statistics for a single connection type
|
|
4
|
+
*/
|
|
5
|
+
export interface ConnectionStats {
|
|
6
|
+
connectionType: ConnectionType;
|
|
7
|
+
successCount: number;
|
|
8
|
+
failureCount: number;
|
|
9
|
+
totalAttempts: number;
|
|
10
|
+
successRate: number;
|
|
11
|
+
averageLatencyMs: number;
|
|
12
|
+
lastAttemptTime?: Date;
|
|
13
|
+
lastSuccessTime?: Date;
|
|
14
|
+
lastFailureTime?: Date;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tracks connection success/failure statistics per device per connection type
|
|
18
|
+
*/
|
|
19
|
+
export declare class ConnectionTracker {
|
|
20
|
+
private deviceId;
|
|
21
|
+
private stats;
|
|
22
|
+
private logger;
|
|
23
|
+
constructor(deviceId: string, logLevel?: number);
|
|
24
|
+
/**
|
|
25
|
+
* Record a successful attempt
|
|
26
|
+
*/
|
|
27
|
+
recordSuccess(connectionType: ConnectionType, latencyMs?: number): void;
|
|
28
|
+
/**
|
|
29
|
+
* Record a failed attempt
|
|
30
|
+
*/
|
|
31
|
+
recordFailure(connectionType: ConnectionType): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get statistics for a connection type
|
|
34
|
+
*/
|
|
35
|
+
getStats(connectionType: ConnectionType): ConnectionStats | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Get all statistics
|
|
38
|
+
*/
|
|
39
|
+
getAllStats(): ConnectionStats[];
|
|
40
|
+
/**
|
|
41
|
+
* Get the best connection type (highest success rate)
|
|
42
|
+
*/
|
|
43
|
+
getBestConnection(availableTypes?: ConnectionType[]): ConnectionType | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Get the most recent successful connection type
|
|
46
|
+
*/
|
|
47
|
+
getMostRecentSuccessful(availableTypes?: ConnectionType[]): ConnectionType | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a connection type is considered reliable
|
|
50
|
+
* (e.g., success rate > 75% with at least 5 attempts)
|
|
51
|
+
*/
|
|
52
|
+
isReliable(connectionType: ConnectionType, minAttempts?: number, minRate?: number): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Get connection recommendation with reasoning
|
|
55
|
+
*/
|
|
56
|
+
getRecommendation(availableTypes?: ConnectionType[]): {
|
|
57
|
+
recommended: ConnectionType | undefined;
|
|
58
|
+
reason: string;
|
|
59
|
+
stats: ConnectionStats[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Reset all statistics
|
|
63
|
+
*/
|
|
64
|
+
reset(): void;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=connection-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-tracker.d.ts","sourceRoot":"","sources":["../../src/utils/connection-tracker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIvD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,cAAc,CAAA;IAC9B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,IAAI,CAAA;CACvB;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,KAAK,CAAkD;IAC/D,OAAO,CAAC,MAAM,CAAQ;gBAGZ,QAAQ,EAAE,MAAM,EACxB,QAAQ,CAAC,EAAE,MAAM;IAqBnB;;OAEG;IACH,aAAa,CAAC,cAAc,EAAE,cAAc,EAAE,SAAS,GAAE,MAAU,GAAG,IAAI;IAwB1E;;OAEG;IACH,aAAa,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI;IAmBnD;;OAEG;IACH,QAAQ,CAAC,cAAc,EAAE,cAAc,GAAG,eAAe,GAAG,SAAS;IAIrE;;OAEG;IACH,WAAW,IAAI,eAAe,EAAE;IAIhC;;OAEG;IACH,iBAAiB,CAAC,cAAc,GAAE,cAAc,EAAmB,GAAG,cAAc,GAAG,SAAS;IAsBhG;;OAEG;IACH,uBAAuB,CAAC,cAAc,GAAE,cAAc,EAAmB,GAAG,cAAc,GAAG,SAAS;IAiBtG;;;OAGG;IACH,UAAU,CAAC,cAAc,EAAE,cAAc,EAAE,WAAW,GAAE,MAAU,EAAE,OAAO,GAAE,MAAa,GAAG,OAAO;IASpG;;OAEG;IACH,iBAAiB,CAAC,cAAc,GAAE,cAAc,EAAmB,GAAG;QACpE,WAAW,EAAE,cAAc,GAAG,SAAS,CAAA;QACvC,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,eAAe,EAAE,CAAA;KACzB;IAsCD;;OAEG;IACH,KAAK,IAAI,IAAI;CAad"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, donavanbecker (https://github.com/donavanbecker). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* utils/connection-tracker.ts: SwitchBot v4.0.0 - Connection Success Tracking
|
|
4
|
+
*/
|
|
5
|
+
import { Logger } from './index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Tracks connection success/failure statistics per device per connection type
|
|
8
|
+
*/
|
|
9
|
+
export class ConnectionTracker {
|
|
10
|
+
deviceId;
|
|
11
|
+
stats = new Map();
|
|
12
|
+
logger;
|
|
13
|
+
constructor(deviceId, logLevel) {
|
|
14
|
+
this.deviceId = deviceId;
|
|
15
|
+
this.logger = new Logger(`ConnectionTracker:${deviceId}`, logLevel);
|
|
16
|
+
// Initialize stats for all connection types
|
|
17
|
+
const connectionTypes = ['ble', 'api'];
|
|
18
|
+
for (const type of connectionTypes) {
|
|
19
|
+
this.stats.set(type, {
|
|
20
|
+
connectionType: type,
|
|
21
|
+
successCount: 0,
|
|
22
|
+
failureCount: 0,
|
|
23
|
+
totalAttempts: 0,
|
|
24
|
+
successRate: 1, // Start optimistic
|
|
25
|
+
averageLatencyMs: 0,
|
|
26
|
+
lastAttemptTime: undefined,
|
|
27
|
+
lastSuccessTime: undefined,
|
|
28
|
+
lastFailureTime: undefined,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Record a successful attempt
|
|
34
|
+
*/
|
|
35
|
+
recordSuccess(connectionType, latencyMs = 0) {
|
|
36
|
+
const stat = this.stats.get(connectionType);
|
|
37
|
+
if (!stat) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
stat.successCount++;
|
|
41
|
+
stat.totalAttempts++;
|
|
42
|
+
stat.lastAttemptTime = new Date();
|
|
43
|
+
stat.lastSuccessTime = new Date();
|
|
44
|
+
// Update average latency (exponential moving average)
|
|
45
|
+
const alpha = 0.3;
|
|
46
|
+
stat.averageLatencyMs = stat.averageLatencyMs * (1 - alpha) + latencyMs * alpha;
|
|
47
|
+
// Update success rate
|
|
48
|
+
stat.successRate = stat.totalAttempts === 0 ? 1 : stat.successCount / stat.totalAttempts;
|
|
49
|
+
this.logger.debug(`${connectionType}: success recorded`, {
|
|
50
|
+
successRate: stat.successRate.toFixed(2),
|
|
51
|
+
latency: latencyMs,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Record a failed attempt
|
|
56
|
+
*/
|
|
57
|
+
recordFailure(connectionType) {
|
|
58
|
+
const stat = this.stats.get(connectionType);
|
|
59
|
+
if (!stat) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
stat.failureCount++;
|
|
63
|
+
stat.totalAttempts++;
|
|
64
|
+
stat.lastAttemptTime = new Date();
|
|
65
|
+
stat.lastFailureTime = new Date();
|
|
66
|
+
// Update success rate
|
|
67
|
+
stat.successRate = stat.totalAttempts === 0 ? 1 : stat.successCount / stat.totalAttempts;
|
|
68
|
+
this.logger.debug(`${connectionType}: failure recorded`, {
|
|
69
|
+
successRate: stat.successRate.toFixed(2),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get statistics for a connection type
|
|
74
|
+
*/
|
|
75
|
+
getStats(connectionType) {
|
|
76
|
+
return this.stats.get(connectionType);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get all statistics
|
|
80
|
+
*/
|
|
81
|
+
getAllStats() {
|
|
82
|
+
return Array.from(this.stats.values());
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get the best connection type (highest success rate)
|
|
86
|
+
*/
|
|
87
|
+
getBestConnection(availableTypes = ['ble', 'api']) {
|
|
88
|
+
let bestType;
|
|
89
|
+
let bestRate = -1;
|
|
90
|
+
for (const type of availableTypes) {
|
|
91
|
+
const stat = this.stats.get(type);
|
|
92
|
+
if (stat && stat.successRate > bestRate) {
|
|
93
|
+
bestRate = stat.successRate;
|
|
94
|
+
bestType = type;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (bestType) {
|
|
98
|
+
const stat = this.stats.get(bestType);
|
|
99
|
+
if (stat.totalAttempts > 0) {
|
|
100
|
+
this.logger.debug(`Best connection: ${bestType} (success rate: ${stat.successRate.toFixed(2)})`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return bestType;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the most recent successful connection type
|
|
107
|
+
*/
|
|
108
|
+
getMostRecentSuccessful(availableTypes = ['ble', 'api']) {
|
|
109
|
+
let mostRecentType;
|
|
110
|
+
let mostRecentTime;
|
|
111
|
+
for (const type of availableTypes) {
|
|
112
|
+
const stat = this.stats.get(type);
|
|
113
|
+
if (stat?.lastSuccessTime) {
|
|
114
|
+
if (!mostRecentTime || stat.lastSuccessTime > mostRecentTime) {
|
|
115
|
+
mostRecentTime = stat.lastSuccessTime;
|
|
116
|
+
mostRecentType = type;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return mostRecentType;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Check if a connection type is considered reliable
|
|
124
|
+
* (e.g., success rate > 75% with at least 5 attempts)
|
|
125
|
+
*/
|
|
126
|
+
isReliable(connectionType, minAttempts = 5, minRate = 0.75) {
|
|
127
|
+
const stat = this.stats.get(connectionType);
|
|
128
|
+
if (!stat) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return stat.totalAttempts >= minAttempts && stat.successRate >= minRate;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get connection recommendation with reasoning
|
|
135
|
+
*/
|
|
136
|
+
getRecommendation(availableTypes = ['ble', 'api']) {
|
|
137
|
+
const stats = availableTypes.map(type => this.stats.get(type)).filter(Boolean);
|
|
138
|
+
// If no attempt history, recommend first available
|
|
139
|
+
if (stats.every(s => s.totalAttempts === 0)) {
|
|
140
|
+
return {
|
|
141
|
+
recommended: availableTypes[0],
|
|
142
|
+
reason: 'No history, using first available connection',
|
|
143
|
+
stats,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Find most reliable based on success rate
|
|
147
|
+
const bestType = this.getBestConnection(availableTypes);
|
|
148
|
+
if (!bestType) {
|
|
149
|
+
return {
|
|
150
|
+
recommended: undefined,
|
|
151
|
+
reason: 'No available connections',
|
|
152
|
+
stats,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const bestStat = this.stats.get(bestType);
|
|
156
|
+
let reason = `${bestType} has best success rate (${bestStat.successRate.toFixed(2)})`;
|
|
157
|
+
// Add context about latency if applicable
|
|
158
|
+
if (bestStat.averageLatencyMs > 0) {
|
|
159
|
+
reason += ` with avg latency ${bestStat.averageLatencyMs.toFixed(0)}ms`;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
recommended: bestType,
|
|
163
|
+
reason,
|
|
164
|
+
stats,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Reset all statistics
|
|
169
|
+
*/
|
|
170
|
+
reset() {
|
|
171
|
+
this.logger.debug('Resetting all statistics');
|
|
172
|
+
for (const stat of this.stats.values()) {
|
|
173
|
+
stat.successCount = 0;
|
|
174
|
+
stat.failureCount = 0;
|
|
175
|
+
stat.totalAttempts = 0;
|
|
176
|
+
stat.successRate = 1;
|
|
177
|
+
stat.averageLatencyMs = 0;
|
|
178
|
+
stat.lastAttemptTime = undefined;
|
|
179
|
+
stat.lastSuccessTime = undefined;
|
|
180
|
+
stat.lastFailureTime = undefined;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=connection-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-tracker.js","sourceRoot":"","sources":["../../src/utils/connection-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ConnectionType } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Information about a fallback event
|
|
4
|
+
*/
|
|
5
|
+
export interface FallbackEvent {
|
|
6
|
+
deviceId: string;
|
|
7
|
+
primaryConnection: ConnectionType;
|
|
8
|
+
fallbackConnection: ConnectionType | undefined;
|
|
9
|
+
reason: string;
|
|
10
|
+
timestamp: Date;
|
|
11
|
+
attemptsCount?: number;
|
|
12
|
+
totalTimeMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Type for custom fallback handler callbacks
|
|
16
|
+
*/
|
|
17
|
+
export type FallbackHandler = (event: FallbackEvent) => void | Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Options for registering a fallback handler
|
|
20
|
+
*/
|
|
21
|
+
export interface FallbackHandlerOptions {
|
|
22
|
+
/** Handler identifier for later removal */
|
|
23
|
+
id?: string;
|
|
24
|
+
/** Handler priority (higher = executes first) */
|
|
25
|
+
priority?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Manages custom fallback handlers and events
|
|
29
|
+
*/
|
|
30
|
+
export declare class FallbackHandlerManager {
|
|
31
|
+
private handlers;
|
|
32
|
+
private logger;
|
|
33
|
+
private handlerCounter;
|
|
34
|
+
constructor(logLevel?: number);
|
|
35
|
+
/**
|
|
36
|
+
* Register a custom fallback handler
|
|
37
|
+
*/
|
|
38
|
+
register(handler: FallbackHandler, options?: FallbackHandlerOptions): string;
|
|
39
|
+
/**
|
|
40
|
+
* Unregister a fallback handler
|
|
41
|
+
*/
|
|
42
|
+
unregister(id: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Emit a fallback event to all registered handlers
|
|
45
|
+
*/
|
|
46
|
+
emit(event: FallbackEvent): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Clear all handlers
|
|
49
|
+
*/
|
|
50
|
+
clear(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get handler count
|
|
53
|
+
*/
|
|
54
|
+
getHandlerCount(): number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Built-in fallback handler: Log fallback events
|
|
58
|
+
*/
|
|
59
|
+
export declare function createLoggingFallbackHandler(logLevel?: number): FallbackHandler;
|
|
60
|
+
/**
|
|
61
|
+
* Built-in fallback handler: Metrics/statistics collection
|
|
62
|
+
*/
|
|
63
|
+
export declare function createMetricsCollectionHandler(): FallbackHandler;
|
|
64
|
+
/**
|
|
65
|
+
* Built-in fallback handler: Alert on repeated fallbacks
|
|
66
|
+
*/
|
|
67
|
+
export declare function createAlertHandler(alertThreshold?: number): FallbackHandler;
|
|
68
|
+
//# sourceMappingURL=fallback-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback-handler.d.ts","sourceRoot":"","sources":["../../src/utils/fallback-handler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIvD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,iBAAiB,EAAE,cAAc,CAAA;IACjC,kBAAkB,EAAE,cAAc,GAAG,SAAS,CAAA;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAE5E;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2CAA2C;IAC3C,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAGH;IAEb,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,cAAc,CAAI;gBAEd,QAAQ,CAAC,EAAE,MAAM;IAI7B;;OAEG;IACH,QAAQ,CACN,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,sBAA2B,GACnC,MAAM;IAUT;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQ/B;;OAEG;IACG,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB/C;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,eAAe,IAAI,MAAM;CAG1B;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,GAAE,MAAU,GAAG,eAAe,CAmBlF;AAED;;GAEG;AACH,wBAAgB,8BAA8B,IAAI,eAAe,CA+BhE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,GAAE,MAAU,GAAG,eAAe,CAkB9E"}
|