@zwave-js/config 14.0.0-beta.1 → 14.0.0-beta.4

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 (214) hide show
  1. package/build/{ConfigManager.d.ts → cjs/ConfigManager.d.ts} +2 -2
  2. package/build/cjs/ConfigManager.js +208 -0
  3. package/build/cjs/ConfigManager.js.map +7 -0
  4. package/build/cjs/JsonTemplate.js +198 -0
  5. package/build/cjs/JsonTemplate.js.map +7 -0
  6. package/build/{Logger.d.ts → cjs/Logger.d.ts} +1 -1
  7. package/build/cjs/Logger.js +50 -0
  8. package/build/cjs/Logger.js.map +7 -0
  9. package/build/cjs/Logger_safe.js +32 -0
  10. package/build/cjs/Logger_safe.js.map +7 -0
  11. package/build/cjs/Logic.js +71 -0
  12. package/build/cjs/Logic.js.map +7 -0
  13. package/build/cjs/LogicParser.js +1343 -0
  14. package/build/cjs/LogicParser.js.map +7 -0
  15. package/build/cjs/Manufacturers.js +88 -0
  16. package/build/cjs/Manufacturers.js.map +7 -0
  17. package/build/cjs/_version.d.ts +2 -0
  18. package/build/cjs/_version.js +29 -0
  19. package/build/cjs/_version.js.map +7 -0
  20. package/build/{devices → cjs/devices}/AssociationConfig.d.ts +2 -2
  21. package/build/cjs/devices/AssociationConfig.js +93 -0
  22. package/build/cjs/devices/AssociationConfig.js.map +7 -0
  23. package/build/{devices → cjs/devices}/CompatConfig.d.ts +2 -2
  24. package/build/cjs/devices/CompatConfig.js +584 -0
  25. package/build/cjs/devices/CompatConfig.js.map +7 -0
  26. package/build/{devices → cjs/devices}/ConditionalItem.d.ts +1 -1
  27. package/build/cjs/devices/ConditionalItem.js +107 -0
  28. package/build/cjs/devices/ConditionalItem.js.map +7 -0
  29. package/build/{devices → cjs/devices}/ConditionalPrimitive.d.ts +2 -2
  30. package/build/cjs/devices/ConditionalPrimitive.js +59 -0
  31. package/build/cjs/devices/ConditionalPrimitive.js.map +7 -0
  32. package/build/{devices → cjs/devices}/DeviceConfig.d.ts +7 -7
  33. package/build/cjs/devices/DeviceConfig.js +502 -0
  34. package/build/cjs/devices/DeviceConfig.js.map +7 -0
  35. package/build/cjs/devices/DeviceConfig.unit._test.js +17 -0
  36. package/build/cjs/devices/DeviceConfig.unit._test.js.map +7 -0
  37. package/build/{devices → cjs/devices}/DeviceMetadata.d.ts +3 -3
  38. package/build/cjs/devices/DeviceMetadata.js +112 -0
  39. package/build/cjs/devices/DeviceMetadata.js.map +7 -0
  40. package/build/{devices → cjs/devices}/EndpointConfig.d.ts +5 -5
  41. package/build/cjs/devices/EndpointConfig.js +87 -0
  42. package/build/cjs/devices/EndpointConfig.js.map +7 -0
  43. package/build/{devices → cjs/devices}/ParamInformation.d.ts +3 -3
  44. package/build/cjs/devices/ParamInformation.js +209 -0
  45. package/build/cjs/devices/ParamInformation.js.map +7 -0
  46. package/build/cjs/devices/shared.js +17 -0
  47. package/build/cjs/devices/shared.js.map +7 -0
  48. package/build/cjs/index.d.ts +13 -0
  49. package/build/cjs/index.js +53 -0
  50. package/build/cjs/index.js.map +7 -0
  51. package/build/cjs/index_safe.d.ts +3 -0
  52. package/build/cjs/index_safe.js +32 -0
  53. package/build/cjs/index_safe.js.map +7 -0
  54. package/build/cjs/package.json +3 -0
  55. package/build/{utils.d.ts → cjs/utils.d.ts} +2 -2
  56. package/build/cjs/utils.js +117 -0
  57. package/build/cjs/utils.js.map +7 -0
  58. package/build/cjs/utils_safe.js +57 -0
  59. package/build/cjs/utils_safe.js.map +7 -0
  60. package/build/esm/ConfigManager.d.ts +57 -0
  61. package/build/esm/ConfigManager.d.ts.map +1 -0
  62. package/build/{ConfigManager.js → esm/ConfigManager.js} +36 -43
  63. package/build/esm/ConfigManager.js.map +1 -0
  64. package/build/esm/JsonTemplate.d.ts +4 -0
  65. package/build/esm/JsonTemplate.d.ts.map +1 -0
  66. package/build/{JsonTemplate.js → esm/JsonTemplate.js} +30 -60
  67. package/build/esm/JsonTemplate.js.map +1 -0
  68. package/build/esm/Logger.d.ts +11 -0
  69. package/build/esm/Logger.d.ts.map +1 -0
  70. package/build/esm/Logger.js +23 -0
  71. package/build/esm/Logger.js.map +1 -0
  72. package/build/esm/Logger_safe.d.ts +5 -0
  73. package/build/esm/Logger_safe.d.ts.map +1 -0
  74. package/build/esm/Logger_safe.js +3 -0
  75. package/build/esm/Logger_safe.js.map +1 -0
  76. package/build/esm/Logic.d.ts +4 -0
  77. package/build/esm/Logic.d.ts.map +1 -0
  78. package/build/esm/Logic.js +35 -0
  79. package/build/esm/Logic.js.map +1 -0
  80. package/build/esm/LogicParser.d.ts +93 -0
  81. package/build/esm/LogicParser.d.ts.map +1 -0
  82. package/build/{LogicParser.js → esm/LogicParser.js} +2 -5
  83. package/build/{LogicParser.js.map → esm/LogicParser.js.map} +1 -1
  84. package/build/esm/Manufacturers.d.ts +6 -0
  85. package/build/esm/Manufacturers.d.ts.map +1 -0
  86. package/build/esm/Manufacturers.js +55 -0
  87. package/build/esm/Manufacturers.js.map +1 -0
  88. package/build/esm/_version.d.ts +2 -0
  89. package/build/esm/_version.d.ts.map +1 -0
  90. package/build/esm/_version.js +3 -0
  91. package/build/esm/_version.js.map +1 -0
  92. package/build/esm/devices/AssociationConfig.d.ts +27 -0
  93. package/build/esm/devices/AssociationConfig.d.ts.map +1 -0
  94. package/build/{devices → esm/devices}/AssociationConfig.js +12 -16
  95. package/build/esm/devices/AssociationConfig.js.map +1 -0
  96. package/build/esm/devices/CompatConfig.d.ts +112 -0
  97. package/build/esm/devices/CompatConfig.d.ts.map +1 -0
  98. package/build/{devices → esm/devices}/CompatConfig.js +112 -119
  99. package/build/esm/devices/CompatConfig.js.map +1 -0
  100. package/build/esm/devices/ConditionalItem.d.ts +14 -0
  101. package/build/esm/devices/ConditionalItem.d.ts.map +1 -0
  102. package/build/{devices → esm/devices}/ConditionalItem.js +15 -21
  103. package/build/esm/devices/ConditionalItem.js.map +1 -0
  104. package/build/esm/devices/ConditionalPrimitive.d.ts +13 -0
  105. package/build/esm/devices/ConditionalPrimitive.d.ts.map +1 -0
  106. package/build/esm/devices/ConditionalPrimitive.js +35 -0
  107. package/build/esm/devices/ConditionalPrimitive.js.map +1 -0
  108. package/build/esm/devices/DeviceConfig.d.ts +117 -0
  109. package/build/esm/devices/DeviceConfig.d.ts.map +1 -0
  110. package/build/{devices → esm/devices}/DeviceConfig.js +96 -108
  111. package/build/esm/devices/DeviceConfig.js.map +1 -0
  112. package/build/esm/devices/DeviceConfig.unit._test.d.ts +2 -0
  113. package/build/esm/devices/DeviceConfig.unit._test.d.ts.map +1 -0
  114. package/build/{devices → esm/devices}/DeviceConfig.unit._test.js +1 -2
  115. package/build/esm/devices/DeviceConfig.unit._test.js.map +1 -0
  116. package/build/esm/devices/DeviceMetadata.d.ts +41 -0
  117. package/build/esm/devices/DeviceMetadata.d.ts.map +1 -0
  118. package/build/{devices → esm/devices}/DeviceMetadata.js +16 -21
  119. package/build/esm/devices/DeviceMetadata.js.map +1 -0
  120. package/build/esm/devices/EndpointConfig.d.ts +20 -0
  121. package/build/esm/devices/EndpointConfig.d.ts.map +1 -0
  122. package/build/esm/devices/EndpointConfig.js +60 -0
  123. package/build/esm/devices/EndpointConfig.js.map +1 -0
  124. package/build/esm/devices/ParamInformation.d.ts +50 -0
  125. package/build/esm/devices/ParamInformation.d.ts.map +1 -0
  126. package/build/{devices → esm/devices}/ParamInformation.js +38 -44
  127. package/build/esm/devices/ParamInformation.js.map +1 -0
  128. package/build/esm/devices/shared.d.ts +11 -0
  129. package/build/esm/devices/shared.d.ts.map +1 -0
  130. package/build/esm/devices/shared.js +2 -0
  131. package/build/{devices → esm/devices}/shared.js.map +1 -1
  132. package/build/esm/index.d.ts +13 -0
  133. package/build/esm/index.d.ts.map +1 -0
  134. package/build/esm/index.js +14 -0
  135. package/build/esm/index.js.map +1 -0
  136. package/build/esm/index_safe.d.ts +3 -0
  137. package/build/esm/index_safe.d.ts.map +1 -0
  138. package/build/esm/index_safe.js +4 -0
  139. package/build/esm/index_safe.js.map +1 -0
  140. package/build/esm/package.json +3 -0
  141. package/build/esm/utils.d.ts +19 -0
  142. package/build/esm/utils.d.ts.map +1 -0
  143. package/build/esm/utils.js +93 -0
  144. package/build/esm/utils.js.map +1 -0
  145. package/build/esm/utils_safe.d.ts +7 -0
  146. package/build/esm/utils_safe.d.ts.map +1 -0
  147. package/build/esm/utils_safe.js +28 -0
  148. package/build/esm/utils_safe.js.map +1 -0
  149. package/package.json +22 -19
  150. package/build/ConfigManager.d.ts.map +0 -1
  151. package/build/ConfigManager.js.map +0 -1
  152. package/build/JsonTemplate.d.ts.map +0 -1
  153. package/build/JsonTemplate.js.map +0 -1
  154. package/build/Logger.d.ts.map +0 -1
  155. package/build/Logger.js +0 -27
  156. package/build/Logger.js.map +0 -1
  157. package/build/Logger_safe.d.ts.map +0 -1
  158. package/build/Logger_safe.js +0 -6
  159. package/build/Logger_safe.js.map +0 -1
  160. package/build/Logic.d.ts.map +0 -1
  161. package/build/Logic.js +0 -60
  162. package/build/Logic.js.map +0 -1
  163. package/build/LogicParser.d.ts.map +0 -1
  164. package/build/Manufacturers.d.ts.map +0 -1
  165. package/build/Manufacturers.js +0 -62
  166. package/build/Manufacturers.js.map +0 -1
  167. package/build/_version.d.ts +0 -2
  168. package/build/_version.d.ts.map +0 -1
  169. package/build/_version.js +0 -6
  170. package/build/_version.js.map +0 -1
  171. package/build/devices/AssociationConfig.d.ts.map +0 -1
  172. package/build/devices/AssociationConfig.js.map +0 -1
  173. package/build/devices/CompatConfig.d.ts.map +0 -1
  174. package/build/devices/CompatConfig.js.map +0 -1
  175. package/build/devices/ConditionalItem.d.ts.map +0 -1
  176. package/build/devices/ConditionalItem.js.map +0 -1
  177. package/build/devices/ConditionalPrimitive.d.ts.map +0 -1
  178. package/build/devices/ConditionalPrimitive.js +0 -40
  179. package/build/devices/ConditionalPrimitive.js.map +0 -1
  180. package/build/devices/DeviceConfig.d.ts.map +0 -1
  181. package/build/devices/DeviceConfig.js.map +0 -1
  182. package/build/devices/DeviceConfig.unit._test.d.ts.map +0 -1
  183. package/build/devices/DeviceConfig.unit._test.js.map +0 -1
  184. package/build/devices/DeviceMetadata.d.ts.map +0 -1
  185. package/build/devices/DeviceMetadata.js.map +0 -1
  186. package/build/devices/EndpointConfig.d.ts.map +0 -1
  187. package/build/devices/EndpointConfig.js +0 -64
  188. package/build/devices/EndpointConfig.js.map +0 -1
  189. package/build/devices/ParamInformation.d.ts.map +0 -1
  190. package/build/devices/ParamInformation.js.map +0 -1
  191. package/build/devices/shared.d.ts.map +0 -1
  192. package/build/devices/shared.js +0 -3
  193. package/build/index.d.ts +0 -13
  194. package/build/index.d.ts.map +0 -1
  195. package/build/index.js +0 -33
  196. package/build/index.js.map +0 -1
  197. package/build/index_safe.d.ts +0 -3
  198. package/build/index_safe.d.ts.map +0 -1
  199. package/build/index_safe.js +0 -22
  200. package/build/index_safe.js.map +0 -1
  201. package/build/utils.d.ts.map +0 -1
  202. package/build/utils.js +0 -124
  203. package/build/utils.js.map +0 -1
  204. package/build/utils_safe.d.ts.map +0 -1
  205. package/build/utils_safe.js +0 -33
  206. package/build/utils_safe.js.map +0 -1
  207. /package/build/{JsonTemplate.d.ts → cjs/JsonTemplate.d.ts} +0 -0
  208. /package/build/{Logger_safe.d.ts → cjs/Logger_safe.d.ts} +0 -0
  209. /package/build/{Logic.d.ts → cjs/Logic.d.ts} +0 -0
  210. /package/build/{LogicParser.d.ts → cjs/LogicParser.d.ts} +0 -0
  211. /package/build/{Manufacturers.d.ts → cjs/Manufacturers.d.ts} +0 -0
  212. /package/build/{devices → cjs/devices}/DeviceConfig.unit._test.d.ts +0 -0
  213. /package/build/{devices → cjs/devices}/shared.d.ts +0 -0
  214. /package/build/{utils_safe.d.ts → cjs/utils_safe.d.ts} +0 -0
@@ -0,0 +1,502 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var DeviceConfig_exports = {};
30
+ __export(DeviceConfig_exports, {
31
+ ConditionalDeviceConfig: () => ConditionalDeviceConfig,
32
+ DeviceConfig: () => DeviceConfig,
33
+ embeddedDevicesDir: () => embeddedDevicesDir,
34
+ generatePriorityDeviceIndex: () => generatePriorityDeviceIndex,
35
+ getDevicesPaths: () => getDevicesPaths,
36
+ loadDeviceIndexInternal: () => loadDeviceIndexInternal,
37
+ loadFulltextDeviceIndexInternal: () => loadFulltextDeviceIndexInternal
38
+ });
39
+ module.exports = __toCommonJS(DeviceConfig_exports);
40
+ var import_core = require("@zwave-js/core");
41
+ var import_shared = require("@zwave-js/shared");
42
+ var import_typeguards = require("alcalzone-shared/typeguards/index.js");
43
+ var import_json5 = __toESM(require("json5"), 1);
44
+ var import_node_crypto = require("node:crypto");
45
+ var import_promises = __toESM(require("node:fs/promises"), 1);
46
+ var import_node_path = __toESM(require("node:path"), 1);
47
+ var import_semver = __toESM(require("semver"), 1);
48
+ var import_JsonTemplate = require("../JsonTemplate.js");
49
+ var import_utils = require("../utils.js");
50
+ var import_utils_safe = require("../utils_safe.js");
51
+ var import_AssociationConfig = require("./AssociationConfig.js");
52
+ var import_CompatConfig = require("./CompatConfig.js");
53
+ var import_ConditionalItem = require("./ConditionalItem.js");
54
+ var import_ConditionalPrimitive = require("./ConditionalPrimitive.js");
55
+ var import_DeviceMetadata = require("./DeviceMetadata.js");
56
+ var import_EndpointConfig = require("./EndpointConfig.js");
57
+ var import_ParamInformation = require("./ParamInformation.js");
58
+ const embeddedDevicesDir = import_node_path.default.join(import_utils.configDir, "devices");
59
+ const fulltextIndexPath = import_node_path.default.join(embeddedDevicesDir, "fulltext_index.json");
60
+ function getDevicesPaths(configDir2) {
61
+ const devicesDir = import_node_path.default.join(configDir2, "devices");
62
+ const indexPath = import_node_path.default.join(devicesDir, "index.json");
63
+ return { devicesDir, indexPath };
64
+ }
65
+ async function hasChangedDeviceFiles(devicesRoot, dir, lastChange) {
66
+ const filesAndDirs = await import_promises.default.readdir(dir);
67
+ for (const f of filesAndDirs) {
68
+ const fullPath = import_node_path.default.join(dir, f);
69
+ const stat = await import_promises.default.stat(fullPath);
70
+ if ((dir !== devicesRoot || f !== "index.json") && (stat.isFile() || stat.isDirectory()) && stat.mtime > lastChange) {
71
+ return true;
72
+ } else if (stat.isDirectory()) {
73
+ if (await hasChangedDeviceFiles(devicesRoot, fullPath, lastChange)) {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ async function generateIndex(devicesDir, isEmbedded, extractIndexEntries, logger) {
81
+ const index = [];
82
+ (0, import_JsonTemplate.clearTemplateCache)();
83
+ const configFiles = await (0, import_shared.enumFilesRecursive)(devicesDir, (file) => file.endsWith(".json") && !file.endsWith("index.json") && !file.includes("/templates/") && !file.includes("\\templates\\"));
84
+ const fallbackDirs = devicesDir !== embeddedDevicesDir ? [embeddedDevicesDir] : void 0;
85
+ for (const file of configFiles) {
86
+ const relativePath = import_node_path.default.relative(devicesDir, file).replaceAll("\\", "/");
87
+ try {
88
+ const config = await DeviceConfig.from(file, isEmbedded, {
89
+ rootDir: devicesDir,
90
+ fallbackDirs,
91
+ relative: true
92
+ });
93
+ index.push(...extractIndexEntries(config).map((entry) => {
94
+ const ret = {
95
+ ...entry,
96
+ filename: relativePath
97
+ };
98
+ if (devicesDir !== embeddedDevicesDir) {
99
+ ret.rootDir = devicesDir;
100
+ }
101
+ return ret;
102
+ }));
103
+ } catch (e) {
104
+ const message = `Error parsing config file ${relativePath}: ${e.message}`;
105
+ if (process.env.NODE_ENV === "test" || !!process.env.CI) {
106
+ throw new import_core.ZWaveError(message, import_core.ZWaveErrorCodes.Config_Invalid);
107
+ } else {
108
+ logger?.print(message, "error");
109
+ }
110
+ }
111
+ }
112
+ return index;
113
+ }
114
+ async function loadDeviceIndexShared(devicesDir, indexPath, extractIndexEntries, logger) {
115
+ let needsUpdate = !await (0, import_shared.pathExists)(indexPath);
116
+ let index;
117
+ let mtimeIndex;
118
+ if (!needsUpdate) {
119
+ try {
120
+ const fileContents = await import_promises.default.readFile(indexPath, "utf8");
121
+ index = import_json5.default.parse(fileContents);
122
+ mtimeIndex = (await import_promises.default.stat(indexPath)).mtime;
123
+ } catch {
124
+ logger?.print("Error while parsing index file - regenerating...", "warn");
125
+ needsUpdate = true;
126
+ } finally {
127
+ if (!index) {
128
+ logger?.print("Index file was malformed - regenerating...", "warn");
129
+ needsUpdate = true;
130
+ }
131
+ }
132
+ }
133
+ if (!needsUpdate) {
134
+ needsUpdate = await hasChangedDeviceFiles(devicesDir, devicesDir, mtimeIndex);
135
+ if (needsUpdate) {
136
+ logger?.print("Device configuration files on disk changed - regenerating index...", "verbose");
137
+ }
138
+ }
139
+ if (needsUpdate) {
140
+ index = await generateIndex(devicesDir, true, extractIndexEntries, logger);
141
+ try {
142
+ await import_promises.default.writeFile(import_node_path.default.join(indexPath), `// This file is auto-generated. DO NOT edit it by hand if you don't know what you're doing!"
143
+ ${(0, import_shared.stringify)(index, " ")}
144
+ `, "utf8");
145
+ logger?.print("Device index regenerated", "verbose");
146
+ } catch (e) {
147
+ logger?.print(`Writing the device index to disk failed: ${e.message}`, "error");
148
+ }
149
+ }
150
+ return index;
151
+ }
152
+ async function generatePriorityDeviceIndex(deviceConfigPriorityDir, logger) {
153
+ return (await generateIndex(deviceConfigPriorityDir, false, (config) => config.devices.map((dev) => ({
154
+ manufacturerId: (0, import_shared.formatId)(config.manufacturerId.toString(16)),
155
+ manufacturer: config.manufacturer,
156
+ label: config.label,
157
+ productType: (0, import_shared.formatId)(dev.productType),
158
+ productId: (0, import_shared.formatId)(dev.productId),
159
+ firmwareVersion: config.firmwareVersion,
160
+ ...config.preferred ? { preferred: true } : {},
161
+ rootDir: deviceConfigPriorityDir
162
+ })), logger)).map(({ filename, ...entry }) => ({
163
+ ...entry,
164
+ // The generated index makes the filenames relative to the given directory
165
+ // but we need them to be absolute
166
+ filename: import_node_path.default.join(deviceConfigPriorityDir, filename)
167
+ }));
168
+ }
169
+ async function loadDeviceIndexInternal(logger, externalConfig) {
170
+ const { devicesDir, indexPath } = getDevicesPaths(externalConfig && (0, import_utils.externalConfigDir)() || import_utils.configDir);
171
+ return loadDeviceIndexShared(devicesDir, indexPath, (config) => config.devices.map((dev) => ({
172
+ manufacturerId: (0, import_shared.formatId)(config.manufacturerId.toString(16)),
173
+ manufacturer: config.manufacturer,
174
+ label: config.label,
175
+ productType: (0, import_shared.formatId)(dev.productType),
176
+ productId: (0, import_shared.formatId)(dev.productId),
177
+ firmwareVersion: config.firmwareVersion,
178
+ ...config.preferred ? { preferred: true } : {}
179
+ })), logger);
180
+ }
181
+ async function loadFulltextDeviceIndexInternal(logger) {
182
+ return loadDeviceIndexShared(embeddedDevicesDir, fulltextIndexPath, (config) => config.devices.map((dev) => ({
183
+ manufacturerId: (0, import_shared.formatId)(config.manufacturerId.toString(16)),
184
+ manufacturer: config.manufacturer,
185
+ label: config.label,
186
+ description: config.description,
187
+ productType: (0, import_shared.formatId)(dev.productType),
188
+ productId: (0, import_shared.formatId)(dev.productId),
189
+ firmwareVersion: config.firmwareVersion,
190
+ ...config.preferred ? { preferred: true } : {},
191
+ rootDir: embeddedDevicesDir
192
+ })), logger);
193
+ }
194
+ function isHexKeyWith4Digits(val) {
195
+ return typeof val === "string" && import_utils_safe.hexKeyRegex4Digits.test(val);
196
+ }
197
+ const firmwareVersionRegex = /^\d{1,3}\.\d{1,3}(\.\d{1,3})?$/;
198
+ function isFirmwareVersion(val) {
199
+ return typeof val === "string" && firmwareVersionRegex.test(val) && val.split(".").map((str) => parseInt(str, 10)).every((num) => num >= 0 && num <= 255);
200
+ }
201
+ class ConditionalDeviceConfig {
202
+ static async from(filename, isEmbedded, options) {
203
+ const { relative, rootDir } = options;
204
+ const relativePath = relative ? import_node_path.default.relative(rootDir, filename).replaceAll("\\", "/") : filename;
205
+ const json = await (0, import_JsonTemplate.readJsonWithTemplate)(filename, [
206
+ options.rootDir,
207
+ ...options.fallbackDirs ?? []
208
+ ]);
209
+ return new ConditionalDeviceConfig(relativePath, isEmbedded, json);
210
+ }
211
+ constructor(filename, isEmbedded, definition) {
212
+ this.filename = filename;
213
+ this.isEmbedded = isEmbedded;
214
+ if (!isHexKeyWith4Digits(definition.manufacturerId)) {
215
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
216
+ manufacturer id must be a lowercase hexadecimal number with 4 digits`);
217
+ }
218
+ this.manufacturerId = parseInt(definition.manufacturerId, 16);
219
+ for (const prop of ["manufacturer", "label", "description"]) {
220
+ this[prop] = (0, import_ConditionalPrimitive.parseConditionalPrimitive)(filename, "string", prop, definition[prop]);
221
+ }
222
+ if (!(0, import_typeguards.isArray)(definition.devices) || !definition.devices.every((dev) => (0, import_typeguards.isObject)(dev) && isHexKeyWith4Digits(dev.productType) && isHexKeyWith4Digits(dev.productId))) {
223
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
224
+ devices is malformed (not an object or type/id that is not a lowercase 4-digit hex key)`);
225
+ }
226
+ this.devices = definition.devices.map(({ productType, productId }) => ({
227
+ productType: parseInt(productType, 16),
228
+ productId: parseInt(productId, 16)
229
+ }));
230
+ if (!(0, import_typeguards.isObject)(definition.firmwareVersion) || !isFirmwareVersion(definition.firmwareVersion.min) || !isFirmwareVersion(definition.firmwareVersion.max)) {
231
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
232
+ firmwareVersion is malformed or invalid. Must be x.y or x.y.z where x, y, and z are integers between 0 and 255`);
233
+ } else {
234
+ const { min, max } = definition.firmwareVersion;
235
+ if (import_semver.default.gt((0, import_shared.padVersion)(min), (0, import_shared.padVersion)(max))) {
236
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
237
+ firmwareVersion.min ${min} must not be greater than firmwareVersion.max ${max}`);
238
+ }
239
+ this.firmwareVersion = { min, max };
240
+ }
241
+ if (definition.preferred != void 0 && definition.preferred !== true) {
242
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
243
+ preferred must be true or omitted`);
244
+ }
245
+ this.preferred = !!definition.preferred;
246
+ if (definition.endpoints != void 0) {
247
+ const endpoints = /* @__PURE__ */ new Map();
248
+ if (!(0, import_typeguards.isObject)(definition.endpoints)) {
249
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
250
+ endpoints is not an object`);
251
+ }
252
+ for (const [key, ep] of Object.entries(definition.endpoints)) {
253
+ if (!/^\d+$/.test(key)) {
254
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
255
+ found non-numeric endpoint index "${key}" in endpoints`);
256
+ }
257
+ const epIndex = parseInt(key, 10);
258
+ endpoints.set(epIndex, new import_EndpointConfig.ConditionalEndpointConfig(this, epIndex, ep));
259
+ }
260
+ this.endpoints = endpoints;
261
+ }
262
+ if (definition.associations != void 0) {
263
+ const associations = /* @__PURE__ */ new Map();
264
+ if (!(0, import_typeguards.isObject)(definition.associations)) {
265
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
266
+ associations is not an object`);
267
+ }
268
+ for (const [key, assocDefinition] of Object.entries(definition.associations)) {
269
+ if (!/^[1-9][0-9]*$/.test(key)) {
270
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
271
+ found non-numeric group id "${key}" in associations`);
272
+ }
273
+ const keyNum = parseInt(key, 10);
274
+ associations.set(keyNum, new import_AssociationConfig.ConditionalAssociationConfig(filename, keyNum, assocDefinition));
275
+ }
276
+ this.associations = associations;
277
+ }
278
+ if (definition.paramInformation != void 0) {
279
+ this.paramInformation = (0, import_ParamInformation.parseConditionalParamInformationMap)(definition, this);
280
+ }
281
+ if (definition.proprietary != void 0) {
282
+ if (!(0, import_typeguards.isObject)(definition.proprietary)) {
283
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
284
+ proprietary is not an object`);
285
+ }
286
+ this.proprietary = definition.proprietary;
287
+ }
288
+ if (definition.compat != void 0) {
289
+ if ((0, import_typeguards.isArray)(definition.compat) && definition.compat.every((item) => (0, import_typeguards.isObject)(item))) {
290
+ for (const entry of definition.compat) {
291
+ (0, import_ConditionalItem.validateCondition)(filename, entry, `At least one entry of compat contains an`);
292
+ }
293
+ this.compat = definition.compat.map((item) => new import_CompatConfig.ConditionalCompatConfig(filename, item));
294
+ } else if ((0, import_typeguards.isObject)(definition.compat)) {
295
+ this.compat = new import_CompatConfig.ConditionalCompatConfig(filename, definition.compat);
296
+ } else {
297
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
298
+ compat must be an object or any array of conditional objects`);
299
+ }
300
+ }
301
+ if (definition.metadata != void 0) {
302
+ if (!(0, import_typeguards.isObject)(definition.metadata)) {
303
+ (0, import_utils_safe.throwInvalidConfig)(`device`, `packages/config/config/devices/${filename}:
304
+ metadata is not an object`);
305
+ }
306
+ this.metadata = new import_DeviceMetadata.ConditionalDeviceMetadata(filename, definition.metadata);
307
+ }
308
+ }
309
+ filename;
310
+ manufacturer;
311
+ manufacturerId;
312
+ label;
313
+ description;
314
+ devices;
315
+ firmwareVersion;
316
+ /** Mark this configuration as preferred over other config files with an overlapping firmware range */
317
+ preferred;
318
+ endpoints;
319
+ associations;
320
+ paramInformation;
321
+ /**
322
+ * Contains manufacturer-specific support information for the
323
+ * ManufacturerProprietary CC
324
+ */
325
+ proprietary;
326
+ /** Contains compatibility options */
327
+ compat;
328
+ /** Contains instructions and other metadata for the device */
329
+ metadata;
330
+ /** Whether this is an embedded configuration or not */
331
+ isEmbedded;
332
+ evaluate(deviceId) {
333
+ return new DeviceConfig(this.filename, this.isEmbedded, (0, import_ConditionalItem.evaluateDeep)(this.manufacturer, deviceId), this.manufacturerId, (0, import_ConditionalItem.evaluateDeep)(this.label, deviceId), (0, import_ConditionalItem.evaluateDeep)(this.description, deviceId), this.devices, this.firmwareVersion, this.preferred, (0, import_ConditionalItem.evaluateDeep)(this.endpoints, deviceId), (0, import_ConditionalItem.evaluateDeep)(this.associations, deviceId), (0, import_ConditionalItem.evaluateDeep)(this.paramInformation, deviceId), this.proprietary, (0, import_ConditionalItem.evaluateDeep)(this.compat, deviceId), (0, import_ConditionalItem.evaluateDeep)(this.metadata, deviceId));
334
+ }
335
+ }
336
+ class DeviceConfig {
337
+ static async from(filename, isEmbedded, options) {
338
+ const ret = await ConditionalDeviceConfig.from(filename, isEmbedded, options);
339
+ return ret.evaluate(options.deviceId);
340
+ }
341
+ constructor(filename, isEmbedded, manufacturer, manufacturerId, label, description, devices, firmwareVersion, preferred, endpoints, associations, paramInformation, proprietary, compat, metadata) {
342
+ this.filename = filename;
343
+ this.isEmbedded = isEmbedded;
344
+ this.manufacturer = manufacturer;
345
+ this.manufacturerId = manufacturerId;
346
+ this.label = label;
347
+ this.description = description;
348
+ this.devices = devices;
349
+ this.firmwareVersion = firmwareVersion;
350
+ this.preferred = preferred;
351
+ this.endpoints = endpoints;
352
+ this.associations = associations;
353
+ this.paramInformation = paramInformation;
354
+ this.proprietary = proprietary;
355
+ this.compat = compat;
356
+ this.metadata = metadata;
357
+ }
358
+ filename;
359
+ /** Whether this is an embedded configuration or not */
360
+ isEmbedded;
361
+ manufacturer;
362
+ manufacturerId;
363
+ label;
364
+ description;
365
+ devices;
366
+ firmwareVersion;
367
+ /** Mark this configuration as preferred over other config files with an overlapping firmware range */
368
+ preferred;
369
+ endpoints;
370
+ associations;
371
+ paramInformation;
372
+ /**
373
+ * Contains manufacturer-specific support information for the
374
+ * ManufacturerProprietary CC
375
+ */
376
+ proprietary;
377
+ /** Contains compatibility options */
378
+ compat;
379
+ /** Contains instructions and other metadata for the device */
380
+ metadata;
381
+ /** Returns the association config for a given endpoint */
382
+ getAssociationConfigForEndpoint(endpointIndex, group) {
383
+ if (endpointIndex === 0) {
384
+ return this.associations?.get(group) ?? this.endpoints?.get(0)?.associations?.get(group);
385
+ } else {
386
+ return this.endpoints?.get(endpointIndex)?.associations?.get(group);
387
+ }
388
+ }
389
+ /**
390
+ * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.
391
+ */
392
+ getHash() {
393
+ let hashable = {
394
+ // endpoints: {
395
+ // associations: {},
396
+ // paramInformation: []
397
+ // },
398
+ // proprietary: {},
399
+ // compat: {},
400
+ };
401
+ const sortObject = (obj) => {
402
+ const ret = {};
403
+ for (const key of Object.keys(obj).sort()) {
404
+ ret[key] = obj[key];
405
+ }
406
+ return ret;
407
+ };
408
+ const cloneAssociationConfig = (a) => {
409
+ return sortObject((0, import_shared.pick)(a, ["maxNodes", "multiChannel", "isLifeline"]));
410
+ };
411
+ const cloneAssociationMap = (target, map) => {
412
+ if (!map || !map.size)
413
+ return;
414
+ target.associations = {};
415
+ for (const [key, value] of map) {
416
+ target.associations[key] = cloneAssociationConfig(value);
417
+ }
418
+ target.associations = sortObject(target.associations);
419
+ };
420
+ const cloneParamInformationMap = (target, map) => {
421
+ if (!map || !map.size)
422
+ return;
423
+ const getParamKey = (param) => `${param.parameterNumber}${param.valueBitMask ? `[${(0, import_shared.num2hex)(param.valueBitMask)}]` : ""}`;
424
+ target.paramInformation = [...map.values()].sort((a, b) => getParamKey(a).localeCompare(getParamKey(b)));
425
+ };
426
+ {
427
+ let ep0 = {};
428
+ cloneAssociationMap(ep0, this.associations);
429
+ cloneParamInformationMap(ep0, this.paramInformation);
430
+ ep0 = sortObject(ep0);
431
+ if (Object.keys(ep0).length > 0) {
432
+ hashable.endpoints ??= {};
433
+ hashable.endpoints[0] = ep0;
434
+ }
435
+ }
436
+ if (this.endpoints) {
437
+ for (const [index, endpoint] of this.endpoints) {
438
+ let ep = {};
439
+ cloneAssociationMap(ep, endpoint.associations);
440
+ cloneParamInformationMap(ep, endpoint.paramInformation);
441
+ ep = sortObject(ep);
442
+ if (Object.keys(ep).length > 0) {
443
+ hashable.endpoints ??= {};
444
+ hashable.endpoints[index] = ep;
445
+ }
446
+ }
447
+ }
448
+ if (this.proprietary && Object.keys(this.proprietary).length > 0) {
449
+ hashable.proprietary = sortObject({ ...this.proprietary });
450
+ }
451
+ if (this.compat) {
452
+ let c = {};
453
+ for (const prop of [
454
+ "forceSceneControllerGroupCount",
455
+ "mapRootReportsToEndpoint",
456
+ "mapBasicSet",
457
+ "preserveRootApplicationCCValueIDs",
458
+ "preserveEndpoints",
459
+ "removeEndpoints",
460
+ "treatMultilevelSwitchSetAsEvent"
461
+ ]) {
462
+ if (this.compat[prop] != void 0) {
463
+ c[prop] = this.compat[prop];
464
+ }
465
+ }
466
+ if (this.compat.overrideQueries) {
467
+ c.overrideQueries = Object.fromEntries(this.compat.overrideQueries["overrides"]);
468
+ }
469
+ if (this.compat.addCCs) {
470
+ c.addCCs = Object.fromEntries([...this.compat.addCCs].map(([ccId, def]) => [
471
+ ccId,
472
+ Object.fromEntries(def.endpoints)
473
+ ]));
474
+ }
475
+ if (this.compat.removeCCs) {
476
+ c.removeCCs = Object.fromEntries(this.compat.removeCCs);
477
+ }
478
+ if (this.compat.treatSetAsReport) {
479
+ c.treatSetAsReport = [...this.compat.treatSetAsReport].sort();
480
+ }
481
+ c = sortObject(c);
482
+ if (Object.keys(c).length > 0) {
483
+ hashable.compat = c;
484
+ }
485
+ }
486
+ hashable = sortObject(hashable);
487
+ const buffer = import_shared.Bytes.from(JSON.stringify(hashable), "utf8");
488
+ const md5 = (0, import_node_crypto.createHash)("md5");
489
+ return md5.update(buffer).digest();
490
+ }
491
+ }
492
+ // Annotate the CommonJS export names for ESM import in node:
493
+ 0 && (module.exports = {
494
+ ConditionalDeviceConfig,
495
+ DeviceConfig,
496
+ embeddedDevicesDir,
497
+ generatePriorityDeviceIndex,
498
+ getDevicesPaths,
499
+ loadDeviceIndexInternal,
500
+ loadFulltextDeviceIndexInternal
501
+ });
502
+ //# sourceMappingURL=DeviceConfig.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/devices/DeviceConfig.ts"],
4
+ "sourcesContent": ["import { ZWaveError, ZWaveErrorCodes } from \"@zwave-js/core\";\nimport {\n\tBytes,\n\ttype JSONObject,\n\tenumFilesRecursive,\n\tformatId,\n\tnum2hex,\n\tpadVersion,\n\tpathExists,\n\tpick,\n\tstringify,\n} from \"@zwave-js/shared\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards/index.js\";\nimport JSON5 from \"json5\";\nimport { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport semver from \"semver\";\nimport { clearTemplateCache, readJsonWithTemplate } from \"../JsonTemplate.js\";\nimport type { ConfigLogger } from \"../Logger.js\";\nimport { configDir, externalConfigDir } from \"../utils.js\";\nimport { hexKeyRegex4Digits, throwInvalidConfig } from \"../utils_safe.js\";\nimport {\n\ttype AssociationConfig,\n\tConditionalAssociationConfig,\n} from \"./AssociationConfig.js\";\nimport { type CompatConfig, ConditionalCompatConfig } from \"./CompatConfig.js\";\nimport { evaluateDeep, validateCondition } from \"./ConditionalItem.js\";\nimport {\n\ttype ConditionalPrimitive,\n\tparseConditionalPrimitive,\n} from \"./ConditionalPrimitive.js\";\nimport {\n\tConditionalDeviceMetadata,\n\ttype DeviceMetadata,\n} from \"./DeviceMetadata.js\";\nimport {\n\tConditionalEndpointConfig,\n\ttype EndpointConfig,\n} from \"./EndpointConfig.js\";\nimport {\n\ttype ConditionalParamInfoMap,\n\ttype ParamInfoMap,\n\ttype ParamInformation,\n\tparseConditionalParamInformationMap,\n} from \"./ParamInformation.js\";\nimport type { DeviceID, FirmwareVersionRange } from \"./shared.js\";\n\nexport interface DeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport interface FulltextDeviceConfigIndexEntry {\n\tmanufacturerId: string;\n\tmanufacturer: string;\n\tlabel: string;\n\tdescription: string;\n\tproductType: string;\n\tproductId: string;\n\tfirmwareVersion: FirmwareVersionRange;\n\tpreferred?: true;\n\trootDir?: string;\n\tfilename: string;\n}\n\nexport const embeddedDevicesDir = path.join(configDir, \"devices\");\nconst fulltextIndexPath = path.join(embeddedDevicesDir, \"fulltext_index.json\");\n\nexport function getDevicesPaths(configDir: string): {\n\tdevicesDir: string;\n\tindexPath: string;\n} {\n\tconst devicesDir = path.join(configDir, \"devices\");\n\tconst indexPath = path.join(devicesDir, \"index.json\");\n\treturn { devicesDir, indexPath };\n}\n\nexport type DeviceConfigIndex = DeviceConfigIndexEntry[];\nexport type FulltextDeviceConfigIndex = FulltextDeviceConfigIndexEntry[];\n\nasync function hasChangedDeviceFiles(\n\tdevicesRoot: string,\n\tdir: string,\n\tlastChange: Date,\n): Promise<boolean> {\n\t// Check if there are any files BUT index.json that were changed\n\t// or directories that were modified\n\tconst filesAndDirs = await fs.readdir(dir);\n\tfor (const f of filesAndDirs) {\n\t\tconst fullPath = path.join(dir, f);\n\n\t\tconst stat = await fs.stat(fullPath);\n\t\tif (\n\t\t\t(dir !== devicesRoot || f !== \"index.json\")\n\t\t\t&& (stat.isFile() || stat.isDirectory())\n\t\t\t&& stat.mtime > lastChange\n\t\t) {\n\t\t\treturn true;\n\t\t} else if (stat.isDirectory()) {\n\t\t\t// we need to go deeper!\n\t\t\tif (\n\t\t\t\tawait hasChangedDeviceFiles(devicesRoot, fullPath, lastChange)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Read all device config files from a given directory and return them as index entries.\n * Does not update the index itself.\n */\nasync function generateIndex<T extends Record<string, unknown>>(\n\tdevicesDir: string,\n\tisEmbedded: boolean,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string; rootDir?: string })[]> {\n\tconst index: (T & { filename: string; rootDir?: string })[] = [];\n\n\tclearTemplateCache();\n\tconst configFiles = await enumFilesRecursive(\n\t\tdevicesDir,\n\t\t(file) =>\n\t\t\tfile.endsWith(\".json\")\n\t\t\t&& !file.endsWith(\"index.json\")\n\t\t\t&& !file.includes(\"/templates/\")\n\t\t\t&& !file.includes(\"\\\\templates\\\\\"),\n\t);\n\n\t// Add the embedded devices dir as a fallback if necessary\n\tconst fallbackDirs = devicesDir !== embeddedDevicesDir\n\t\t? [embeddedDevicesDir]\n\t\t: undefined;\n\n\tfor (const file of configFiles) {\n\t\tconst relativePath = path\n\t\t\t.relative(devicesDir, file)\n\t\t\t.replaceAll(\"\\\\\", \"/\");\n\t\t// Try parsing the file\n\t\ttry {\n\t\t\tconst config = await DeviceConfig.from(file, isEmbedded, {\n\t\t\t\trootDir: devicesDir,\n\t\t\t\tfallbackDirs,\n\t\t\t\trelative: true,\n\t\t\t});\n\t\t\t// Add the file to the index\n\t\t\tindex.push(\n\t\t\t\t...extractIndexEntries(config).map((entry) => {\n\t\t\t\t\tconst ret: T & { filename: string; rootDir?: string } = {\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\tfilename: relativePath,\n\t\t\t\t\t};\n\t\t\t\t\t// Only add the root dir to the index if necessary\n\t\t\t\t\tif (devicesDir !== embeddedDevicesDir) {\n\t\t\t\t\t\tret.rootDir = devicesDir;\n\t\t\t\t\t}\n\t\t\t\t\treturn ret;\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tconst message = `Error parsing config file ${relativePath}: ${\n\t\t\t\t(e as Error).message\n\t\t\t}`;\n\t\t\t// Crash hard during tests, just print an error when in production systems.\n\t\t\t// A user could have changed a config file\n\t\t\tif (process.env.NODE_ENV === \"test\" || !!process.env.CI) {\n\t\t\t\tthrow new ZWaveError(message, ZWaveErrorCodes.Config_Invalid);\n\t\t\t} else {\n\t\t\t\tlogger?.print(message, \"error\");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn index;\n}\n\nasync function loadDeviceIndexShared<T extends Record<string, unknown>>(\n\tdevicesDir: string,\n\tindexPath: string,\n\textractIndexEntries: (config: DeviceConfig) => T[],\n\tlogger?: ConfigLogger,\n): Promise<(T & { filename: string })[]> {\n\t// The index file needs to be regenerated if it does not exist\n\tlet needsUpdate = !(await pathExists(indexPath));\n\tlet index: (T & { filename: string })[] | undefined;\n\tlet mtimeIndex: Date | undefined;\n\t// ...or if cannot be parsed\n\tif (!needsUpdate) {\n\t\ttry {\n\t\t\tconst fileContents = await fs.readFile(indexPath, \"utf8\");\n\t\t\tindex = JSON5.parse(fileContents);\n\t\t\tmtimeIndex = (await fs.stat(indexPath)).mtime;\n\t\t} catch {\n\t\t\tlogger?.print(\n\t\t\t\t\"Error while parsing index file - regenerating...\",\n\t\t\t\t\"warn\",\n\t\t\t);\n\t\t\tneedsUpdate = true;\n\t\t} finally {\n\t\t\tif (!index) {\n\t\t\t\tlogger?.print(\n\t\t\t\t\t\"Index file was malformed - regenerating...\",\n\t\t\t\t\t\"warn\",\n\t\t\t\t);\n\t\t\t\tneedsUpdate = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ...or if there were any changes in the file system\n\tif (!needsUpdate) {\n\t\tneedsUpdate = await hasChangedDeviceFiles(\n\t\t\tdevicesDir,\n\t\t\tdevicesDir,\n\t\t\tmtimeIndex!,\n\t\t);\n\t\tif (needsUpdate) {\n\t\t\tlogger?.print(\n\t\t\t\t\"Device configuration files on disk changed - regenerating index...\",\n\t\t\t\t\"verbose\",\n\t\t\t);\n\t\t}\n\t}\n\n\tif (needsUpdate) {\n\t\t// Read all files from disk and generate an index\n\t\tindex = await generateIndex(\n\t\t\tdevicesDir,\n\t\t\ttrue,\n\t\t\textractIndexEntries,\n\t\t\tlogger,\n\t\t);\n\t\t// Save the index to disk\n\t\ttry {\n\t\t\tawait fs.writeFile(\n\t\t\t\tpath.join(indexPath),\n\t\t\t\t`// This file is auto-generated. DO NOT edit it by hand if you don't know what you're doing!\"\n${stringify(index, \"\\t\")}\n`,\n\t\t\t\t\"utf8\",\n\t\t\t);\n\t\t\tlogger?.print(\"Device index regenerated\", \"verbose\");\n\t\t} catch (e) {\n\t\t\tlogger?.print(\n\t\t\t\t`Writing the device index to disk failed: ${\n\t\t\t\t\t(e as Error).message\n\t\t\t\t}`,\n\t\t\t\t\"error\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn index!;\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function generatePriorityDeviceIndex(\n\tdeviceConfigPriorityDir: string,\n\tlogger?: ConfigLogger,\n): Promise<DeviceConfigIndex> {\n\treturn (\n\t\tawait generateIndex(\n\t\t\tdeviceConfigPriorityDir,\n\t\t\tfalse,\n\t\t\t(config) =>\n\t\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\t\tmanufacturerId: formatId(\n\t\t\t\t\t\tconfig.manufacturerId.toString(16),\n\t\t\t\t\t),\n\t\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\t\tlabel: config.label,\n\t\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\t\trootDir: deviceConfigPriorityDir,\n\t\t\t\t})),\n\t\t\tlogger,\n\t\t)\n\t).map(({ filename, ...entry }) => ({\n\t\t...entry,\n\t\t// The generated index makes the filenames relative to the given directory\n\t\t// but we need them to be absolute\n\t\tfilename: path.join(deviceConfigPriorityDir, filename),\n\t}));\n}\n\n/**\n * @internal\n * Loads the index file to quickly access the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadDeviceIndexInternal(\n\tlogger?: ConfigLogger,\n\texternalConfig?: boolean,\n): Promise<DeviceConfigIndex> {\n\tconst { devicesDir, indexPath } = getDevicesPaths(\n\t\t(externalConfig && externalConfigDir()) || configDir,\n\t);\n\n\treturn loadDeviceIndexShared(\n\t\tdevicesDir,\n\t\tindexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\n/**\n * @internal\n * Loads the full text index file to quickly search the device configs.\n * Transparently handles updating the index if necessary\n */\nexport async function loadFulltextDeviceIndexInternal(\n\tlogger?: ConfigLogger,\n): Promise<FulltextDeviceConfigIndex> {\n\t// This method is not meant to operate with the external device index!\n\treturn loadDeviceIndexShared(\n\t\tembeddedDevicesDir,\n\t\tfulltextIndexPath,\n\t\t(config) =>\n\t\t\tconfig.devices.map((dev) => ({\n\t\t\t\tmanufacturerId: formatId(config.manufacturerId.toString(16)),\n\t\t\t\tmanufacturer: config.manufacturer,\n\t\t\t\tlabel: config.label,\n\t\t\t\tdescription: config.description,\n\t\t\t\tproductType: formatId(dev.productType),\n\t\t\t\tproductId: formatId(dev.productId),\n\t\t\t\tfirmwareVersion: config.firmwareVersion,\n\t\t\t\t...(config.preferred ? { preferred: true as const } : {}),\n\t\t\t\trootDir: embeddedDevicesDir,\n\t\t\t})),\n\t\tlogger,\n\t);\n}\n\nfunction isHexKeyWith4Digits(val: any): val is string {\n\treturn typeof val === \"string\" && hexKeyRegex4Digits.test(val);\n}\n\nconst firmwareVersionRegex = /^\\d{1,3}\\.\\d{1,3}(\\.\\d{1,3})?$/;\nfunction isFirmwareVersion(val: any): val is string {\n\treturn (\n\t\ttypeof val === \"string\"\n\t\t&& firmwareVersionRegex.test(val)\n\t\t&& val\n\t\t\t.split(\".\")\n\t\t\t.map((str) => parseInt(str, 10))\n\t\t\t.every((num) => num >= 0 && num <= 255)\n\t);\n}\n\n/** This class represents a device config entry whose conditional settings have not been evaluated yet */\nexport class ConditionalDeviceConfig {\n\tpublic static async from(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t},\n\t): Promise<ConditionalDeviceConfig> {\n\t\tconst { relative, rootDir } = options;\n\n\t\tconst relativePath = relative\n\t\t\t? path.relative(rootDir, filename).replaceAll(\"\\\\\", \"/\")\n\t\t\t: filename;\n\t\tconst json = await readJsonWithTemplate(filename, [\n\t\t\toptions.rootDir,\n\t\t\t...(options.fallbackDirs ?? []),\n\t\t]);\n\t\treturn new ConditionalDeviceConfig(relativePath, isEmbedded, json);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tdefinition: JSONObject,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\n\t\tif (!isHexKeyWith4Digits(definition.manufacturerId)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nmanufacturer id must be a lowercase hexadecimal number with 4 digits`,\n\t\t\t);\n\t\t}\n\t\tthis.manufacturerId = parseInt(definition.manufacturerId, 16);\n\n\t\tfor (const prop of [\"manufacturer\", \"label\", \"description\"] as const) {\n\t\t\tthis[prop] = parseConditionalPrimitive(\n\t\t\t\tfilename,\n\t\t\t\t\"string\",\n\t\t\t\tprop,\n\t\t\t\tdefinition[prop],\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\t!isArray(definition.devices)\n\t\t\t|| !(definition.devices as any[]).every(\n\t\t\t\t(dev: unknown) =>\n\t\t\t\t\tisObject(dev)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productType)\n\t\t\t\t\t&& isHexKeyWith4Digits(dev.productId),\n\t\t\t)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\ndevices is malformed (not an object or type/id that is not a lowercase 4-digit hex key)`,\n\t\t\t);\n\t\t}\n\t\tthis.devices = (definition.devices as any[]).map(\n\t\t\t({ productType, productId }) => ({\n\t\t\t\tproductType: parseInt(productType, 16),\n\t\t\t\tproductId: parseInt(productId, 16),\n\t\t\t}),\n\t\t);\n\n\t\tif (\n\t\t\t!isObject(definition.firmwareVersion)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.min)\n\t\t\t|| !isFirmwareVersion(definition.firmwareVersion.max)\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion is malformed or invalid. Must be x.y or x.y.z where x, y, and z are integers between 0 and 255`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst { min, max } = definition.firmwareVersion;\n\t\t\tif (semver.gt(padVersion(min), padVersion(max))) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nfirmwareVersion.min ${min} must not be greater than firmwareVersion.max ${max}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.firmwareVersion = { min, max };\n\t\t}\n\n\t\tif (\n\t\t\tdefinition.preferred != undefined\n\t\t\t&& definition.preferred !== true\n\t\t) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t`device`,\n\t\t\t\t`packages/config/config/devices/${filename}:\npreferred must be true or omitted`,\n\t\t\t);\n\t\t}\n\t\tthis.preferred = !!definition.preferred;\n\n\t\tif (definition.endpoints != undefined) {\n\t\t\tconst endpoints = new Map<number, ConditionalEndpointConfig>();\n\t\t\tif (!isObject(definition.endpoints)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nendpoints is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const [key, ep] of Object.entries(definition.endpoints)) {\n\t\t\t\tif (!/^\\d+$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric endpoint index \"${key}\" in endpoints`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst epIndex = parseInt(key, 10);\n\t\t\t\tendpoints.set(\n\t\t\t\t\tepIndex,\n\t\t\t\t\tnew ConditionalEndpointConfig(this, epIndex, ep as any),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.endpoints = endpoints;\n\t\t}\n\n\t\tif (definition.associations != undefined) {\n\t\t\tconst associations = new Map<\n\t\t\t\tnumber,\n\t\t\t\tConditionalAssociationConfig\n\t\t\t>();\n\t\t\tif (!isObject(definition.associations)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nassociations is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tconst [key, assocDefinition] of Object.entries(\n\t\t\t\t\tdefinition.associations,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tif (!/^[1-9][0-9]*$/.test(key)) {\n\t\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\t`device`,\n\t\t\t\t\t\t`packages/config/config/devices/${filename}:\nfound non-numeric group id \"${key}\" in associations`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst keyNum = parseInt(key, 10);\n\t\t\t\tassociations.set(\n\t\t\t\t\tkeyNum,\n\t\t\t\t\tnew ConditionalAssociationConfig(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tkeyNum,\n\t\t\t\t\t\tassocDefinition as any,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.associations = associations;\n\t\t}\n\n\t\tif (definition.paramInformation != undefined) {\n\t\t\tthis.paramInformation = parseConditionalParamInformationMap(\n\t\t\t\tdefinition,\n\t\t\t\tthis,\n\t\t\t);\n\t\t}\n\n\t\tif (definition.proprietary != undefined) {\n\t\t\tif (!isObject(definition.proprietary)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nproprietary is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.proprietary = definition.proprietary;\n\t\t}\n\n\t\tif (definition.compat != undefined) {\n\t\t\tif (\n\t\t\t\tisArray(definition.compat)\n\t\t\t\t&& definition.compat.every((item: any) => isObject(item))\n\t\t\t) {\n\t\t\t\t// Make sure all conditions are valid\n\t\t\t\tfor (const entry of definition.compat) {\n\t\t\t\t\tvalidateCondition(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\t`At least one entry of compat contains an`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.compat = definition.compat.map(\n\t\t\t\t\t(item: any) => new ConditionalCompatConfig(filename, item),\n\t\t\t\t);\n\t\t\t} else if (isObject(definition.compat)) {\n\t\t\t\tthis.compat = new ConditionalCompatConfig(\n\t\t\t\t\tfilename,\n\t\t\t\t\tdefinition.compat,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\ncompat must be an object or any array of conditional objects`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (definition.metadata != undefined) {\n\t\t\tif (!isObject(definition.metadata)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t`device`,\n\t\t\t\t\t`packages/config/config/devices/${filename}:\nmetadata is not an object`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.metadata = new ConditionalDeviceMetadata(\n\t\t\t\tfilename,\n\t\t\t\tdefinition.metadata,\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic readonly filename: string;\n\n\tpublic readonly manufacturer!: ConditionalPrimitive<string>;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label!: ConditionalPrimitive<string>;\n\tpublic readonly description!: ConditionalPrimitive<string>;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, ConditionalEndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<\n\t\tnumber,\n\t\tConditionalAssociationConfig\n\t>;\n\tpublic readonly paramInformation?: ConditionalParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?:\n\t\t| ConditionalCompatConfig\n\t\t| ConditionalCompatConfig[];\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: ConditionalDeviceMetadata;\n\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\n\tpublic evaluate(deviceId?: DeviceID): DeviceConfig {\n\t\treturn new DeviceConfig(\n\t\t\tthis.filename,\n\t\t\tthis.isEmbedded,\n\t\t\tevaluateDeep(this.manufacturer, deviceId),\n\t\t\tthis.manufacturerId,\n\t\t\tevaluateDeep(this.label, deviceId),\n\t\t\tevaluateDeep(this.description, deviceId),\n\t\t\tthis.devices,\n\t\t\tthis.firmwareVersion,\n\t\t\tthis.preferred,\n\t\t\tevaluateDeep(this.endpoints, deviceId),\n\t\t\tevaluateDeep(this.associations, deviceId),\n\t\t\tevaluateDeep(this.paramInformation, deviceId),\n\t\t\tthis.proprietary,\n\t\t\tevaluateDeep(this.compat, deviceId),\n\t\t\tevaluateDeep(this.metadata, deviceId),\n\t\t);\n\t}\n}\n\nexport class DeviceConfig {\n\tpublic static async from(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\toptions: {\n\t\t\trootDir: string;\n\t\t\tfallbackDirs?: string[];\n\t\t\trelative?: boolean;\n\t\t\tdeviceId?: DeviceID;\n\t\t},\n\t): Promise<DeviceConfig> {\n\t\tconst ret = await ConditionalDeviceConfig.from(\n\t\t\tfilename,\n\t\t\tisEmbedded,\n\t\t\toptions,\n\t\t);\n\t\treturn ret.evaluate(options.deviceId);\n\t}\n\n\tpublic constructor(\n\t\tfilename: string,\n\t\tisEmbedded: boolean,\n\t\tmanufacturer: string,\n\t\tmanufacturerId: number,\n\t\tlabel: string,\n\t\tdescription: string,\n\t\tdevices: readonly {\n\t\t\tproductType: number;\n\t\t\tproductId: number;\n\t\t}[],\n\t\tfirmwareVersion: FirmwareVersionRange,\n\t\tpreferred: boolean,\n\t\tendpoints?: ReadonlyMap<number, EndpointConfig>,\n\t\tassociations?: ReadonlyMap<number, AssociationConfig>,\n\t\tparamInformation?: ParamInfoMap,\n\t\tproprietary?: Record<string, unknown>,\n\t\tcompat?: CompatConfig,\n\t\tmetadata?: DeviceMetadata,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.isEmbedded = isEmbedded;\n\t\tthis.manufacturer = manufacturer;\n\t\tthis.manufacturerId = manufacturerId;\n\t\tthis.label = label;\n\t\tthis.description = description;\n\t\tthis.devices = devices;\n\t\tthis.firmwareVersion = firmwareVersion;\n\t\tthis.preferred = preferred;\n\t\tthis.endpoints = endpoints;\n\t\tthis.associations = associations;\n\t\tthis.paramInformation = paramInformation;\n\t\tthis.proprietary = proprietary;\n\t\tthis.compat = compat;\n\t\tthis.metadata = metadata;\n\t}\n\n\tpublic readonly filename: string;\n\t/** Whether this is an embedded configuration or not */\n\tpublic readonly isEmbedded: boolean;\n\tpublic readonly manufacturer: string;\n\tpublic readonly manufacturerId: number;\n\tpublic readonly label: string;\n\tpublic readonly description: string;\n\tpublic readonly devices: readonly {\n\t\tproductType: number;\n\t\tproductId: number;\n\t}[];\n\tpublic readonly firmwareVersion: FirmwareVersionRange;\n\t/** Mark this configuration as preferred over other config files with an overlapping firmware range */\n\tpublic readonly preferred: boolean;\n\tpublic readonly endpoints?: ReadonlyMap<number, EndpointConfig>;\n\tpublic readonly associations?: ReadonlyMap<number, AssociationConfig>;\n\tpublic readonly paramInformation?: ParamInfoMap;\n\t/**\n\t * Contains manufacturer-specific support information for the\n\t * ManufacturerProprietary CC\n\t */\n\tpublic readonly proprietary?: Record<string, unknown>;\n\t/** Contains compatibility options */\n\tpublic readonly compat?: CompatConfig;\n\t/** Contains instructions and other metadata for the device */\n\tpublic readonly metadata?: DeviceMetadata;\n\n\t/** Returns the association config for a given endpoint */\n\tpublic getAssociationConfigForEndpoint(\n\t\tendpointIndex: number,\n\t\tgroup: number,\n\t): AssociationConfig | undefined {\n\t\tif (endpointIndex === 0) {\n\t\t\t// The root endpoint's associations may be configured separately or as part of \"endpoints\"\n\t\t\treturn (\n\t\t\t\tthis.associations?.get(group)\n\t\t\t\t\t?? this.endpoints?.get(0)?.associations?.get(group)\n\t\t\t);\n\t\t} else {\n\t\t\t// The other endpoints can only have a configuration as part of \"endpoints\"\n\t\t\treturn this.endpoints?.get(endpointIndex)?.associations?.get(group);\n\t\t}\n\t}\n\n\t/**\n\t * Returns a hash code that can be used to check whether a device config has changed enough to require a re-interview.\n\t */\n\tpublic getHash(): Uint8Array {\n\t\t// We only need to compare the information that is persisted elsewhere:\n\t\t// - config parameters\n\t\t// - functional association settings\n\t\t// - CC-related compat flags\n\n\t\tlet hashable: Record<string, any> = {\n\t\t\t// endpoints: {\n\t\t\t// \tassociations: {},\n\t\t\t// \tparamInformation: []\n\t\t\t// },\n\t\t\t// proprietary: {},\n\t\t\t// compat: {},\n\t\t};\n\n\t\tconst sortObject = (obj: Record<string, any>) => {\n\t\t\tconst ret: Record<string, any> = {};\n\t\t\tfor (const key of Object.keys(obj).sort()) {\n\t\t\t\tret[key] = obj[key];\n\t\t\t}\n\t\t\treturn ret;\n\t\t};\n\n\t\tconst cloneAssociationConfig = (a: AssociationConfig) => {\n\t\t\treturn sortObject(\n\t\t\t\tpick(a, [\"maxNodes\", \"multiChannel\", \"isLifeline\"]),\n\t\t\t);\n\t\t};\n\t\tconst cloneAssociationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ReadonlyMap<number, AssociationConfig> | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\ttarget.associations = {};\n\t\t\tfor (const [key, value] of map) {\n\t\t\t\ttarget.associations[key] = cloneAssociationConfig(value);\n\t\t\t}\n\t\t\ttarget.associations = sortObject(target.associations);\n\t\t};\n\n\t\tconst cloneParamInformationMap = (\n\t\t\ttarget: Record<string, any>,\n\t\t\tmap: ParamInfoMap | undefined,\n\t\t) => {\n\t\t\tif (!map || !map.size) return;\n\t\t\tconst getParamKey = (param: ParamInformation) =>\n\t\t\t\t`${param.parameterNumber}${\n\t\t\t\t\tparam.valueBitMask ? `[${num2hex(param.valueBitMask)}]` : \"\"\n\t\t\t\t}`;\n\t\t\ttarget.paramInformation = [...map.values()].sort((a, b) =>\n\t\t\t\tgetParamKey(a).localeCompare(getParamKey(b))\n\t\t\t);\n\t\t};\n\n\t\t// Clone associations and param information on the root (ep 0) and endpoints\n\t\t{\n\t\t\tlet ep0: Record<string, any> = {};\n\t\t\tcloneAssociationMap(ep0, this.associations);\n\t\t\tcloneParamInformationMap(ep0, this.paramInformation);\n\t\t\tep0 = sortObject(ep0);\n\n\t\t\tif (Object.keys(ep0).length > 0) {\n\t\t\t\thashable.endpoints ??= {};\n\t\t\t\thashable.endpoints[0] = ep0;\n\t\t\t}\n\t\t}\n\n\t\tif (this.endpoints) {\n\t\t\tfor (const [index, endpoint] of this.endpoints) {\n\t\t\t\tlet ep: Record<string, any> = {};\n\n\t\t\t\tcloneAssociationMap(ep, endpoint.associations);\n\t\t\t\tcloneParamInformationMap(ep, endpoint.paramInformation);\n\n\t\t\t\tep = sortObject(ep);\n\n\t\t\t\tif (Object.keys(ep).length > 0) {\n\t\t\t\t\thashable.endpoints ??= {};\n\t\t\t\t\thashable.endpoints[index] = ep;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Clone proprietary config\n\t\tif (this.proprietary && Object.keys(this.proprietary).length > 0) {\n\t\t\thashable.proprietary = sortObject({ ...this.proprietary });\n\t\t}\n\n\t\t// Clone relevant compat flags\n\t\tif (this.compat) {\n\t\t\tlet c: Record<string, any> = {};\n\n\t\t\t// Copy some simple flags over\n\t\t\tfor (\n\t\t\t\tconst prop of [\n\t\t\t\t\t\"forceSceneControllerGroupCount\",\n\t\t\t\t\t\"mapRootReportsToEndpoint\",\n\t\t\t\t\t\"mapBasicSet\",\n\t\t\t\t\t\"preserveRootApplicationCCValueIDs\",\n\t\t\t\t\t\"preserveEndpoints\",\n\t\t\t\t\t\"removeEndpoints\",\n\t\t\t\t\t\"treatMultilevelSwitchSetAsEvent\",\n\t\t\t\t] as const\n\t\t\t) {\n\t\t\t\tif (this.compat[prop] != undefined) {\n\t\t\t\t\tc[prop] = this.compat[prop];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Copy other, more complex flags\n\t\t\tif (this.compat.overrideQueries) {\n\t\t\t\tc.overrideQueries = Object.fromEntries(\n\t\t\t\t\tthis.compat.overrideQueries[\"overrides\"],\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.addCCs) {\n\t\t\t\tc.addCCs = Object.fromEntries(\n\t\t\t\t\t[...this.compat.addCCs].map(([ccId, def]) => [\n\t\t\t\t\t\tccId,\n\t\t\t\t\t\tObject.fromEntries(def.endpoints),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (this.compat.removeCCs) {\n\t\t\t\tc.removeCCs = Object.fromEntries(this.compat.removeCCs);\n\t\t\t}\n\t\t\tif (this.compat.treatSetAsReport) {\n\t\t\t\tc.treatSetAsReport = [...this.compat.treatSetAsReport].sort();\n\t\t\t}\n\n\t\t\tc = sortObject(c);\n\t\t\tif (Object.keys(c).length > 0) {\n\t\t\t\thashable.compat = c;\n\t\t\t}\n\t\t}\n\n\t\thashable = sortObject(hashable);\n\n\t\t// And create a hash from it. This does not need to be cryptographically secure, just good enough to detect changes.\n\t\tconst buffer = Bytes.from(JSON.stringify(hashable), \"utf8\");\n\t\tconst md5 = createHash(\"md5\");\n\t\treturn md5.update(buffer).digest();\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAAA,kBAA4C;AAC5C,oBAUO;AACP,wBAAkC;AAClC,mBAAkB;AAClB,yBAA2B;AAC3B,sBAAe;AACf,uBAAiB;AACjB,oBAAmB;AACnB,0BAAyD;AAEzD,mBAA6C;AAC7C,wBAAuD;AACvD,+BAGO;AACP,0BAA2D;AAC3D,6BAAgD;AAChD,kCAGO;AACP,4BAGO;AACP,4BAGO;AACP,8BAKO;AA0BA,MAAM,qBAAqB,iBAAAA,QAAK,KAAK,wBAAW,SAAS;AAChE,MAAM,oBAAoB,iBAAAA,QAAK,KAAK,oBAAoB,qBAAqB;AAEvE,SAAU,gBAAgBC,YAAiB;AAIhD,QAAM,aAAa,iBAAAD,QAAK,KAAKC,YAAW,SAAS;AACjD,QAAM,YAAY,iBAAAD,QAAK,KAAK,YAAY,YAAY;AACpD,SAAO,EAAE,YAAY,UAAS;AAC/B;AAKA,eAAe,sBACd,aACA,KACA,YAAgB;AAIhB,QAAM,eAAe,MAAM,gBAAAE,QAAG,QAAQ,GAAG;AACzC,aAAW,KAAK,cAAc;AAC7B,UAAM,WAAW,iBAAAF,QAAK,KAAK,KAAK,CAAC;AAEjC,UAAM,OAAO,MAAM,gBAAAE,QAAG,KAAK,QAAQ;AACnC,SACE,QAAQ,eAAe,MAAM,kBAC1B,KAAK,OAAM,KAAM,KAAK,YAAW,MAClC,KAAK,QAAQ,YACf;AACD,aAAO;IACR,WAAW,KAAK,YAAW,GAAI;AAE9B,UACC,MAAM,sBAAsB,aAAa,UAAU,UAAU,GAC5D;AACD,eAAO;MACR;IACD;EACD;AACA,SAAO;AACR;AAMA,eAAe,cACd,YACA,YACA,qBACA,QAAqB;AAErB,QAAM,QAAwD,CAAA;AAE9D,8CAAkB;AAClB,QAAM,cAAc,UAAM,kCACzB,YACA,CAAC,SACA,KAAK,SAAS,OAAO,KAClB,CAAC,KAAK,SAAS,YAAY,KAC3B,CAAC,KAAK,SAAS,aAAa,KAC5B,CAAC,KAAK,SAAS,eAAe,CAAC;AAIpC,QAAM,eAAe,eAAe,qBACjC,CAAC,kBAAkB,IACnB;AAEH,aAAW,QAAQ,aAAa;AAC/B,UAAM,eAAe,iBAAAF,QACnB,SAAS,YAAY,IAAI,EACzB,WAAW,MAAM,GAAG;AAEtB,QAAI;AACH,YAAM,SAAS,MAAM,aAAa,KAAK,MAAM,YAAY;QACxD,SAAS;QACT;QACA,UAAU;OACV;AAED,YAAM,KACL,GAAG,oBAAoB,MAAM,EAAE,IAAI,CAAC,UAAS;AAC5C,cAAM,MAAkD;UACvD,GAAG;UACH,UAAU;;AAGX,YAAI,eAAe,oBAAoB;AACtC,cAAI,UAAU;QACf;AACA,eAAO;MACR,CAAC,CAAC;IAEJ,SAAS,GAAG;AACX,YAAM,UAAU,6BAA6B,YAAY,KACvD,EAAY,OACd;AAGA,UAAI,QAAQ,IAAI,aAAa,UAAU,CAAC,CAAC,QAAQ,IAAI,IAAI;AACxD,cAAM,IAAI,uBAAW,SAAS,4BAAgB,cAAc;MAC7D,OAAO;AACN,gBAAQ,MAAM,SAAS,OAAO;MAC/B;IACD;EACD;AAEA,SAAO;AACR;AAEA,eAAe,sBACd,YACA,WACA,qBACA,QAAqB;AAGrB,MAAI,cAAc,CAAE,UAAM,0BAAW,SAAS;AAC9C,MAAI;AACJ,MAAI;AAEJ,MAAI,CAAC,aAAa;AACjB,QAAI;AACH,YAAM,eAAe,MAAM,gBAAAE,QAAG,SAAS,WAAW,MAAM;AACxD,cAAQ,aAAAC,QAAM,MAAM,YAAY;AAChC,oBAAc,MAAM,gBAAAD,QAAG,KAAK,SAAS,GAAG;IACzC,QAAQ;AACP,cAAQ,MACP,oDACA,MAAM;AAEP,oBAAc;IACf;AACC,UAAI,CAAC,OAAO;AACX,gBAAQ,MACP,8CACA,MAAM;AAEP,sBAAc;MACf;IACD;EACD;AAGA,MAAI,CAAC,aAAa;AACjB,kBAAc,MAAM,sBACnB,YACA,YACA,UAAW;AAEZ,QAAI,aAAa;AAChB,cAAQ,MACP,sEACA,SAAS;IAEX;EACD;AAEA,MAAI,aAAa;AAEhB,YAAQ,MAAM,cACb,YACA,MACA,qBACA,MAAM;AAGP,QAAI;AACH,YAAM,gBAAAA,QAAG,UACR,iBAAAF,QAAK,KAAK,SAAS,GACnB;MACF,yBAAU,OAAO,GAAI,CAAC;GAEpB,MAAM;AAEP,cAAQ,MAAM,4BAA4B,SAAS;IACpD,SAAS,GAAG;AACX,cAAQ,MACP,4CACE,EAAY,OACd,IACA,OAAO;IAET;EACD;AAEA,SAAO;AACR;AAOA,eAAsB,4BACrB,yBACA,QAAqB;AAErB,UACC,MAAM,cACL,yBACA,OACA,CAAC,WACA,OAAO,QAAQ,IAAI,CAAC,SAAS;IAC5B,oBAAgB,wBACf,OAAO,eAAe,SAAS,EAAE,CAAC;IAEnC,cAAc,OAAO;IACrB,OAAO,OAAO;IACd,iBAAa,wBAAS,IAAI,WAAW;IACrC,eAAW,wBAAS,IAAI,SAAS;IACjC,iBAAiB,OAAO;IACxB,GAAI,OAAO,YAAY,EAAE,WAAW,KAAa,IAAK,CAAA;IACtD,SAAS;IACR,GACH,MAAM,GAEN,IAAI,CAAC,EAAE,UAAU,GAAG,MAAK,OAAQ;IAClC,GAAG;;;IAGH,UAAU,iBAAAA,QAAK,KAAK,yBAAyB,QAAQ;IACpD;AACH;AAOA,eAAsB,wBACrB,QACA,gBAAwB;AAExB,QAAM,EAAE,YAAY,UAAS,IAAK,gBAChC,sBAAkB,gCAAiB,KAAO,sBAAS;AAGrD,SAAO,sBACN,YACA,WACA,CAAC,WACA,OAAO,QAAQ,IAAI,CAAC,SAAS;IAC5B,oBAAgB,wBAAS,OAAO,eAAe,SAAS,EAAE,CAAC;IAC3D,cAAc,OAAO;IACrB,OAAO,OAAO;IACd,iBAAa,wBAAS,IAAI,WAAW;IACrC,eAAW,wBAAS,IAAI,SAAS;IACjC,iBAAiB,OAAO;IACxB,GAAI,OAAO,YAAY,EAAE,WAAW,KAAa,IAAK,CAAA;IACrD,GACH,MAAM;AAER;AAOA,eAAsB,gCACrB,QAAqB;AAGrB,SAAO,sBACN,oBACA,mBACA,CAAC,WACA,OAAO,QAAQ,IAAI,CAAC,SAAS;IAC5B,oBAAgB,wBAAS,OAAO,eAAe,SAAS,EAAE,CAAC;IAC3D,cAAc,OAAO;IACrB,OAAO,OAAO;IACd,aAAa,OAAO;IACpB,iBAAa,wBAAS,IAAI,WAAW;IACrC,eAAW,wBAAS,IAAI,SAAS;IACjC,iBAAiB,OAAO;IACxB,GAAI,OAAO,YAAY,EAAE,WAAW,KAAa,IAAK,CAAA;IACtD,SAAS;IACR,GACH,MAAM;AAER;AAEA,SAAS,oBAAoB,KAAQ;AACpC,SAAO,OAAO,QAAQ,YAAY,qCAAmB,KAAK,GAAG;AAC9D;AAEA,MAAM,uBAAuB;AAC7B,SAAS,kBAAkB,KAAQ;AAClC,SACC,OAAO,QAAQ,YACZ,qBAAqB,KAAK,GAAG,KAC7B,IACD,MAAM,GAAG,EACT,IAAI,CAAC,QAAQ,SAAS,KAAK,EAAE,CAAC,EAC9B,MAAM,CAAC,QAAQ,OAAO,KAAK,OAAO,GAAG;AAEzC;AAGM,MAAO,wBAAuB;EAC5B,aAAa,KACnB,UACA,YACA,SAIC;AAED,UAAM,EAAE,UAAU,QAAO,IAAK;AAE9B,UAAM,eAAe,WAClB,iBAAAA,QAAK,SAAS,SAAS,QAAQ,EAAE,WAAW,MAAM,GAAG,IACrD;AACH,UAAM,OAAO,UAAM,0CAAqB,UAAU;MACjD,QAAQ;MACR,GAAI,QAAQ,gBAAgB,CAAA;KAC5B;AACD,WAAO,IAAI,wBAAwB,cAAc,YAAY,IAAI;EAClE;EAEA,YACC,UACA,YACA,YAAsB;AAEtB,SAAK,WAAW;AAChB,SAAK,aAAa;AAElB,QAAI,CAAC,oBAAoB,WAAW,cAAc,GAAG;AACpD,gDACC,UACA,kCAAkC,QAAQ;qEACuB;IAEnE;AACA,SAAK,iBAAiB,SAAS,WAAW,gBAAgB,EAAE;AAE5D,eAAW,QAAQ,CAAC,gBAAgB,SAAS,aAAa,GAAY;AACrE,WAAK,IAAI,QAAI,uDACZ,UACA,UACA,MACA,WAAW,IAAI,CAAC;IAElB;AAEA,QACC,KAAC,2BAAQ,WAAW,OAAO,KACxB,CAAE,WAAW,QAAkB,MACjC,CAAC,YACA,4BAAS,GAAG,KACT,oBAAoB,IAAI,WAAW,KACnC,oBAAoB,IAAI,SAAS,CAAC,GAEtC;AACD,gDACC,UACA,kCAAkC,QAAQ;wFAC0C;IAEtF;AACA,SAAK,UAAW,WAAW,QAAkB,IAC5C,CAAC,EAAE,aAAa,UAAS,OAAQ;MAChC,aAAa,SAAS,aAAa,EAAE;MACrC,WAAW,SAAS,WAAW,EAAE;MAChC;AAGH,QACC,KAAC,4BAAS,WAAW,eAAe,KACjC,CAAC,kBAAkB,WAAW,gBAAgB,GAAG,KACjD,CAAC,kBAAkB,WAAW,gBAAgB,GAAG,GACnD;AACD,gDACC,UACA,kCAAkC,QAAQ;+GACiE;IAE7G,OAAO;AACN,YAAM,EAAE,KAAK,IAAG,IAAK,WAAW;AAChC,UAAI,cAAAI,QAAO,OAAG,0BAAW,GAAG,OAAG,0BAAW,GAAG,CAAC,GAAG;AAChD,kDACC,UACA,kCAAkC,QAAQ;sBACzB,GAAG,iDAAiD,GAAG,EAAE;MAE5E;AACA,WAAK,kBAAkB,EAAE,KAAK,IAAG;IAClC;AAEA,QACC,WAAW,aAAa,UACrB,WAAW,cAAc,MAC3B;AACD,gDACC,UACA,kCAAkC,QAAQ;kCACZ;IAEhC;AACA,SAAK,YAAY,CAAC,CAAC,WAAW;AAE9B,QAAI,WAAW,aAAa,QAAW;AACtC,YAAM,YAAY,oBAAI,IAAG;AACzB,UAAI,KAAC,4BAAS,WAAW,SAAS,GAAG;AACpC,kDACC,UACA,kCAAkC,QAAQ;2BACpB;MAExB;AACA,iBAAW,CAAC,KAAK,EAAE,KAAK,OAAO,QAAQ,WAAW,SAAS,GAAG;AAC7D,YAAI,CAAC,QAAQ,KAAK,GAAG,GAAG;AACvB,oDACC,UACA,kCAAkC,QAAQ;oCACZ,GAAG,gBAAgB;QAEnD;AAEA,cAAM,UAAU,SAAS,KAAK,EAAE;AAChC,kBAAU,IACT,SACA,IAAI,gDAA0B,MAAM,SAAS,EAAS,CAAC;MAEzD;AACA,WAAK,YAAY;IAClB;AAEA,QAAI,WAAW,gBAAgB,QAAW;AACzC,YAAM,eAAe,oBAAI,IAAG;AAI5B,UAAI,KAAC,4BAAS,WAAW,YAAY,GAAG;AACvC,kDACC,UACA,kCAAkC,QAAQ;8BACjB;MAE3B;AACA,iBACO,CAAC,KAAK,eAAe,KAAK,OAAO,QACtC,WAAW,YAAY,GAEvB;AACD,YAAI,CAAC,gBAAgB,KAAK,GAAG,GAAG;AAC/B,oDACC,UACA,kCAAkC,QAAQ;8BAClB,GAAG,mBAAmB;QAEhD;AAEA,cAAM,SAAS,SAAS,KAAK,EAAE;AAC/B,qBAAa,IACZ,QACA,IAAI,sDACH,UACA,QACA,eAAsB,CACtB;MAEH;AACA,WAAK,eAAe;IACrB;AAEA,QAAI,WAAW,oBAAoB,QAAW;AAC7C,WAAK,uBAAmB,6DACvB,YACA,IAAI;IAEN;AAEA,QAAI,WAAW,eAAe,QAAW;AACxC,UAAI,KAAC,4BAAS,WAAW,WAAW,GAAG;AACtC,kDACC,UACA,kCAAkC,QAAQ;6BAClB;MAE1B;AACA,WAAK,cAAc,WAAW;IAC/B;AAEA,QAAI,WAAW,UAAU,QAAW;AACnC,cACC,2BAAQ,WAAW,MAAM,KACtB,WAAW,OAAO,MAAM,CAAC,aAAc,4BAAS,IAAI,CAAC,GACvD;AAED,mBAAW,SAAS,WAAW,QAAQ;AACtC,wDACC,UACA,OACA,0CAA0C;QAE5C;AAEA,aAAK,SAAS,WAAW,OAAO,IAC/B,CAAC,SAAc,IAAI,4CAAwB,UAAU,IAAI,CAAC;MAE5D,eAAW,4BAAS,WAAW,MAAM,GAAG;AACvC,aAAK,SAAS,IAAI,4CACjB,UACA,WAAW,MAAM;MAEnB,OAAO;AACN,kDACC,UACA,kCAAkC,QAAQ;6DACc;MAE1D;IACD;AAEA,QAAI,WAAW,YAAY,QAAW;AACrC,UAAI,KAAC,4BAAS,WAAW,QAAQ,GAAG;AACnC,kDACC,UACA,kCAAkC,QAAQ;0BACrB;MAEvB;AACA,WAAK,WAAW,IAAI,gDACnB,UACA,WAAW,QAAQ;IAErB;EACD;EAEgB;EAEA;EACA;EACA;EACA;EACA;EAIA;;EAEA;EACA;EACA;EAIA;;;;;EAKA;;EAEA;;EAIA;;EAGA;EAET,SAAS,UAAmB;AAClC,WAAO,IAAI,aACV,KAAK,UACL,KAAK,gBACL,qCAAa,KAAK,cAAc,QAAQ,GACxC,KAAK,oBACL,qCAAa,KAAK,OAAO,QAAQ,OACjC,qCAAa,KAAK,aAAa,QAAQ,GACvC,KAAK,SACL,KAAK,iBACL,KAAK,eACL,qCAAa,KAAK,WAAW,QAAQ,OACrC,qCAAa,KAAK,cAAc,QAAQ,OACxC,qCAAa,KAAK,kBAAkB,QAAQ,GAC5C,KAAK,iBACL,qCAAa,KAAK,QAAQ,QAAQ,OAClC,qCAAa,KAAK,UAAU,QAAQ,CAAC;EAEvC;;AAGK,MAAO,aAAY;EACjB,aAAa,KACnB,UACA,YACA,SAKC;AAED,UAAM,MAAM,MAAM,wBAAwB,KACzC,UACA,YACA,OAAO;AAER,WAAO,IAAI,SAAS,QAAQ,QAAQ;EACrC;EAEA,YACC,UACA,YACA,cACA,gBACA,OACA,aACA,SAIA,iBACA,WACA,WACA,cACA,kBACA,aACA,QACA,UAAyB;AAEzB,SAAK,WAAW;AAChB,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,iBAAiB;AACtB,SAAK,QAAQ;AACb,SAAK,cAAc;AACnB,SAAK,UAAU;AACf,SAAK,kBAAkB;AACvB,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,eAAe;AACpB,SAAK,mBAAmB;AACxB,SAAK,cAAc;AACnB,SAAK,SAAS;AACd,SAAK,WAAW;EACjB;EAEgB;;EAEA;EACA;EACA;EACA;EACA;EACA;EAIA;;EAEA;EACA;EACA;EACA;;;;;EAKA;;EAEA;;EAEA;;EAGT,gCACN,eACA,OAAa;AAEb,QAAI,kBAAkB,GAAG;AAExB,aACC,KAAK,cAAc,IAAI,KAAK,KACxB,KAAK,WAAW,IAAI,CAAC,GAAG,cAAc,IAAI,KAAK;IAErD,OAAO;AAEN,aAAO,KAAK,WAAW,IAAI,aAAa,GAAG,cAAc,IAAI,KAAK;IACnE;EACD;;;;EAKO,UAAO;AAMb,QAAI,WAAgC;;;;;;;;AASpC,UAAM,aAAa,CAAC,QAA4B;AAC/C,YAAM,MAA2B,CAAA;AACjC,iBAAW,OAAO,OAAO,KAAK,GAAG,EAAE,KAAI,GAAI;AAC1C,YAAI,GAAG,IAAI,IAAI,GAAG;MACnB;AACA,aAAO;IACR;AAEA,UAAM,yBAAyB,CAAC,MAAwB;AACvD,aAAO,eACN,oBAAK,GAAG,CAAC,YAAY,gBAAgB,YAAY,CAAC,CAAC;IAErD;AACA,UAAM,sBAAsB,CAC3B,QACA,QACG;AACH,UAAI,CAAC,OAAO,CAAC,IAAI;AAAM;AACvB,aAAO,eAAe,CAAA;AACtB,iBAAW,CAAC,KAAK,KAAK,KAAK,KAAK;AAC/B,eAAO,aAAa,GAAG,IAAI,uBAAuB,KAAK;MACxD;AACA,aAAO,eAAe,WAAW,OAAO,YAAY;IACrD;AAEA,UAAM,2BAA2B,CAChC,QACA,QACG;AACH,UAAI,CAAC,OAAO,CAAC,IAAI;AAAM;AACvB,YAAM,cAAc,CAAC,UACpB,GAAG,MAAM,eAAe,GACvB,MAAM,eAAe,QAAI,uBAAQ,MAAM,YAAY,CAAC,MAAM,EAC3D;AACD,aAAO,mBAAmB,CAAC,GAAG,IAAI,OAAM,CAAE,EAAE,KAAK,CAAC,GAAG,MACpD,YAAY,CAAC,EAAE,cAAc,YAAY,CAAC,CAAC,CAAC;IAE9C;AAGA;AACC,UAAI,MAA2B,CAAA;AAC/B,0BAAoB,KAAK,KAAK,YAAY;AAC1C,+BAAyB,KAAK,KAAK,gBAAgB;AACnD,YAAM,WAAW,GAAG;AAEpB,UAAI,OAAO,KAAK,GAAG,EAAE,SAAS,GAAG;AAChC,iBAAS,cAAc,CAAA;AACvB,iBAAS,UAAU,CAAC,IAAI;MACzB;IACD;AAEA,QAAI,KAAK,WAAW;AACnB,iBAAW,CAAC,OAAO,QAAQ,KAAK,KAAK,WAAW;AAC/C,YAAI,KAA0B,CAAA;AAE9B,4BAAoB,IAAI,SAAS,YAAY;AAC7C,iCAAyB,IAAI,SAAS,gBAAgB;AAEtD,aAAK,WAAW,EAAE;AAElB,YAAI,OAAO,KAAK,EAAE,EAAE,SAAS,GAAG;AAC/B,mBAAS,cAAc,CAAA;AACvB,mBAAS,UAAU,KAAK,IAAI;QAC7B;MACD;IACD;AAGA,QAAI,KAAK,eAAe,OAAO,KAAK,KAAK,WAAW,EAAE,SAAS,GAAG;AACjE,eAAS,cAAc,WAAW,EAAE,GAAG,KAAK,YAAW,CAAE;IAC1D;AAGA,QAAI,KAAK,QAAQ;AAChB,UAAI,IAAyB,CAAA;AAG7B,iBACO,QAAQ;QACb;QACA;QACA;QACA;QACA;QACA;QACA;SAEA;AACD,YAAI,KAAK,OAAO,IAAI,KAAK,QAAW;AACnC,YAAE,IAAI,IAAI,KAAK,OAAO,IAAI;QAC3B;MACD;AAGA,UAAI,KAAK,OAAO,iBAAiB;AAChC,UAAE,kBAAkB,OAAO,YAC1B,KAAK,OAAO,gBAAgB,WAAW,CAAC;MAE1C;AACA,UAAI,KAAK,OAAO,QAAQ;AACvB,UAAE,SAAS,OAAO,YACjB,CAAC,GAAG,KAAK,OAAO,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;UAC5C;UACA,OAAO,YAAY,IAAI,SAAS;SAChC,CAAC;MAEJ;AACA,UAAI,KAAK,OAAO,WAAW;AAC1B,UAAE,YAAY,OAAO,YAAY,KAAK,OAAO,SAAS;MACvD;AACA,UAAI,KAAK,OAAO,kBAAkB;AACjC,UAAE,mBAAmB,CAAC,GAAG,KAAK,OAAO,gBAAgB,EAAE,KAAI;MAC5D;AAEA,UAAI,WAAW,CAAC;AAChB,UAAI,OAAO,KAAK,CAAC,EAAE,SAAS,GAAG;AAC9B,iBAAS,SAAS;MACnB;IACD;AAEA,eAAW,WAAW,QAAQ;AAG9B,UAAM,SAAS,oBAAM,KAAK,KAAK,UAAU,QAAQ,GAAG,MAAM;AAC1D,UAAM,UAAM,+BAAW,KAAK;AAC5B,WAAO,IAAI,OAAO,MAAM,EAAE,OAAM;EACjC;;",
6
+ "names": ["path", "configDir", "fs", "JSON5", "semver"]
7
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var DeviceConfig_unit_test_exports = {};
16
+ module.exports = __toCommonJS(DeviceConfig_unit_test_exports);
17
+ //# sourceMappingURL=DeviceConfig.unit._test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/devices/DeviceConfig.unit._test.ts"],
4
+ "sourcesContent": ["// FIXME: These tests are incompatible with auto-generating the index file\n\n// import fsExtra from \"fs-extra\";\n// import path from \"path\";\n// import { ConfigManager } from \"./ConfigManager\";\n// import { configDir } from \"./utils\";\n\n// jest.mock(\"fs-extra\");\n// const readFileMock = fsExtra.readFile as jest.Mock;\n// const pathExistsMock = fsExtra.pathExists as jest.Mock;\n\n// describe(\"lib/config/Devices\", () => {\n// \tdescribe(\"lookupDevice (with missing index)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValue(false);\n// \t\t\treadFileMock.mockRejectedValue(new Error(\"File does not exist\"));\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(1, 2, 3),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(1, 2, 5),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupDevice (with missing file)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValueOnce(true).mockResolvedValue(false);\n// \t\t\treadFileMock\n// \t\t\t\t.mockResolvedValueOnce(\n// \t\t\t\t\t// Index\n// \t\t\t\t\tJSON.stringify([\n// \t\t\t\t\t\t{\n// \t\t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t\t},\n// \t\t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t]),\n// \t\t\t\t)\n// \t\t\t\t.mockRejectedValue(new Error(\"File does not exist\"));\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0023),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupManufacturer (with invalid file)\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\tpathExistsMock.mockClear();\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\treadFileMock\n// \t\t\t\t.mockResolvedValueOnce(\n// \t\t\t\t\t// Index\n// \t\t\t\t\tJSON.stringify([\n// \t\t\t\t\t\t{\n// \t\t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t\t},\n// \t\t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t]),\n// \t\t\t\t)\n// \t\t\t\t.mockResolvedValueOnce(`{`);\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tit(\"returns undefined instead of throwing\", async () => {\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0023),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n\n// \tdescribe(\"lookupDevice()\", () => {\n// \t\tlet configManager: ConfigManager;\n\n// \t\tbeforeAll(async () => {\n// \t\t\treadFileMock.mockReset();\n// \t\t\treadFileMock.mockResolvedValueOnce(\n// \t\t\t\t// Index\n// \t\t\t\tJSON.stringify([\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0023\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/abcdef.json\",\n// \t\t\t\t\t},\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0034\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"0.0\",\n// \t\t\t\t\t\t\tmax: \"1.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/123456.json\",\n// \t\t\t\t\t},\n// \t\t\t\t\t{\n// \t\t\t\t\t\tmanufacturerId: \"0x0abc\",\n// \t\t\t\t\t\tproductType: \"0x0001\",\n// \t\t\t\t\t\tproductId: \"0x0034\",\n// \t\t\t\t\t\tfirmwareVersion: {\n// \t\t\t\t\t\t\tmin: \"2.0\",\n// \t\t\t\t\t\t\tmax: \"255.255\",\n// \t\t\t\t\t\t},\n// \t\t\t\t\t\tfilename: \"0x0abc/123456-8.json\",\n// \t\t\t\t\t},\n// \t\t\t\t]),\n// \t\t\t);\n// \t\t\tpathExistsMock.mockReset();\n// \t\t\tpathExistsMock.mockResolvedValueOnce(true);\n\n// \t\t\tconfigManager = new ConfigManager();\n// \t\t\tawait configManager.loadDeviceIndex();\n// \t\t});\n\n// \t\tbeforeEach(() => {\n// \t\t\treadFileMock.mockClear();\n// \t\t\tpathExistsMock.mockClear();\n// \t\t});\n\n// \t\tit(\"tests if the corresponding file exists\", async () => {\n// \t\t\tpathExistsMock.mockResolvedValue(false);\n// \t\t\tawait configManager.lookupDevice(0x0abc, 0x0001, 0x0023);\n// \t\t\texpect(pathExistsMock).toBeCalledTimes(1);\n// \t\t\tconst expectedPath = path.join(\n// \t\t\t\tconfigDir,\n// \t\t\t\t\"devices/0x0abc/abcdef.json\",\n// \t\t\t);\n// \t\t\texpect(pathExistsMock.mock.calls[0][0]).toBe(expectedPath);\n// \t\t});\n\n// \t\tit(\"looks up the file with the correct firmware version\", async () => {\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\tawait configManager.lookupDevice(0x0abc, 0x0001, 0x0034, \"2.1\");\n// \t\t\texpect(pathExistsMock).toBeCalledTimes(1);\n// \t\t\tconst expectedPath = path.join(\n// \t\t\t\tconfigDir,\n// \t\t\t\t\"devices/0x0abc/123456-8.json\",\n// \t\t\t);\n// \t\t\texpect(pathExistsMock.mock.calls[0][0]).toBe(expectedPath);\n// \t\t});\n\n// \t\tit(\"returns the contents of a found file, parsed as JSON5\", async () => {\n// \t\t\t// The first attempt at reading the file should succeed\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\t// Return a dummy file that must be parsed as JSON5\n// \t\t\treadFileMock.mockResolvedValue(\n// \t\t\t\t`// This is a minimal valid device config\n// {\n// \t\"manufacturer\": \"Test manufacturer\",\n// \t\"manufacturerId\": \"0x0abc\",\n// \t\"label\": \"LABEL\",\n// \t\"description\": \"desc rip tion\",\n// \t\"devices\": [\n// \t\t{\n// \t\t\t\"productType\": \"0x0001\",\n// \t\t\t\"productId\": \"0x0001\"\n// \t\t}\n// \t],\n// \t\"firmwareVersion\": {\n// \t\t\"min\": \"0.0\",\n// \t\t\"max\": \"255.255\"\n// \t}\n// }`,\n// \t\t\t);\n\n// \t\t\tconst result = await configManager.lookupDevice(\n// \t\t\t\t0x0abc,\n// \t\t\t\t0x0001,\n// \t\t\t\t0x0034,\n// \t\t\t\t\"2.1\",\n// \t\t\t);\n// \t\t\texpect(result).toBeDefined();\n// \t\t\texpect(result!.manufacturer).toBe(\"Test manufacturer\");\n// \t\t});\n\n// \t\tit(\"does not throw if the JSON file is invalid\", async () => {\n// \t\t\t// The first attempt at reading the file should succeed\n// \t\t\tpathExistsMock.mockResolvedValue(true);\n// \t\t\t// Return an invalid JSON file\n// \t\t\treadFileMock.mockResolvedValue(`{\"name\": }`);\n\n// \t\t\t// return undefined instead of throwing\n// \t\t\tawait expect(\n// \t\t\t\tconfigManager.lookupDevice(0x0abc, 0x0001, 0x0034, \"2.1\"),\n// \t\t\t).resolves.toBeUndefined();\n// \t\t});\n// \t});\n// });\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAAA;;",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
1
1
  import { type JSONObject } from "@zwave-js/shared/safe";
2
- import { type ConditionalItem } from "./ConditionalItem";
3
- import { type ConditionalPrimitive } from "./ConditionalPrimitive";
4
- import type { DeviceID } from "./shared";
2
+ import { type ConditionalItem } from "./ConditionalItem.js";
3
+ import { type ConditionalPrimitive } from "./ConditionalPrimitive.js";
4
+ import type { DeviceID } from "./shared.js";
5
5
  export declare class ConditionalDeviceMetadata implements ConditionalItem<DeviceMetadata> {
6
6
  constructor(filename: string, definition: JSONObject);
7
7
  readonly condition?: string;