matterbridge 3.5.0-dev-20260119-f9ea00e → 3.5.0

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