matterbridge 3.4.0-dev-20251125-6f11a4f → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/CHANGELOG.md +3 -2
  2. package/README.md +1 -1
  3. package/dist/broadcastServer.d.ts +115 -0
  4. package/dist/broadcastServer.d.ts.map +1 -0
  5. package/dist/broadcastServer.js +93 -1
  6. package/dist/broadcastServer.js.map +1 -0
  7. package/dist/broadcastServerTypes.d.ts +838 -0
  8. package/dist/broadcastServerTypes.d.ts.map +1 -0
  9. package/dist/broadcastServerTypes.js +24 -0
  10. package/dist/broadcastServerTypes.js.map +1 -0
  11. package/dist/cli.d.ts +30 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +97 -1
  14. package/dist/cli.js.map +1 -0
  15. package/dist/cliEmitter.d.ts +50 -0
  16. package/dist/cliEmitter.d.ts.map +1 -0
  17. package/dist/cliEmitter.js +37 -0
  18. package/dist/cliEmitter.js.map +1 -0
  19. package/dist/cliHistory.d.ts +48 -0
  20. package/dist/cliHistory.d.ts.map +1 -0
  21. package/dist/cliHistory.js +38 -0
  22. package/dist/cliHistory.js.map +1 -0
  23. package/dist/clusters/export.d.ts +2 -0
  24. package/dist/clusters/export.d.ts.map +1 -0
  25. package/dist/clusters/export.js +2 -0
  26. package/dist/clusters/export.js.map +1 -0
  27. package/dist/defaultConfigSchema.d.ts +28 -0
  28. package/dist/defaultConfigSchema.d.ts.map +1 -0
  29. package/dist/defaultConfigSchema.js +24 -0
  30. package/dist/defaultConfigSchema.js.map +1 -0
  31. package/dist/deviceManager.d.ts +135 -0
  32. package/dist/deviceManager.d.ts.map +1 -0
  33. package/dist/deviceManager.js +113 -1
  34. package/dist/deviceManager.js.map +1 -0
  35. package/dist/devices/airConditioner.d.ts +98 -0
  36. package/dist/devices/airConditioner.d.ts.map +1 -0
  37. package/dist/devices/airConditioner.js +57 -0
  38. package/dist/devices/airConditioner.js.map +1 -0
  39. package/dist/devices/batteryStorage.d.ts +48 -0
  40. package/dist/devices/batteryStorage.d.ts.map +1 -0
  41. package/dist/devices/batteryStorage.js +48 -1
  42. package/dist/devices/batteryStorage.js.map +1 -0
  43. package/dist/devices/cooktop.d.ts +61 -0
  44. package/dist/devices/cooktop.d.ts.map +1 -0
  45. package/dist/devices/cooktop.js +56 -0
  46. package/dist/devices/cooktop.js.map +1 -0
  47. package/dist/devices/dishwasher.d.ts +71 -0
  48. package/dist/devices/dishwasher.d.ts.map +1 -0
  49. package/dist/devices/dishwasher.js +57 -0
  50. package/dist/devices/dishwasher.js.map +1 -0
  51. package/dist/devices/evse.d.ts +76 -0
  52. package/dist/devices/evse.d.ts.map +1 -0
  53. package/dist/devices/evse.js +74 -10
  54. package/dist/devices/evse.js.map +1 -0
  55. package/dist/devices/export.d.ts +17 -0
  56. package/dist/devices/export.d.ts.map +1 -0
  57. package/dist/devices/export.js +5 -0
  58. package/dist/devices/export.js.map +1 -0
  59. package/dist/devices/extractorHood.d.ts +46 -0
  60. package/dist/devices/extractorHood.d.ts.map +1 -0
  61. package/dist/devices/extractorHood.js +43 -0
  62. package/dist/devices/extractorHood.js.map +1 -0
  63. package/dist/devices/heatPump.d.ts +47 -0
  64. package/dist/devices/heatPump.d.ts.map +1 -0
  65. package/dist/devices/heatPump.js +50 -2
  66. package/dist/devices/heatPump.js.map +1 -0
  67. package/dist/devices/laundryDryer.d.ts +67 -0
  68. package/dist/devices/laundryDryer.d.ts.map +1 -0
  69. package/dist/devices/laundryDryer.js +62 -3
  70. package/dist/devices/laundryDryer.js.map +1 -0
  71. package/dist/devices/laundryWasher.d.ts +81 -0
  72. package/dist/devices/laundryWasher.d.ts.map +1 -0
  73. package/dist/devices/laundryWasher.js +70 -4
  74. package/dist/devices/laundryWasher.js.map +1 -0
  75. package/dist/devices/microwaveOven.d.ts +168 -0
  76. package/dist/devices/microwaveOven.d.ts.map +1 -0
  77. package/dist/devices/microwaveOven.js +88 -5
  78. package/dist/devices/microwaveOven.js.map +1 -0
  79. package/dist/devices/oven.d.ts +105 -0
  80. package/dist/devices/oven.d.ts.map +1 -0
  81. package/dist/devices/oven.js +85 -0
  82. package/dist/devices/oven.js.map +1 -0
  83. package/dist/devices/refrigerator.d.ts +118 -0
  84. package/dist/devices/refrigerator.d.ts.map +1 -0
  85. package/dist/devices/refrigerator.js +102 -0
  86. package/dist/devices/refrigerator.js.map +1 -0
  87. package/dist/devices/roboticVacuumCleaner.d.ts +112 -0
  88. package/dist/devices/roboticVacuumCleaner.d.ts.map +1 -0
  89. package/dist/devices/roboticVacuumCleaner.js +100 -9
  90. package/dist/devices/roboticVacuumCleaner.js.map +1 -0
  91. package/dist/devices/solarPower.d.ts +40 -0
  92. package/dist/devices/solarPower.d.ts.map +1 -0
  93. package/dist/devices/solarPower.js +38 -0
  94. package/dist/devices/solarPower.js.map +1 -0
  95. package/dist/devices/speaker.d.ts +87 -0
  96. package/dist/devices/speaker.d.ts.map +1 -0
  97. package/dist/devices/speaker.js +84 -0
  98. package/dist/devices/speaker.js.map +1 -0
  99. package/dist/devices/temperatureControl.d.ts +166 -0
  100. package/dist/devices/temperatureControl.d.ts.map +1 -0
  101. package/dist/devices/temperatureControl.js +24 -3
  102. package/dist/devices/temperatureControl.js.map +1 -0
  103. package/dist/devices/waterHeater.d.ts +111 -0
  104. package/dist/devices/waterHeater.d.ts.map +1 -0
  105. package/dist/devices/waterHeater.js +82 -2
  106. package/dist/devices/waterHeater.js.map +1 -0
  107. package/dist/dgram/coap.d.ts +205 -0
  108. package/dist/dgram/coap.d.ts.map +1 -0
  109. package/dist/dgram/coap.js +126 -13
  110. package/dist/dgram/coap.js.map +1 -0
  111. package/dist/dgram/dgram.d.ts +141 -0
  112. package/dist/dgram/dgram.d.ts.map +1 -0
  113. package/dist/dgram/dgram.js +114 -2
  114. package/dist/dgram/dgram.js.map +1 -0
  115. package/dist/dgram/mb_coap.d.ts +24 -0
  116. package/dist/dgram/mb_coap.d.ts.map +1 -0
  117. package/dist/dgram/mb_coap.js +41 -3
  118. package/dist/dgram/mb_coap.js.map +1 -0
  119. package/dist/dgram/mb_mdns.d.ts +24 -0
  120. package/dist/dgram/mb_mdns.d.ts.map +1 -0
  121. package/dist/dgram/mb_mdns.js +80 -15
  122. package/dist/dgram/mb_mdns.js.map +1 -0
  123. package/dist/dgram/mdns.d.ts +290 -0
  124. package/dist/dgram/mdns.d.ts.map +1 -0
  125. package/dist/dgram/mdns.js +299 -137
  126. package/dist/dgram/mdns.js.map +1 -0
  127. package/dist/dgram/multicast.d.ts +67 -0
  128. package/dist/dgram/multicast.d.ts.map +1 -0
  129. package/dist/dgram/multicast.js +62 -1
  130. package/dist/dgram/multicast.js.map +1 -0
  131. package/dist/dgram/unicast.d.ts +56 -0
  132. package/dist/dgram/unicast.d.ts.map +1 -0
  133. package/dist/dgram/unicast.js +54 -0
  134. package/dist/dgram/unicast.js.map +1 -0
  135. package/dist/frontend.d.ts +238 -0
  136. package/dist/frontend.d.ts.map +1 -0
  137. package/dist/frontend.js +455 -35
  138. package/dist/frontend.js.map +1 -0
  139. package/dist/frontendTypes.d.ts +529 -0
  140. package/dist/frontendTypes.d.ts.map +1 -0
  141. package/dist/frontendTypes.js +45 -0
  142. package/dist/frontendTypes.js.map +1 -0
  143. package/dist/helpers.d.ts +48 -0
  144. package/dist/helpers.d.ts.map +1 -0
  145. package/dist/helpers.js +53 -0
  146. package/dist/helpers.js.map +1 -0
  147. package/dist/index.d.ts +34 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +25 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/jestutils/export.d.ts +2 -0
  152. package/dist/jestutils/export.d.ts.map +1 -0
  153. package/dist/jestutils/export.js +1 -0
  154. package/dist/jestutils/export.js.map +1 -0
  155. package/dist/jestutils/jestHelpers.d.ts +303 -0
  156. package/dist/jestutils/jestHelpers.d.ts.map +1 -0
  157. package/dist/jestutils/jestHelpers.js +354 -13
  158. package/dist/jestutils/jestHelpers.js.map +1 -0
  159. package/dist/logger/export.d.ts +2 -0
  160. package/dist/logger/export.d.ts.map +1 -0
  161. package/dist/logger/export.js +1 -0
  162. package/dist/logger/export.js.map +1 -0
  163. package/dist/matter/behaviors.d.ts +2 -0
  164. package/dist/matter/behaviors.d.ts.map +1 -0
  165. package/dist/matter/behaviors.js +2 -0
  166. package/dist/matter/behaviors.js.map +1 -0
  167. package/dist/matter/clusters.d.ts +2 -0
  168. package/dist/matter/clusters.d.ts.map +1 -0
  169. package/dist/matter/clusters.js +2 -0
  170. package/dist/matter/clusters.js.map +1 -0
  171. package/dist/matter/devices.d.ts +2 -0
  172. package/dist/matter/devices.d.ts.map +1 -0
  173. package/dist/matter/devices.js +2 -0
  174. package/dist/matter/devices.js.map +1 -0
  175. package/dist/matter/endpoints.d.ts +2 -0
  176. package/dist/matter/endpoints.d.ts.map +1 -0
  177. package/dist/matter/endpoints.js +2 -0
  178. package/dist/matter/endpoints.js.map +1 -0
  179. package/dist/matter/export.d.ts +5 -0
  180. package/dist/matter/export.d.ts.map +1 -0
  181. package/dist/matter/export.js +3 -0
  182. package/dist/matter/export.js.map +1 -0
  183. package/dist/matter/types.d.ts +3 -0
  184. package/dist/matter/types.d.ts.map +1 -0
  185. package/dist/matter/types.js +3 -0
  186. package/dist/matter/types.js.map +1 -0
  187. package/dist/matterNode.d.ts +342 -0
  188. package/dist/matterNode.d.ts.map +1 -0
  189. package/dist/matterNode.js +369 -8
  190. package/dist/matterNode.js.map +1 -0
  191. package/dist/matterbridge.d.ts +473 -0
  192. package/dist/matterbridge.d.ts.map +1 -0
  193. package/dist/matterbridge.js +787 -46
  194. package/dist/matterbridge.js.map +1 -0
  195. package/dist/matterbridgeAccessoryPlatform.d.ts +41 -0
  196. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
  197. package/dist/matterbridgeAccessoryPlatform.js +38 -0
  198. package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
  199. package/dist/matterbridgeBehaviors.d.ts +2404 -0
  200. package/dist/matterbridgeBehaviors.d.ts.map +1 -0
  201. package/dist/matterbridgeBehaviors.js +68 -5
  202. package/dist/matterbridgeBehaviors.js.map +1 -0
  203. package/dist/matterbridgeDeviceTypes.d.ts +698 -0
  204. package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
  205. package/dist/matterbridgeDeviceTypes.js +635 -14
  206. package/dist/matterbridgeDeviceTypes.js.map +1 -0
  207. package/dist/matterbridgeDynamicPlatform.d.ts +41 -0
  208. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
  209. package/dist/matterbridgeDynamicPlatform.js +38 -0
  210. package/dist/matterbridgeDynamicPlatform.js.map +1 -0
  211. package/dist/matterbridgeEndpoint.d.ts +1507 -0
  212. package/dist/matterbridgeEndpoint.d.ts.map +1 -0
  213. package/dist/matterbridgeEndpoint.js +1444 -53
  214. package/dist/matterbridgeEndpoint.js.map +1 -0
  215. package/dist/matterbridgeEndpointHelpers.d.ts +787 -0
  216. package/dist/matterbridgeEndpointHelpers.d.ts.map +1 -0
  217. package/dist/matterbridgeEndpointHelpers.js +483 -20
  218. package/dist/matterbridgeEndpointHelpers.js.map +1 -0
  219. package/dist/matterbridgeEndpointTypes.d.ts +166 -0
  220. package/dist/matterbridgeEndpointTypes.d.ts.map +1 -0
  221. package/dist/matterbridgeEndpointTypes.js +25 -0
  222. package/dist/matterbridgeEndpointTypes.js.map +1 -0
  223. package/dist/matterbridgePlatform.d.ts +524 -0
  224. package/dist/matterbridgePlatform.d.ts.map +1 -0
  225. package/dist/matterbridgePlatform.js +439 -1
  226. package/dist/matterbridgePlatform.js.map +1 -0
  227. package/dist/matterbridgeTypes.d.ts +251 -0
  228. package/dist/matterbridgeTypes.d.ts.map +1 -0
  229. package/dist/matterbridgeTypes.js +26 -0
  230. package/dist/matterbridgeTypes.js.map +1 -0
  231. package/dist/pluginManager.d.ts +371 -0
  232. package/dist/pluginManager.d.ts.map +1 -0
  233. package/dist/pluginManager.js +340 -5
  234. package/dist/pluginManager.js.map +1 -0
  235. package/dist/shelly.d.ts +174 -0
  236. package/dist/shelly.d.ts.map +1 -0
  237. package/dist/shelly.js +168 -7
  238. package/dist/shelly.js.map +1 -0
  239. package/dist/storage/export.d.ts +2 -0
  240. package/dist/storage/export.d.ts.map +1 -0
  241. package/dist/storage/export.js +1 -0
  242. package/dist/storage/export.js.map +1 -0
  243. package/dist/update.d.ts +75 -0
  244. package/dist/update.d.ts.map +1 -0
  245. package/dist/update.js +69 -0
  246. package/dist/update.js.map +1 -0
  247. package/dist/utils/colorUtils.d.ts +101 -0
  248. package/dist/utils/colorUtils.d.ts.map +1 -0
  249. package/dist/utils/colorUtils.js +97 -2
  250. package/dist/utils/colorUtils.js.map +1 -0
  251. package/dist/utils/commandLine.d.ts +66 -0
  252. package/dist/utils/commandLine.d.ts.map +1 -0
  253. package/dist/utils/commandLine.js +60 -0
  254. package/dist/utils/commandLine.js.map +1 -0
  255. package/dist/utils/copyDirectory.d.ts +35 -0
  256. package/dist/utils/copyDirectory.d.ts.map +1 -0
  257. package/dist/utils/copyDirectory.js +37 -0
  258. package/dist/utils/copyDirectory.js.map +1 -0
  259. package/dist/utils/createDirectory.d.ts +34 -0
  260. package/dist/utils/createDirectory.d.ts.map +1 -0
  261. package/dist/utils/createDirectory.js +33 -0
  262. package/dist/utils/createDirectory.js.map +1 -0
  263. package/dist/utils/createZip.d.ts +39 -0
  264. package/dist/utils/createZip.d.ts.map +1 -0
  265. package/dist/utils/createZip.js +47 -2
  266. package/dist/utils/createZip.js.map +1 -0
  267. package/dist/utils/deepCopy.d.ts +32 -0
  268. package/dist/utils/deepCopy.d.ts.map +1 -0
  269. package/dist/utils/deepCopy.js +39 -0
  270. package/dist/utils/deepCopy.js.map +1 -0
  271. package/dist/utils/deepEqual.d.ts +54 -0
  272. package/dist/utils/deepEqual.d.ts.map +1 -0
  273. package/dist/utils/deepEqual.js +72 -1
  274. package/dist/utils/deepEqual.js.map +1 -0
  275. package/dist/utils/error.d.ts +44 -0
  276. package/dist/utils/error.d.ts.map +1 -0
  277. package/dist/utils/error.js +41 -0
  278. package/dist/utils/error.js.map +1 -0
  279. package/dist/utils/export.d.ts +13 -0
  280. package/dist/utils/export.d.ts.map +1 -0
  281. package/dist/utils/export.js +1 -0
  282. package/dist/utils/export.js.map +1 -0
  283. package/dist/utils/format.d.ts +53 -0
  284. package/dist/utils/format.d.ts.map +1 -0
  285. package/dist/utils/format.js +49 -0
  286. package/dist/utils/format.js.map +1 -0
  287. package/dist/utils/hex.d.ts +89 -0
  288. package/dist/utils/hex.d.ts.map +1 -0
  289. package/dist/utils/hex.js +124 -0
  290. package/dist/utils/hex.js.map +1 -0
  291. package/dist/utils/inspector.d.ts +87 -0
  292. package/dist/utils/inspector.d.ts.map +1 -0
  293. package/dist/utils/inspector.js +69 -1
  294. package/dist/utils/inspector.js.map +1 -0
  295. package/dist/utils/isvalid.d.ts +103 -0
  296. package/dist/utils/isvalid.d.ts.map +1 -0
  297. package/dist/utils/isvalid.js +101 -0
  298. package/dist/utils/isvalid.js.map +1 -0
  299. package/dist/utils/network.d.ts +111 -0
  300. package/dist/utils/network.d.ts.map +1 -0
  301. package/dist/utils/network.js +96 -5
  302. package/dist/utils/network.js.map +1 -0
  303. package/dist/utils/spawn.d.ts +33 -0
  304. package/dist/utils/spawn.d.ts.map +1 -0
  305. package/dist/utils/spawn.js +71 -1
  306. package/dist/utils/spawn.js.map +1 -0
  307. package/dist/utils/tracker.d.ts +108 -0
  308. package/dist/utils/tracker.d.ts.map +1 -0
  309. package/dist/utils/tracker.js +65 -2
  310. package/dist/utils/tracker.js.map +1 -0
  311. package/dist/utils/wait.d.ts +54 -0
  312. package/dist/utils/wait.d.ts.map +1 -0
  313. package/dist/utils/wait.js +60 -8
  314. package/dist/utils/wait.js.map +1 -0
  315. package/marked.ps1 +10 -0
  316. package/npm-shrinkwrap.json +2 -2
  317. package/package.json +2 -1
@@ -1,16 +1,47 @@
1
+ /**
2
+ * @description This file contains the Jest helpers.
3
+ * @file src/helpers.test.ts
4
+ * @author Luca Liguori
5
+ * @created 2025-09-03
6
+ * @version 1.0.13
7
+ * @license Apache-2.0
8
+ *
9
+ * Copyright 2025, 2026, 2027 Luca Liguori.
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
1
23
  import { rmSync } from 'node:fs';
2
24
  import { inspect } from 'node:util';
3
25
  import path from 'node:path';
26
+ // Imports from node-ansi-logger
4
27
  import { AnsiLogger, er, rs, UNDERLINE, UNDERLINEOFF } from 'node-ansi-logger';
28
+ // Imports from @matter
5
29
  import { LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Environment, Lifecycle } from '@matter/general';
6
30
  import { Endpoint, ServerNode, ServerNodeStore } from '@matter/node';
7
31
  import { DeviceTypeId, VendorId } from '@matter/types/datatype';
8
32
  import { AggregatorEndpoint } from '@matter/node/endpoints';
9
33
  import { MdnsService } from '@matter/main/protocol';
10
34
  import { NodeStorageManager } from 'node-persist-manager';
35
+ // Imports from Matterbridge
11
36
  import { Matterbridge } from '../matterbridge.js';
12
37
  import { MATTER_STORAGE_NAME, NODE_STORAGE_DIR } from '../matterbridgeTypes.js';
13
38
  import { bridge } from '../matterbridgeDeviceTypes.js';
39
+ /* Imports from a plugin
40
+ import { AnsiLogger, LogLevel } from 'matterbridge/logger';
41
+ import { DeviceTypeId, Endpoint, Environment, MdnsService, ServerNode, ServerNodeStore, VendorId, LogFormat as MatterLogFormat, LogLevel as MatterLogLevel, Lifecycle } from 'matterbridge/matter';
42
+ import { RootEndpoint, AggregatorEndpoint } from 'matterbridge/matter/endpoints';
43
+ import { MATTER_STORAGE_NAME, Matterbridge, MatterbridgePlatform } from 'matterbridge';
44
+ */
14
45
  export const originalProcessArgv = Object.freeze([...process.argv]);
15
46
  export const originalProcessEnv = Object.freeze({ ...process.env });
16
47
  export let loggerLogSpy;
@@ -38,12 +69,30 @@ export let environment;
38
69
  export let server;
39
70
  export let aggregator;
40
71
  export let log;
72
+ /**
73
+ * Setup the Jest environment:
74
+ * - it will remove any existing home directory
75
+ * - setup the spies for logging
76
+ *
77
+ * @param {string} name The name of the test suite.
78
+ * @param {boolean} debug If true, the logging is not mocked.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { consoleDebugSpy, consoleErrorSpy, consoleInfoSpy, consoleLogSpy, consoleWarnSpy, loggerLogSpy, setDebug, setupTest } from './jestutils/jestHelpers.js';
83
+ *
84
+ * // Setup the test environment
85
+ * setupTest(NAME, false);
86
+ *
87
+ * ```
88
+ */
41
89
  export async function setupTest(name, debug = false) {
42
90
  expect(name).toBeDefined();
43
91
  expect(typeof name).toBe('string');
44
92
  expect(name.length).toBeGreaterThanOrEqual(4);
45
93
  NAME = name;
46
94
  HOMEDIR = path.join('jest', name);
95
+ // Cleanup any existing home directory
47
96
  rmSync(HOMEDIR, { recursive: true, force: true });
48
97
  const { jest } = await import('@jest/globals');
49
98
  loggerDebugSpy = jest.spyOn(AnsiLogger.prototype, 'debug');
@@ -72,6 +121,22 @@ export async function setupTest(name, debug = false) {
72
121
  removeBridgedEndpointSpy = jest.spyOn(Matterbridge.prototype, 'removeBridgedEndpoint');
73
122
  removeAllBridgedEndpointsSpy = jest.spyOn(Matterbridge.prototype, 'removeAllBridgedEndpoints');
74
123
  }
124
+ /**
125
+ * Set or unset the debug mode.
126
+ *
127
+ * @param {boolean} debug If true, the logging is not mocked.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * // Set the debug mode in test environment
132
+ * setDebug(true);
133
+ * ```
134
+ *
135
+ * ```typescript
136
+ * // Reset the debug mode in test environment
137
+ * setDebug(false);
138
+ * ```
139
+ */
75
140
  export async function setDebug(debug) {
76
141
  const { jest } = await import('@jest/globals');
77
142
  if (debug) {
@@ -97,18 +162,35 @@ export async function setDebug(debug) {
97
162
  consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
98
163
  }
99
164
  }
165
+ /**
166
+ * Start a Matterbridge instance for testing.
167
+ *
168
+ * @param {('bridge' | 'childbridge' | 'controller' | '')} bridgeMode The bridge mode to start the Matterbridge instance in.
169
+ * @param {number} frontendPort The frontend port number.
170
+ * @param {number} matterPort The matter port number.
171
+ * @param {number} passcode The passcode number.
172
+ * @param {number} discriminator The discriminator number.
173
+ * @param {number} pluginSize The expected number of plugins.
174
+ * @param {number} devicesSize The expected number of devices.
175
+ * @returns {Promise<Matterbridge>} The Matterbridge instance.
176
+ */
100
177
  export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 8283, matterPort = 5540, passcode = 20252026, discriminator = 3840, pluginSize = 0, devicesSize = 0) {
178
+ // Set the environment variables
101
179
  process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS'] = '100';
102
180
  process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS'] = '100';
181
+ // Setup the process arguments
103
182
  process.argv.length = 0;
104
183
  process.argv.push(...originalProcessArgv, '-novirtual', '-debug', '-verbose', '-logger', 'debug', '-matterlogger', 'debug', bridgeMode === '' ? '-test' : '-' + bridgeMode, '-homedir', HOMEDIR, '-frontend', frontendPort.toString(), '-port', matterPort.toString(), '-passcode', passcode.toString(), '-discriminator', discriminator.toString());
184
+ // Load Matterbridge instance and initialize it
105
185
  matterbridge = await Matterbridge.loadInstance(true);
106
186
  expect(matterbridge).toBeDefined();
107
187
  expect(matterbridge.profile).toBeUndefined();
108
188
  expect(matterbridge.bridgeMode).toBe(bridgeMode);
189
+ // Get the frontend, plugins and devices
109
190
  frontend = matterbridge.frontend;
110
191
  plugins = matterbridge.plugins;
111
192
  devices = matterbridge.devices;
193
+ // @ts-expect-error - access to private member for testing
112
194
  expect(matterbridge.initialized).toBeTruthy();
113
195
  expect(matterbridge.log).toBeDefined();
114
196
  expect(matterbridge.rootDirectory).toBe(path.resolve('./'));
@@ -121,10 +203,15 @@ export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 82
121
203
  expect(devices).toBeDefined();
122
204
  expect(devices.size).toBe(devicesSize);
123
205
  expect(frontend).toBeDefined();
206
+ // @ts-expect-error - access to private member for testing
124
207
  expect(frontend.listening).toBeTruthy();
208
+ // @ts-expect-error - access to private member for testing
125
209
  expect(frontend.httpServer).toBeDefined();
210
+ // @ts-expect-error - access to private member for testing
126
211
  expect(frontend.httpsServer).toBeUndefined();
212
+ // @ts-expect-error - access to private member for testing
127
213
  expect(frontend.expressApp).toBeDefined();
214
+ // @ts-expect-error - access to private member for testing
128
215
  expect(frontend.webSocketServer).toBeDefined();
129
216
  expect(matterbridge.nodeStorage).toBeDefined();
130
217
  expect(matterbridge.nodeContext).toBeDefined();
@@ -162,25 +249,45 @@ export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 82
162
249
  });
163
250
  });
164
251
  }
165
- expect(loggerLogSpy).toHaveBeenCalledWith("info", `The frontend http server is listening on ${UNDERLINE}http://${matterbridge.systemInformation.ipv4Address}:${frontendPort}${UNDERLINEOFF}${rs}`);
252
+ expect(loggerLogSpy).toHaveBeenCalledWith("info" /* LogLevel.INFO */, `The frontend http server is listening on ${UNDERLINE}http://${matterbridge.systemInformation.ipv4Address}:${frontendPort}${UNDERLINEOFF}${rs}`);
166
253
  if (bridgeMode === 'bridge') {
167
- expect(loggerLogSpy).toHaveBeenCalledWith("notice", `Starting Matterbridge server node`);
168
- expect(loggerLogSpy).toHaveBeenCalledWith("notice", `Server node for Matterbridge is online`);
169
- expect(loggerLogSpy).toHaveBeenCalledWith("debug", `Starting start matter interval in bridge mode...`);
170
- expect(loggerLogSpy).toHaveBeenCalledWith("debug", `Cleared startMatterInterval interval in bridge mode`);
171
- expect(loggerLogSpy).toHaveBeenCalledWith("notice", `Matterbridge bridge started successfully`);
254
+ expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Starting Matterbridge server node`);
255
+ expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Server node for Matterbridge is online`);
256
+ expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Starting start matter interval in bridge mode...`);
257
+ expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Cleared startMatterInterval interval in bridge mode`);
258
+ expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Matterbridge bridge started successfully`);
172
259
  }
173
260
  else if (bridgeMode === 'childbridge') {
174
- expect(loggerLogSpy).toHaveBeenCalledWith("debug", `Starting start matter interval in childbridge mode...`);
175
- expect(loggerLogSpy).toHaveBeenCalledWith("debug", `Cleared startMatterInterval interval in childbridge mode`);
176
- expect(loggerLogSpy).toHaveBeenCalledWith("notice", `Matterbridge childbridge started successfully`);
261
+ expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Starting start matter interval in childbridge mode...`);
262
+ expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Cleared startMatterInterval interval in childbridge mode`);
263
+ expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Matterbridge childbridge started successfully`);
177
264
  }
178
265
  return matterbridge;
179
266
  }
267
+ /**
268
+ * Stop the active Matterbridge instance.
269
+ *
270
+ * @param {cleanupPause} cleanupPause The pause duration before cleanup. Default is 10 ms.
271
+ * @param {destroyPause} destroyPause The pause duration before destruction. Default is 250 ms.
272
+ */
180
273
  export async function stopMatterbridge(cleanupPause = 10, destroyPause = 250) {
181
274
  await destroyMatterbridgeEnvironment(cleanupPause, destroyPause);
182
275
  }
276
+ /**
277
+ * Create a Matterbridge instance for testing without initializing it.
278
+ *
279
+ * @param {string} name - Name for the environment (jest/name).
280
+ * @returns {Promise<Matterbridge>} The Matterbridge instance.
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * // Create Matterbridge environment
285
+ * await createMatterbridgeEnvironment(NAME);
286
+ * await startMatterbridgeEnvironment(MATTER_PORT);
287
+ * ```
288
+ */
183
289
  export async function createMatterbridgeEnvironment(name) {
290
+ // Create a MatterbridgeEdge instance
184
291
  matterbridge = await Matterbridge.loadInstance(false);
185
292
  expect(matterbridge).toBeDefined();
186
293
  expect(matterbridge).toBeInstanceOf(Matterbridge);
@@ -191,25 +298,48 @@ export async function createMatterbridgeEnvironment(name) {
191
298
  matterbridge.matterbridgeDirectory = path.join('jest', name, '.matterbridge');
192
299
  matterbridge.matterbridgePluginDirectory = path.join('jest', name, 'Matterbridge');
193
300
  matterbridge.matterbridgeCertDirectory = path.join('jest', name, '.mattercert');
194
- matterbridge.log.logLevel = "debug";
195
- log = new AnsiLogger({ logName: 'Plugin platform', logTimestampFormat: 4, logLevel: "debug" });
301
+ matterbridge.log.logLevel = "debug" /* LogLevel.DEBUG */;
302
+ log = new AnsiLogger({ logName: 'Plugin platform', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
303
+ // Setup matter environment
304
+ // @ts-expect-error - access to private member for testing
196
305
  matterbridge.environment = createTestEnvironment(name);
306
+ // @ts-expect-error - access to private member for testing
197
307
  expect(matterbridge.environment).toBeDefined();
308
+ // @ts-expect-error - access to private member for testing
198
309
  expect(matterbridge.environment).toBeInstanceOf(Environment);
199
310
  return matterbridge;
200
311
  }
312
+ /**
313
+ * Start the matterbridge environment.
314
+ * Only node storage, matter storage and the server and aggregator nodes are started.
315
+ *
316
+ * @param {number} port The matter server port.
317
+ * @returns {Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>} The started server and aggregator.
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * // Create Matterbridge environment
322
+ * await createMatterbridgeEnvironment(NAME);
323
+ * await startMatterbridgeEnvironment(MATTER_PORT);
324
+ * ```
325
+ */
201
326
  export async function startMatterbridgeEnvironment(port = 5540) {
327
+ // Create the node storage
202
328
  matterbridge.nodeStorage = new NodeStorageManager({ dir: path.join(matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
203
329
  matterbridge.nodeContext = await matterbridge.nodeStorage.createStorage('matterbridge');
330
+ // Create the matter storage
331
+ // @ts-expect-error - access to private member for testing
204
332
  await matterbridge.startMatterStorage();
205
333
  expect(matterbridge.matterStorageService).toBeDefined();
206
334
  expect(matterbridge.matterStorageManager).toBeDefined();
207
335
  expect(matterbridge.matterbridgeContext).toBeDefined();
336
+ // @ts-expect-error - access to private member for testing
208
337
  server = await matterbridge.createServerNode(matterbridge.matterbridgeContext, port);
209
338
  expect(server).toBeDefined();
210
339
  expect(server).toBeDefined();
211
340
  expect(server.lifecycle.isReady).toBeTruthy();
212
341
  matterbridge.serverNode = server;
342
+ // @ts-expect-error - access to private member for testing
213
343
  aggregator = await matterbridge.createAggregatorNode(matterbridge.matterbridgeContext);
214
344
  expect(aggregator).toBeDefined();
215
345
  matterbridge.aggregatorNode = aggregator;
@@ -217,6 +347,7 @@ export async function startMatterbridgeEnvironment(port = 5540) {
217
347
  expect(server.parts.has(aggregator.id)).toBeTruthy();
218
348
  expect(server.parts.has(aggregator)).toBeTruthy();
219
349
  expect(aggregator.lifecycle.isReady).toBeTruthy();
350
+ // Wait for the server to be online
220
351
  expect(server.lifecycle.isOnline).toBeFalsy();
221
352
  await new Promise((resolve) => {
222
353
  server.lifecycle.online.on(async () => {
@@ -224,6 +355,7 @@ export async function startMatterbridgeEnvironment(port = 5540) {
224
355
  });
225
356
  server.start();
226
357
  });
358
+ // Check if the server is online
227
359
  expect(server.lifecycle.isReady).toBeTruthy();
228
360
  expect(server.lifecycle.isOnline).toBeTruthy();
229
361
  expect(server.lifecycle.isCommissioned).toBeFalsy();
@@ -235,9 +367,23 @@ export async function startMatterbridgeEnvironment(port = 5540) {
235
367
  expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
236
368
  expect(aggregator.lifecycle.hasId).toBeTruthy();
237
369
  expect(aggregator.lifecycle.hasNumber).toBeTruthy();
370
+ // Ensure the queue is empty and pause
238
371
  await flushAsync();
239
372
  return [server, aggregator];
240
373
  }
374
+ /**
375
+ * Add a matterbridge platform for testing.
376
+ *
377
+ * @param {MatterbridgePlatform} platform The platform to add.
378
+ * @param {string} [name] Optional name of the platform.
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * platform = new Platform(matterbridge, log, config);
383
+ * // Add the platform to the Matterbridge environment
384
+ * addMatterbridgePlatform(platform);
385
+ * ```
386
+ */
241
387
  export function addMatterbridgePlatform(platform, name) {
242
388
  if (name)
243
389
  platform.config.name = name;
@@ -249,6 +395,7 @@ export function addMatterbridgePlatform(platform, name) {
249
395
  expect(platform.version).toBeDefined();
250
396
  expect(platform.config.debug).toBeDefined();
251
397
  expect(platform.config.unregisterOnShutdown).toBeDefined();
398
+ // @ts-expect-error accessing private member for testing
252
399
  matterbridge.plugins._plugins.set(platform.config.name, {
253
400
  name: platform.config.name,
254
401
  path: './',
@@ -260,63 +407,148 @@ export function addMatterbridgePlatform(platform, name) {
260
407
  });
261
408
  platform['name'] = platform.config.name;
262
409
  }
410
+ /**
411
+ * Stop the matterbridge environment
412
+ *
413
+ * @example
414
+ * ```typescript
415
+ * // Destroy Matterbridge environment
416
+ * await stopMatterbridgeEnvironment();
417
+ * await destroyMatterbridgeEnvironment();
418
+ * ```
419
+ */
263
420
  export async function stopMatterbridgeEnvironment() {
264
421
  expect(matterbridge).toBeDefined();
265
422
  expect(server).toBeDefined();
266
423
  expect(aggregator).toBeDefined();
424
+ // Flush any pending endpoint number persistence
267
425
  await flushAllEndpointNumberPersistence(server);
426
+ // Ensure all endpoint numbers are persisted
268
427
  await assertAllEndpointNumbersPersisted(server);
428
+ // Close the server node
269
429
  expect(server.lifecycle.isReady).toBeTruthy();
270
430
  expect(server.lifecycle.isOnline).toBeTruthy();
271
431
  await server.close();
272
432
  expect(server.lifecycle.isReady).toBeTruthy();
273
433
  expect(server.lifecycle.isOnline).toBeFalsy();
434
+ // Stop the matter storage
435
+ // @ts-expect-error - access to private member for testing
274
436
  await matterbridge.stopMatterStorage();
275
437
  expect(matterbridge.matterStorageService).not.toBeDefined();
276
438
  expect(matterbridge.matterStorageManager).not.toBeDefined();
277
439
  expect(matterbridge.matterbridgeContext).not.toBeDefined();
440
+ // Stop the node storage
278
441
  await matterbridge.nodeContext?.close();
279
442
  await matterbridge.nodeStorage?.close();
443
+ // Ensure the queue is empty and pause
280
444
  await flushAsync();
281
445
  }
446
+ /**
447
+ * Destroy the matterbridge environment
448
+ *
449
+ * @param {number} cleanupPause The timeout for the destroy operation (default 10ms).
450
+ * @param {number} destroyPause The pause duration after cleanup before destroying the instance (default 250ms).
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * // Destroy Matterbridge environment
455
+ * await stopMatterbridgeEnvironment();
456
+ * await destroyMatterbridgeEnvironment();
457
+ * ```
458
+ */
282
459
  export async function destroyMatterbridgeEnvironment(cleanupPause = 10, destroyPause = 250) {
460
+ // Destroy a matterbridge instance
283
461
  await destroyInstance(matterbridge, cleanupPause, destroyPause);
462
+ // Close the mDNS service
284
463
  await closeMdnsInstance(matterbridge);
464
+ // Reset the singleton instance
465
+ // @ts-expect-error - accessing private member for testing
285
466
  Matterbridge.instance = undefined;
286
467
  }
468
+ /**
469
+ * Destroy a matterbridge instance
470
+ *
471
+ * @param {Matterbridge} matterbridge The matterbridge instance to destroy.
472
+ * @param {number} cleanupPause The pause duration to wait for the cleanup to complete in milliseconds (default 10ms).
473
+ * @param {number} destroyPause The pause duration to wait after cleanup before destroying the instance in milliseconds (default 250ms).
474
+ */
287
475
  export async function destroyInstance(matterbridge, cleanupPause = 10, destroyPause = 250) {
476
+ // Cleanup the Matterbridge instance
477
+ // @ts-expect-error - accessing private member for testing
288
478
  await matterbridge.cleanup('destroying instance...', false, cleanupPause);
479
+ // Pause before destroying the instance
289
480
  if (destroyPause > 0)
290
481
  await flushAsync(undefined, undefined, destroyPause);
291
482
  }
483
+ /**
484
+ * Close the mDNS instance in the matterbridge environment.
485
+ *
486
+ * @param {Matterbridge} matterbridge The matterbridge instance.
487
+ * @returns {Promise<void>} A promise that resolves when the mDNS instance is closed.
488
+ */
292
489
  export async function closeMdnsInstance(matterbridge) {
490
+ // TODO: matter.js 0.16.0 - provide close method to close the mDNS service
491
+ // @ts-expect-error - accessing private member for testing
492
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
293
493
  const mdns = matterbridge.environment.get(MdnsService);
294
494
  if (mdns && mdns[Symbol.asyncDispose] && typeof mdns[Symbol.asyncDispose] === 'function')
295
495
  await mdns[Symbol.asyncDispose]();
296
496
  if (mdns && mdns.close && typeof mdns.close === 'function')
297
497
  await mdns.close();
298
498
  }
499
+ /**
500
+ * Create a matter test environment for testing:
501
+ * - it will remove any existing home directory
502
+ * - setup the matter environment with name, debug logging and ANSI format
503
+ * - setup the mDNS service in the environment
504
+ *
505
+ * @param {string} name - Name for the environment (jest/name).
506
+ * @returns {Environment} - The default matter environment.
507
+ */
299
508
  export function createTestEnvironment(name) {
300
509
  expect(name).toBeDefined();
301
510
  expect(typeof name).toBe('string');
302
- expect(name.length).toBeGreaterThanOrEqual(4);
511
+ expect(name.length).toBeGreaterThanOrEqual(4); // avoid accidental deletion of short paths like "/" or "C:\"
512
+ // Cleanup any existing home directory
303
513
  rmSync(path.join('jest', name), { recursive: true, force: true });
514
+ // Setup the matter environment
304
515
  environment = Environment.default;
305
516
  environment.vars.set('log.level', MatterLogLevel.DEBUG);
306
517
  environment.vars.set('log.format', MatterLogFormat.ANSI);
307
518
  environment.vars.set('path.root', path.join('jest', name, '.matterbridge', MATTER_STORAGE_NAME));
308
519
  environment.vars.set('runtime.signals', false);
309
520
  environment.vars.set('runtime.exitcode', false);
521
+ // Setup the mDNS service in the environment
310
522
  new MdnsService(environment);
523
+ // await environment.get(MdnsService)?.construction.ready;
311
524
  return environment;
312
525
  }
526
+ /**
527
+ * Destroy the matter test environment by closing the mDNS service.
528
+ *
529
+ * @returns {Promise<void>} A promise that resolves when the test environment is destroyed.
530
+ */
313
531
  export async function destroyTestEnvironment() {
532
+ // stop the mDNS service
533
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
314
534
  const mdns = environment.get(MdnsService);
315
535
  if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
316
536
  await mdns[Symbol.asyncDispose]();
317
537
  if (mdns && typeof mdns.close === 'function')
318
538
  await mdns.close();
319
539
  }
540
+ /**
541
+ * Advance the Node.js event loop deterministically to allow chained asynchronous work (Promises scheduled in
542
+ * microtasks and follow‑up macrotasks) to complete inside tests without adding arbitrary long timeouts.
543
+ *
544
+ * NOTE: This does not guarantee OS level network IO completion—only JavaScript task queue progression inside the
545
+ * current process.
546
+ *
547
+ * @param {number} ticks Number of macrotask (setImmediate) turns to yield (default 3).
548
+ * @param {number} microTurns Number of microtask drains (Promise.resolve chains) after macrotask yielding (default 10).
549
+ * @param {number} pause Final timer delay in ms; set 0 to disable (default 250ms).
550
+ * @returns {Promise<void>} Resolves after the requested event loop advancement has completed.
551
+ */
320
552
  export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
321
553
  for (let i = 0; i < ticks; i++)
322
554
  await new Promise((resolve) => setImmediate(resolve));
@@ -325,16 +557,33 @@ export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
325
557
  if (pause)
326
558
  await new Promise((resolve) => setTimeout(resolve, pause));
327
559
  }
560
+ /**
561
+ * Summarize live libuv handles/requests inside a process.
562
+ *
563
+ * @param {AnsiLogger} log - Logger to use for output
564
+ *
565
+ * @returns {number} - The total number of active handles and requests
566
+ */
328
567
  export function logKeepAlives(log) {
568
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
329
569
  const handles = process._getActiveHandles?.() ?? [];
570
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
330
571
  const requests = process._getActiveRequests?.() ?? [];
572
+ // istanbul ignore next
331
573
  const fmtHandle = (h, i) => {
332
574
  const ctor = h?.constructor?.name ?? 'Unknown';
575
+ // Timer-like?
576
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
333
577
  const hasRef = typeof h?.hasRef === 'function' ? h.hasRef() : undefined;
578
+ // MessagePort?
579
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
334
580
  const isPort = h?.constructor?.name?.includes('MessagePort');
581
+ // Socket/Server?
582
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
335
583
  const fd = h?.fd ?? h?._handle?.fd;
336
584
  return { i, type: ctor, hasRef, isPort, fd };
337
585
  };
586
+ // istanbul ignore next
338
587
  const fmtReq = (r, i) => {
339
588
  const ctor = r?.constructor?.name ?? 'Unknown';
340
589
  return { i, type: ctor };
@@ -343,6 +592,7 @@ export function logKeepAlives(log) {
343
592
  handles: handles.map(fmtHandle),
344
593
  requests: requests.map(fmtReq),
345
594
  };
595
+ // istanbul ignore next if
346
596
  if (summary.handles.length === 0 && summary.requests.length === 0) {
347
597
  log?.debug('KeepAlive: no active handles or requests.');
348
598
  }
@@ -354,6 +604,19 @@ export function logKeepAlives(log) {
354
604
  }
355
605
  return summary.handles.length + summary.requests.length;
356
606
  }
607
+ /**
608
+ * Flush (await) the lazy endpoint number persistence mechanism used by matter.js.
609
+ *
610
+ * Background:
611
+ * assignNumber() batches persistence (store.saveNumber + updating __nextNumber__) via an internal promise (#numbersPersisted).
612
+ * Calling endpointStores.close() waits for the current batch only. If new endpoints were added in the same macrotask
613
+ * cycle additional micro/macro turns might be needed to ensure the batch started. We defensively yield macrotasks
614
+ * (setImmediate) and then await close() multiple rounds.
615
+ *
616
+ * @param {ServerNode} targetServer The server whose endpoint numbering persistence should be flushed.
617
+ * @param {number} rounds Number of macrotask + close cycles to run (2 is usually sufficient; 1 often works).
618
+ * @returns {Promise<void>} Resolves when pending number persistence batches have completed.
619
+ */
357
620
  export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2) {
358
621
  const nodeStore = targetServer.env.get(ServerNodeStore);
359
622
  for (let i = 0; i < rounds; i++) {
@@ -361,6 +624,12 @@ export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2
361
624
  await nodeStore.endpointStores.close();
362
625
  }
363
626
  }
627
+ /**
628
+ * Collect all endpoints in the server endpoint tree (root -> descendants).
629
+ *
630
+ * @param {Endpoint} root Root endpoint (typically the ServerNode root endpoint cast as Endpoint).
631
+ * @returns {Endpoint[]} Flat array including the root and every descendant once.
632
+ */
364
633
  function collectAllEndpoints(root) {
365
634
  const list = [];
366
635
  const walk = (ep) => {
@@ -374,14 +643,26 @@ function collectAllEndpoints(root) {
374
643
  walk(root);
375
644
  return list;
376
645
  }
646
+ /**
647
+ * Assert that every endpoint attached to the server has an assigned and (batch-)persisted endpoint number.
648
+ *
649
+ * This waits for any outstanding number persistence batch (endpointStores.close()), then traverses the endpoint
650
+ * graph and asserts:
651
+ * - Root endpoint: number is 0 (allowing undefined to coerce to 0 via nullish coalescing check).
652
+ * - All other endpoints: number > 0.
653
+ *
654
+ * @param {ServerNode} targetServer The server whose endpoint numbers are verified.
655
+ * @returns {Promise<void>} Resolves when assertions complete.
656
+ */
377
657
  export async function assertAllEndpointNumbersPersisted(targetServer) {
378
658
  const nodeStore = targetServer.env.get(ServerNodeStore);
659
+ // Ensure any pending persistence finished (flush any in-flight batch promise)
379
660
  await nodeStore.endpointStores.close();
380
661
  const all = collectAllEndpoints(targetServer);
381
662
  for (const ep of all) {
382
663
  const store = nodeStore.storeForEndpoint(ep);
383
664
  if (ep.maybeNumber === 0) {
384
- expect(store.number ?? 0).toBe(0);
665
+ expect(store.number ?? 0).toBe(0); // root
385
666
  }
386
667
  else {
387
668
  expect(store.number).toBeGreaterThan(0);
@@ -389,21 +670,42 @@ export async function assertAllEndpointNumbersPersisted(targetServer) {
389
670
  }
390
671
  return all.length;
391
672
  }
673
+ /**
674
+ * Close the server node stores to flush any pending endpoint number persistence.
675
+ *
676
+ * @param {ServerNode} targetServer The server whose endpoint stores should be closed.
677
+ * @returns {Promise<void>} Resolves when the stores have been closed.
678
+ */
392
679
  export async function closeServerNodeStores(targetServer) {
680
+ // Close endpoint stores to avoid number persistence issues
681
+ if (!targetServer)
682
+ targetServer = server;
393
683
  await targetServer?.env.get(ServerNodeStore)?.endpointStores.close();
394
684
  }
685
+ /**
686
+ * Start a matter server node for testing.
687
+ *
688
+ * @param {string} name Name of the server (used for logging and product description).
689
+ * @param {number} port TCP port to listen on.
690
+ * @param {DeviceTypeId} deviceType Device type identifier for the server node.
691
+ * @returns {Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>} Resolves to an array containing the created ServerNode and its AggregatorNode.
692
+ */
395
693
  export async function startServerNode(name, port, deviceType = bridge.code) {
396
694
  const { randomBytes } = await import('node:crypto');
397
695
  const random = randomBytes(8).toString('hex');
696
+ // Create the server node
398
697
  server = await ServerNode.create({
399
698
  id: name + 'ServerNode',
699
+ // Provide the environment
400
700
  environment,
701
+ // Provide Node announcement settings
401
702
  productDescription: {
402
703
  name: name + 'ServerNode',
403
704
  deviceType: DeviceTypeId(deviceType),
404
705
  vendorId: VendorId(0xfff1),
405
706
  productId: 0x8000,
406
707
  },
708
+ // Provide defaults for the BasicInformation cluster on the Root endpoint
407
709
  basicInformation: {
408
710
  vendorId: VendorId(0xfff1),
409
711
  vendorName: 'Matterbridge',
@@ -416,32 +718,39 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
416
718
  serialNumber: 'SN' + random,
417
719
  uniqueId: 'UI' + random,
418
720
  },
721
+ // Provide Network relevant configuration like the port
419
722
  network: {
420
723
  listeningAddressIpv4: undefined,
421
724
  listeningAddressIpv6: undefined,
422
725
  port,
423
726
  },
727
+ // Provide the certificate for the device
424
728
  operationalCredentials: {
425
729
  certification: undefined,
426
730
  },
427
731
  });
428
732
  expect(server).toBeDefined();
429
733
  expect(server.lifecycle.isReady).toBeTruthy();
734
+ // Create the aggregator node
430
735
  aggregator = new Endpoint(AggregatorEndpoint, {
431
736
  id: name + 'AggregatorNode',
432
737
  });
433
738
  expect(aggregator).toBeDefined();
739
+ // Add the aggregator to the server
434
740
  await server.add(aggregator);
435
741
  expect(server.parts.has(aggregator.id)).toBeTruthy();
436
742
  expect(server.parts.has(aggregator)).toBeTruthy();
437
743
  expect(aggregator.lifecycle.isReady).toBeTruthy();
744
+ // Run the server
438
745
  expect(server.lifecycle.isOnline).toBeFalsy();
746
+ // Wait for the server to be online
439
747
  await new Promise((resolve) => {
440
748
  server.lifecycle.online.on(async () => {
441
749
  resolve();
442
750
  });
443
751
  server.start();
444
752
  });
753
+ // Check if the server is online
445
754
  expect(server.lifecycle.isReady).toBeTruthy();
446
755
  expect(server.lifecycle.isOnline).toBeTruthy();
447
756
  expect(server.lifecycle.isCommissioned).toBeFalsy();
@@ -453,31 +762,53 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
453
762
  expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
454
763
  expect(aggregator.lifecycle.hasId).toBeTruthy();
455
764
  expect(aggregator.lifecycle.hasNumber).toBeTruthy();
765
+ // Ensure the queue is empty and pause 250ms
456
766
  await flushAsync();
457
767
  return [server, aggregator];
458
768
  }
769
+ /**
770
+ * Stop a matter server node.
771
+ *
772
+ * @param {ServerNode<ServerNode.RootEndpoint>} server The server to stop.
773
+ * @returns {Promise<void>} Resolves when the server has stopped.
774
+ */
459
775
  export async function stopServerNode(server) {
776
+ // Flush any pending endpoint number persistence
460
777
  await flushAllEndpointNumberPersistence(server);
778
+ // Ensure all endpoint numbers are persisted
461
779
  await assertAllEndpointNumbersPersisted(server);
780
+ // Stop the server
462
781
  expect(server).toBeDefined();
463
782
  expect(server.lifecycle.isReady).toBeTruthy();
464
783
  expect(server.lifecycle.isOnline).toBeTruthy();
465
784
  await server.close();
466
785
  expect(server.lifecycle.isReady).toBeTruthy();
467
786
  expect(server.lifecycle.isOnline).toBeFalsy();
787
+ // stop the mDNS service
788
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
468
789
  const mdns = environment.get(MdnsService);
469
790
  if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
470
791
  await mdns[Symbol.asyncDispose]();
471
792
  if (mdns && typeof mdns.close === 'function')
472
793
  await mdns.close();
794
+ // Ensure the queue is empty and pause 100ms
473
795
  await flushAsync();
474
796
  }
797
+ /**
798
+ * Add a device (endpoint) to a matter server node or an aggregator.
799
+ *
800
+ * @param {ServerNode<ServerNode.RootEndpoint> | Endpoint<AggregatorEndpoint>} owner The server or aggregator to add the device to.
801
+ * @param {Endpoint} device The device to add.
802
+ * @param {number} pause The pause time in milliseconds after addition (default 10ms).
803
+ * @returns {Promise<void>} Resolves when the device has been added and is ready.
804
+ */
475
805
  export async function addDevice(owner, device, pause = 10) {
476
806
  expect(owner).toBeDefined();
477
807
  expect(device).toBeDefined();
478
808
  expect(owner.lifecycle.isReady).toBeTruthy();
479
809
  expect(owner.construction.status).toBe(Lifecycle.Status.Active);
480
810
  expect(owner.lifecycle.isPartsReady).toBeTruthy();
811
+ // istanbul ignore next
481
812
  try {
482
813
  await owner.add(device);
483
814
  }
@@ -497,12 +828,21 @@ export async function addDevice(owner, device, pause = 10) {
497
828
  await flushAsync(undefined, undefined, pause);
498
829
  return true;
499
830
  }
831
+ /**
832
+ * Delete a device (endpoint) from a matter server node or an aggregator.
833
+ *
834
+ * @param {ServerNode<ServerNode.RootEndpoint> | Endpoint<AggregatorEndpoint>} owner The server or aggregator to remove the device from.
835
+ * @param {Endpoint} device The device to remove.
836
+ * @param {number} pause The pause time in milliseconds after deletion (default 10ms).
837
+ * @returns {Promise<void>} Resolves when the device has been removed and is no longer ready.
838
+ */
500
839
  export async function deleteDevice(owner, device, pause = 10) {
501
840
  expect(owner).toBeDefined();
502
841
  expect(device).toBeDefined();
503
842
  expect(owner.lifecycle.isReady).toBeTruthy();
504
843
  expect(owner.construction.status).toBe(Lifecycle.Status.Active);
505
844
  expect(owner.lifecycle.isPartsReady).toBeTruthy();
845
+ // istanbul ignore next
506
846
  try {
507
847
  await device.delete();
508
848
  }
@@ -522,3 +862,4 @@ export async function deleteDevice(owner, device, pause = 10) {
522
862
  await flushAsync(undefined, undefined, pause);
523
863
  return true;
524
864
  }
865
+ //# sourceMappingURL=jestHelpers.js.map