matterbridge 3.4.1-dev-20251130-422c2fc → 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 -0
  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 +375 -14
  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 +808 -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 +471 -8
  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 +342 -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,13 +435,30 @@ 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) {
456
+ expect(platform).toBeDefined();
457
+ // Setup the platform MatterNode helpers
458
+ // @ts-expect-error - setMatterNode is intentionally private
459
+ platform.setMatterNode?.(matterbridge.addBridgedEndpoint.bind(matterbridge), matterbridge.removeBridgedEndpoint.bind(matterbridge), matterbridge.removeAllBridgedEndpoints.bind(matterbridge), matterbridge.addVirtualEndpoint.bind(matterbridge));
293
460
  if (name)
294
461
  platform.config.name = name;
295
- expect(platform).toBeDefined();
296
462
  expect(platform.config.name).toBeDefined();
297
463
  expect(platform.config.type).toBeDefined();
298
464
  expect(platform.type).toBeDefined();
@@ -300,6 +466,7 @@ export function addMatterbridgePlatform(platform, name) {
300
466
  expect(platform.version).toBeDefined();
301
467
  expect(platform.config.debug).toBeDefined();
302
468
  expect(platform.config.unregisterOnShutdown).toBeDefined();
469
+ // @ts-expect-error accessing private member for testing
303
470
  matterbridge.plugins._plugins.set(platform.config.name, {
304
471
  name: platform.config.name,
305
472
  path: './',
@@ -311,63 +478,150 @@ export function addMatterbridgePlatform(platform, name) {
311
478
  });
312
479
  platform['name'] = platform.config.name;
313
480
  }
481
+ /**
482
+ * Stop the matterbridge environment
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * // Destroy Matterbridge environment
487
+ * await stopMatterbridgeEnvironment();
488
+ * await destroyMatterbridgeEnvironment();
489
+ * ```
490
+ */
314
491
  export async function stopMatterbridgeEnvironment() {
315
492
  expect(matterbridge).toBeDefined();
316
493
  expect(server).toBeDefined();
317
494
  expect(aggregator).toBeDefined();
495
+ // Flush any pending endpoint number persistence
318
496
  await flushAllEndpointNumberPersistence(server);
497
+ // Ensure all endpoint numbers are persisted
319
498
  await assertAllEndpointNumbersPersisted(server);
499
+ // Close the server node
320
500
  expect(server.lifecycle.isReady).toBeTruthy();
321
501
  expect(server.lifecycle.isOnline).toBeTruthy();
322
502
  await server.close();
323
503
  expect(server.lifecycle.isReady).toBeTruthy();
324
504
  expect(server.lifecycle.isOnline).toBeFalsy();
505
+ // Stop the matter storage
506
+ // @ts-expect-error - access to private member for testing
325
507
  await matterbridge.stopMatterStorage();
326
508
  expect(matterbridge.matterStorageService).not.toBeDefined();
327
509
  expect(matterbridge.matterStorageManager).not.toBeDefined();
328
510
  expect(matterbridge.matterbridgeContext).not.toBeDefined();
511
+ // Stop the node storage
329
512
  await matterbridge.nodeContext?.close();
513
+ matterbridge.nodeContext = undefined;
330
514
  await matterbridge.nodeStorage?.close();
515
+ matterbridge.nodeStorage = undefined;
516
+ // Ensure the queue is empty and pause
331
517
  await flushAsync();
332
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
+ */
333
532
  export async function destroyMatterbridgeEnvironment(cleanupPause = 10, destroyPause = 250) {
533
+ // Destroy a matterbridge instance
334
534
  await destroyInstance(matterbridge, cleanupPause, destroyPause);
535
+ // Close the mDNS service
335
536
  await closeMdnsInstance(matterbridge);
537
+ // Reset the singleton instance
538
+ // @ts-expect-error - accessing private member for testing
336
539
  Matterbridge.instance = undefined;
337
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
+ */
338
548
  export async function destroyInstance(matterbridge, cleanupPause = 10, destroyPause = 250) {
549
+ // Cleanup the Matterbridge instance
550
+ // @ts-expect-error - accessing private member for testing
339
551
  await matterbridge.cleanup('destroying instance...', false, cleanupPause);
552
+ // Pause before destroying the instance
340
553
  if (destroyPause > 0)
341
554
  await flushAsync(undefined, undefined, destroyPause);
342
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
+ */
343
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
344
566
  const mdns = matterbridge.environment.get(MdnsService);
345
567
  if (mdns && mdns[Symbol.asyncDispose] && typeof mdns[Symbol.asyncDispose] === 'function')
346
568
  await mdns[Symbol.asyncDispose]();
347
569
  if (mdns && mdns.close && typeof mdns.close === 'function')
348
570
  await mdns.close();
349
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
+ */
350
581
  export function createTestEnvironment(name) {
351
582
  expect(name).toBeDefined();
352
583
  expect(typeof name).toBe('string');
353
- 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
354
586
  rmSync(path.join('jest', name), { recursive: true, force: true });
587
+ // Setup the matter environment
355
588
  environment = Environment.default;
356
589
  environment.vars.set('log.level', MatterLogLevel.DEBUG);
357
590
  environment.vars.set('log.format', MatterLogFormat.ANSI);
358
591
  environment.vars.set('path.root', path.join('jest', name, '.matterbridge', MATTER_STORAGE_NAME));
359
592
  environment.vars.set('runtime.signals', false);
360
593
  environment.vars.set('runtime.exitcode', false);
594
+ // Setup the mDNS service in the environment
361
595
  new MdnsService(environment);
596
+ // await environment.get(MdnsService)?.construction.ready;
362
597
  return environment;
363
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
+ */
364
604
  export async function destroyTestEnvironment() {
605
+ // stop the mDNS service
606
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
365
607
  const mdns = environment.get(MdnsService);
366
608
  if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
367
609
  await mdns[Symbol.asyncDispose]();
368
610
  if (mdns && typeof mdns.close === 'function')
369
611
  await mdns.close();
370
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
+ */
371
625
  export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
372
626
  for (let i = 0; i < ticks; i++)
373
627
  await new Promise((resolve) => setImmediate(resolve));
@@ -376,16 +630,33 @@ export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
376
630
  if (pause)
377
631
  await new Promise((resolve) => setTimeout(resolve, pause));
378
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
+ */
379
640
  export function logKeepAlives(log) {
641
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
642
  const handles = process._getActiveHandles?.() ?? [];
643
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
381
644
  const requests = process._getActiveRequests?.() ?? [];
645
+ // istanbul ignore next
382
646
  const fmtHandle = (h, i) => {
383
647
  const ctor = h?.constructor?.name ?? 'Unknown';
648
+ // Timer-like?
649
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
384
650
  const hasRef = typeof h?.hasRef === 'function' ? h.hasRef() : undefined;
651
+ // MessagePort?
652
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
385
653
  const isPort = h?.constructor?.name?.includes('MessagePort');
654
+ // Socket/Server?
655
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
386
656
  const fd = h?.fd ?? h?._handle?.fd;
387
657
  return { i, type: ctor, hasRef, isPort, fd };
388
658
  };
659
+ // istanbul ignore next
389
660
  const fmtReq = (r, i) => {
390
661
  const ctor = r?.constructor?.name ?? 'Unknown';
391
662
  return { i, type: ctor };
@@ -394,6 +665,7 @@ export function logKeepAlives(log) {
394
665
  handles: handles.map(fmtHandle),
395
666
  requests: requests.map(fmtReq),
396
667
  };
668
+ // istanbul ignore next if
397
669
  if (summary.handles.length === 0 && summary.requests.length === 0) {
398
670
  log?.debug('KeepAlive: no active handles or requests.');
399
671
  }
@@ -405,6 +677,19 @@ export function logKeepAlives(log) {
405
677
  }
406
678
  return summary.handles.length + summary.requests.length;
407
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
+ */
408
693
  export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2) {
409
694
  const nodeStore = targetServer.env.get(ServerNodeStore);
410
695
  for (let i = 0; i < rounds; i++) {
@@ -412,6 +697,12 @@ export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2
412
697
  await nodeStore.endpointStores.close();
413
698
  }
414
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
+ */
415
706
  function collectAllEndpoints(root) {
416
707
  const list = [];
417
708
  const walk = (ep) => {
@@ -425,14 +716,26 @@ function collectAllEndpoints(root) {
425
716
  walk(root);
426
717
  return list;
427
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
+ */
428
730
  export async function assertAllEndpointNumbersPersisted(targetServer) {
429
731
  const nodeStore = targetServer.env.get(ServerNodeStore);
732
+ // Ensure any pending persistence finished (flush any in-flight batch promise)
430
733
  await nodeStore.endpointStores.close();
431
734
  const all = collectAllEndpoints(targetServer);
432
735
  for (const ep of all) {
433
736
  const store = nodeStore.storeForEndpoint(ep);
434
737
  if (ep.maybeNumber === 0) {
435
- expect(store.number ?? 0).toBe(0);
738
+ expect(store.number ?? 0).toBe(0); // root
436
739
  }
437
740
  else {
438
741
  expect(store.number).toBeGreaterThan(0);
@@ -440,23 +743,42 @@ export async function assertAllEndpointNumbersPersisted(targetServer) {
440
743
  }
441
744
  return all.length;
442
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
+ */
443
752
  export async function closeServerNodeStores(targetServer) {
753
+ // Close endpoint stores to avoid number persistence issues
444
754
  if (!targetServer)
445
755
  targetServer = server;
446
756
  await targetServer?.env.get(ServerNodeStore)?.endpointStores.close();
447
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
+ */
448
766
  export async function startServerNode(name, port, deviceType = bridge.code) {
449
767
  const { randomBytes } = await import('node:crypto');
450
768
  const random = randomBytes(8).toString('hex');
769
+ // Create the server node
451
770
  server = await ServerNode.create({
452
771
  id: name + 'ServerNode',
772
+ // Provide the environment
453
773
  environment,
774
+ // Provide Node announcement settings
454
775
  productDescription: {
455
776
  name: name + 'ServerNode',
456
777
  deviceType: DeviceTypeId(deviceType),
457
778
  vendorId: VendorId(0xfff1),
458
779
  productId: 0x8000,
459
780
  },
781
+ // Provide defaults for the BasicInformation cluster on the Root endpoint
460
782
  basicInformation: {
461
783
  vendorId: VendorId(0xfff1),
462
784
  vendorName: 'Matterbridge',
@@ -469,32 +791,39 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
469
791
  serialNumber: 'SN' + random,
470
792
  uniqueId: 'UI' + random,
471
793
  },
794
+ // Provide Network relevant configuration like the port
472
795
  network: {
473
796
  listeningAddressIpv4: undefined,
474
797
  listeningAddressIpv6: undefined,
475
798
  port,
476
799
  },
800
+ // Provide the certificate for the device
477
801
  operationalCredentials: {
478
802
  certification: undefined,
479
803
  },
480
804
  });
481
805
  expect(server).toBeDefined();
482
806
  expect(server.lifecycle.isReady).toBeTruthy();
807
+ // Create the aggregator node
483
808
  aggregator = new Endpoint(AggregatorEndpoint, {
484
809
  id: name + 'AggregatorNode',
485
810
  });
486
811
  expect(aggregator).toBeDefined();
812
+ // Add the aggregator to the server
487
813
  await server.add(aggregator);
488
814
  expect(server.parts.has(aggregator.id)).toBeTruthy();
489
815
  expect(server.parts.has(aggregator)).toBeTruthy();
490
816
  expect(aggregator.lifecycle.isReady).toBeTruthy();
817
+ // Run the server
491
818
  expect(server.lifecycle.isOnline).toBeFalsy();
819
+ // Wait for the server to be online
492
820
  await new Promise((resolve) => {
493
821
  server.lifecycle.online.on(async () => {
494
822
  resolve();
495
823
  });
496
824
  server.start();
497
825
  });
826
+ // Check if the server is online
498
827
  expect(server.lifecycle.isReady).toBeTruthy();
499
828
  expect(server.lifecycle.isOnline).toBeTruthy();
500
829
  expect(server.lifecycle.isCommissioned).toBeFalsy();
@@ -506,31 +835,53 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
506
835
  expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
507
836
  expect(aggregator.lifecycle.hasId).toBeTruthy();
508
837
  expect(aggregator.lifecycle.hasNumber).toBeTruthy();
838
+ // Ensure the queue is empty and pause 250ms
509
839
  await flushAsync();
510
840
  return [server, aggregator];
511
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
+ */
512
848
  export async function stopServerNode(server) {
849
+ // Flush any pending endpoint number persistence
513
850
  await flushAllEndpointNumberPersistence(server);
851
+ // Ensure all endpoint numbers are persisted
514
852
  await assertAllEndpointNumbersPersisted(server);
853
+ // Stop the server
515
854
  expect(server).toBeDefined();
516
855
  expect(server.lifecycle.isReady).toBeTruthy();
517
856
  expect(server.lifecycle.isOnline).toBeTruthy();
518
857
  await server.close();
519
858
  expect(server.lifecycle.isReady).toBeTruthy();
520
859
  expect(server.lifecycle.isOnline).toBeFalsy();
860
+ // stop the mDNS service
861
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
521
862
  const mdns = environment.get(MdnsService);
522
863
  if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
523
864
  await mdns[Symbol.asyncDispose]();
524
865
  if (mdns && typeof mdns.close === 'function')
525
866
  await mdns.close();
867
+ // Ensure the queue is empty and pause 250ms
526
868
  await flushAsync();
527
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
+ */
528
878
  export async function addDevice(owner, device, pause = 10) {
529
879
  expect(owner).toBeDefined();
530
880
  expect(device).toBeDefined();
531
881
  expect(owner.lifecycle.isReady).toBeTruthy();
532
882
  expect(owner.construction.status).toBe(Lifecycle.Status.Active);
533
883
  expect(owner.lifecycle.isPartsReady).toBeTruthy();
884
+ // istanbul ignore next
534
885
  try {
535
886
  await owner.add(device);
536
887
  }
@@ -550,12 +901,21 @@ export async function addDevice(owner, device, pause = 10) {
550
901
  await flushAsync(undefined, undefined, pause);
551
902
  return true;
552
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
+ */
553
912
  export async function deleteDevice(owner, device, pause = 10) {
554
913
  expect(owner).toBeDefined();
555
914
  expect(device).toBeDefined();
556
915
  expect(owner.lifecycle.isReady).toBeTruthy();
557
916
  expect(owner.construction.status).toBe(Lifecycle.Status.Active);
558
917
  expect(owner.lifecycle.isPartsReady).toBeTruthy();
918
+ // istanbul ignore next
559
919
  try {
560
920
  await device.delete();
561
921
  }
@@ -575,3 +935,4 @@ export async function deleteDevice(owner, device, pause = 10) {
575
935
  await flushAsync(undefined, undefined, pause);
576
936
  return true;
577
937
  }
938
+ //# sourceMappingURL=jestHelpers.js.map