@zwave-js/config 14.3.10 → 15.0.0-beta.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.
- package/build/cjs/ConfigManager.d.ts +9 -3
- package/build/cjs/ConfigManager.js +42 -20
- package/build/cjs/ConfigManager.js.map +2 -2
- package/build/cjs/JsonTemplate.d.ts +2 -1
- package/build/cjs/JsonTemplate.js +16 -17
- package/build/cjs/JsonTemplate.js.map +3 -3
- package/build/cjs/Logger.d.ts +2 -2
- package/build/cjs/Logger.js.map +2 -2
- package/build/cjs/Manufacturers.d.ts +2 -1
- package/build/cjs/Manufacturers.js +8 -9
- package/build/cjs/Manufacturers.js.map +3 -3
- package/build/cjs/_version.d.ts +1 -1
- package/build/cjs/_version.js +1 -1
- package/build/cjs/_version.js.map +1 -1
- package/build/cjs/devices/DeviceConfig.d.ts +3 -2
- package/build/cjs/devices/DeviceConfig.js +34 -35
- package/build/cjs/devices/DeviceConfig.js.map +3 -3
- package/build/cjs/index.d.ts +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/index_safe.d.ts +1 -0
- package/build/cjs/index_safe.js.map +1 -1
- package/build/cjs/traits.d.ts +11 -0
- package/build/cjs/traits.js +17 -0
- package/build/cjs/traits.js.map +7 -0
- package/build/cjs/utils.d.ts +2 -1
- package/build/cjs/utils.js +13 -14
- package/build/cjs/utils.js.map +3 -3
- package/build/esm/ConfigManager.d.ts +9 -3
- package/build/esm/ConfigManager.d.ts.map +1 -1
- package/build/esm/ConfigManager.js +42 -19
- package/build/esm/ConfigManager.js.map +1 -1
- package/build/esm/JsonTemplate.d.ts +2 -1
- package/build/esm/JsonTemplate.d.ts.map +1 -1
- package/build/esm/JsonTemplate.js +13 -14
- package/build/esm/JsonTemplate.js.map +1 -1
- package/build/esm/Logger.d.ts +2 -2
- package/build/esm/Logger.d.ts.map +1 -1
- package/build/esm/Logger.js.map +1 -1
- package/build/esm/Manufacturers.d.ts +2 -1
- package/build/esm/Manufacturers.d.ts.map +1 -1
- package/build/esm/Manufacturers.js +7 -8
- package/build/esm/Manufacturers.js.map +1 -1
- package/build/esm/_version.d.ts +1 -1
- package/build/esm/_version.d.ts.map +1 -1
- package/build/esm/_version.js +1 -1
- package/build/esm/_version.js.map +1 -1
- package/build/esm/devices/DeviceConfig.d.ts +3 -2
- package/build/esm/devices/DeviceConfig.d.ts.map +1 -1
- package/build/esm/devices/DeviceConfig.js +25 -26
- package/build/esm/devices/DeviceConfig.js.map +1 -1
- package/build/esm/index.d.ts +1 -0
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index_safe.d.ts +1 -0
- package/build/esm/index_safe.d.ts.map +1 -1
- package/build/esm/traits.d.ts +11 -0
- package/build/esm/traits.d.ts.map +1 -0
- package/build/esm/traits.js +2 -0
- package/build/esm/traits.js.map +1 -0
- package/build/esm/utils.d.ts +2 -1
- package/build/esm/utils.d.ts.map +1 -1
- package/build/esm/utils.js +16 -14
- package/build/esm/utils.js.map +1 -1
- package/config/README.md +1 -1
- package/config/devices/0x0005/pe653.json +1 -1
- package/config/devices/0x003b/be469zp.json +1 -1
- package/config/devices/0x0090/hc620.json +1 -1
- package/config/devices/0x0098/ct100.json +2 -2
- package/config/devices/0x0098/ct101.json +1 -1
- package/config/devices/0x0098/ct200x.json +1 -1
- package/config/devices/0x0287/iblindsv3.json +1 -1
- package/config/devices/0x031e/vzw31-sn.json +1 -1
- package/config/devices/0x0344/he-zw-therm-fl2.json +1 -1
- package/config/devices/0x0441/ibt4zwave.json +1 -1
- package/config/devices/0x045a/Z-CM-V01.json +1 -1
- package/config/devices/templates/master_template.json +1 -1
- package/package.json +15 -16
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type LogContainer } from "@zwave-js/core";
|
|
2
|
+
import { type FileSystem } from "@zwave-js/shared/bindings";
|
|
2
3
|
import { type ManufacturersMap } from "./Manufacturers.js";
|
|
3
4
|
import { ConditionalDeviceConfig, type DeviceConfig, type DeviceConfigIndex, type FulltextDeviceConfigIndex } from "./devices/DeviceConfig.js";
|
|
4
5
|
export interface ConfigManagerOptions {
|
|
5
|
-
|
|
6
|
+
bindings?: FileSystem;
|
|
7
|
+
logContainer?: LogContainer;
|
|
6
8
|
deviceConfigPriorityDir?: string;
|
|
7
9
|
deviceConfigExternalDir?: string;
|
|
8
10
|
}
|
|
9
11
|
export declare class ConfigManager {
|
|
10
12
|
constructor(options?: ConfigManagerOptions);
|
|
13
|
+
private _fs;
|
|
14
|
+
private getFS;
|
|
11
15
|
private _configVersion;
|
|
12
16
|
get configVersion(): string;
|
|
13
|
-
private
|
|
17
|
+
private _logContainer;
|
|
18
|
+
private _logger;
|
|
19
|
+
private getLogger;
|
|
14
20
|
private _manufacturers;
|
|
15
21
|
get manufacturers(): ManufacturersMap;
|
|
16
22
|
private deviceConfigPriorityDir;
|
|
@@ -34,7 +34,7 @@ __export(ConfigManager_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(ConfigManager_exports);
|
|
35
35
|
var import_core = require("@zwave-js/core");
|
|
36
36
|
var import_shared = require("@zwave-js/shared");
|
|
37
|
-
var
|
|
37
|
+
var import_pathe = __toESM(require("pathe"), 1);
|
|
38
38
|
var import_Logger = require("./Logger.js");
|
|
39
39
|
var import_Manufacturers = require("./Manufacturers.js");
|
|
40
40
|
var import_version = require("./_version.js");
|
|
@@ -45,16 +45,34 @@ class ConfigManager {
|
|
|
45
45
|
__name(this, "ConfigManager");
|
|
46
46
|
}
|
|
47
47
|
constructor(options = {}) {
|
|
48
|
-
this.
|
|
48
|
+
this._fs = options.bindings;
|
|
49
|
+
this._logContainer = options.logContainer;
|
|
49
50
|
this.deviceConfigPriorityDir = options.deviceConfigPriorityDir;
|
|
50
51
|
this.deviceConfigExternalDir = options.deviceConfigExternalDir;
|
|
51
52
|
this._configVersion = import_version.PACKAGE_VERSION;
|
|
52
53
|
}
|
|
54
|
+
_fs;
|
|
55
|
+
async getFS() {
|
|
56
|
+
this._fs ??= (await import("@zwave-js/core/bindings/fs/node")).fs;
|
|
57
|
+
return this._fs;
|
|
58
|
+
}
|
|
53
59
|
_configVersion;
|
|
54
60
|
get configVersion() {
|
|
55
61
|
return this._configVersion;
|
|
56
62
|
}
|
|
57
|
-
|
|
63
|
+
_logContainer;
|
|
64
|
+
_logger;
|
|
65
|
+
async getLogger() {
|
|
66
|
+
if (!this._logContainer) {
|
|
67
|
+
this._logContainer = (await import("@zwave-js/core/bindings/log/node")).log({
|
|
68
|
+
enabled: false
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (!this._logger) {
|
|
72
|
+
this._logger = new import_Logger.ConfigLogger(this._logContainer);
|
|
73
|
+
}
|
|
74
|
+
return this._logger;
|
|
75
|
+
}
|
|
58
76
|
_manufacturers;
|
|
59
77
|
get manufacturers() {
|
|
60
78
|
if (!this._manufacturers) {
|
|
@@ -74,30 +92,31 @@ class ConfigManager {
|
|
|
74
92
|
return this._useExternalConfig;
|
|
75
93
|
}
|
|
76
94
|
async loadAll() {
|
|
95
|
+
const logger = await this.getLogger();
|
|
77
96
|
let syncResult;
|
|
78
97
|
const externalConfigDir = this.externalConfigDir;
|
|
79
98
|
if (externalConfigDir) {
|
|
80
|
-
syncResult = await (0, import_utils.syncExternalConfigDir)(externalConfigDir,
|
|
99
|
+
syncResult = await (0, import_utils.syncExternalConfigDir)(await this.getFS(), externalConfigDir, logger);
|
|
81
100
|
}
|
|
82
101
|
if (syncResult?.success) {
|
|
83
102
|
this._useExternalConfig = true;
|
|
84
|
-
|
|
103
|
+
logger.print(`Using external configuration dir ${externalConfigDir}`);
|
|
85
104
|
this._configVersion = syncResult.version;
|
|
86
105
|
} else {
|
|
87
106
|
this._useExternalConfig = false;
|
|
88
107
|
this._configVersion = import_version.PACKAGE_VERSION;
|
|
89
108
|
}
|
|
90
|
-
|
|
109
|
+
logger.print(`version ${this._configVersion}`, "info");
|
|
91
110
|
await this.loadManufacturers();
|
|
92
111
|
await this.loadDeviceIndex();
|
|
93
112
|
}
|
|
94
113
|
async loadManufacturers() {
|
|
95
114
|
try {
|
|
96
|
-
this._manufacturers = await (0, import_Manufacturers.loadManufacturersInternal)(this._useExternalConfig && this.externalConfigDir || void 0);
|
|
115
|
+
this._manufacturers = await (0, import_Manufacturers.loadManufacturersInternal)(await this.getFS(), this._useExternalConfig && this.externalConfigDir || void 0);
|
|
97
116
|
} catch (e) {
|
|
98
117
|
if ((0, import_core.isZWaveError)(e) && e.code === import_core.ZWaveErrorCodes.Config_Invalid) {
|
|
99
118
|
if (process.env.NODE_ENV !== "test") {
|
|
100
|
-
this.
|
|
119
|
+
(await this.getLogger()).print(`Could not load manufacturers config: ${e.message}`, "error");
|
|
101
120
|
}
|
|
102
121
|
if (!this._manufacturers)
|
|
103
122
|
this._manufacturers = /* @__PURE__ */ new Map();
|
|
@@ -110,7 +129,7 @@ class ConfigManager {
|
|
|
110
129
|
if (!this._manufacturers) {
|
|
111
130
|
throw new import_core.ZWaveError("The config has not been loaded yet!", import_core.ZWaveErrorCodes.Driver_NotReady);
|
|
112
131
|
}
|
|
113
|
-
await (0, import_Manufacturers.saveManufacturersInternal)(this._manufacturers);
|
|
132
|
+
await (0, import_Manufacturers.saveManufacturersInternal)(await this.getFS(), this._manufacturers);
|
|
114
133
|
}
|
|
115
134
|
/**
|
|
116
135
|
* Looks up the name of the manufacturer with the given ID in the configuration DB
|
|
@@ -134,14 +153,16 @@ class ConfigManager {
|
|
|
134
153
|
this._manufacturers.set(manufacturerId, manufacturerName);
|
|
135
154
|
}
|
|
136
155
|
async loadDeviceIndex() {
|
|
156
|
+
const fs = await this.getFS();
|
|
157
|
+
const logger = await this.getLogger();
|
|
137
158
|
try {
|
|
138
|
-
const embeddedIndex = await (0, import_DeviceConfig.loadDeviceIndexInternal)(
|
|
159
|
+
const embeddedIndex = await (0, import_DeviceConfig.loadDeviceIndexInternal)(fs, logger, this._useExternalConfig && this.externalConfigDir || void 0);
|
|
139
160
|
const priorityIndex = [];
|
|
140
161
|
if (this.deviceConfigPriorityDir) {
|
|
141
|
-
if (await (0, import_shared.pathExists)(this.deviceConfigPriorityDir)) {
|
|
142
|
-
priorityIndex.push(...await (0, import_DeviceConfig.generatePriorityDeviceIndex)(this.deviceConfigPriorityDir,
|
|
162
|
+
if (await (0, import_shared.pathExists)(fs, this.deviceConfigPriorityDir)) {
|
|
163
|
+
priorityIndex.push(...await (0, import_DeviceConfig.generatePriorityDeviceIndex)(fs, this.deviceConfigPriorityDir, logger));
|
|
143
164
|
} else {
|
|
144
|
-
|
|
165
|
+
logger.print(`Priority device configuration directory ${this.deviceConfigPriorityDir} not found`, "warn");
|
|
145
166
|
}
|
|
146
167
|
}
|
|
147
168
|
this.index = [...priorityIndex, ...embeddedIndex];
|
|
@@ -150,7 +171,7 @@ class ConfigManager {
|
|
|
150
171
|
if (!this.index)
|
|
151
172
|
this.index = [];
|
|
152
173
|
if (process.env.NODE_ENV !== "test") {
|
|
153
|
-
|
|
174
|
+
logger.print(`Could not load or regenerate device config index: ${e.message}`, "error");
|
|
154
175
|
return;
|
|
155
176
|
}
|
|
156
177
|
throw e;
|
|
@@ -161,7 +182,7 @@ class ConfigManager {
|
|
|
161
182
|
return this.index;
|
|
162
183
|
}
|
|
163
184
|
async loadFulltextDeviceIndex() {
|
|
164
|
-
this.fulltextIndex = await (0, import_DeviceConfig.loadFulltextDeviceIndexInternal)(this.
|
|
185
|
+
this.fulltextIndex = await (0, import_DeviceConfig.loadFulltextDeviceIndexInternal)(await this.getFS(), await this.getLogger());
|
|
165
186
|
}
|
|
166
187
|
getFulltextIndex() {
|
|
167
188
|
return this.fulltextIndex;
|
|
@@ -177,21 +198,22 @@ class ConfigManager {
|
|
|
177
198
|
async lookupDevicePreserveConditions(manufacturerId, productType, productId, firmwareVersion) {
|
|
178
199
|
if (!this.index)
|
|
179
200
|
await this.loadDeviceIndex();
|
|
201
|
+
const fs = await this.getFS();
|
|
180
202
|
const indexEntries = this.index.filter((0, import_utils.getDeviceEntryPredicate)(manufacturerId, productType, productId, firmwareVersion));
|
|
181
203
|
const indexEntry = indexEntries.find((e) => !!e.preferred) ?? indexEntries[0];
|
|
182
204
|
if (indexEntry) {
|
|
183
205
|
const devicesDir = (0, import_DeviceConfig.getDevicesPaths)(this._useExternalConfig && this.externalConfigDir || import_utils.configDir).devicesDir;
|
|
184
|
-
const filePath =
|
|
185
|
-
if (!await (0, import_shared.pathExists)(filePath))
|
|
206
|
+
const filePath = import_pathe.default.isAbsolute(indexEntry.filename) ? indexEntry.filename : import_pathe.default.join(devicesDir, indexEntry.filename);
|
|
207
|
+
if (!await (0, import_shared.pathExists)(fs, filePath))
|
|
186
208
|
return;
|
|
187
|
-
const isEmbedded = !
|
|
209
|
+
const isEmbedded = !import_pathe.default.relative(devicesDir, filePath).startsWith("..");
|
|
188
210
|
const rootDir = indexEntry.rootDir ?? devicesDir;
|
|
189
211
|
const fallbackDirs = rootDir === devicesDir ? void 0 : [devicesDir];
|
|
190
212
|
try {
|
|
191
|
-
return await import_DeviceConfig.ConditionalDeviceConfig.from(filePath, isEmbedded, { rootDir, fallbackDirs });
|
|
213
|
+
return await import_DeviceConfig.ConditionalDeviceConfig.from(fs, filePath, isEmbedded, { rootDir, fallbackDirs });
|
|
192
214
|
} catch (e) {
|
|
193
215
|
if (process.env.NODE_ENV !== "test") {
|
|
194
|
-
this.
|
|
216
|
+
(await this.getLogger()).print(`Error loading device config ${filePath}: ${(0, import_shared.getErrorMessage)(e, true)}`, "error");
|
|
195
217
|
}
|
|
196
218
|
}
|
|
197
219
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ConfigManager.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tZWaveError,\n\tZWaveErrorCodes,\n\tZWaveLogContainer,\n\tisZWaveError,\n} from \"@zwave-js/core\";\nimport { getErrorMessage, pathExists } from \"@zwave-js/shared\";\nimport path from \"node:path\";\nimport { ConfigLogger } from \"./Logger.js\";\nimport {\n\ttype ManufacturersMap,\n\tloadManufacturersInternal,\n\tsaveManufacturersInternal,\n} from \"./Manufacturers.js\";\nimport { PACKAGE_VERSION } from \"./_version.js\";\nimport {\n\tConditionalDeviceConfig,\n\ttype DeviceConfig,\n\ttype DeviceConfigIndex,\n\ttype FulltextDeviceConfigIndex,\n\tgeneratePriorityDeviceIndex,\n\tgetDevicesPaths,\n\tloadDeviceIndexInternal,\n\tloadFulltextDeviceIndexInternal,\n} from \"./devices/DeviceConfig.js\";\nimport {\n\ttype SyncExternalConfigDirResult,\n\tconfigDir,\n\tgetDeviceEntryPredicate,\n\tgetExternalConfigDirEnvVariable,\n\tsyncExternalConfigDir,\n} from \"./utils.js\";\n\nexport interface ConfigManagerOptions {\n\tlogContainer?: ZWaveLogContainer;\n\tdeviceConfigPriorityDir?: string;\n\tdeviceConfigExternalDir?: string;\n}\n\nexport class ConfigManager {\n\tpublic constructor(options: ConfigManagerOptions = {}) {\n\t\tthis.logger = new ConfigLogger(\n\t\t\toptions.logContainer ?? new ZWaveLogContainer({ enabled: false }),\n\t\t);\n\t\tthis.deviceConfigPriorityDir = options.deviceConfigPriorityDir;\n\t\tthis.deviceConfigExternalDir = options.deviceConfigExternalDir;\n\n\t\tthis._configVersion = PACKAGE_VERSION;\n\t}\n\n\tprivate _configVersion: string;\n\tpublic get configVersion(): string {\n\t\treturn this._configVersion;\n\t}\n\n\tprivate logger: ConfigLogger;\n\n\tprivate _manufacturers: ManufacturersMap | undefined;\n\tpublic get manufacturers(): ManufacturersMap {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._manufacturers;\n\t}\n\n\tprivate deviceConfigPriorityDir: string | undefined;\n\tprivate deviceConfigExternalDir: string | undefined;\n\tpublic get externalConfigDir(): string | undefined {\n\t\treturn this.deviceConfigExternalDir\n\t\t\t?? getExternalConfigDirEnvVariable();\n\t}\n\n\tprivate index: DeviceConfigIndex | undefined;\n\tprivate fulltextIndex: FulltextDeviceConfigIndex | undefined;\n\n\tprivate _useExternalConfig: boolean = false;\n\tpublic get useExternalConfig(): boolean {\n\t\treturn this._useExternalConfig;\n\t}\n\n\tpublic async loadAll(): Promise<void> {\n\t\t// If the environment option for an external config dir is set\n\t\t// try to sync it and then use it\n\t\tlet syncResult: SyncExternalConfigDirResult | undefined;\n\t\tconst externalConfigDir = this.externalConfigDir;\n\t\tif (externalConfigDir) {\n\t\t\tsyncResult = await syncExternalConfigDir(\n\t\t\t\texternalConfigDir,\n\t\t\t\tthis.logger,\n\t\t\t);\n\t\t}\n\n\t\tif (syncResult?.success) {\n\t\t\tthis._useExternalConfig = true;\n\t\t\tthis.logger.print(\n\t\t\t\t`Using external configuration dir ${externalConfigDir}`,\n\t\t\t);\n\t\t\tthis._configVersion = syncResult.version;\n\t\t} else {\n\t\t\tthis._useExternalConfig = false;\n\t\t\tthis._configVersion = PACKAGE_VERSION;\n\t\t}\n\t\tthis.logger.print(`version ${this._configVersion}`, \"info\");\n\n\t\tawait this.loadManufacturers();\n\t\tawait this.loadDeviceIndex();\n\t}\n\n\tpublic async loadManufacturers(): Promise<void> {\n\t\ttry {\n\t\t\tthis._manufacturers = await loadManufacturersInternal(\n\t\t\t\tthis._useExternalConfig && this.externalConfigDir || undefined,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\t// If the config file is missing or invalid, don't try to find it again\n\t\t\tif (isZWaveError(e) && e.code === ZWaveErrorCodes.Config_Invalid) {\n\t\t\t\tif (process.env.NODE_ENV !== \"test\") {\n\t\t\t\t\tthis.logger.print(\n\t\t\t\t\t\t`Could not load manufacturers config: ${e.message}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!this._manufacturers) this._manufacturers = new Map();\n\t\t\t} else {\n\t\t\t\t// This is an unexpected error\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic async saveManufacturers(): Promise<void> {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\n\t\tawait saveManufacturersInternal(this._manufacturers);\n\t}\n\n\t/**\n\t * Looks up the name of the manufacturer with the given ID in the configuration DB\n\t * @param manufacturerId The manufacturer id to look up\n\t */\n\tpublic lookupManufacturer(manufacturerId: number): string | undefined {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\n\t\treturn this._manufacturers.get(manufacturerId);\n\t}\n\n\t/**\n\t * Add new manufacturers to configuration DB\n\t * @param manufacturerId The manufacturer id to look up\n\t * @param manufacturerName The manufacturer name\n\t */\n\tpublic setManufacturer(\n\t\tmanufacturerId: number,\n\t\tmanufacturerName: string,\n\t): void {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\n\t\tthis._manufacturers.set(manufacturerId, manufacturerName);\n\t}\n\n\tpublic async loadDeviceIndex(): Promise<void> {\n\t\ttry {\n\t\t\t// The index of config files included in this package\n\t\t\tconst embeddedIndex = await loadDeviceIndexInternal(\n\t\t\t\tthis.logger,\n\t\t\t\tthis._useExternalConfig && this.externalConfigDir || undefined,\n\t\t\t);\n\t\t\t// A dynamic index of the user-defined priority device config files\n\t\t\tconst priorityIndex: DeviceConfigIndex = [];\n\t\t\tif (this.deviceConfigPriorityDir) {\n\t\t\t\tif (await pathExists(this.deviceConfigPriorityDir)) {\n\t\t\t\t\tpriorityIndex.push(\n\t\t\t\t\t\t...(await generatePriorityDeviceIndex(\n\t\t\t\t\t\t\tthis.deviceConfigPriorityDir,\n\t\t\t\t\t\t\tthis.logger,\n\t\t\t\t\t\t)),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.print(\n\t\t\t\t\t\t`Priority device configuration directory ${this.deviceConfigPriorityDir} not found`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Put the priority index in front, so the files get resolved first\n\t\t\tthis.index = [...priorityIndex, ...embeddedIndex];\n\t\t} catch (e) {\n\t\t\t// If the index file is missing or invalid, don't try to find it again\n\t\t\tif (\n\t\t\t\t(!isZWaveError(e) && e instanceof Error)\n\t\t\t\t|| (isZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.Config_Invalid)\n\t\t\t) {\n\t\t\t\t// Fall back to no index on production systems\n\t\t\t\tif (!this.index) this.index = [];\n\t\t\t\tif (process.env.NODE_ENV !== \"test\") {\n\t\t\t\t\tthis.logger.print(\n\t\t\t\t\t\t`Could not load or regenerate device config index: ${e.message}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t\t// and don't throw\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// But fail hard in tests\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic getIndex(): DeviceConfigIndex | undefined {\n\t\treturn this.index;\n\t}\n\n\tpublic async loadFulltextDeviceIndex(): Promise<void> {\n\t\tthis.fulltextIndex = await loadFulltextDeviceIndexInternal(this.logger);\n\t}\n\n\tpublic getFulltextIndex(): FulltextDeviceConfigIndex | undefined {\n\t\treturn this.fulltextIndex;\n\t}\n\n\t/**\n\t * Looks up the definition of a given device in the configuration DB, but does not evaluate conditional settings.\n\t * @param manufacturerId The manufacturer id of the device\n\t * @param productType The product type of the device\n\t * @param productId The product id of the device\n\t * @param firmwareVersion If known, configuration for a specific firmware version can be loaded.\n\t * If this is `undefined` or not given, the first matching file with a defined firmware range will be returned.\n\t */\n\tpublic async lookupDevicePreserveConditions(\n\t\tmanufacturerId: number,\n\t\tproductType: number,\n\t\tproductId: number,\n\t\tfirmwareVersion?: string,\n\t): Promise<ConditionalDeviceConfig | undefined> {\n\t\t// Load/regenerate the index if necessary\n\t\tif (!this.index) await this.loadDeviceIndex();\n\n\t\t// Look up the device in the index\n\t\tconst indexEntries = this.index!.filter(\n\t\t\tgetDeviceEntryPredicate(\n\t\t\t\tmanufacturerId,\n\t\t\t\tproductType,\n\t\t\t\tproductId,\n\t\t\t\tfirmwareVersion,\n\t\t\t),\n\t\t);\n\t\t// If there are multiple with overlapping firmware ranges, return the preferred one first\n\t\tconst indexEntry = indexEntries.find((e) => !!e.preferred)\n\t\t\t?? indexEntries[0];\n\n\t\tif (indexEntry) {\n\t\t\tconst devicesDir = getDevicesPaths(\n\t\t\t\tthis._useExternalConfig && this.externalConfigDir || configDir,\n\t\t\t).devicesDir;\n\t\t\tconst filePath = path.isAbsolute(indexEntry.filename)\n\t\t\t\t? indexEntry.filename\n\t\t\t\t: path.join(devicesDir, indexEntry.filename);\n\t\t\tif (!(await pathExists(filePath))) return;\n\n\t\t\t// A config file is treated as am embedded one when it is located under the devices root dir\n\t\t\t// or the external config dir\n\t\t\tconst isEmbedded = !path\n\t\t\t\t.relative(devicesDir, filePath)\n\t\t\t\t.startsWith(\"..\");\n\n\t\t\t// When a device file is located in a different root directory than the embedded config files,\n\t\t\t// we use the embedded dir a fallback\n\t\t\tconst rootDir = indexEntry.rootDir ?? devicesDir;\n\t\t\tconst fallbackDirs = rootDir === devicesDir\n\t\t\t\t? undefined\n\t\t\t\t: [devicesDir];\n\n\t\t\ttry {\n\t\t\t\treturn await ConditionalDeviceConfig.from(\n\t\t\t\t\tfilePath,\n\t\t\t\t\tisEmbedded,\n\t\t\t\t\t{ rootDir, fallbackDirs },\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tif (process.env.NODE_ENV !== \"test\") {\n\t\t\t\t\tthis.logger.print(\n\t\t\t\t\t\t`Error loading device config ${filePath}: ${\n\t\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Looks up the definition of a given device in the configuration DB\n\t * @param manufacturerId The manufacturer id of the device\n\t * @param productType The product type of the device\n\t * @param productId The product id of the device\n\t * @param firmwareVersion If known, configuration for a specific firmware version can be loaded.\n\t * If this is `undefined` or not given, the first matching file with a defined firmware range will be returned.\n\t */\n\tpublic async lookupDevice(\n\t\tmanufacturerId: number,\n\t\tproductType: number,\n\t\tproductId: number,\n\t\tfirmwareVersion?: string,\n\t): Promise<DeviceConfig | undefined> {\n\t\tconst ret = await this.lookupDevicePreserveConditions(\n\t\t\tmanufacturerId,\n\t\t\tproductType,\n\t\t\tproductId,\n\t\t\tfirmwareVersion,\n\t\t);\n\t\treturn ret?.evaluate({\n\t\t\tmanufacturerId,\n\t\t\tproductType,\n\t\t\tproductId,\n\t\t\tfirmwareVersion,\n\t\t});\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,kBAKO;AACP,oBAA4C;
|
|
4
|
+
"sourcesContent": ["import {\n\ttype LogContainer,\n\tZWaveError,\n\tZWaveErrorCodes,\n\tisZWaveError,\n} from \"@zwave-js/core\";\nimport { getErrorMessage, pathExists } from \"@zwave-js/shared\";\nimport { type FileSystem } from \"@zwave-js/shared/bindings\";\nimport path from \"pathe\";\nimport { ConfigLogger } from \"./Logger.js\";\nimport {\n\ttype ManufacturersMap,\n\tloadManufacturersInternal,\n\tsaveManufacturersInternal,\n} from \"./Manufacturers.js\";\nimport { PACKAGE_VERSION } from \"./_version.js\";\nimport {\n\tConditionalDeviceConfig,\n\ttype DeviceConfig,\n\ttype DeviceConfigIndex,\n\ttype FulltextDeviceConfigIndex,\n\tgeneratePriorityDeviceIndex,\n\tgetDevicesPaths,\n\tloadDeviceIndexInternal,\n\tloadFulltextDeviceIndexInternal,\n} from \"./devices/DeviceConfig.js\";\nimport {\n\ttype SyncExternalConfigDirResult,\n\tconfigDir,\n\tgetDeviceEntryPredicate,\n\tgetExternalConfigDirEnvVariable,\n\tsyncExternalConfigDir,\n} from \"./utils.js\";\n\nexport interface ConfigManagerOptions {\n\tbindings?: FileSystem;\n\tlogContainer?: LogContainer;\n\tdeviceConfigPriorityDir?: string;\n\tdeviceConfigExternalDir?: string;\n}\n\nexport class ConfigManager {\n\tpublic constructor(options: ConfigManagerOptions = {}) {\n\t\tthis._fs = options.bindings;\n\t\tthis._logContainer = options.logContainer;\n\n\t\tthis.deviceConfigPriorityDir = options.deviceConfigPriorityDir;\n\t\tthis.deviceConfigExternalDir = options.deviceConfigExternalDir;\n\n\t\tthis._configVersion = PACKAGE_VERSION;\n\t}\n\n\tprivate _fs: FileSystem | undefined;\n\tprivate async getFS(): Promise<FileSystem> {\n\t\tthis._fs ??= (await import(\"@zwave-js/core/bindings/fs/node\")).fs;\n\t\treturn this._fs;\n\t}\n\n\tprivate _configVersion: string;\n\tpublic get configVersion(): string {\n\t\treturn this._configVersion;\n\t}\n\n\tprivate _logContainer: LogContainer | undefined;\n\tprivate _logger: ConfigLogger | undefined;\n\tprivate async getLogger(): Promise<ConfigLogger> {\n\t\tif (!this._logContainer) {\n\t\t\tthis._logContainer =\n\t\t\t\t(await import(\"@zwave-js/core/bindings/log/node\")).log({\n\t\t\t\t\tenabled: false,\n\t\t\t\t});\n\t\t}\n\t\tif (!this._logger) {\n\t\t\tthis._logger = new ConfigLogger(this._logContainer);\n\t\t}\n\t\treturn this._logger;\n\t}\n\n\tprivate _manufacturers: ManufacturersMap | undefined;\n\tpublic get manufacturers(): ManufacturersMap {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\t\treturn this._manufacturers;\n\t}\n\n\tprivate deviceConfigPriorityDir: string | undefined;\n\tprivate deviceConfigExternalDir: string | undefined;\n\tpublic get externalConfigDir(): string | undefined {\n\t\treturn this.deviceConfigExternalDir\n\t\t\t?? getExternalConfigDirEnvVariable();\n\t}\n\n\tprivate index: DeviceConfigIndex | undefined;\n\tprivate fulltextIndex: FulltextDeviceConfigIndex | undefined;\n\n\tprivate _useExternalConfig: boolean = false;\n\tpublic get useExternalConfig(): boolean {\n\t\treturn this._useExternalConfig;\n\t}\n\n\tpublic async loadAll(): Promise<void> {\n\t\tconst logger = await this.getLogger();\n\t\t// If the environment option for an external config dir is set\n\t\t// try to sync it and then use it\n\t\tlet syncResult: SyncExternalConfigDirResult | undefined;\n\t\tconst externalConfigDir = this.externalConfigDir;\n\t\tif (externalConfigDir) {\n\t\t\tsyncResult = await syncExternalConfigDir(\n\t\t\t\tawait this.getFS(),\n\t\t\t\texternalConfigDir,\n\t\t\t\tlogger,\n\t\t\t);\n\t\t}\n\n\t\tif (syncResult?.success) {\n\t\t\tthis._useExternalConfig = true;\n\t\t\tlogger.print(\n\t\t\t\t`Using external configuration dir ${externalConfigDir}`,\n\t\t\t);\n\t\t\tthis._configVersion = syncResult.version;\n\t\t} else {\n\t\t\tthis._useExternalConfig = false;\n\t\t\tthis._configVersion = PACKAGE_VERSION;\n\t\t}\n\t\tlogger.print(`version ${this._configVersion}`, \"info\");\n\n\t\tawait this.loadManufacturers();\n\t\tawait this.loadDeviceIndex();\n\t}\n\n\tpublic async loadManufacturers(): Promise<void> {\n\t\ttry {\n\t\t\tthis._manufacturers = await loadManufacturersInternal(\n\t\t\t\tawait this.getFS(),\n\t\t\t\tthis._useExternalConfig && this.externalConfigDir || undefined,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\t// If the config file is missing or invalid, don't try to find it again\n\t\t\tif (isZWaveError(e) && e.code === ZWaveErrorCodes.Config_Invalid) {\n\t\t\t\tif (process.env.NODE_ENV !== \"test\") {\n\t\t\t\t\t(await this.getLogger()).print(\n\t\t\t\t\t\t`Could not load manufacturers config: ${e.message}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!this._manufacturers) this._manufacturers = new Map();\n\t\t\t} else {\n\t\t\t\t// This is an unexpected error\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic async saveManufacturers(): Promise<void> {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\n\t\tawait saveManufacturersInternal(\n\t\t\tawait this.getFS(),\n\t\t\tthis._manufacturers,\n\t\t);\n\t}\n\n\t/**\n\t * Looks up the name of the manufacturer with the given ID in the configuration DB\n\t * @param manufacturerId The manufacturer id to look up\n\t */\n\tpublic lookupManufacturer(manufacturerId: number): string | undefined {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\n\t\treturn this._manufacturers.get(manufacturerId);\n\t}\n\n\t/**\n\t * Add new manufacturers to configuration DB\n\t * @param manufacturerId The manufacturer id to look up\n\t * @param manufacturerName The manufacturer name\n\t */\n\tpublic setManufacturer(\n\t\tmanufacturerId: number,\n\t\tmanufacturerName: string,\n\t): void {\n\t\tif (!this._manufacturers) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t\"The config has not been loaded yet!\",\n\t\t\t\tZWaveErrorCodes.Driver_NotReady,\n\t\t\t);\n\t\t}\n\n\t\tthis._manufacturers.set(manufacturerId, manufacturerName);\n\t}\n\n\tpublic async loadDeviceIndex(): Promise<void> {\n\t\tconst fs = await this.getFS();\n\t\tconst logger = await this.getLogger();\n\t\ttry {\n\t\t\t// The index of config files included in this package\n\t\t\tconst embeddedIndex = await loadDeviceIndexInternal(\n\t\t\t\tfs,\n\t\t\t\tlogger,\n\t\t\t\tthis._useExternalConfig && this.externalConfigDir || undefined,\n\t\t\t);\n\t\t\t// A dynamic index of the user-defined priority device config files\n\t\t\tconst priorityIndex: DeviceConfigIndex = [];\n\t\t\tif (this.deviceConfigPriorityDir) {\n\t\t\t\tif (await pathExists(fs, this.deviceConfigPriorityDir)) {\n\t\t\t\t\tpriorityIndex.push(\n\t\t\t\t\t\t...(await generatePriorityDeviceIndex(\n\t\t\t\t\t\t\tfs,\n\t\t\t\t\t\t\tthis.deviceConfigPriorityDir,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t)),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tlogger.print(\n\t\t\t\t\t\t`Priority device configuration directory ${this.deviceConfigPriorityDir} not found`,\n\t\t\t\t\t\t\"warn\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Put the priority index in front, so the files get resolved first\n\t\t\tthis.index = [...priorityIndex, ...embeddedIndex];\n\t\t} catch (e) {\n\t\t\t// If the index file is missing or invalid, don't try to find it again\n\t\t\tif (\n\t\t\t\t(!isZWaveError(e) && e instanceof Error)\n\t\t\t\t|| (isZWaveError(e)\n\t\t\t\t\t&& e.code === ZWaveErrorCodes.Config_Invalid)\n\t\t\t) {\n\t\t\t\t// Fall back to no index on production systems\n\t\t\t\tif (!this.index) this.index = [];\n\t\t\t\tif (process.env.NODE_ENV !== \"test\") {\n\t\t\t\t\tlogger.print(\n\t\t\t\t\t\t`Could not load or regenerate device config index: ${e.message}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t\t// and don't throw\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// But fail hard in tests\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic getIndex(): DeviceConfigIndex | undefined {\n\t\treturn this.index;\n\t}\n\n\tpublic async loadFulltextDeviceIndex(): Promise<void> {\n\t\tthis.fulltextIndex = await loadFulltextDeviceIndexInternal(\n\t\t\tawait this.getFS(),\n\t\t\tawait this.getLogger(),\n\t\t);\n\t}\n\n\tpublic getFulltextIndex(): FulltextDeviceConfigIndex | undefined {\n\t\treturn this.fulltextIndex;\n\t}\n\n\t/**\n\t * Looks up the definition of a given device in the configuration DB, but does not evaluate conditional settings.\n\t * @param manufacturerId The manufacturer id of the device\n\t * @param productType The product type of the device\n\t * @param productId The product id of the device\n\t * @param firmwareVersion If known, configuration for a specific firmware version can be loaded.\n\t * If this is `undefined` or not given, the first matching file with a defined firmware range will be returned.\n\t */\n\tpublic async lookupDevicePreserveConditions(\n\t\tmanufacturerId: number,\n\t\tproductType: number,\n\t\tproductId: number,\n\t\tfirmwareVersion?: string,\n\t): Promise<ConditionalDeviceConfig | undefined> {\n\t\t// Load/regenerate the index if necessary\n\t\tif (!this.index) await this.loadDeviceIndex();\n\n\t\tconst fs = await this.getFS();\n\n\t\t// Look up the device in the index\n\t\tconst indexEntries = this.index!.filter(\n\t\t\tgetDeviceEntryPredicate(\n\t\t\t\tmanufacturerId,\n\t\t\t\tproductType,\n\t\t\t\tproductId,\n\t\t\t\tfirmwareVersion,\n\t\t\t),\n\t\t);\n\t\t// If there are multiple with overlapping firmware ranges, return the preferred one first\n\t\tconst indexEntry = indexEntries.find((e) => !!e.preferred)\n\t\t\t?? indexEntries[0];\n\n\t\tif (indexEntry) {\n\t\t\tconst devicesDir = getDevicesPaths(\n\t\t\t\tthis._useExternalConfig && this.externalConfigDir || configDir,\n\t\t\t).devicesDir;\n\t\t\tconst filePath = path.isAbsolute(indexEntry.filename)\n\t\t\t\t? indexEntry.filename\n\t\t\t\t: path.join(devicesDir, indexEntry.filename);\n\t\t\tif (!(await pathExists(fs, filePath))) return;\n\n\t\t\t// A config file is treated as am embedded one when it is located under the devices root dir\n\t\t\t// or the external config dir\n\t\t\tconst isEmbedded = !path\n\t\t\t\t.relative(devicesDir, filePath)\n\t\t\t\t.startsWith(\"..\");\n\n\t\t\t// When a device file is located in a different root directory than the embedded config files,\n\t\t\t// we use the embedded dir a fallback\n\t\t\tconst rootDir = indexEntry.rootDir ?? devicesDir;\n\t\t\tconst fallbackDirs = rootDir === devicesDir\n\t\t\t\t? undefined\n\t\t\t\t: [devicesDir];\n\n\t\t\ttry {\n\t\t\t\treturn await ConditionalDeviceConfig.from(\n\t\t\t\t\tfs,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tisEmbedded,\n\t\t\t\t\t{ rootDir, fallbackDirs },\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tif (process.env.NODE_ENV !== \"test\") {\n\t\t\t\t\t(await this.getLogger()).print(\n\t\t\t\t\t\t`Error loading device config ${filePath}: ${\n\t\t\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\t\t\te,\n\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Looks up the definition of a given device in the configuration DB\n\t * @param manufacturerId The manufacturer id of the device\n\t * @param productType The product type of the device\n\t * @param productId The product id of the device\n\t * @param firmwareVersion If known, configuration for a specific firmware version can be loaded.\n\t * If this is `undefined` or not given, the first matching file with a defined firmware range will be returned.\n\t */\n\tpublic async lookupDevice(\n\t\tmanufacturerId: number,\n\t\tproductType: number,\n\t\tproductId: number,\n\t\tfirmwareVersion?: string,\n\t): Promise<DeviceConfig | undefined> {\n\t\tconst ret = await this.lookupDevicePreserveConditions(\n\t\t\tmanufacturerId,\n\t\t\tproductType,\n\t\t\tproductId,\n\t\t\tfirmwareVersion,\n\t\t);\n\t\treturn ret?.evaluate({\n\t\t\tmanufacturerId,\n\t\t\tproductType,\n\t\t\tproductId,\n\t\t\tfirmwareVersion,\n\t\t});\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["path"]
|
|
7
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { type ReadFile, type ReadFileSystemInfo } from "@zwave-js/shared/bindings";
|
|
1
2
|
export declare function clearTemplateCache(): void;
|
|
2
3
|
/** Parses a JSON file with $import keys and replaces them with the selected objects */
|
|
3
|
-
export declare function readJsonWithTemplate(filename: string, rootDirs?: string | string[]): Promise<Record<string, unknown>>;
|
|
4
|
+
export declare function readJsonWithTemplate(fs: ReadFileSystemInfo & ReadFile, filename: string, rootDirs?: string | string[]): Promise<Record<string, unknown>>;
|
|
4
5
|
//# sourceMappingURL=JsonTemplate.d.ts.map
|
|
@@ -38,8 +38,7 @@ var import_shared = require("@zwave-js/shared");
|
|
|
38
38
|
var import_safe2 = require("@zwave-js/shared/safe");
|
|
39
39
|
var import_typeguards = require("alcalzone-shared/typeguards");
|
|
40
40
|
var import_json5 = __toESM(require("json5"), 1);
|
|
41
|
-
var
|
|
42
|
-
var path = __toESM(require("node:path"), 1);
|
|
41
|
+
var import_pathe = __toESM(require("pathe"), 1);
|
|
43
42
|
const IMPORT_KEY = "$import";
|
|
44
43
|
const importSpecifierRegex = /^(?<filename>(?:~\/)?[\w\d\/\\\._-]+\.json)?(?:#(?<selector>[\w\d\/\._-]+(?:\[0x[0-9a-fA-F]+\])?))?$/i;
|
|
45
44
|
const templateCache = /* @__PURE__ */ new Map();
|
|
@@ -47,14 +46,14 @@ function clearTemplateCache() {
|
|
|
47
46
|
templateCache.clear();
|
|
48
47
|
}
|
|
49
48
|
__name(clearTemplateCache, "clearTemplateCache");
|
|
50
|
-
async function readJsonWithTemplate(filename, rootDirs) {
|
|
51
|
-
if (!await (0, import_shared.pathExists)(filename)) {
|
|
49
|
+
async function readJsonWithTemplate(fs, filename, rootDirs) {
|
|
50
|
+
if (!await (0, import_shared.pathExists)(fs, filename)) {
|
|
52
51
|
throw new import_safe.ZWaveError(`Could not open config file ${filename}: not found!`, import_safe.ZWaveErrorCodes.Config_NotFound);
|
|
53
52
|
}
|
|
54
53
|
if (typeof rootDirs === "string")
|
|
55
54
|
rootDirs = [rootDirs];
|
|
56
55
|
const fileCache = new Map(templateCache);
|
|
57
|
-
const ret = await readJsonWithTemplateInternal(filename, void 0, [], fileCache, rootDirs);
|
|
56
|
+
const ret = await readJsonWithTemplateInternal(fs, filename, void 0, [], fileCache, rootDirs);
|
|
58
57
|
for (const [filename2, cached] of fileCache) {
|
|
59
58
|
if (/[\\/]templates[\\/]/.test(filename2)) {
|
|
60
59
|
templateCache.set(filename2, cached);
|
|
@@ -109,11 +108,11 @@ Import stack: ${source.map((s) => `
|
|
|
109
108
|
return "";
|
|
110
109
|
}
|
|
111
110
|
__name(getImportStack, "getImportStack");
|
|
112
|
-
async function readJsonWithTemplateInternal(filename, selector, visited, fileCache, rootDirs) {
|
|
113
|
-
filename =
|
|
111
|
+
async function readJsonWithTemplateInternal(fs, filename, selector, visited, fileCache, rootDirs) {
|
|
112
|
+
filename = import_pathe.default.normalize(filename);
|
|
114
113
|
if (rootDirs) {
|
|
115
114
|
const outsideAllRootDirs = rootDirs.every((rootDir) => {
|
|
116
|
-
const relativeToRoot =
|
|
115
|
+
const relativeToRoot = import_pathe.default.relative(rootDir, filename);
|
|
117
116
|
return relativeToRoot.startsWith("..");
|
|
118
117
|
});
|
|
119
118
|
if (outsideAllRootDirs) {
|
|
@@ -136,17 +135,17 @@ ${getImportStack(visited, selector)}`, import_safe.ZWaveErrorCodes.Config_Invali
|
|
|
136
135
|
json = fileCache.get(filename);
|
|
137
136
|
} else {
|
|
138
137
|
try {
|
|
139
|
-
const fileContent = await
|
|
138
|
+
const fileContent = await (0, import_shared.readTextFile)(fs, filename, "utf8");
|
|
140
139
|
json = import_json5.default.parse(fileContent);
|
|
141
140
|
fileCache.set(filename, json);
|
|
142
141
|
} catch (e) {
|
|
143
142
|
throw new import_safe.ZWaveError(`Could not parse config file ${filename}: ${(0, import_safe2.getErrorMessage)(e)}${getImportStack(visited, selector)}`, import_safe.ZWaveErrorCodes.Config_Invalid);
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
|
-
return resolveJsonImports(selector ? select(json, selector) : json, filename, [...visited, specifier], fileCache, rootDirs);
|
|
145
|
+
return resolveJsonImports(fs, selector ? select(json, selector) : json, filename, [...visited, specifier], fileCache, rootDirs);
|
|
147
146
|
}
|
|
148
147
|
__name(readJsonWithTemplateInternal, "readJsonWithTemplateInternal");
|
|
149
|
-
async function resolveJsonImports(json, filename, visited, fileCache, rootDirs) {
|
|
148
|
+
async function resolveJsonImports(fs, json, filename, visited, fileCache, rootDirs) {
|
|
150
149
|
const ret = {};
|
|
151
150
|
for (const [prop, val] of Object.entries(json)) {
|
|
152
151
|
if (prop === IMPORT_KEY) {
|
|
@@ -157,8 +156,8 @@ async function resolveJsonImports(json, filename, visited, fileCache, rootDirs)
|
|
|
157
156
|
if (importFilename.startsWith("~/")) {
|
|
158
157
|
if (rootDirs) {
|
|
159
158
|
for (const rootDir of rootDirs) {
|
|
160
|
-
newFilename =
|
|
161
|
-
if (await (0, import_shared.pathExists)(newFilename)) {
|
|
159
|
+
newFilename = import_pathe.default.join(rootDir, importFilename.slice(2));
|
|
160
|
+
if (await (0, import_shared.pathExists)(fs, newFilename)) {
|
|
162
161
|
break;
|
|
163
162
|
} else {
|
|
164
163
|
newFilename = void 0;
|
|
@@ -173,20 +172,20 @@ ${getImportStack(visited, selector)}`, import_safe.ZWaveErrorCodes.Config_Invali
|
|
|
173
172
|
throw new import_safe.ZWaveError(`An $import specifier cannot start with ~/ when no root directory is defined!${getImportStack(visited, selector)}`, import_safe.ZWaveErrorCodes.Config_Invalid);
|
|
174
173
|
}
|
|
175
174
|
} else {
|
|
176
|
-
newFilename =
|
|
175
|
+
newFilename = import_pathe.default.join(import_pathe.default.dirname(filename), importFilename);
|
|
177
176
|
}
|
|
178
177
|
} else {
|
|
179
178
|
newFilename = filename;
|
|
180
179
|
}
|
|
181
|
-
const imported = await readJsonWithTemplateInternal(newFilename, selector, visited, fileCache, rootDirs);
|
|
180
|
+
const imported = await readJsonWithTemplateInternal(fs, newFilename, selector, visited, fileCache, rootDirs);
|
|
182
181
|
Object.assign(ret, imported);
|
|
183
182
|
} else if ((0, import_typeguards.isObject)(val)) {
|
|
184
|
-
ret[prop] = await resolveJsonImports(val, filename, visited, fileCache, rootDirs);
|
|
183
|
+
ret[prop] = await resolveJsonImports(fs, val, filename, visited, fileCache, rootDirs);
|
|
185
184
|
} else if ((0, import_typeguards.isArray)(val)) {
|
|
186
185
|
const vals = [];
|
|
187
186
|
for (const v of val) {
|
|
188
187
|
if ((0, import_typeguards.isObject)(v)) {
|
|
189
|
-
vals.push(await resolveJsonImports(v, filename, visited, fileCache, rootDirs));
|
|
188
|
+
vals.push(await resolveJsonImports(fs, v, filename, visited, fileCache, rootDirs));
|
|
190
189
|
} else {
|
|
191
190
|
vals.push(v);
|
|
192
191
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/JsonTemplate.ts"],
|
|
4
|
-
"sourcesContent": ["import { ZWaveError, ZWaveErrorCodes } from \"@zwave-js/core/safe\";\nimport { pathExists } from \"@zwave-js/shared\";\nimport { getErrorMessage } from \"@zwave-js/shared/safe\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nconst IMPORT_KEY = \"$import\";\nconst importSpecifierRegex =\n\t/^(?<filename>(?:~\\/)?[\\w\\d\\/\\\\\\._-]+\\.json)?(?:#(?<selector>[\\w\\d\\/\\._-]+(?:\\[0x[0-9a-fA-F]+\\])?))?$/i;\n\ntype FileCache = Map<string, Record<string, unknown>>;\n\n// The template cache is used to speed up cases where the same files get parsed multiple times,\n// e.g. during config file linting. It should be cleared whenever the files need to be loaded fresh\n// from disk, like when creating an index\nconst templateCache: FileCache = new Map();\nexport function clearTemplateCache(): void {\n\ttemplateCache.clear();\n}\n\n/** Parses a JSON file with $import keys and replaces them with the selected objects */\nexport async function readJsonWithTemplate(\n\tfilename: string,\n\trootDirs?: string | string[],\n): Promise<Record<string, unknown>> {\n\tif (!(await pathExists(filename))) {\n\t\tthrow new ZWaveError(\n\t\t\t`Could not open config file ${filename}: not found!`,\n\t\t\tZWaveErrorCodes.Config_NotFound,\n\t\t);\n\t}\n\n\tif (typeof rootDirs === \"string\") rootDirs = [rootDirs];\n\n\t// Try to use the cached versions of the template files to speed up the loading\n\tconst fileCache = new Map(templateCache);\n\tconst ret = await readJsonWithTemplateInternal(\n\t\tfilename,\n\t\tundefined,\n\t\t[],\n\t\tfileCache,\n\t\trootDirs,\n\t);\n\n\t// Only remember the cached templates, not the individual files to save RAM\n\tfor (const [filename, cached] of fileCache) {\n\t\tif (/[\\\\/]templates[\\\\/]/.test(filename)) {\n\t\t\ttemplateCache.set(filename, cached);\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nfunction assertImportSpecifier(\n\tval: unknown,\n\tsource?: string,\n): asserts val is string {\n\tif (typeof val !== \"string\") {\n\t\tthrow new ZWaveError(\n\t\t\t`Invalid import specifier ${String(val)}!${\n\t\t\t\tsource != undefined ? ` Source: ${source}` : \"\"\n\t\t\t}`,\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n\tif (!importSpecifierRegex.test(val)) {\n\t\tthrow new ZWaveError(\n\t\t\t`Import specifier \"${val}\" is invalid!${\n\t\t\t\tsource != undefined ? ` Source: ${source}` : \"\"\n\t\t\t}`,\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n}\n\nfunction getImportSpecifier(filename: string, selector?: string): string {\n\tlet ret = filename;\n\tif (selector) ret += `#${selector}`;\n\treturn ret;\n}\n\nfunction select(\n\tobj: Record<string, unknown>,\n\tselector: string,\n): Record<string, unknown> {\n\tlet ret: Record<string, unknown> = obj;\n\tconst selectorParts = selector.split(\"/\").filter((s) => !!s);\n\tfor (const part of selectorParts) {\n\t\t// Special case for paramInformation selectors to select params by #\n\t\tif (isArray(ret)) {\n\t\t\tconst item = (ret as any).find(\n\t\t\t\t(r: any) => isObject(r) && \"#\" in r && r[\"#\"] === part,\n\t\t\t);\n\t\t\tif (item != undefined) {\n\t\t\t\t// Don't copy the param number\n\t\t\t\tconst { [\"#\"]: _, ...rest } = item;\n\t\t\t\tret = rest;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t// By default select the object property\n\t\tret = (ret as any)[part];\n\t}\n\tif (!isObject(ret)) {\n\t\tthrow new ZWaveError(\n\t\t\t`The import target \"${selector}\" is not an object!`,\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n\treturn ret;\n}\n\nfunction getImportStack(\n\tvisited: string[],\n\tselector: string | undefined,\n): string {\n\tconst source = [...visited, selector ? `#${selector}` : undefined]\n\t\t.reverse()\n\t\t.filter((s) => !!s) as string[];\n\tif (source.length > 0) {\n\t\treturn `\\nImport stack: ${source.map((s) => `\\n in ${s}`).join(\"\")}`;\n\t}\n\treturn \"\";\n}\n\nasync function readJsonWithTemplateInternal(\n\tfilename: string,\n\tselector: string | undefined,\n\tvisited: string[],\n\tfileCache: FileCache,\n\trootDirs?: string[],\n): Promise<Record<string, unknown>> {\n\tfilename = path.normalize(filename);\n\n\t// If we're limited by one or more root directories, make sure the file is inside one of those\n\tif (rootDirs) {\n\t\tconst outsideAllRootDirs = rootDirs.every((rootDir) => {\n\t\t\tconst relativeToRoot = path.relative(rootDir, filename);\n\t\t\treturn relativeToRoot.startsWith(\"..\");\n\t\t});\n\n\t\tif (outsideAllRootDirs) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Tried to import config file \"${filename}\" from outside all root directories: ${\n\t\t\t\t\trootDirs\n\t\t\t\t\t\t.map((d) => `\\n\u00B7 ${d}`)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}\n${getImportStack(visited, selector)}`,\n\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t);\n\t\t}\n\t}\n\n\tconst specifier = getImportSpecifier(filename, selector);\n\tif (visited.includes(specifier)) {\n\t\tconst msg = `Circular $import in config files: ${\n\t\t\t[\n\t\t\t\t...visited,\n\t\t\t\tspecifier,\n\t\t\t].join(\" -> \")\n\t\t}\\n`;\n\t\t// process.stderr.write(msg + \"\\n\");\n\t\tthrow new ZWaveError(msg, ZWaveErrorCodes.Config_CircularImport);\n\t}\n\n\tlet json: Record<string, unknown>;\n\tif (fileCache.has(filename)) {\n\t\tjson = fileCache.get(filename)!;\n\t} else {\n\t\ttry {\n\t\t\tconst fileContent = await fs.readFile(filename, \"utf8\");\n\t\t\tjson = JSON5.parse(fileContent);\n\t\t\tfileCache.set(filename, json);\n\t\t} catch (e) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Could not parse config file ${filename}: ${\n\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\te,\n\t\t\t\t\t)\n\t\t\t\t}${getImportStack(visited, selector)}`,\n\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t);\n\t\t}\n\t}\n\t// Resolve the JSON imports for (a subset) of the file and return the compound file\n\treturn resolveJsonImports(\n\t\tselector ? select(json, selector) : json,\n\t\tfilename,\n\t\t[...visited, specifier],\n\t\tfileCache,\n\t\trootDirs,\n\t);\n}\n\n/** Replaces all `$import` properties in a JSON object with object spreads of the referenced file/property */\nasync function resolveJsonImports(\n\tjson: Record<string, unknown>,\n\tfilename: string,\n\tvisited: string[],\n\tfileCache: FileCache,\n\trootDirs?: string[],\n): Promise<Record<string, unknown>> {\n\tconst ret: Record<string, unknown> = {};\n\t// Loop through all properties and copy them to the resulting object\n\tfor (const [prop, val] of Object.entries(json)) {\n\t\tif (prop === IMPORT_KEY) {\n\t\t\t// This is an import statement. Make sure we're working with a string\n\t\t\tassertImportSpecifier(val, visited.join(\" -> \"));\n\t\t\tconst { filename: importFilename, selector } = importSpecifierRegex\n\t\t\t\t.exec(val)!.groups!;\n\n\t\t\t// Resolve the correct import path\n\t\t\tlet newFilename: string | undefined;\n\t\t\tif (importFilename) {\n\t\t\t\tif (importFilename.startsWith(\"~/\")) {\n\t\t\t\t\t// This is a special import specifier that is relative to the root directory\n\t\t\t\t\t// Try to find at least one root directory that contains the referenced file\n\t\t\t\t\tif (rootDirs) {\n\t\t\t\t\t\tfor (const rootDir of rootDirs) {\n\t\t\t\t\t\t\tnewFilename = path.join(\n\t\t\t\t\t\t\t\trootDir,\n\t\t\t\t\t\t\t\timportFilename.slice(2),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (await pathExists(newFilename)) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Try the next\n\t\t\t\t\t\t\t\tnewFilename = undefined!;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!newFilename) {\n\t\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\t`Could not find the referenced file ${\n\t\t\t\t\t\t\t\t\timportFilename.slice(\n\t\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t} in any of the root directories: ${\n\t\t\t\t\t\t\t\t\trootDirs\n\t\t\t\t\t\t\t\t\t\t.map((d) => `\\n\u00B7 ${d}`)\n\t\t\t\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t\t\t\t}\\n${\n\t\t\t\t\t\t\t\t\tgetImportStack(\n\t\t\t\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t`An $import specifier cannot start with ~/ when no root directory is defined!${\n\t\t\t\t\t\t\t\tgetImportStack(\n\t\t\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tnewFilename = path.join(\n\t\t\t\t\t\tpath.dirname(filename),\n\t\t\t\t\t\timportFilename,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewFilename = filename;\n\t\t\t}\n\n\t\t\t// const importFilename = path.join(path.dirname(filename), val);\n\t\t\tconst imported = await readJsonWithTemplateInternal(\n\t\t\t\tnewFilename,\n\t\t\t\tselector,\n\t\t\t\tvisited,\n\t\t\t\tfileCache,\n\t\t\t\trootDirs,\n\t\t\t);\n\t\t\tObject.assign(ret, imported);\n\t\t} else if (isObject(val)) {\n\t\t\t// We're looking at an object, recurse into it\n\t\t\tret[prop] = await resolveJsonImports(\n\t\t\t\tval,\n\t\t\t\tfilename,\n\t\t\t\tvisited,\n\t\t\t\tfileCache,\n\t\t\t\trootDirs,\n\t\t\t);\n\t\t} else if (isArray(val)) {\n\t\t\t// We're looking at an array, check if there are objects we need to recurse into\n\t\t\tconst vals: unknown[] = [];\n\t\t\tfor (const v of val) {\n\t\t\t\tif (isObject(v)) {\n\t\t\t\t\tvals.push(\n\t\t\t\t\t\tawait resolveJsonImports(\n\t\t\t\t\t\t\tv,\n\t\t\t\t\t\t\tfilename,\n\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t\tfileCache,\n\t\t\t\t\t\t\trootDirs,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tvals.push(v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tret[prop] = vals;\n\t\t} else {\n\t\t\tret[prop] = val;\n\t\t}\n\t}\n\treturn ret;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;AAAA,kBAA4C;AAC5C,
|
|
6
|
-
"names": ["import_safe", "filename", "
|
|
4
|
+
"sourcesContent": ["import { ZWaveError, ZWaveErrorCodes } from \"@zwave-js/core/safe\";\nimport { pathExists, readTextFile } from \"@zwave-js/shared\";\nimport {\n\ttype ReadFile,\n\ttype ReadFileSystemInfo,\n} from \"@zwave-js/shared/bindings\";\nimport { getErrorMessage } from \"@zwave-js/shared/safe\";\nimport { isArray, isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport path from \"pathe\";\n\nconst IMPORT_KEY = \"$import\";\nconst importSpecifierRegex =\n\t/^(?<filename>(?:~\\/)?[\\w\\d\\/\\\\\\._-]+\\.json)?(?:#(?<selector>[\\w\\d\\/\\._-]+(?:\\[0x[0-9a-fA-F]+\\])?))?$/i;\n\ntype FileCache = Map<string, Record<string, unknown>>;\n\n// The template cache is used to speed up cases where the same files get parsed multiple times,\n// e.g. during config file linting. It should be cleared whenever the files need to be loaded fresh\n// from disk, like when creating an index\nconst templateCache: FileCache = new Map();\nexport function clearTemplateCache(): void {\n\ttemplateCache.clear();\n}\n\n/** Parses a JSON file with $import keys and replaces them with the selected objects */\nexport async function readJsonWithTemplate(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tfilename: string,\n\trootDirs?: string | string[],\n): Promise<Record<string, unknown>> {\n\tif (!(await pathExists(fs, filename))) {\n\t\tthrow new ZWaveError(\n\t\t\t`Could not open config file ${filename}: not found!`,\n\t\t\tZWaveErrorCodes.Config_NotFound,\n\t\t);\n\t}\n\n\tif (typeof rootDirs === \"string\") rootDirs = [rootDirs];\n\n\t// Try to use the cached versions of the template files to speed up the loading\n\tconst fileCache = new Map(templateCache);\n\tconst ret = await readJsonWithTemplateInternal(\n\t\tfs,\n\t\tfilename,\n\t\tundefined,\n\t\t[],\n\t\tfileCache,\n\t\trootDirs,\n\t);\n\n\t// Only remember the cached templates, not the individual files to save RAM\n\tfor (const [filename, cached] of fileCache) {\n\t\tif (/[\\\\/]templates[\\\\/]/.test(filename)) {\n\t\t\ttemplateCache.set(filename, cached);\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nfunction assertImportSpecifier(\n\tval: unknown,\n\tsource?: string,\n): asserts val is string {\n\tif (typeof val !== \"string\") {\n\t\tthrow new ZWaveError(\n\t\t\t`Invalid import specifier ${String(val)}!${\n\t\t\t\tsource != undefined ? ` Source: ${source}` : \"\"\n\t\t\t}`,\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n\tif (!importSpecifierRegex.test(val)) {\n\t\tthrow new ZWaveError(\n\t\t\t`Import specifier \"${val}\" is invalid!${\n\t\t\t\tsource != undefined ? ` Source: ${source}` : \"\"\n\t\t\t}`,\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n}\n\nfunction getImportSpecifier(filename: string, selector?: string): string {\n\tlet ret = filename;\n\tif (selector) ret += `#${selector}`;\n\treturn ret;\n}\n\nfunction select(\n\tobj: Record<string, unknown>,\n\tselector: string,\n): Record<string, unknown> {\n\tlet ret: Record<string, unknown> = obj;\n\tconst selectorParts = selector.split(\"/\").filter((s) => !!s);\n\tfor (const part of selectorParts) {\n\t\t// Special case for paramInformation selectors to select params by #\n\t\tif (isArray(ret)) {\n\t\t\tconst item = (ret as any).find(\n\t\t\t\t(r: any) => isObject(r) && \"#\" in r && r[\"#\"] === part,\n\t\t\t);\n\t\t\tif (item != undefined) {\n\t\t\t\t// Don't copy the param number\n\t\t\t\tconst { [\"#\"]: _, ...rest } = item;\n\t\t\t\tret = rest;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t// By default select the object property\n\t\tret = (ret as any)[part];\n\t}\n\tif (!isObject(ret)) {\n\t\tthrow new ZWaveError(\n\t\t\t`The import target \"${selector}\" is not an object!`,\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n\treturn ret;\n}\n\nfunction getImportStack(\n\tvisited: string[],\n\tselector: string | undefined,\n): string {\n\tconst source = [...visited, selector ? `#${selector}` : undefined]\n\t\t.reverse()\n\t\t.filter((s) => !!s) as string[];\n\tif (source.length > 0) {\n\t\treturn `\\nImport stack: ${source.map((s) => `\\n in ${s}`).join(\"\")}`;\n\t}\n\treturn \"\";\n}\n\nasync function readJsonWithTemplateInternal(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tfilename: string,\n\tselector: string | undefined,\n\tvisited: string[],\n\tfileCache: FileCache,\n\trootDirs?: string[],\n): Promise<Record<string, unknown>> {\n\tfilename = path.normalize(filename);\n\n\t// If we're limited by one or more root directories, make sure the file is inside one of those\n\tif (rootDirs) {\n\t\tconst outsideAllRootDirs = rootDirs.every((rootDir) => {\n\t\t\tconst relativeToRoot = path.relative(rootDir, filename);\n\t\t\treturn relativeToRoot.startsWith(\"..\");\n\t\t});\n\n\t\tif (outsideAllRootDirs) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Tried to import config file \"${filename}\" from outside all root directories: ${\n\t\t\t\t\trootDirs\n\t\t\t\t\t\t.map((d) => `\\n\u00B7 ${d}`)\n\t\t\t\t\t\t.join(\"\")\n\t\t\t\t}\n${getImportStack(visited, selector)}`,\n\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t);\n\t\t}\n\t}\n\n\tconst specifier = getImportSpecifier(filename, selector);\n\tif (visited.includes(specifier)) {\n\t\tconst msg = `Circular $import in config files: ${\n\t\t\t[\n\t\t\t\t...visited,\n\t\t\t\tspecifier,\n\t\t\t].join(\" -> \")\n\t\t}\\n`;\n\t\t// process.stderr.write(msg + \"\\n\");\n\t\tthrow new ZWaveError(msg, ZWaveErrorCodes.Config_CircularImport);\n\t}\n\n\tlet json: Record<string, unknown>;\n\tif (fileCache.has(filename)) {\n\t\tjson = fileCache.get(filename)!;\n\t} else {\n\t\ttry {\n\t\t\tconst fileContent = await readTextFile(fs, filename, \"utf8\");\n\t\t\tjson = JSON5.parse(fileContent);\n\t\t\tfileCache.set(filename, json);\n\t\t} catch (e) {\n\t\t\tthrow new ZWaveError(\n\t\t\t\t`Could not parse config file ${filename}: ${\n\t\t\t\t\tgetErrorMessage(\n\t\t\t\t\t\te,\n\t\t\t\t\t)\n\t\t\t\t}${getImportStack(visited, selector)}`,\n\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t);\n\t\t}\n\t}\n\t// Resolve the JSON imports for (a subset) of the file and return the compound file\n\treturn resolveJsonImports(\n\t\tfs,\n\t\tselector ? select(json, selector) : json,\n\t\tfilename,\n\t\t[...visited, specifier],\n\t\tfileCache,\n\t\trootDirs,\n\t);\n}\n\n/** Replaces all `$import` properties in a JSON object with object spreads of the referenced file/property */\nasync function resolveJsonImports(\n\tfs: ReadFileSystemInfo & ReadFile,\n\tjson: Record<string, unknown>,\n\tfilename: string,\n\tvisited: string[],\n\tfileCache: FileCache,\n\trootDirs?: string[],\n): Promise<Record<string, unknown>> {\n\tconst ret: Record<string, unknown> = {};\n\t// Loop through all properties and copy them to the resulting object\n\tfor (const [prop, val] of Object.entries(json)) {\n\t\tif (prop === IMPORT_KEY) {\n\t\t\t// This is an import statement. Make sure we're working with a string\n\t\t\tassertImportSpecifier(val, visited.join(\" -> \"));\n\t\t\tconst { filename: importFilename, selector } = importSpecifierRegex\n\t\t\t\t.exec(val)!.groups!;\n\n\t\t\t// Resolve the correct import path\n\t\t\tlet newFilename: string | undefined;\n\t\t\tif (importFilename) {\n\t\t\t\tif (importFilename.startsWith(\"~/\")) {\n\t\t\t\t\t// This is a special import specifier that is relative to the root directory\n\t\t\t\t\t// Try to find at least one root directory that contains the referenced file\n\t\t\t\t\tif (rootDirs) {\n\t\t\t\t\t\tfor (const rootDir of rootDirs) {\n\t\t\t\t\t\t\tnewFilename = path.join(\n\t\t\t\t\t\t\t\trootDir,\n\t\t\t\t\t\t\t\timportFilename.slice(2),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (await pathExists(fs, newFilename)) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Try the next\n\t\t\t\t\t\t\t\tnewFilename = undefined!;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!newFilename) {\n\t\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t\t`Could not find the referenced file ${\n\t\t\t\t\t\t\t\t\timportFilename.slice(\n\t\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t} in any of the root directories: ${\n\t\t\t\t\t\t\t\t\trootDirs\n\t\t\t\t\t\t\t\t\t\t.map((d) => `\\n\u00B7 ${d}`)\n\t\t\t\t\t\t\t\t\t\t.join(\"\")\n\t\t\t\t\t\t\t\t}\\n${\n\t\t\t\t\t\t\t\t\tgetImportStack(\n\t\t\t\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ZWaveError(\n\t\t\t\t\t\t\t`An $import specifier cannot start with ~/ when no root directory is defined!${\n\t\t\t\t\t\t\t\tgetImportStack(\n\t\t\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tnewFilename = path.join(\n\t\t\t\t\t\tpath.dirname(filename),\n\t\t\t\t\t\timportFilename,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewFilename = filename;\n\t\t\t}\n\n\t\t\t// const importFilename = path.join(path.dirname(filename), val);\n\t\t\tconst imported = await readJsonWithTemplateInternal(\n\t\t\t\tfs,\n\t\t\t\tnewFilename,\n\t\t\t\tselector,\n\t\t\t\tvisited,\n\t\t\t\tfileCache,\n\t\t\t\trootDirs,\n\t\t\t);\n\t\t\tObject.assign(ret, imported);\n\t\t} else if (isObject(val)) {\n\t\t\t// We're looking at an object, recurse into it\n\t\t\tret[prop] = await resolveJsonImports(\n\t\t\t\tfs,\n\t\t\t\tval,\n\t\t\t\tfilename,\n\t\t\t\tvisited,\n\t\t\t\tfileCache,\n\t\t\t\trootDirs,\n\t\t\t);\n\t\t} else if (isArray(val)) {\n\t\t\t// We're looking at an array, check if there are objects we need to recurse into\n\t\t\tconst vals: unknown[] = [];\n\t\t\tfor (const v of val) {\n\t\t\t\tif (isObject(v)) {\n\t\t\t\t\tvals.push(\n\t\t\t\t\t\tawait resolveJsonImports(\n\t\t\t\t\t\t\tfs,\n\t\t\t\t\t\t\tv,\n\t\t\t\t\t\t\tfilename,\n\t\t\t\t\t\t\tvisited,\n\t\t\t\t\t\t\tfileCache,\n\t\t\t\t\t\t\trootDirs,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tvals.push(v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tret[prop] = vals;\n\t\t} else {\n\t\t\tret[prop] = val;\n\t\t}\n\t}\n\treturn ret;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_safe", "filename", "path", "JSON5"]
|
|
7
7
|
}
|
package/build/cjs/Logger.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type LogContainer, ZWaveLoggerBase } from "@zwave-js/core";
|
|
2
2
|
import { type ConfigLogContext } from "./Logger_safe.js";
|
|
3
3
|
export declare class ConfigLogger extends ZWaveLoggerBase<ConfigLogContext> {
|
|
4
|
-
constructor(loggers:
|
|
4
|
+
constructor(loggers: LogContainer);
|
|
5
5
|
/**
|
|
6
6
|
* Logs a message
|
|
7
7
|
* @param msg The message to output
|
package/build/cjs/Logger.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/Logger.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\ttype
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,kBAIO;AACP,yBAIO;AAED,MAAO,qBAAqB,4BAAiC;EAXnE,OAWmE;;;EAClE,YAAY,
|
|
4
|
+
"sourcesContent": ["import {\n\ttype LogContainer,\n\tZWaveLoggerBase,\n\tgetDirectionPrefix,\n} from \"@zwave-js/core\";\nimport {\n\tCONFIG_LABEL,\n\tCONFIG_LOGLEVEL,\n\ttype ConfigLogContext,\n} from \"./Logger_safe.js\";\n\nexport class ConfigLogger extends ZWaveLoggerBase<ConfigLogContext> {\n\tconstructor(loggers: LogContainer) {\n\t\tsuper(loggers, CONFIG_LABEL);\n\t}\n\n\t/**\n\t * Logs a message\n\t * @param msg The message to output\n\t */\n\tpublic print(\n\t\tmessage: string,\n\t\tlevel?: \"debug\" | \"verbose\" | \"warn\" | \"error\" | \"info\",\n\t): void {\n\t\tconst actualLevel = level || CONFIG_LOGLEVEL;\n\t\tif (!this.container.isLoglevelVisible(actualLevel)) return;\n\n\t\tthis.logger.log({\n\t\t\tlevel: actualLevel,\n\t\t\tmessage,\n\t\t\tdirection: getDirectionPrefix(\"none\"),\n\t\t\tcontext: { source: \"config\" },\n\t\t});\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,kBAIO;AACP,yBAIO;AAED,MAAO,qBAAqB,4BAAiC;EAXnE,OAWmE;;;EAClE,YAAY,SAAqB;AAChC,UAAM,SAAS,+BAAY;EAC5B;;;;;EAMO,MACN,SACA,OAAuD;AAEvD,UAAM,cAAc,SAAS;AAC7B,QAAI,CAAC,KAAK,UAAU,kBAAkB,WAAW;AAAG;AAEpD,SAAK,OAAO,IAAI;MACf,OAAO;MACP;MACA,eAAW,gCAAmB,MAAM;MACpC,SAAS,EAAE,QAAQ,SAAQ;KAC3B;EACF;;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type WriteFile } from "@zwave-js/shared/bindings";
|
|
1
2
|
export type ManufacturersMap = Map<number, string>;
|
|
2
3
|
/**
|
|
3
4
|
* Write current manufacturers map to json
|
|
4
5
|
*/
|
|
5
|
-
export declare function saveManufacturersInternal(manufacturers: ManufacturersMap): Promise<void>;
|
|
6
|
+
export declare function saveManufacturersInternal(fs: WriteFile, manufacturers: ManufacturersMap): Promise<void>;
|
|
6
7
|
//# sourceMappingURL=Manufacturers.d.ts.map
|
|
@@ -37,17 +37,16 @@ var import_core = require("@zwave-js/core");
|
|
|
37
37
|
var import_shared = require("@zwave-js/shared");
|
|
38
38
|
var import_typeguards = require("alcalzone-shared/typeguards");
|
|
39
39
|
var import_json5 = __toESM(require("json5"), 1);
|
|
40
|
-
var
|
|
41
|
-
var import_node_path = __toESM(require("node:path"), 1);
|
|
40
|
+
var import_pathe = __toESM(require("pathe"), 1);
|
|
42
41
|
var import_utils = require("./utils.js");
|
|
43
42
|
var import_utils_safe = require("./utils_safe.js");
|
|
44
|
-
async function loadManufacturersInternal(externalConfigDir) {
|
|
45
|
-
const configPath =
|
|
46
|
-
if (!await (0, import_shared.pathExists)(configPath)) {
|
|
43
|
+
async function loadManufacturersInternal(fs, externalConfigDir) {
|
|
44
|
+
const configPath = import_pathe.default.join(externalConfigDir || import_utils.configDir, "manufacturers.json");
|
|
45
|
+
if (!await (0, import_shared.pathExists)(fs, configPath)) {
|
|
47
46
|
throw new import_core.ZWaveError("The manufacturer config file does not exist!", import_core.ZWaveErrorCodes.Config_Invalid);
|
|
48
47
|
}
|
|
49
48
|
try {
|
|
50
|
-
const fileContents = await
|
|
49
|
+
const fileContents = await (0, import_shared.readTextFile)(fs, configPath, "utf8");
|
|
51
50
|
const definition = import_json5.default.parse(fileContents);
|
|
52
51
|
if (!(0, import_typeguards.isObject)(definition)) {
|
|
53
52
|
(0, import_utils_safe.throwInvalidConfig)("manufacturers", `the database is not an object!`);
|
|
@@ -73,14 +72,14 @@ async function loadManufacturersInternal(externalConfigDir) {
|
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
__name(loadManufacturersInternal, "loadManufacturersInternal");
|
|
76
|
-
async function saveManufacturersInternal(manufacturers) {
|
|
75
|
+
async function saveManufacturersInternal(fs, manufacturers) {
|
|
77
76
|
const data = {};
|
|
78
77
|
const orderedMap = new Map([...manufacturers].sort((a, b) => a[0] > b[0] ? 1 : -1));
|
|
79
78
|
for (const [id, name] of orderedMap) {
|
|
80
79
|
data[(0, import_shared.formatId)(id)] = name;
|
|
81
80
|
}
|
|
82
|
-
const configPath =
|
|
83
|
-
await
|
|
81
|
+
const configPath = import_pathe.default.join(import_utils.configDir, "manufacturers.json");
|
|
82
|
+
await (0, import_shared.writeTextFile)(fs, configPath, (0, import_shared.stringify)(data, " ") + "\n");
|
|
84
83
|
}
|
|
85
84
|
__name(saveManufacturersInternal, "saveManufacturersInternal");
|
|
86
85
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/Manufacturers.ts"],
|
|
4
|
-
"sourcesContent": ["import { ZWaveError, ZWaveErrorCodes, isZWaveError } from \"@zwave-js/core\";\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;AAAA,kBAA0D;AAC1D,
|
|
6
|
-
"names": ["path", "
|
|
4
|
+
"sourcesContent": ["import { ZWaveError, ZWaveErrorCodes, isZWaveError } from \"@zwave-js/core\";\nimport {\n\tformatId,\n\tpathExists,\n\treadTextFile,\n\tstringify,\n\twriteTextFile,\n} from \"@zwave-js/shared\";\nimport {\n\ttype ReadFile,\n\ttype ReadFileSystemInfo,\n\ttype WriteFile,\n} from \"@zwave-js/shared/bindings\";\nimport { isObject } from \"alcalzone-shared/typeguards\";\nimport JSON5 from \"json5\";\nimport path from \"pathe\";\nimport { configDir } from \"./utils.js\";\nimport { hexKeyRegex4Digits, throwInvalidConfig } from \"./utils_safe.js\";\n\nexport type ManufacturersMap = Map<number, string>;\n\n/** @internal */\nexport async function loadManufacturersInternal(\n\tfs: ReadFileSystemInfo & ReadFile,\n\texternalConfigDir?: string,\n): Promise<ManufacturersMap> {\n\tconst configPath = path.join(\n\t\texternalConfigDir || configDir,\n\t\t\"manufacturers.json\",\n\t);\n\n\tif (!(await pathExists(fs, configPath))) {\n\t\tthrow new ZWaveError(\n\t\t\t\"The manufacturer config file does not exist!\",\n\t\t\tZWaveErrorCodes.Config_Invalid,\n\t\t);\n\t}\n\ttry {\n\t\tconst fileContents = await readTextFile(fs, configPath, \"utf8\");\n\t\tconst definition = JSON5.parse(fileContents);\n\t\tif (!isObject(definition)) {\n\t\t\tthrowInvalidConfig(\n\t\t\t\t\"manufacturers\",\n\t\t\t\t`the database is not an object!`,\n\t\t\t);\n\t\t}\n\n\t\tconst manufacturers = new Map();\n\t\tfor (const [id, name] of Object.entries(definition)) {\n\t\t\tif (!hexKeyRegex4Digits.test(id)) {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"manufacturers\",\n\t\t\t\t\t`found invalid key ${id} at the root level. Manufacturer IDs must be hexadecimal lowercase.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (typeof name !== \"string\") {\n\t\t\t\tthrowInvalidConfig(\n\t\t\t\t\t\"manufacturers\",\n\t\t\t\t\t`Key ${id} has a non-string manufacturer name`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst idNum = parseInt(id.slice(2), 16);\n\t\t\tmanufacturers.set(idNum, name);\n\t\t}\n\n\t\treturn manufacturers;\n\t} catch (e) {\n\t\tif (isZWaveError(e) || ((e as any).code === \"ENOENT\")) {\n\t\t\tthrow e;\n\t\t} else {\n\t\t\tthrowInvalidConfig(\"manufacturers\");\n\t\t}\n\t}\n}\n\n/**\n * Write current manufacturers map to json\n */\nexport async function saveManufacturersInternal(\n\tfs: WriteFile,\n\tmanufacturers: ManufacturersMap,\n): Promise<void> {\n\tconst data: Record<string, string> = {};\n\n\tconst orderedMap = new Map(\n\t\t[...manufacturers].sort((a, b) => (a[0] > b[0] ? 1 : -1)),\n\t);\n\n\tfor (const [id, name] of orderedMap) {\n\t\tdata[formatId(id)] = name;\n\t}\n\n\tconst configPath = path.join(configDir, \"manufacturers.json\");\n\tawait writeTextFile(fs, configPath, stringify(data, \"\\t\") + \"\\n\");\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;AAAA,kBAA0D;AAC1D,oBAMO;AAMP,wBAAyB;AACzB,mBAAkB;AAClB,mBAAiB;AACjB,mBAA0B;AAC1B,wBAAuD;AAKvD,eAAsB,0BACrB,IACA,mBAA0B;AAE1B,QAAM,aAAa,aAAAA,QAAK,KACvB,qBAAqB,wBACrB,oBAAoB;AAGrB,MAAI,CAAE,UAAM,0BAAW,IAAI,UAAU,GAAI;AACxC,UAAM,IAAI,uBACT,gDACA,4BAAgB,cAAc;EAEhC;AACA,MAAI;AACH,UAAM,eAAe,UAAM,4BAAa,IAAI,YAAY,MAAM;AAC9D,UAAM,aAAa,aAAAC,QAAM,MAAM,YAAY;AAC3C,QAAI,KAAC,4BAAS,UAAU,GAAG;AAC1B,gDACC,iBACA,gCAAgC;IAElC;AAEA,UAAM,gBAAgB,oBAAI,IAAG;AAC7B,eAAW,CAAC,IAAI,IAAI,KAAK,OAAO,QAAQ,UAAU,GAAG;AACpD,UAAI,CAAC,qCAAmB,KAAK,EAAE,GAAG;AACjC,kDACC,iBACA,qBAAqB,EAAE,qEAAqE;MAE9F;AACA,UAAI,OAAO,SAAS,UAAU;AAC7B,kDACC,iBACA,OAAO,EAAE,qCAAqC;MAEhD;AACA,YAAM,QAAQ,SAAS,GAAG,MAAM,CAAC,GAAG,EAAE;AACtC,oBAAc,IAAI,OAAO,IAAI;IAC9B;AAEA,WAAO;EACR,SAAS,GAAG;AACX,YAAI,0BAAa,CAAC,KAAO,EAAU,SAAS,UAAW;AACtD,YAAM;IACP,OAAO;AACN,gDAAmB,eAAe;IACnC;EACD;AACD;AAnDsB;AAwDtB,eAAsB,0BACrB,IACA,eAA+B;AAE/B,QAAM,OAA+B,CAAA;AAErC,QAAM,aAAa,IAAI,IACtB,CAAC,GAAG,aAAa,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,EAAG,CAAC;AAG1D,aAAW,CAAC,IAAI,IAAI,KAAK,YAAY;AACpC,aAAK,wBAAS,EAAE,CAAC,IAAI;EACtB;AAEA,QAAM,aAAa,aAAAD,QAAK,KAAK,wBAAW,oBAAoB;AAC5D,YAAM,6BAAc,IAAI,gBAAY,yBAAU,MAAM,GAAI,IAAI,IAAI;AACjE;AAhBsB;",
|
|
6
|
+
"names": ["path", "JSON5"]
|
|
7
7
|
}
|
package/build/cjs/_version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "
|
|
1
|
+
export declare const PACKAGE_VERSION = "15.0.0-beta.1";
|
|
2
2
|
//# sourceMappingURL=_version.d.ts.map
|
package/build/cjs/_version.js
CHANGED
|
@@ -21,7 +21,7 @@ __export(version_exports, {
|
|
|
21
21
|
PACKAGE_VERSION: () => PACKAGE_VERSION
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(version_exports);
|
|
24
|
-
const PACKAGE_VERSION = "
|
|
24
|
+
const PACKAGE_VERSION = "15.0.0-beta.1";
|
|
25
25
|
// Annotate the CommonJS export names for ESM import in node:
|
|
26
26
|
0 && (module.exports = {
|
|
27
27
|
PACKAGE_VERSION
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/_version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is auto-generated by the codegen maintenance script\nexport const PACKAGE_VERSION = \"
|
|
4
|
+
"sourcesContent": ["// This file is auto-generated by the codegen maintenance script\nexport const PACKAGE_VERSION = \"15.0.0-beta.1\";\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;;;;;AACO,MAAM,kBAAkB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type JSONObject } from "@zwave-js/shared";
|
|
2
|
+
import { type ReadFile, type ReadFileSystemInfo } from "@zwave-js/shared/bindings";
|
|
2
3
|
import { type AssociationConfig, ConditionalAssociationConfig } from "./AssociationConfig.js";
|
|
3
4
|
import { type CompatConfig, ConditionalCompatConfig } from "./CompatConfig.js";
|
|
4
5
|
import { type ConditionalPrimitive } from "./ConditionalPrimitive.js";
|
|
@@ -36,7 +37,7 @@ export type DeviceConfigIndex = DeviceConfigIndexEntry[];
|
|
|
36
37
|
export type FulltextDeviceConfigIndex = FulltextDeviceConfigIndexEntry[];
|
|
37
38
|
/** This class represents a device config entry whose conditional settings have not been evaluated yet */
|
|
38
39
|
export declare class ConditionalDeviceConfig {
|
|
39
|
-
static from(filename: string, isEmbedded: boolean, options: {
|
|
40
|
+
static from(fs: ReadFileSystemInfo & ReadFile, filename: string, isEmbedded: boolean, options: {
|
|
40
41
|
rootDir: string;
|
|
41
42
|
fallbackDirs?: string[];
|
|
42
43
|
relative?: boolean;
|
|
@@ -71,7 +72,7 @@ export declare class ConditionalDeviceConfig {
|
|
|
71
72
|
evaluate(deviceId?: DeviceID): DeviceConfig;
|
|
72
73
|
}
|
|
73
74
|
export declare class DeviceConfig {
|
|
74
|
-
static from(filename: string, isEmbedded: boolean, options: {
|
|
75
|
+
static from(fs: ReadFileSystemInfo & ReadFile, filename: string, isEmbedded: boolean, options: {
|
|
75
76
|
rootDir: string;
|
|
76
77
|
fallbackDirs?: string[];
|
|
77
78
|
relative?: boolean;
|