matterbridge 3.4.1-dev-20251130-cfb291e → 3.4.1

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