homebridge-myleviton 3.0.0
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/LICENSE +202 -0
- package/README.md +112 -0
- package/config.schema.json +136 -0
- package/dist/api/cache.d.ts +108 -0
- package/dist/api/cache.d.ts.map +1 -0
- package/dist/api/cache.js +206 -0
- package/dist/api/cache.js.map +1 -0
- package/dist/api/circuit-breaker.d.ts +118 -0
- package/dist/api/circuit-breaker.d.ts.map +1 -0
- package/dist/api/circuit-breaker.js +223 -0
- package/dist/api/circuit-breaker.js.map +1 -0
- package/dist/api/client.d.ts +116 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +358 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/index.d.ts +23 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +47 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/persistence.d.ts +107 -0
- package/dist/api/persistence.d.ts.map +1 -0
- package/dist/api/persistence.js +285 -0
- package/dist/api/persistence.js.map +1 -0
- package/dist/api/rate-limiter.d.ts +102 -0
- package/dist/api/rate-limiter.d.ts.map +1 -0
- package/dist/api/rate-limiter.js +173 -0
- package/dist/api/rate-limiter.js.map +1 -0
- package/dist/api/request-queue.d.ts +104 -0
- package/dist/api/request-queue.d.ts.map +1 -0
- package/dist/api/request-queue.js +223 -0
- package/dist/api/request-queue.js.map +1 -0
- package/dist/api/websocket.d.ts +116 -0
- package/dist/api/websocket.d.ts.map +1 -0
- package/dist/api/websocket.js +319 -0
- package/dist/api/websocket.js.map +1 -0
- package/dist/errors/index.d.ts +182 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +273 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +139 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +664 -0
- package/dist/platform.js.map +1 -0
- package/dist/types/index.d.ts +225 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +34 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +52 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +103 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +184 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +56 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +141 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/sanitizers.d.ts +37 -0
- package/dist/utils/sanitizers.d.ts.map +1 -0
- package/dist/utils/sanitizers.js +128 -0
- package/dist/utils/sanitizers.js.map +1 -0
- package/dist/utils/validators.d.ts +51 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +243 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 tbaur
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
* See LICENSE file for full license text
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview Structured logging utilities
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.StructuredLogger = exports.LeveledLogger = exports.DEFAULT_STRUCTURED_LOGGER_CONFIG = exports.DEFAULT_LOG_LEVEL = exports.LOG_LEVELS = void 0;
|
|
12
|
+
exports.createLogger = createLogger;
|
|
13
|
+
exports.createStructuredLogger = createStructuredLogger;
|
|
14
|
+
const sanitizers_1 = require("./sanitizers");
|
|
15
|
+
/**
|
|
16
|
+
* Log levels in order of severity
|
|
17
|
+
*/
|
|
18
|
+
exports.LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
19
|
+
/**
|
|
20
|
+
* Default log level
|
|
21
|
+
*/
|
|
22
|
+
exports.DEFAULT_LOG_LEVEL = 'info';
|
|
23
|
+
/**
|
|
24
|
+
* Default structured logger configuration
|
|
25
|
+
*/
|
|
26
|
+
exports.DEFAULT_STRUCTURED_LOGGER_CONFIG = {
|
|
27
|
+
structured: false,
|
|
28
|
+
includeCorrelationId: true,
|
|
29
|
+
includeTimestamp: true,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Logger wrapper that supports level filtering and structured output
|
|
33
|
+
*/
|
|
34
|
+
class LeveledLogger {
|
|
35
|
+
baseLog;
|
|
36
|
+
minLevel;
|
|
37
|
+
debug;
|
|
38
|
+
info;
|
|
39
|
+
warn;
|
|
40
|
+
error;
|
|
41
|
+
constructor(log, level = exports.DEFAULT_LOG_LEVEL) {
|
|
42
|
+
this.baseLog = typeof log === 'function' ? log : (msg) => log.info(msg);
|
|
43
|
+
this.minLevel = exports.LOG_LEVELS.indexOf(level);
|
|
44
|
+
// Create level methods
|
|
45
|
+
this.debug = this.createLevelMethod('debug');
|
|
46
|
+
this.info = this.createLevelMethod('info');
|
|
47
|
+
this.warn = this.createLevelMethod('warn');
|
|
48
|
+
this.error = this.createLevelMethod('error');
|
|
49
|
+
}
|
|
50
|
+
createLevelMethod(level) {
|
|
51
|
+
const levelIndex = exports.LOG_LEVELS.indexOf(level);
|
|
52
|
+
return (message) => {
|
|
53
|
+
if (levelIndex >= this.minLevel) {
|
|
54
|
+
this.baseLog(message);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.LeveledLogger = LeveledLogger;
|
|
60
|
+
/**
|
|
61
|
+
* Structured logger with correlation ID support
|
|
62
|
+
*/
|
|
63
|
+
class StructuredLogger {
|
|
64
|
+
log;
|
|
65
|
+
config;
|
|
66
|
+
correlationId = null;
|
|
67
|
+
constructor(log, config = {}) {
|
|
68
|
+
this.log = log instanceof LeveledLogger ? log : new LeveledLogger(log);
|
|
69
|
+
this.config = { ...exports.DEFAULT_STRUCTURED_LOGGER_CONFIG, ...config };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Set correlation ID for request tracing
|
|
73
|
+
*/
|
|
74
|
+
setCorrelationId(id) {
|
|
75
|
+
this.correlationId = id;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Generate a new correlation ID
|
|
79
|
+
*/
|
|
80
|
+
generateCorrelationId() {
|
|
81
|
+
this.correlationId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
82
|
+
return this.correlationId;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Clear correlation ID
|
|
86
|
+
*/
|
|
87
|
+
clearCorrelationId() {
|
|
88
|
+
this.correlationId = null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format a log entry
|
|
92
|
+
*/
|
|
93
|
+
format(level, message, context) {
|
|
94
|
+
if (this.config.structured) {
|
|
95
|
+
const entry = {
|
|
96
|
+
timestamp: new Date().toISOString(),
|
|
97
|
+
level,
|
|
98
|
+
message: typeof message === 'string' ? message : '',
|
|
99
|
+
...(typeof message === 'object' ? (0, sanitizers_1.sanitizeObject)(message) : {}),
|
|
100
|
+
...(context ? (0, sanitizers_1.sanitizeObject)(context) : {}),
|
|
101
|
+
};
|
|
102
|
+
if (this.config.includeCorrelationId && this.correlationId) {
|
|
103
|
+
entry.correlationId = this.correlationId;
|
|
104
|
+
}
|
|
105
|
+
return JSON.stringify(entry);
|
|
106
|
+
}
|
|
107
|
+
// Traditional string format
|
|
108
|
+
let result = '';
|
|
109
|
+
if (typeof message === 'object') {
|
|
110
|
+
const { event, ...rest } = message;
|
|
111
|
+
const contextStr = Object.keys(rest).length > 0
|
|
112
|
+
? ` ${JSON.stringify((0, sanitizers_1.sanitizeObject)(rest))}`
|
|
113
|
+
: '';
|
|
114
|
+
result = `[${event || 'log'}]${contextStr}`;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
result = message;
|
|
118
|
+
}
|
|
119
|
+
if (context) {
|
|
120
|
+
result += ` ${JSON.stringify((0, sanitizers_1.sanitizeObject)(context))}`;
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Log debug message
|
|
126
|
+
*/
|
|
127
|
+
debug(message, context) {
|
|
128
|
+
this.log.debug(this.format('debug', message, context));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Log info message
|
|
132
|
+
*/
|
|
133
|
+
info(message, context) {
|
|
134
|
+
this.log.info(this.format('info', message, context));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Log warning message
|
|
138
|
+
*/
|
|
139
|
+
warn(message, context) {
|
|
140
|
+
this.log.warn(this.format('warn', message, context));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Log error message
|
|
144
|
+
*/
|
|
145
|
+
error(message, context) {
|
|
146
|
+
this.log.error(this.format('error', message, context));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Log an error with stack trace
|
|
150
|
+
*/
|
|
151
|
+
logError(message, error, context) {
|
|
152
|
+
const errorInfo = {
|
|
153
|
+
code: error instanceof Error ? error.name : 'UNKNOWN',
|
|
154
|
+
message: (0, sanitizers_1.sanitizeError)(error),
|
|
155
|
+
};
|
|
156
|
+
this.error(message, { ...context, error: errorInfo });
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a child logger with additional context
|
|
160
|
+
*/
|
|
161
|
+
child(context) {
|
|
162
|
+
// Create a new logger that includes the context in every message
|
|
163
|
+
const childLogger = new StructuredLogger(this.log, this.config);
|
|
164
|
+
const originalFormat = childLogger.format.bind(childLogger);
|
|
165
|
+
childLogger['format'] = (level, message, ctx) => {
|
|
166
|
+
return originalFormat(level, message, { ...context, ...ctx });
|
|
167
|
+
};
|
|
168
|
+
return childLogger;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.StructuredLogger = StructuredLogger;
|
|
172
|
+
/**
|
|
173
|
+
* Create a leveled logger from Homebridge logger
|
|
174
|
+
*/
|
|
175
|
+
function createLogger(log, level = exports.DEFAULT_LOG_LEVEL) {
|
|
176
|
+
return new LeveledLogger(log, level);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Create a structured logger
|
|
180
|
+
*/
|
|
181
|
+
function createStructuredLogger(log, config) {
|
|
182
|
+
return new StructuredLogger(log, config);
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 tbaur
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0
|
|
5
|
+
* See LICENSE file for full license text
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Centralized retry logic with configurable policies
|
|
8
|
+
*/
|
|
9
|
+
import type { RetryPolicy } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* Default retry policy
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_RETRY_POLICY: RetryPolicy;
|
|
14
|
+
/**
|
|
15
|
+
* Aggressive retry policy for critical operations
|
|
16
|
+
*/
|
|
17
|
+
export declare const AGGRESSIVE_RETRY_POLICY: RetryPolicy;
|
|
18
|
+
/**
|
|
19
|
+
* Conservative retry policy for non-critical operations
|
|
20
|
+
*/
|
|
21
|
+
export declare const CONSERVATIVE_RETRY_POLICY: RetryPolicy;
|
|
22
|
+
/**
|
|
23
|
+
* Sleep for a specified duration
|
|
24
|
+
*/
|
|
25
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Calculate delay with exponential backoff and jitter
|
|
28
|
+
*/
|
|
29
|
+
export declare function calculateBackoffDelay(attempt: number, baseDelay: number, maxDelay: number, multiplier: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Execute a function with retry logic
|
|
32
|
+
*/
|
|
33
|
+
export declare function withRetry<T>(fn: () => Promise<T>, policy?: Partial<RetryPolicy>): Promise<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Create a retryable version of a function
|
|
36
|
+
*/
|
|
37
|
+
export declare function makeRetryable<T extends unknown[], R>(fn: (...args: T) => Promise<R>, policy?: Partial<RetryPolicy>): (...args: T) => Promise<R>;
|
|
38
|
+
/**
|
|
39
|
+
* Retry with timeout
|
|
40
|
+
*/
|
|
41
|
+
export declare function withRetryAndTimeout<T>(fn: () => Promise<T>, timeoutMs: number, policy?: Partial<RetryPolicy>): Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Context for retry operations (for logging)
|
|
44
|
+
*/
|
|
45
|
+
export interface RetryContext {
|
|
46
|
+
operation: string;
|
|
47
|
+
attempt: number;
|
|
48
|
+
maxAttempts: number;
|
|
49
|
+
error?: Error;
|
|
50
|
+
delay?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Execute with retry and context logging
|
|
54
|
+
*/
|
|
55
|
+
export declare function withRetryContext<T>(operation: string, fn: () => Promise<T>, policy?: Partial<RetryPolicy>, logger?: (context: RetryContext) => void): Promise<T>;
|
|
56
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAMlC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAMrC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,WAMvC,CAAA;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CASR;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAChC,OAAO,CAAC,CAAC,CAAC,CA4CZ;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EAClD,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC9B,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAChC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAE5B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAChC,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,EACjC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,GACvC,OAAO,CAAC,CAAC,CAAC,CA0BZ"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 tbaur
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
* See LICENSE file for full license text
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview Centralized retry logic with configurable policies
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.CONSERVATIVE_RETRY_POLICY = exports.AGGRESSIVE_RETRY_POLICY = exports.DEFAULT_RETRY_POLICY = void 0;
|
|
12
|
+
exports.sleep = sleep;
|
|
13
|
+
exports.calculateBackoffDelay = calculateBackoffDelay;
|
|
14
|
+
exports.withRetry = withRetry;
|
|
15
|
+
exports.makeRetryable = makeRetryable;
|
|
16
|
+
exports.withRetryAndTimeout = withRetryAndTimeout;
|
|
17
|
+
exports.withRetryContext = withRetryContext;
|
|
18
|
+
const errors_1 = require("../errors");
|
|
19
|
+
/**
|
|
20
|
+
* Default retry policy
|
|
21
|
+
*/
|
|
22
|
+
exports.DEFAULT_RETRY_POLICY = {
|
|
23
|
+
maxAttempts: 3,
|
|
24
|
+
baseDelay: 1000,
|
|
25
|
+
maxDelay: 30000,
|
|
26
|
+
backoffMultiplier: 2,
|
|
27
|
+
retryableErrors: ['AUTH_ERROR', 'TOKEN_EXPIRED', 'NETWORK_ERROR', 'TIMEOUT', 'RATE_LIMITED'],
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Aggressive retry policy for critical operations
|
|
31
|
+
*/
|
|
32
|
+
exports.AGGRESSIVE_RETRY_POLICY = {
|
|
33
|
+
maxAttempts: 5,
|
|
34
|
+
baseDelay: 500,
|
|
35
|
+
maxDelay: 60000,
|
|
36
|
+
backoffMultiplier: 2,
|
|
37
|
+
retryableErrors: ['AUTH_ERROR', 'TOKEN_EXPIRED', 'NETWORK_ERROR', 'TIMEOUT', 'RATE_LIMITED', 'API_ERROR'],
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Conservative retry policy for non-critical operations
|
|
41
|
+
*/
|
|
42
|
+
exports.CONSERVATIVE_RETRY_POLICY = {
|
|
43
|
+
maxAttempts: 2,
|
|
44
|
+
baseDelay: 2000,
|
|
45
|
+
maxDelay: 10000,
|
|
46
|
+
backoffMultiplier: 1.5,
|
|
47
|
+
retryableErrors: ['NETWORK_ERROR', 'TIMEOUT'],
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Sleep for a specified duration
|
|
51
|
+
*/
|
|
52
|
+
function sleep(ms) {
|
|
53
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Calculate delay with exponential backoff and jitter
|
|
57
|
+
*/
|
|
58
|
+
function calculateBackoffDelay(attempt, baseDelay, maxDelay, multiplier) {
|
|
59
|
+
// Exponential backoff
|
|
60
|
+
const exponentialDelay = baseDelay * Math.pow(multiplier, attempt - 1);
|
|
61
|
+
// Add jitter (±25%)
|
|
62
|
+
const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1);
|
|
63
|
+
// Cap at max delay
|
|
64
|
+
return Math.min(exponentialDelay + jitter, maxDelay);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute a function with retry logic
|
|
68
|
+
*/
|
|
69
|
+
async function withRetry(fn, policy = {}) {
|
|
70
|
+
const { maxAttempts, baseDelay, maxDelay, backoffMultiplier, retryableErrors, onRetry, } = { ...exports.DEFAULT_RETRY_POLICY, ...policy };
|
|
71
|
+
let lastError;
|
|
72
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
73
|
+
try {
|
|
74
|
+
return await fn();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
lastError = error;
|
|
78
|
+
const errorCode = (0, errors_1.getErrorCode)(error);
|
|
79
|
+
// Check if error is retryable
|
|
80
|
+
const isRetryable = (0, errors_1.isRetryableError)(error) || retryableErrors.includes(errorCode);
|
|
81
|
+
if (!isRetryable || attempt === maxAttempts) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
// Calculate delay
|
|
85
|
+
let delay;
|
|
86
|
+
if (error instanceof errors_1.RateLimitError) {
|
|
87
|
+
// Use retry-after from rate limit error if available
|
|
88
|
+
delay = error.retryAfter * 1000;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
delay = calculateBackoffDelay(attempt, baseDelay, maxDelay, backoffMultiplier);
|
|
92
|
+
}
|
|
93
|
+
// Call retry callback if provided
|
|
94
|
+
onRetry?.(attempt, error);
|
|
95
|
+
// Wait before retrying
|
|
96
|
+
await sleep(delay);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
throw lastError;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a retryable version of a function
|
|
103
|
+
*/
|
|
104
|
+
function makeRetryable(fn, policy = {}) {
|
|
105
|
+
return (...args) => withRetry(() => fn(...args), policy);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Retry with timeout
|
|
109
|
+
*/
|
|
110
|
+
async function withRetryAndTimeout(fn, timeoutMs, policy = {}) {
|
|
111
|
+
return withRetry(async () => {
|
|
112
|
+
return Promise.race([
|
|
113
|
+
fn(),
|
|
114
|
+
new Promise((_, reject) => {
|
|
115
|
+
setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
116
|
+
}),
|
|
117
|
+
]);
|
|
118
|
+
}, policy);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Execute with retry and context logging
|
|
122
|
+
*/
|
|
123
|
+
async function withRetryContext(operation, fn, policy = {}, logger) {
|
|
124
|
+
const mergedPolicy = { ...exports.DEFAULT_RETRY_POLICY, ...policy };
|
|
125
|
+
const policyWithLogging = {
|
|
126
|
+
...mergedPolicy,
|
|
127
|
+
onRetry: (attempt, error) => {
|
|
128
|
+
const delay = calculateBackoffDelay(attempt, mergedPolicy.baseDelay, mergedPolicy.maxDelay, mergedPolicy.backoffMultiplier);
|
|
129
|
+
logger?.({
|
|
130
|
+
operation,
|
|
131
|
+
attempt,
|
|
132
|
+
maxAttempts: mergedPolicy.maxAttempts,
|
|
133
|
+
error,
|
|
134
|
+
delay,
|
|
135
|
+
});
|
|
136
|
+
mergedPolicy.onRetry?.(attempt, error);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
return withRetry(fn, policyWithLogging);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 tbaur
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0
|
|
5
|
+
* See LICENSE file for full license text
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Data sanitization utilities for security
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Sanitize error messages to prevent exposing sensitive data
|
|
11
|
+
*/
|
|
12
|
+
export declare function sanitizeError(err: unknown): string;
|
|
13
|
+
/**
|
|
14
|
+
* Sanitize a string by removing sensitive data
|
|
15
|
+
*/
|
|
16
|
+
export declare function sanitizeString(str: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Sanitize an object by redacting sensitive fields
|
|
19
|
+
*/
|
|
20
|
+
export declare function sanitizeObject<T extends Record<string, unknown>>(obj: T): T;
|
|
21
|
+
/**
|
|
22
|
+
* Truncate a string to a maximum length
|
|
23
|
+
*/
|
|
24
|
+
export declare function truncate(str: string, maxLength: number, suffix?: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Mask a token for logging (show first and last few characters)
|
|
27
|
+
*/
|
|
28
|
+
export declare function maskToken(token: string, visibleChars?: number): string;
|
|
29
|
+
/**
|
|
30
|
+
* Create a safe preview of a response body for logging
|
|
31
|
+
*/
|
|
32
|
+
export declare function createResponsePreview(body: string, maxLength?: number): string;
|
|
33
|
+
/**
|
|
34
|
+
* Sanitize stack trace by removing file paths that might expose system info
|
|
35
|
+
*/
|
|
36
|
+
export declare function sanitizeStackTrace(stack: string | undefined): string | undefined;
|
|
37
|
+
//# sourceMappingURL=sanitizers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizers.d.ts","sourceRoot":"","sources":["../../src/utils/sanitizers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAYlD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CA6B3E;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAQ,GAAG,MAAM,CAK/E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,MAAM,CAKjE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,MAAM,CAG3E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAQhF"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 tbaur
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
* See LICENSE file for full license text
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview Data sanitization utilities for security
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.sanitizeError = sanitizeError;
|
|
12
|
+
exports.sanitizeString = sanitizeString;
|
|
13
|
+
exports.sanitizeObject = sanitizeObject;
|
|
14
|
+
exports.truncate = truncate;
|
|
15
|
+
exports.maskToken = maskToken;
|
|
16
|
+
exports.createResponsePreview = createResponsePreview;
|
|
17
|
+
exports.sanitizeStackTrace = sanitizeStackTrace;
|
|
18
|
+
/**
|
|
19
|
+
* Patterns for sensitive data that should be redacted
|
|
20
|
+
*/
|
|
21
|
+
const SENSITIVE_PATTERNS = [
|
|
22
|
+
{ pattern: /password[=:]\s*\S+/gi, replacement: 'password=***' },
|
|
23
|
+
{ pattern: /token[=:]\s*\S+/gi, replacement: 'token=***' },
|
|
24
|
+
{ pattern: /email[=:]\s*\S+/gi, replacement: 'email=***' },
|
|
25
|
+
{ pattern: /authorization[=:]\s*\S+/gi, replacement: 'Authorization=***' },
|
|
26
|
+
{ pattern: /bearer\s+\S+/gi, replacement: 'Bearer ***' },
|
|
27
|
+
{ pattern: /"password"\s*:\s*"[^"]+"/gi, replacement: '"password":"***"' },
|
|
28
|
+
{ pattern: /"token"\s*:\s*"[^"]+"/gi, replacement: '"token":"***"' },
|
|
29
|
+
{ pattern: /"id"\s*:\s*"[a-zA-Z0-9]{20,}"/gi, replacement: '"id":"***"' },
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Sanitize error messages to prevent exposing sensitive data
|
|
33
|
+
*/
|
|
34
|
+
function sanitizeError(err) {
|
|
35
|
+
let message;
|
|
36
|
+
if (err instanceof Error) {
|
|
37
|
+
message = err.message;
|
|
38
|
+
}
|
|
39
|
+
else if (typeof err === 'string') {
|
|
40
|
+
message = err;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
message = String(err);
|
|
44
|
+
}
|
|
45
|
+
return sanitizeString(message);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sanitize a string by removing sensitive data
|
|
49
|
+
*/
|
|
50
|
+
function sanitizeString(str) {
|
|
51
|
+
let result = str;
|
|
52
|
+
for (const { pattern, replacement } of SENSITIVE_PATTERNS) {
|
|
53
|
+
result = result.replace(pattern, replacement);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Sanitize an object by redacting sensitive fields
|
|
59
|
+
*/
|
|
60
|
+
function sanitizeObject(obj) {
|
|
61
|
+
const sensitiveKeys = new Set([
|
|
62
|
+
'password',
|
|
63
|
+
'token',
|
|
64
|
+
'authorization',
|
|
65
|
+
'secret',
|
|
66
|
+
'apiKey',
|
|
67
|
+
'api_key',
|
|
68
|
+
'accessToken',
|
|
69
|
+
'access_token',
|
|
70
|
+
'refreshToken',
|
|
71
|
+
'refresh_token',
|
|
72
|
+
]);
|
|
73
|
+
const result = {};
|
|
74
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
75
|
+
if (sensitiveKeys.has(key.toLowerCase())) {
|
|
76
|
+
result[key] = '***';
|
|
77
|
+
}
|
|
78
|
+
else if (typeof value === 'object' && value !== null) {
|
|
79
|
+
result[key] = sanitizeObject(value);
|
|
80
|
+
}
|
|
81
|
+
else if (typeof value === 'string') {
|
|
82
|
+
result[key] = sanitizeString(value);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
result[key] = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Truncate a string to a maximum length
|
|
92
|
+
*/
|
|
93
|
+
function truncate(str, maxLength, suffix = '...') {
|
|
94
|
+
if (str.length <= maxLength) {
|
|
95
|
+
return str;
|
|
96
|
+
}
|
|
97
|
+
return str.substring(0, maxLength - suffix.length) + suffix;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Mask a token for logging (show first and last few characters)
|
|
101
|
+
*/
|
|
102
|
+
function maskToken(token, visibleChars = 4) {
|
|
103
|
+
if (token.length <= visibleChars * 2) {
|
|
104
|
+
return '***';
|
|
105
|
+
}
|
|
106
|
+
return `${token.substring(0, visibleChars)}...${token.substring(token.length - visibleChars)}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create a safe preview of a response body for logging
|
|
110
|
+
*/
|
|
111
|
+
function createResponsePreview(body, maxLength = 200) {
|
|
112
|
+
const sanitized = sanitizeString(body);
|
|
113
|
+
return truncate(sanitized, maxLength);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Sanitize stack trace by removing file paths that might expose system info
|
|
117
|
+
*/
|
|
118
|
+
function sanitizeStackTrace(stack) {
|
|
119
|
+
if (!stack) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
// Remove absolute paths, keep relative
|
|
123
|
+
return stack.replace(/\s+at\s+.*\((\/[^)]+)\)/g, (match, path) => {
|
|
124
|
+
const filename = path.split('/').pop() || path;
|
|
125
|
+
return match.replace(path, filename);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=sanitizers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../src/utils/sanitizers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 tbaur
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0
|
|
5
|
+
* See LICENSE file for full license text
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Input validation utilities
|
|
8
|
+
*/
|
|
9
|
+
import type { LevitonConfig, PowerState } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* Validate email format
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateEmail(email: unknown): string;
|
|
14
|
+
/**
|
|
15
|
+
* Validate password
|
|
16
|
+
*/
|
|
17
|
+
export declare function validatePassword(password: unknown): string;
|
|
18
|
+
/**
|
|
19
|
+
* Validate device ID
|
|
20
|
+
* Accepts strings or numbers (Leviton API returns numeric IDs)
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateDeviceId(id: unknown): string;
|
|
23
|
+
/**
|
|
24
|
+
* Validate device serial
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateSerial(serial: unknown): string;
|
|
27
|
+
/**
|
|
28
|
+
* Validate authentication token
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateToken(token: unknown): string;
|
|
31
|
+
/**
|
|
32
|
+
* Validate power state
|
|
33
|
+
*/
|
|
34
|
+
export declare function validatePowerState(power: unknown): PowerState;
|
|
35
|
+
/**
|
|
36
|
+
* Validate brightness value (0-100)
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateBrightness(brightness: unknown): number;
|
|
39
|
+
/**
|
|
40
|
+
* Validate plugin configuration
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateConfig(config: unknown): LevitonConfig;
|
|
43
|
+
/**
|
|
44
|
+
* Validate that a value is defined (not null or undefined)
|
|
45
|
+
*/
|
|
46
|
+
export declare function assertDefined<T>(value: T | null | undefined, name: string): T;
|
|
47
|
+
/**
|
|
48
|
+
* Validate that a value is a non-empty array
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateNonEmptyArray<T>(arr: unknown, name: string): T[];
|
|
51
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAiBzD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAc1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CAoBpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAetD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAK7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAc9D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,CAqF7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAK7E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,CAQxE"}
|