@mp-consulting/homebridge-daikin-cloud 1.5.2 → 1.6.1

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.
Files changed (211) hide show
  1. package/README.md +40 -27
  2. package/config.schema.json +66 -12
  3. package/dist/src/accessories/air-conditioning-accessory.js +1 -1
  4. package/dist/src/accessories/air-conditioning-accessory.js.map +1 -1
  5. package/dist/src/accessories/altherma-accessory.js +1 -1
  6. package/dist/src/accessories/altherma-accessory.js.map +1 -1
  7. package/dist/src/accessories/base-accessory.d.ts +5 -4
  8. package/dist/src/accessories/base-accessory.d.ts.map +1 -1
  9. package/dist/src/accessories/base-accessory.js +9 -10
  10. package/dist/src/accessories/base-accessory.js.map +1 -1
  11. package/dist/src/api/daikin-api.d.ts +22 -6
  12. package/dist/src/api/daikin-api.d.ts.map +1 -1
  13. package/dist/src/api/daikin-api.js +49 -23
  14. package/dist/src/api/daikin-api.js.map +1 -1
  15. package/dist/src/api/daikin-controller.d.ts +4 -48
  16. package/dist/src/api/daikin-controller.d.ts.map +1 -1
  17. package/dist/src/api/daikin-controller.js +14 -84
  18. package/dist/src/api/daikin-controller.js.map +1 -1
  19. package/dist/src/api/daikin-mobile-oauth.d.ts +11 -40
  20. package/dist/src/api/daikin-mobile-oauth.d.ts.map +1 -1
  21. package/dist/src/api/daikin-mobile-oauth.js +36 -160
  22. package/dist/src/api/daikin-mobile-oauth.js.map +1 -1
  23. package/dist/src/api/daikin-oauth.d.ts +10 -37
  24. package/dist/src/api/daikin-oauth.d.ts.map +1 -1
  25. package/dist/src/api/daikin-oauth.js +36 -166
  26. package/dist/src/api/daikin-oauth.js.map +1 -1
  27. package/dist/src/api/daikin-schemas.d.ts +20 -22
  28. package/dist/src/api/daikin-schemas.d.ts.map +1 -1
  29. package/dist/src/api/daikin-schemas.js +17 -49
  30. package/dist/src/api/daikin-schemas.js.map +1 -1
  31. package/dist/src/api/daikin-types.d.ts +11 -23
  32. package/dist/src/api/daikin-types.d.ts.map +1 -1
  33. package/dist/src/api/daikin-types.js.map +1 -1
  34. package/dist/src/api/daikin-websocket.d.ts +4 -5
  35. package/dist/src/api/daikin-websocket.d.ts.map +1 -1
  36. package/dist/src/api/daikin-websocket.js +62 -34
  37. package/dist/src/api/daikin-websocket.js.map +1 -1
  38. package/dist/src/api/http-defaults.d.ts +3 -2
  39. package/dist/src/api/http-defaults.d.ts.map +1 -1
  40. package/dist/src/api/http-defaults.js.map +1 -1
  41. package/dist/src/api/http-transport.d.ts +14 -2
  42. package/dist/src/api/http-transport.d.ts.map +1 -1
  43. package/dist/src/api/http-transport.js +32 -9
  44. package/dist/src/api/http-transport.js.map +1 -1
  45. package/dist/src/api/token-client.d.ts +82 -0
  46. package/dist/src/api/token-client.d.ts.map +1 -0
  47. package/dist/src/api/token-client.js +188 -0
  48. package/dist/src/api/token-client.js.map +1 -0
  49. package/dist/src/api/token-storage.d.ts +6 -0
  50. package/dist/src/api/token-storage.d.ts.map +1 -1
  51. package/dist/src/api/token-storage.js +27 -1
  52. package/dist/src/api/token-storage.js.map +1 -1
  53. package/dist/src/config/config-manager.d.ts +69 -125
  54. package/dist/src/config/config-manager.d.ts.map +1 -1
  55. package/dist/src/config/config-manager.js +288 -225
  56. package/dist/src/config/config-manager.js.map +1 -1
  57. package/dist/src/constants/api.constants.d.ts +6 -0
  58. package/dist/src/constants/api.constants.d.ts.map +1 -1
  59. package/dist/src/constants/api.constants.js +7 -1
  60. package/dist/src/constants/api.constants.js.map +1 -1
  61. package/dist/src/constants/auth.constants.d.ts +11 -0
  62. package/dist/src/constants/auth.constants.d.ts.map +1 -1
  63. package/dist/src/constants/auth.constants.js +12 -1
  64. package/dist/src/constants/auth.constants.js.map +1 -1
  65. package/dist/src/constants/device.constants.d.ts +1 -7
  66. package/dist/src/constants/device.constants.d.ts.map +1 -1
  67. package/dist/src/constants/device.constants.js +2 -11
  68. package/dist/src/constants/device.constants.js.map +1 -1
  69. package/dist/src/constants/time.constants.d.ts +0 -2
  70. package/dist/src/constants/time.constants.d.ts.map +1 -1
  71. package/dist/src/constants/time.constants.js +1 -3
  72. package/dist/src/constants/time.constants.js.map +1 -1
  73. package/dist/src/device/accessory-factory.d.ts +0 -5
  74. package/dist/src/device/accessory-factory.d.ts.map +1 -1
  75. package/dist/src/device/accessory-factory.js +0 -7
  76. package/dist/src/device/accessory-factory.js.map +1 -1
  77. package/dist/src/device/capability-detector.d.ts +9 -1
  78. package/dist/src/device/capability-detector.d.ts.map +1 -1
  79. package/dist/src/device/capability-detector.js +32 -28
  80. package/dist/src/device/capability-detector.js.map +1 -1
  81. package/dist/src/device/capability-docs.d.ts.map +1 -1
  82. package/dist/src/device/capability-docs.js +3 -0
  83. package/dist/src/device/capability-docs.js.map +1 -1
  84. package/dist/src/device/data-paths.d.ts +18 -0
  85. package/dist/src/device/data-paths.d.ts.map +1 -0
  86. package/dist/src/device/data-paths.js +20 -0
  87. package/dist/src/device/data-paths.js.map +1 -0
  88. package/dist/src/device/profiles/device-profile.d.ts +0 -5
  89. package/dist/src/device/profiles/device-profile.d.ts.map +1 -1
  90. package/dist/src/device/profiles/device-profile.js +0 -21
  91. package/dist/src/device/profiles/device-profile.js.map +1 -1
  92. package/dist/src/features/base-feature.d.ts +53 -9
  93. package/dist/src/features/base-feature.d.ts.map +1 -1
  94. package/dist/src/features/base-feature.js +93 -40
  95. package/dist/src/features/base-feature.js.map +1 -1
  96. package/dist/src/features/feature-manager.d.ts +27 -15
  97. package/dist/src/features/feature-manager.d.ts.map +1 -1
  98. package/dist/src/features/feature-manager.js +27 -24
  99. package/dist/src/features/feature-manager.js.map +1 -1
  100. package/dist/src/features/index.d.ts +1 -0
  101. package/dist/src/features/index.d.ts.map +1 -1
  102. package/dist/src/features/index.js +1 -0
  103. package/dist/src/features/index.js.map +1 -1
  104. package/dist/src/features/modes/auto-fan-mode.feature.d.ts +16 -10
  105. package/dist/src/features/modes/auto-fan-mode.feature.d.ts.map +1 -1
  106. package/dist/src/features/modes/auto-fan-mode.feature.js +16 -40
  107. package/dist/src/features/modes/auto-fan-mode.feature.js.map +1 -1
  108. package/dist/src/features/modes/dry-operation-mode.feature.d.ts +12 -9
  109. package/dist/src/features/modes/dry-operation-mode.feature.d.ts.map +1 -1
  110. package/dist/src/features/modes/dry-operation-mode.feature.js +11 -30
  111. package/dist/src/features/modes/dry-operation-mode.feature.js.map +1 -1
  112. package/dist/src/features/modes/econo-mode.feature.d.ts +12 -9
  113. package/dist/src/features/modes/econo-mode.feature.d.ts.map +1 -1
  114. package/dist/src/features/modes/econo-mode.feature.js +11 -31
  115. package/dist/src/features/modes/econo-mode.feature.js.map +1 -1
  116. package/dist/src/features/modes/fan-only-operation-mode.feature.d.ts +12 -9
  117. package/dist/src/features/modes/fan-only-operation-mode.feature.d.ts.map +1 -1
  118. package/dist/src/features/modes/fan-only-operation-mode.feature.js +11 -30
  119. package/dist/src/features/modes/fan-only-operation-mode.feature.js.map +1 -1
  120. package/dist/src/features/modes/firmware-update.feature.d.ts +7 -6
  121. package/dist/src/features/modes/firmware-update.feature.d.ts.map +1 -1
  122. package/dist/src/features/modes/firmware-update.feature.js +15 -22
  123. package/dist/src/features/modes/firmware-update.feature.js.map +1 -1
  124. package/dist/src/features/modes/holiday-mode.feature.d.ts +2 -1
  125. package/dist/src/features/modes/holiday-mode.feature.d.ts.map +1 -1
  126. package/dist/src/features/modes/holiday-mode.feature.js +5 -15
  127. package/dist/src/features/modes/holiday-mode.feature.js.map +1 -1
  128. package/dist/src/features/modes/indoor-silent-mode.feature.d.ts +15 -12
  129. package/dist/src/features/modes/indoor-silent-mode.feature.d.ts.map +1 -1
  130. package/dist/src/features/modes/indoor-silent-mode.feature.js +15 -39
  131. package/dist/src/features/modes/indoor-silent-mode.feature.js.map +1 -1
  132. package/dist/src/features/modes/oscillation-mode.feature.d.ts +5 -6
  133. package/dist/src/features/modes/oscillation-mode.feature.d.ts.map +1 -1
  134. package/dist/src/features/modes/oscillation-mode.feature.js +14 -36
  135. package/dist/src/features/modes/oscillation-mode.feature.js.map +1 -1
  136. package/dist/src/features/modes/outdoor-silent-mode.feature.d.ts +12 -9
  137. package/dist/src/features/modes/outdoor-silent-mode.feature.d.ts.map +1 -1
  138. package/dist/src/features/modes/outdoor-silent-mode.feature.js +11 -31
  139. package/dist/src/features/modes/outdoor-silent-mode.feature.js.map +1 -1
  140. package/dist/src/features/modes/powerful-mode.feature.d.ts +14 -10
  141. package/dist/src/features/modes/powerful-mode.feature.d.ts.map +1 -1
  142. package/dist/src/features/modes/powerful-mode.feature.js +13 -32
  143. package/dist/src/features/modes/powerful-mode.feature.js.map +1 -1
  144. package/dist/src/features/modes/streamer-mode.feature.d.ts +12 -9
  145. package/dist/src/features/modes/streamer-mode.feature.d.ts.map +1 -1
  146. package/dist/src/features/modes/streamer-mode.feature.js +11 -31
  147. package/dist/src/features/modes/streamer-mode.feature.js.map +1 -1
  148. package/dist/src/features/on-off-data-point-feature.d.ts +43 -0
  149. package/dist/src/features/on-off-data-point-feature.d.ts.map +1 -0
  150. package/dist/src/features/on-off-data-point-feature.js +49 -0
  151. package/dist/src/features/on-off-data-point-feature.js.map +1 -0
  152. package/dist/src/platform.d.ts +56 -9
  153. package/dist/src/platform.d.ts.map +1 -1
  154. package/dist/src/platform.js +248 -128
  155. package/dist/src/platform.js.map +1 -1
  156. package/dist/src/services/climate-control/fan-speed.d.ts +45 -0
  157. package/dist/src/services/climate-control/fan-speed.d.ts.map +1 -0
  158. package/dist/src/services/climate-control/fan-speed.js +104 -0
  159. package/dist/src/services/climate-control/fan-speed.js.map +1 -0
  160. package/dist/src/services/climate-control/separate-fan.service.d.ts +45 -0
  161. package/dist/src/services/climate-control/separate-fan.service.d.ts.map +1 -0
  162. package/dist/src/services/climate-control/separate-fan.service.js +82 -0
  163. package/dist/src/services/climate-control/separate-fan.service.js.map +1 -0
  164. package/dist/src/services/climate-control/setpoint-resolver.d.ts +68 -0
  165. package/dist/src/services/climate-control/setpoint-resolver.d.ts.map +1 -0
  166. package/dist/src/services/climate-control/setpoint-resolver.js +170 -0
  167. package/dist/src/services/climate-control/setpoint-resolver.js.map +1 -0
  168. package/dist/src/services/climate-control/swing-controller.d.ts +28 -0
  169. package/dist/src/services/climate-control/swing-controller.d.ts.map +1 -0
  170. package/dist/src/services/climate-control/swing-controller.js +65 -0
  171. package/dist/src/services/climate-control/swing-controller.js.map +1 -0
  172. package/dist/src/services/climate-control.service.d.ts +39 -48
  173. package/dist/src/services/climate-control.service.d.ts.map +1 -1
  174. package/dist/src/services/climate-control.service.js +223 -554
  175. package/dist/src/services/climate-control.service.js.map +1 -1
  176. package/dist/src/services/hot-water-tank.service.d.ts +17 -8
  177. package/dist/src/services/hot-water-tank.service.d.ts.map +1 -1
  178. package/dist/src/services/hot-water-tank.service.js +102 -157
  179. package/dist/src/services/hot-water-tank.service.js.map +1 -1
  180. package/dist/src/types/daikin-enums.d.ts +0 -5
  181. package/dist/src/types/daikin-enums.d.ts.map +1 -1
  182. package/dist/src/types/daikin-enums.js +1 -7
  183. package/dist/src/types/daikin-enums.js.map +1 -1
  184. package/dist/src/types/device-capabilities.d.ts +1 -0
  185. package/dist/src/types/device-capabilities.d.ts.map +1 -1
  186. package/dist/src/utils/errors.d.ts +9 -0
  187. package/dist/src/utils/errors.d.ts.map +1 -0
  188. package/dist/src/utils/errors.js +25 -0
  189. package/dist/src/utils/errors.js.map +1 -0
  190. package/dist/src/utils/hap-write.d.ts +22 -0
  191. package/dist/src/utils/hap-write.d.ts.map +1 -0
  192. package/dist/src/utils/hap-write.js +33 -0
  193. package/dist/src/utils/hap-write.js.map +1 -0
  194. package/dist/src/utils/sleep.d.ts +5 -0
  195. package/dist/src/utils/sleep.d.ts.map +1 -0
  196. package/dist/src/utils/sleep.js +10 -0
  197. package/dist/src/utils/sleep.js.map +1 -0
  198. package/homebridge-ui/README.md +25 -2
  199. package/homebridge-ui/public/index.html +19 -17
  200. package/homebridge-ui/public/script.js +95 -32
  201. package/homebridge-ui/public/styles.css +109 -0
  202. package/homebridge-ui/server.js +687 -453
  203. package/package.json +3 -3
  204. package/dist/src/utils/log-context.d.ts +0 -105
  205. package/dist/src/utils/log-context.d.ts.map +0 -1
  206. package/dist/src/utils/log-context.js +0 -206
  207. package/dist/src/utils/log-context.js.map +0 -1
  208. package/dist/src/utils/update-mapper.d.ts +0 -59
  209. package/dist/src/utils/update-mapper.d.ts.map +0 -1
  210. package/dist/src/utils/update-mapper.js +0 -192
  211. package/dist/src/utils/update-mapper.js.map +0 -1
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ /**
3
+ * Token Client
4
+ *
5
+ * Token lifecycle shared by both OAuth flows (Developer Portal and Mobile
6
+ * App): persistence to the token file, expiry checks with a refresh buffer,
7
+ * single-flight refresh, and strict parsing of token endpoint responses.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.TokenClient = void 0;
11
+ exports.withExpiresAt = withExpiresAt;
12
+ exports.isTokenExpiring = isTokenExpiring;
13
+ exports.parseJsonBody = parseJsonBody;
14
+ exports.parseTokenResponse = parseTokenResponse;
15
+ const daikin_schemas_1 = require("./daikin-schemas");
16
+ const token_storage_1 = require("./token-storage");
17
+ const constants_1 = require("../constants");
18
+ const errors_1 = require("../utils/errors");
19
+ function nowSeconds() {
20
+ return Math.floor(Date.now() / 1000);
21
+ }
22
+ /**
23
+ * Return a copy of the token set with `expires_at` derived from `expires_in`
24
+ * when the server only sent the relative lifetime.
25
+ */
26
+ function withExpiresAt(tokenSet) {
27
+ if (tokenSet.expires_in && !tokenSet.expires_at) {
28
+ return { ...tokenSet, expires_at: nowSeconds() + tokenSet.expires_in };
29
+ }
30
+ return tokenSet;
31
+ }
32
+ /**
33
+ * Whether the token is expired or expires within TOKEN_EXPIRY_BUFFER_SECONDS.
34
+ * A token without `expires_at` is treated as expired.
35
+ */
36
+ function isTokenExpiring(tokenSet) {
37
+ return (tokenSet.expires_at ?? 0) < nowSeconds() + constants_1.TOKEN_EXPIRY_BUFFER_SECONDS;
38
+ }
39
+ /**
40
+ * Parse a JSON response body, reporting the endpoint and status instead of a
41
+ * bare "Unexpected token <" when a proxy/WAF answers with HTML or nothing.
42
+ */
43
+ function parseJsonBody(response, url, context) {
44
+ try {
45
+ return JSON.parse(response.body);
46
+ }
47
+ catch {
48
+ const { hostname } = new URL(url);
49
+ const snippet = response.body.trim().replace(/\s+/g, ' ').slice(0, 200);
50
+ throw new Error(`${context}: ${hostname} returned a non-JSON response (HTTP ${response.statusCode})`
51
+ + (snippet ? `: ${snippet}` : ' with an empty body'));
52
+ }
53
+ }
54
+ /**
55
+ * Validate a token endpoint response: OAuth error bodies, non-2xx statuses and
56
+ * malformed token sets all become errors with a readable message. The message
57
+ * keeps the OAuth error code (e.g. `invalid_grant`), which callers match on.
58
+ * Token values are never included.
59
+ */
60
+ function parseTokenResponse(response, url, context) {
61
+ const body = parseJsonBody(response, url, context);
62
+ const oauthError = daikin_schemas_1.OAuthErrorSchema.safeParse(body);
63
+ if (oauthError.success) {
64
+ const { error, error_description: description } = oauthError.data;
65
+ throw new Error(`${context}: ${error}${description ? ` - ${description}` : ''} (HTTP ${response.statusCode})`);
66
+ }
67
+ const { hostname } = new URL(url);
68
+ if (response.statusCode < 200 || response.statusCode >= 300) {
69
+ throw new Error(`${context}: ${hostname} returned HTTP ${response.statusCode}`);
70
+ }
71
+ const result = daikin_schemas_1.TokenSetSchema.safeParse(body);
72
+ if (!result.success) {
73
+ const fields = [...new Set(result.error.issues.map((issue) => issue.path.join('.') || '(body)'))];
74
+ throw new Error(`${context}: ${hostname} returned an invalid token response (missing or malformed: ${fields.join(', ')})`);
75
+ }
76
+ return result.data;
77
+ }
78
+ /**
79
+ * Base class holding the current token set and its refresh logic. Subclasses
80
+ * implement only the grant-specific refresh request.
81
+ */
82
+ class TokenClient {
83
+ tokenFilePath;
84
+ onTokenUpdate;
85
+ onError;
86
+ tokenSet = null;
87
+ refreshPromise = null;
88
+ constructor(tokenFilePath, onTokenUpdate, onError) {
89
+ this.tokenFilePath = tokenFilePath;
90
+ this.onTokenUpdate = onTokenUpdate;
91
+ this.onError = onError;
92
+ this.loadFromFile();
93
+ }
94
+ /**
95
+ * Refresh the access token. Concurrent callers share one in-flight request;
96
+ * a failed refresh is not cached, so the next call retries.
97
+ */
98
+ refreshToken() {
99
+ const refreshToken = this.tokenSet?.refresh_token;
100
+ if (!refreshToken) {
101
+ return Promise.reject(new Error('No refresh token available'));
102
+ }
103
+ if (!this.refreshPromise) {
104
+ this.refreshPromise = this.requestTokenRefresh(refreshToken)
105
+ // Providers may omit refresh_token when they do not rotate it; the
106
+ // previous one stays valid then (RFC 6749 section 6).
107
+ .then((tokenSet) => this.setTokenSet({ ...tokenSet, refresh_token: tokenSet.refresh_token ?? refreshToken }))
108
+ .finally(() => {
109
+ this.refreshPromise = null;
110
+ });
111
+ }
112
+ return this.refreshPromise;
113
+ }
114
+ /**
115
+ * Get a valid access token, refreshing if necessary
116
+ */
117
+ async getAccessToken() {
118
+ const current = this.tokenSet;
119
+ if (!current) {
120
+ throw new Error('Not authenticated. Please authenticate first.');
121
+ }
122
+ if (!isTokenExpiring(current)) {
123
+ return current.access_token;
124
+ }
125
+ if (!current.refresh_token) {
126
+ throw new Error('Token expired and no refresh token available. Please re-authenticate.');
127
+ }
128
+ const refreshed = await this.refreshToken();
129
+ return refreshed.access_token;
130
+ }
131
+ /**
132
+ * Check if we have a token (it may still need a refresh)
133
+ */
134
+ isAuthenticated() {
135
+ return !!this.tokenSet?.access_token;
136
+ }
137
+ /**
138
+ * Get token expiration date
139
+ */
140
+ getTokenExpiration() {
141
+ if (!this.tokenSet?.expires_at) {
142
+ return null;
143
+ }
144
+ return new Date(this.tokenSet.expires_at * 1000);
145
+ }
146
+ /**
147
+ * Get current token set (for status display)
148
+ */
149
+ getTokenSet() {
150
+ return this.tokenSet;
151
+ }
152
+ /**
153
+ * Forget the current tokens and delete the token file
154
+ */
155
+ clearTokens() {
156
+ (0, token_storage_1.deleteTokenFile)(this.tokenFilePath);
157
+ this.tokenSet = null;
158
+ }
159
+ /**
160
+ * Store a new token set (in memory and on disk) and notify listeners.
161
+ * Returns the stored set, with `expires_at` filled in.
162
+ */
163
+ setTokenSet(tokenSet) {
164
+ const stored = withExpiresAt(tokenSet);
165
+ this.tokenSet = stored;
166
+ this.saveToFile(stored);
167
+ this.onTokenUpdate?.(stored);
168
+ return stored;
169
+ }
170
+ loadFromFile() {
171
+ try {
172
+ this.tokenSet = (0, token_storage_1.loadTokenFromFile)(this.tokenFilePath);
173
+ }
174
+ catch (error) {
175
+ this.onError?.(new Error(`Failed to load token file: ${(0, errors_1.toMessage)(error)}`));
176
+ }
177
+ }
178
+ saveToFile(tokenSet) {
179
+ try {
180
+ (0, token_storage_1.saveTokenToFile)(this.tokenFilePath, tokenSet);
181
+ }
182
+ catch (error) {
183
+ this.onError?.(new Error(`Failed to save token file: ${(0, errors_1.toMessage)(error)}`));
184
+ }
185
+ }
186
+ }
187
+ exports.TokenClient = TokenClient;
188
+ //# sourceMappingURL=token-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-client.js","sourceRoot":"","sources":["../../../src/api/token-client.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,12 @@ import type { TokenSet } from './daikin-types';
12
12
  export declare function loadTokenFromFile(filePath: string): TokenSet | null;
13
13
  /**
14
14
  * Save a token set to a file with restricted permissions.
15
+ *
16
+ * Writes atomically (temp file + rename) so a crash mid-write never leaves a
17
+ * truncated token file behind. `mode` on writeFileSync only applies when a
18
+ * file is created, so a pre-existing world-readable token file would keep its
19
+ * permissions: the temp file is always freshly created with 0600 and replaces
20
+ * the old inode, and the final file is chmod'ed again for good measure.
15
21
  */
16
22
  export declare function saveTokenToFile(filePath: string, tokenSet: TokenSet): void;
17
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"token-storage.d.ts","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAgBnE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAM1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQtD"}
1
+ {"version":3,"file":"token-storage.d.ts","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAgBnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAsB1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQtD"}
@@ -43,6 +43,7 @@ exports.loadTokenFromFile = loadTokenFromFile;
43
43
  exports.saveTokenToFile = saveTokenToFile;
44
44
  exports.deleteTokenFile = deleteTokenFile;
45
45
  const fs = __importStar(require("node:fs"));
46
+ const crypto = __importStar(require("node:crypto"));
46
47
  const daikin_schemas_1 = require("./daikin-schemas");
47
48
  /** File permissions: owner read/write only */
48
49
  const TOKEN_FILE_MODE = 0o600;
@@ -70,9 +71,34 @@ function loadTokenFromFile(filePath) {
70
71
  }
71
72
  /**
72
73
  * Save a token set to a file with restricted permissions.
74
+ *
75
+ * Writes atomically (temp file + rename) so a crash mid-write never leaves a
76
+ * truncated token file behind. `mode` on writeFileSync only applies when a
77
+ * file is created, so a pre-existing world-readable token file would keep its
78
+ * permissions: the temp file is always freshly created with 0600 and replaces
79
+ * the old inode, and the final file is chmod'ed again for good measure.
73
80
  */
74
81
  function saveTokenToFile(filePath, tokenSet) {
75
- fs.writeFileSync(filePath, JSON.stringify(tokenSet, null, 2), { encoding: 'utf8', mode: TOKEN_FILE_MODE });
82
+ const tempPath = `${filePath}.tmp-${crypto.randomBytes(8).toString('hex')}`;
83
+ try {
84
+ fs.writeFileSync(tempPath, JSON.stringify(tokenSet, null, 2), { encoding: 'utf8', mode: TOKEN_FILE_MODE, flag: 'wx' });
85
+ fs.renameSync(tempPath, filePath);
86
+ }
87
+ catch (error) {
88
+ try {
89
+ fs.rmSync(tempPath, { force: true });
90
+ }
91
+ catch {
92
+ // Best effort cleanup
93
+ }
94
+ throw error;
95
+ }
96
+ try {
97
+ fs.chmodSync(filePath, TOKEN_FILE_MODE);
98
+ }
99
+ catch {
100
+ // Platforms without POSIX permissions (Windows)
101
+ }
76
102
  }
77
103
  /**
78
104
  * Delete a token file if it exists.
@@ -1 +1 @@
1
- {"version":3,"file":"token-storage.js","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaH,8CAgBC;AAKD,0CAMC;AAKD,0CAQC;AAnDD,4CAA8B;AAE9B,qDAAkD;AAElD,8CAA8C;AAC9C,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,+BAAc,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,MAAM,CAAC,IAAgB,CAAC;YACjC,CAAC;YACD,sDAAsD;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,QAAkB;IAClE,EAAE,CAAC,aAAa,CACd,QAAQ,EACR,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EACjC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAC5C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"token-storage.js","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcH,8CAgBC;AAWD,0CAsBC;AAKD,0CAQC;AA1ED,4CAA8B;AAC9B,oDAAsC;AAEtC,qDAAkD;AAElD,8CAA8C;AAC9C,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,+BAAc,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,MAAM,CAAC,IAAI,CAAC;YACrB,CAAC;YACD,sDAAsD;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,QAAkB;IAClE,MAAM,QAAQ,GAAG,GAAG,QAAQ,QAAQ,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5E,IAAI,CAAC;QACH,EAAE,CAAC,aAAa,CACd,QAAQ,EACR,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EACjC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,EAAE,CACxD,CAAC;QACF,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC;YACH,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;AACH,CAAC"}
@@ -1,18 +1,42 @@
1
1
  /**
2
2
  * Configuration Manager
3
3
  *
4
- * Centralizes configuration management with validation,
5
- * defaults, and type-safe access to config values.
4
+ * Single source of truth for the plugin configuration: the raw Homebridge
5
+ * platform config is normalized once (type coercion, schema bounds, defaults)
6
+ * and every consumer reads it through the typed getters below.
6
7
  */
7
- import type { AuthMode } from '../api/daikin-types';
8
- export interface PluginConfig {
8
+ import type { AuthMode, DaikinControllerConfig } from '../api/daikin-types';
9
+ import type { HttpTransportMode } from '../api/http-transport';
10
+ /**
11
+ * Per-feature config toggles (the `show*` keys of config.schema.json).
12
+ *
13
+ * - `extra`: switches that follow the legacy `showExtraFeatures` catch-all when
14
+ * their own key is not set.
15
+ * - `standalone`: default off and never follow `showExtraFeatures` (a separate
16
+ * Fan tile, and a switch that installs firmware).
17
+ *
18
+ * Kept in sync with config.schema.json and homebridge-ui/public/script.js by
19
+ * test/unit/config/feature-config-keys.test.ts.
20
+ */
21
+ export declare const FEATURE_CONFIG_KEYS: {
22
+ readonly extra: readonly ["showPowerfulMode", "showEconoMode", "showStreamerMode", "showOutdoorSilentMode", "showIndoorSilentMode", "showAutoFanMode", "showOscillationSwitch", "showDryMode", "showFanOnlyMode", "showHolidayMode"];
23
+ readonly standalone: readonly ["showSeparateFanControl", "showFirmwareUpdateSwitch"];
24
+ };
25
+ export type ExtraFeatureConfigKey = typeof FEATURE_CONFIG_KEYS.extra[number];
26
+ export type StandaloneFeatureConfigKey = typeof FEATURE_CONFIG_KEYS.standalone[number];
27
+ export type FeatureConfigKey = ExtraFeatureConfigKey | StandaloneFeatureConfigKey;
28
+ /**
29
+ * Raw plugin config as written by Homebridge. Values come from a user-edited
30
+ * JSON file, so the declared types are what the schema intends, not a guarantee.
31
+ */
32
+ export interface PluginConfig extends Partial<Record<FeatureConfigKey, boolean | null>> {
9
33
  platform: string;
10
34
  name?: string;
11
35
  authMode?: AuthMode;
12
36
  clientId?: string;
13
37
  clientSecret?: string;
14
38
  callbackServerExternalAddress?: string;
15
- callbackServerPort?: number;
39
+ callbackServerPort?: number | string;
16
40
  oidcCallbackServerBindAddr?: string;
17
41
  daikinEmail?: string;
18
42
  daikinPassword?: string;
@@ -24,139 +48,59 @@ export interface PluginConfig {
24
48
  */
25
49
  email?: string;
26
50
  password?: string;
27
- updateIntervalInMinutes?: number;
28
- forceUpdateDelay?: number;
51
+ updateIntervalInMinutes?: number | string;
52
+ forceUpdateDelay?: number | string;
29
53
  excludedDevicesByDeviceId?: string[];
30
- showPowerfulMode?: boolean;
31
- showEconoMode?: boolean;
32
- showStreamerMode?: boolean;
33
- showOutdoorSilentMode?: boolean;
34
- showIndoorSilentMode?: boolean;
35
- showAutoFanMode?: boolean;
36
- showOscillationSwitch?: boolean;
37
- showDryMode?: boolean;
38
- showFanOnlyMode?: boolean;
39
- showHolidayMode?: boolean;
40
- showExtraFeatures?: boolean;
41
- showSeparateFanControl?: boolean;
42
- showFirmwareUpdateSwitch?: boolean;
43
- httpTransport?: 'node' | 'curl';
54
+ showExtraFeatures?: boolean | null;
55
+ httpTransport?: HttpTransportMode;
44
56
  enableWebSocket?: boolean;
45
57
  }
46
- export interface NormalizedConfig {
47
- authMode: AuthMode;
48
- updateIntervalMs: number;
49
- forceUpdateDelayMs: number;
50
- excludedDeviceIds: Set<string>;
51
- features: {
52
- powerfulMode: boolean;
53
- econoMode: boolean;
54
- streamerMode: boolean;
55
- outdoorSilentMode: boolean;
56
- indoorSilentMode: boolean;
57
- autoFanMode: boolean;
58
- oscillationMode: boolean;
59
- dryMode: boolean;
60
- fanOnlyMode: boolean;
61
- holidayMode: boolean;
62
- };
63
- websocketEnabled: boolean;
58
+ export interface ConfigValidationResult {
59
+ valid: boolean;
60
+ errors: string[];
61
+ warnings: string[];
64
62
  }
63
+ /**
64
+ * Validate a raw plugin config. Pure: also used by the custom UI server
65
+ * (homebridge-ui/server.js) for its /config/validate endpoint.
66
+ *
67
+ * Errors are configurations the plugin cannot work with; out-of-range numeric
68
+ * settings are only warnings, because normalization clamps them.
69
+ */
70
+ export declare function validateConfig(config: PluginConfig): ConfigValidationResult;
65
71
  export declare class ConfigManager {
66
72
  private readonly config;
67
- private normalized;
73
+ private readonly normalized;
68
74
  constructor(config: PluginConfig);
69
- /**
70
- * Get the normalized configuration with defaults applied
71
- */
72
- getNormalized(): NormalizedConfig;
73
- /**
74
- * Get authentication mode
75
- */
75
+ /** Platform name as configured in Homebridge */
76
+ getName(): string | undefined;
77
+ /** HTTP transport from the config (the DAIKIN_HTTP_TRANSPORT env var still wins, see configureHttpTransport) */
78
+ getHttpTransport(): HttpTransportMode | undefined;
79
+ /** Polling interval in milliseconds, clamped to 1-60 minutes */
80
+ getUpdateIntervalMs(): number;
81
+ /** Delay before refreshing after a change, in milliseconds, clamped to 5-300 seconds */
82
+ getForceUpdateDelayMs(): number;
83
+ /** Whether the device (raw Daikin device ID) is listed in excludedDevicesByDeviceId */
84
+ isDeviceExcluded(deviceId: string): boolean;
85
+ /** WebSocket push updates are on unless explicitly disabled */
86
+ isWebSocketEnabled(): boolean;
76
87
  getAuthMode(): AuthMode;
77
- /**
78
- * Check if mobile app mode is enabled
79
- */
80
88
  isMobileAppMode(): boolean;
81
- /**
82
- * Check if developer portal mode is enabled
83
- */
84
- isDeveloperPortalMode(): boolean;
85
- /**
86
- * Get developer portal credentials
87
- */
88
- getDeveloperCredentials(): {
89
- clientId: string;
90
- clientSecret: string;
91
- callbackServerExternalAddress: string;
92
- callbackServerPort: number;
93
- oidcCallbackServerBindAddr?: string;
94
- } | null;
95
- /**
96
- * Get mobile app credentials
97
- */
89
+ /** Client ID and secret are present (the minimum to start the Developer Portal controller) */
90
+ hasDeveloperCredentials(): boolean;
91
+ /** Mobile app email and password (falling back to the legacy keys), or null when either is missing */
98
92
  getMobileCredentials(): {
99
93
  email: string;
100
94
  password: string;
101
95
  } | null;
102
- /**
103
- * Mobile app email, falling back to the legacy `email` key
104
- */
105
- getMobileEmail(): string | undefined;
106
- /**
107
- * Mobile app password, falling back to the legacy `password` key
108
- */
109
- getMobilePassword(): string | undefined;
110
- /**
111
- * Legacy credential keys that are in use because the canonical key is absent
112
- */
96
+ /** Legacy credential keys that are in use because the canonical key is absent */
113
97
  getLegacyCredentialKeysInUse(): string[];
114
- /**
115
- * Get update interval in milliseconds
116
- */
117
- getUpdateIntervalMs(): number;
118
- /**
119
- * Get force update delay in milliseconds
120
- */
121
- getForceUpdateDelayMs(): number;
122
- /**
123
- * Get excluded device IDs as a Set
124
- */
125
- getExcludedDeviceIds(): Set<string>;
126
- /**
127
- * Check if a device is excluded
128
- */
129
- isDeviceExcluded(deviceId: string): boolean;
130
- /**
131
- * Get feature configuration
132
- */
133
- getFeatures(): NormalizedConfig['features'];
134
- /**
135
- * Check if WebSocket is enabled
136
- */
137
- isWebSocketEnabled(): boolean;
138
- /**
139
- * Validate configuration using Zod schemas
140
- */
141
- validateWithZod(): {
142
- valid: boolean;
143
- errors: string[];
144
- };
145
- /**
146
- * Validate configuration
147
- */
148
- validate(): {
149
- valid: boolean;
150
- errors: string[];
151
- warnings: string[];
152
- };
153
- /**
154
- * Normalize the configuration with all defaults applied
155
- */
156
- private normalize;
157
- /**
158
- * Get raw config value (escape hatch for migration)
159
- */
160
- getRawConfig(): PluginConfig;
98
+ /** Configuration for the DaikinCloudController, with defaults applied */
99
+ getControllerConfig(tokenFilePath: string): DaikinControllerConfig;
100
+ /** Whether a feature switch / service is enabled (see resolveFeatures for the semantics) */
101
+ isFeatureEnabled(key: FeatureConfigKey): boolean;
102
+ validate(): ConfigValidationResult;
103
+ /** The raw config with credentials and device IDs masked, for debug logging */
104
+ getRedactedConfig(): object;
161
105
  }
162
106
  //# sourceMappingURL=config-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/config/config-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAQpD,MAAM,WAAW,YAAY;IAEzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAGpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAGpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,yBAAyB,CAAC,EAAE,MAAM,EAAE,CAAC;IAGrC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAG5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAIjC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAInC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAGhC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,EAAE;QACN,YAAY,EAAE,OAAO,CAAC;QACtB,SAAS,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,OAAO,CAAC;QACtB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,gBAAgB,EAAE,OAAO,CAAC;QAC1B,WAAW,EAAE,OAAO,CAAC;QACrB,eAAe,EAAE,OAAO,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,gBAAgB,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,UAAU,CAAiC;gBAEvC,MAAM,EAAE,YAAY;IAIhC;;SAEK;IACL,aAAa,IAAI,gBAAgB;IAOjC;;SAEK;IACL,WAAW,IAAI,QAAQ;IAIvB;;SAEK;IACL,eAAe,IAAI,OAAO;IAI1B;;SAEK;IACL,qBAAqB,IAAI,OAAO;IAIhC;;SAEK;IACL,uBAAuB,IAAI;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,6BAA6B,EAAE,MAAM,CAAC;QACtC,kBAAkB,EAAE,MAAM,CAAC;QAC3B,0BAA0B,CAAC,EAAE,MAAM,CAAC;KACvC,GAAG,IAAI;IAgBV;;SAEK;IACL,oBAAoB,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAWlE;;SAEK;IACL,cAAc,IAAI,MAAM,GAAG,SAAS;IAIpC;;SAEK;IACL,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAIvC;;SAEK;IACL,4BAA4B,IAAI,MAAM,EAAE;IAWxC;;SAEK;IACL,mBAAmB,IAAI,MAAM;IAK7B;;SAEK;IACL,qBAAqB,IAAI,MAAM;IAI/B;;SAEK;IACL,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC;IAInC;;SAEK;IACL,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI3C;;SAEK;IACL,WAAW,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAiB3C;;SAEK;IACL,kBAAkB,IAAI,OAAO;IAI7B;;SAEK;IACL,eAAe,IAAI;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAwBvD;;SAEK;IACL,QAAQ,IAAI;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE;IA4EpE;;SAEK;IACL,OAAO,CAAC,SAAS;IAWjB;;SAEK;IACL,YAAY,IAAI,YAAY;CAG7B"}
1
+ {"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/config/config-manager.ts"],"names":[],"mappings":"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"}