@smarthome.exchange/integrations 1.0.0 → 3.0.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 (529) hide show
  1. package/changelog.md +84 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/core/classes.configstore.d.ts +2 -0
  4. package/dist_ts/core/classes.configstore.js +84 -27
  5. package/dist_ts/core/classes.nativehomeadmission.d.ts +19 -0
  6. package/dist_ts/core/classes.nativehomeadmission.js +84 -0
  7. package/dist_ts/core/classes.nativeintegrationowner.d.ts +81 -0
  8. package/dist_ts/core/classes.nativeintegrationowner.js +273 -0
  9. package/dist_ts/core/classes.runtimemanager.d.ts +2 -0
  10. package/dist_ts/core/classes.runtimemanager.js +11 -3
  11. package/dist_ts/core/index.d.ts +2 -0
  12. package/dist_ts/core/index.js +3 -1
  13. package/dist_ts/core/types.d.ts +16 -0
  14. package/dist_ts/integrations/braviatv/braviatv.classes.client.d.ts +23 -72
  15. package/dist_ts/integrations/braviatv/braviatv.classes.client.js +265 -625
  16. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.d.ts +7 -7
  17. package/dist_ts/integrations/braviatv/braviatv.classes.configflow.js +51 -68
  18. package/dist_ts/integrations/braviatv/braviatv.classes.integration.d.ts +8 -11
  19. package/dist_ts/integrations/braviatv/braviatv.classes.integration.js +186 -135
  20. package/dist_ts/integrations/braviatv/braviatv.discovery.js +12 -11
  21. package/dist_ts/integrations/braviatv/braviatv.mapper.d.ts +6 -14
  22. package/dist_ts/integrations/braviatv/braviatv.mapper.js +37 -131
  23. package/dist_ts/integrations/braviatv/braviatv.types.d.ts +55 -133
  24. package/dist_ts/integrations/braviatv/braviatv.validation.d.ts +7 -0
  25. package/dist_ts/integrations/braviatv/braviatv.validation.js +72 -0
  26. package/dist_ts/integrations/broadlink/broadlink.actions.d.ts +2 -0
  27. package/dist_ts/integrations/broadlink/broadlink.actions.js +36 -0
  28. package/dist_ts/integrations/broadlink/broadlink.classes.client.d.ts +21 -19
  29. package/dist_ts/integrations/broadlink/broadlink.classes.client.js +185 -124
  30. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.d.ts +5 -4
  31. package/dist_ts/integrations/broadlink/broadlink.classes.configflow.js +64 -83
  32. package/dist_ts/integrations/broadlink/broadlink.classes.integration.d.ts +16 -19
  33. package/dist_ts/integrations/broadlink/broadlink.classes.integration.js +114 -59
  34. package/dist_ts/integrations/broadlink/broadlink.mapper.d.ts +3 -2
  35. package/dist_ts/integrations/broadlink/broadlink.mapper.js +2 -1
  36. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.d.ts +10 -0
  37. package/dist_ts/integrations/broadlink/broadlink.selected.mapper.js +41 -0
  38. package/dist_ts/integrations/broadlink/broadlink.types.d.ts +43 -7
  39. package/dist_ts/integrations/broadlink/broadlink.validation.d.ts +7 -0
  40. package/dist_ts/integrations/broadlink/broadlink.validation.js +63 -0
  41. package/dist_ts/integrations/daikin/daikin.classes.client.d.ts +10 -52
  42. package/dist_ts/integrations/daikin/daikin.classes.client.js +55 -614
  43. package/dist_ts/integrations/daikin/daikin.classes.configflow.d.ts +8 -12
  44. package/dist_ts/integrations/daikin/daikin.classes.configflow.js +77 -138
  45. package/dist_ts/integrations/daikin/daikin.classes.integration.d.ts +7 -24
  46. package/dist_ts/integrations/daikin/daikin.classes.integration.js +175 -67
  47. package/dist_ts/integrations/daikin/daikin.discovery.d.ts +11 -24
  48. package/dist_ts/integrations/daikin/daikin.discovery.js +98 -305
  49. package/dist_ts/integrations/daikin/daikin.mapper.d.ts +6 -7
  50. package/dist_ts/integrations/daikin/daikin.mapper.js +95 -155
  51. package/dist_ts/integrations/daikin/daikin.types.d.ts +24 -44
  52. package/dist_ts/integrations/daikin/daikin.types.js +1 -1
  53. package/dist_ts/integrations/daikin/daikin.validation.d.ts +5 -0
  54. package/dist_ts/integrations/daikin/daikin.validation.js +53 -0
  55. package/dist_ts/integrations/deconz/deconz.classes.client.d.ts +37 -29
  56. package/dist_ts/integrations/deconz/deconz.classes.client.js +521 -294
  57. package/dist_ts/integrations/deconz/deconz.classes.configflow.d.ts +6 -1
  58. package/dist_ts/integrations/deconz/deconz.classes.configflow.js +63 -33
  59. package/dist_ts/integrations/deconz/deconz.classes.integration.d.ts +4 -3
  60. package/dist_ts/integrations/deconz/deconz.classes.integration.js +85 -40
  61. package/dist_ts/integrations/deconz/deconz.mapper.d.ts +1 -0
  62. package/dist_ts/integrations/deconz/deconz.mapper.js +24 -6
  63. package/dist_ts/integrations/deconz/deconz.types.d.ts +43 -6
  64. package/dist_ts/integrations/deconz/deconz.validation.d.ts +7 -1
  65. package/dist_ts/integrations/deconz/deconz.validation.js +215 -4
  66. package/dist_ts/integrations/denonavr/denonavr.classes.client.d.ts +31 -40
  67. package/dist_ts/integrations/denonavr/denonavr.classes.client.js +149 -404
  68. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.d.ts +7 -2
  69. package/dist_ts/integrations/denonavr/denonavr.classes.configflow.js +44 -36
  70. package/dist_ts/integrations/denonavr/denonavr.classes.integration.d.ts +6 -11
  71. package/dist_ts/integrations/denonavr/denonavr.classes.integration.js +192 -113
  72. package/dist_ts/integrations/denonavr/denonavr.discovery.d.ts +10 -18
  73. package/dist_ts/integrations/denonavr/denonavr.discovery.js +71 -193
  74. package/dist_ts/integrations/denonavr/denonavr.mapper.d.ts +5 -13
  75. package/dist_ts/integrations/denonavr/denonavr.mapper.js +35 -159
  76. package/dist_ts/integrations/denonavr/denonavr.types.d.ts +55 -90
  77. package/dist_ts/integrations/denonavr/denonavr.validation.d.ts +10 -0
  78. package/dist_ts/integrations/denonavr/denonavr.validation.js +86 -0
  79. package/dist_ts/integrations/esphome/esphome.classes.client.d.ts +19 -7
  80. package/dist_ts/integrations/esphome/esphome.classes.client.js +183 -89
  81. package/dist_ts/integrations/esphome/esphome.classes.configflow.d.ts +5 -2
  82. package/dist_ts/integrations/esphome/esphome.classes.configflow.js +43 -33
  83. package/dist_ts/integrations/esphome/esphome.classes.integration.d.ts +8 -6
  84. package/dist_ts/integrations/esphome/esphome.classes.integration.js +71 -43
  85. package/dist_ts/integrations/esphome/esphome.discovery.js +4 -12
  86. package/dist_ts/integrations/esphome/esphome.mapper.d.ts +2 -2
  87. package/dist_ts/integrations/esphome/esphome.mapper.js +6 -4
  88. package/dist_ts/integrations/esphome/esphome.types.d.ts +31 -6
  89. package/dist_ts/integrations/esphome/esphome.types.js +1 -1
  90. package/dist_ts/integrations/esphome/esphome.validation.d.ts +7 -0
  91. package/dist_ts/integrations/esphome/esphome.validation.js +66 -0
  92. package/dist_ts/integrations/homeassistant/homeassistant.classes.client.d.ts +32 -4
  93. package/dist_ts/integrations/homeassistant/homeassistant.classes.client.js +345 -116
  94. package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.d.ts +8 -5
  95. package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.js +34 -8
  96. package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.d.ts +7 -4
  97. package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.js +44 -56
  98. package/dist_ts/integrations/homeassistant/homeassistant.types.d.ts +12 -1
  99. package/dist_ts/integrations/hue/hue.classes.client.d.ts +6 -1
  100. package/dist_ts/integrations/hue/hue.classes.client.js +120 -45
  101. package/dist_ts/integrations/hue/hue.classes.configflow.js +16 -7
  102. package/dist_ts/integrations/hue/hue.classes.integration.d.ts +2 -0
  103. package/dist_ts/integrations/hue/hue.classes.integration.js +103 -30
  104. package/dist_ts/integrations/hue/hue.validation.d.ts +1 -0
  105. package/dist_ts/integrations/hue/hue.validation.js +14 -4
  106. package/dist_ts/integrations/kodi/kodi.classes.client.d.ts +11 -45
  107. package/dist_ts/integrations/kodi/kodi.classes.client.js +84 -310
  108. package/dist_ts/integrations/kodi/kodi.classes.configflow.d.ts +9 -8
  109. package/dist_ts/integrations/kodi/kodi.classes.configflow.js +53 -48
  110. package/dist_ts/integrations/kodi/kodi.classes.integration.d.ts +11 -11
  111. package/dist_ts/integrations/kodi/kodi.classes.integration.js +114 -171
  112. package/dist_ts/integrations/kodi/kodi.discovery.js +53 -29
  113. package/dist_ts/integrations/kodi/kodi.mapper.d.ts +7 -2
  114. package/dist_ts/integrations/kodi/kodi.mapper.js +48 -10
  115. package/dist_ts/integrations/kodi/kodi.types.d.ts +32 -28
  116. package/dist_ts/integrations/kodi/kodi.validation.d.ts +9 -0
  117. package/dist_ts/integrations/kodi/kodi.validation.js +62 -0
  118. package/dist_ts/integrations/modbus/modbus.classes.client.d.ts +15 -61
  119. package/dist_ts/integrations/modbus/modbus.classes.client.js +117 -776
  120. package/dist_ts/integrations/modbus/modbus.classes.configflow.d.ts +9 -5
  121. package/dist_ts/integrations/modbus/modbus.classes.configflow.js +70 -38
  122. package/dist_ts/integrations/modbus/modbus.classes.integration.d.ts +6 -17
  123. package/dist_ts/integrations/modbus/modbus.classes.integration.js +133 -57
  124. package/dist_ts/integrations/modbus/modbus.discovery.d.ts +5 -8
  125. package/dist_ts/integrations/modbus/modbus.discovery.js +38 -93
  126. package/dist_ts/integrations/modbus/modbus.mapper.d.ts +8 -32
  127. package/dist_ts/integrations/modbus/modbus.mapper.js +71 -431
  128. package/dist_ts/integrations/modbus/modbus.types.d.ts +82 -332
  129. package/dist_ts/integrations/modbus/modbus.validation.d.ts +10 -0
  130. package/dist_ts/integrations/modbus/modbus.validation.js +128 -0
  131. package/dist_ts/integrations/mqtt/mqtt.classes.client.d.ts +15 -2
  132. package/dist_ts/integrations/mqtt/mqtt.classes.client.js +173 -23
  133. package/dist_ts/integrations/mqtt/mqtt.classes.configflow.d.ts +5 -1
  134. package/dist_ts/integrations/mqtt/mqtt.classes.configflow.js +53 -33
  135. package/dist_ts/integrations/mqtt/mqtt.classes.integration.d.ts +4 -1
  136. package/dist_ts/integrations/mqtt/mqtt.classes.integration.js +127 -56
  137. package/dist_ts/integrations/mqtt/mqtt.classes.session.d.ts +13 -0
  138. package/dist_ts/integrations/mqtt/mqtt.classes.session.js +130 -0
  139. package/dist_ts/integrations/mqtt/mqtt.mapper.d.ts +3 -0
  140. package/dist_ts/integrations/mqtt/mqtt.mapper.js +44 -1
  141. package/dist_ts/integrations/mqtt/mqtt.types.d.ts +46 -0
  142. package/dist_ts/integrations/mqtt/mqtt.validation.d.ts +7 -0
  143. package/dist_ts/integrations/mqtt/mqtt.validation.js +180 -0
  144. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.d.ts +12 -34
  145. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.js +143 -231
  146. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.d.ts +6 -1
  147. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.js +105 -18
  148. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.d.ts +8 -8
  149. package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.js +141 -155
  150. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.d.ts +9 -0
  151. package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.js +35 -0
  152. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.d.ts +6 -18
  153. package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.js +45 -220
  154. package/dist_ts/integrations/nanoleaf/nanoleaf.types.d.ts +33 -147
  155. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.d.ts +6 -0
  156. package/dist_ts/integrations/nanoleaf/nanoleaf.validation.js +82 -0
  157. package/dist_ts/integrations/onkyo/onkyo.classes.client.d.ts +15 -20
  158. package/dist_ts/integrations/onkyo/onkyo.classes.client.js +161 -404
  159. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.d.ts +9 -4
  160. package/dist_ts/integrations/onkyo/onkyo.classes.configflow.js +51 -6
  161. package/dist_ts/integrations/onkyo/onkyo.classes.integration.d.ts +22 -6
  162. package/dist_ts/integrations/onkyo/onkyo.classes.integration.js +144 -11
  163. package/dist_ts/integrations/onkyo/onkyo.discovery.d.ts +6 -1
  164. package/dist_ts/integrations/onkyo/onkyo.discovery.js +40 -4
  165. package/dist_ts/integrations/onkyo/onkyo.mapper.d.ts +7 -8
  166. package/dist_ts/integrations/onkyo/onkyo.mapper.js +45 -12
  167. package/dist_ts/integrations/onkyo/onkyo.types.d.ts +58 -8
  168. package/dist_ts/integrations/onkyo/onkyo.types.js +13 -102
  169. package/dist_ts/integrations/onkyo/onkyo.validation.d.ts +16 -0
  170. package/dist_ts/integrations/onkyo/onkyo.validation.js +77 -0
  171. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.d.ts +25 -25
  172. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.js +116 -360
  173. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.d.ts +11 -3
  174. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.js +76 -6
  175. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.d.ts +26 -4
  176. package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.js +142 -9
  177. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.d.ts +2 -1
  178. package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.js +31 -4
  179. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.d.ts +9 -9
  180. package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.js +31 -149
  181. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.d.ts +51 -11
  182. package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.js +22 -87
  183. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.d.ts +6 -0
  184. package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.js +56 -0
  185. package/dist_ts/integrations/pioneer/pioneer.classes.client.d.ts +31 -16
  186. package/dist_ts/integrations/pioneer/pioneer.classes.client.js +167 -409
  187. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.d.ts +10 -3
  188. package/dist_ts/integrations/pioneer/pioneer.classes.configflow.js +66 -6
  189. package/dist_ts/integrations/pioneer/pioneer.classes.integration.d.ts +13 -5
  190. package/dist_ts/integrations/pioneer/pioneer.classes.integration.js +239 -11
  191. package/dist_ts/integrations/pioneer/pioneer.discovery.d.ts +14 -1
  192. package/dist_ts/integrations/pioneer/pioneer.discovery.js +51 -4
  193. package/dist_ts/integrations/pioneer/pioneer.mapper.d.ts +14 -8
  194. package/dist_ts/integrations/pioneer/pioneer.mapper.js +54 -12
  195. package/dist_ts/integrations/pioneer/pioneer.types.d.ts +79 -12
  196. package/dist_ts/integrations/pioneer/pioneer.types.js +12 -76
  197. package/dist_ts/integrations/pioneer/pioneer.validation.d.ts +10 -0
  198. package/dist_ts/integrations/pioneer/pioneer.validation.js +85 -0
  199. package/dist_ts/integrations/samsungtv/samsungtv.classes.client.d.ts +4 -2
  200. package/dist_ts/integrations/samsungtv/samsungtv.classes.client.js +41 -16
  201. package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.d.ts +5 -0
  202. package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.js +20 -7
  203. package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.d.ts +1 -0
  204. package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.js +53 -50
  205. package/dist_ts/integrations/samsungtv/samsungtv.types.d.ts +4 -1
  206. package/dist_ts/integrations/shelly/shelly.classes.client.d.ts +26 -30
  207. package/dist_ts/integrations/shelly/shelly.classes.client.js +504 -276
  208. package/dist_ts/integrations/shelly/shelly.classes.configflow.d.ts +8 -3
  209. package/dist_ts/integrations/shelly/shelly.classes.configflow.js +43 -38
  210. package/dist_ts/integrations/shelly/shelly.classes.integration.d.ts +6 -6
  211. package/dist_ts/integrations/shelly/shelly.classes.integration.js +103 -101
  212. package/dist_ts/integrations/shelly/shelly.classes.subscription.d.ts +4 -0
  213. package/dist_ts/integrations/shelly/shelly.classes.subscription.js +24 -2
  214. package/dist_ts/integrations/shelly/shelly.mapper.d.ts +1 -2
  215. package/dist_ts/integrations/shelly/shelly.mapper.js +21 -20
  216. package/dist_ts/integrations/shelly/shelly.rpc.js +4 -1
  217. package/dist_ts/integrations/shelly/shelly.types.d.ts +42 -2
  218. package/dist_ts/integrations/shelly/shelly.types.js +11 -2
  219. package/dist_ts/integrations/shelly/shelly.validation.d.ts +7 -0
  220. package/dist_ts/integrations/shelly/shelly.validation.js +79 -0
  221. package/dist_ts/integrations/sonos/sonos.classes.client.d.ts +79 -22
  222. package/dist_ts/integrations/sonos/sonos.classes.client.js +427 -139
  223. package/dist_ts/integrations/sonos/sonos.classes.configflow.d.ts +9 -2
  224. package/dist_ts/integrations/sonos/sonos.classes.configflow.js +26 -28
  225. package/dist_ts/integrations/sonos/sonos.classes.integration.d.ts +7 -6
  226. package/dist_ts/integrations/sonos/sonos.classes.integration.js +289 -224
  227. package/dist_ts/integrations/sonos/sonos.config.d.ts +6 -1
  228. package/dist_ts/integrations/sonos/sonos.config.js +23 -2
  229. package/dist_ts/integrations/sonos/sonos.mapper.d.ts +1 -1
  230. package/dist_ts/integrations/sonos/sonos.mapper.js +14 -9
  231. package/dist_ts/integrations/sonos/sonos.matchers.js +3 -3
  232. package/dist_ts/integrations/sonos/sonos.types.d.ts +26 -1
  233. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.d.ts +13 -36
  234. package/dist_ts/integrations/soundtouch/soundtouch.classes.client.js +179 -502
  235. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.d.ts +6 -1
  236. package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.js +50 -40
  237. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.d.ts +14 -15
  238. package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.js +108 -128
  239. package/dist_ts/integrations/soundtouch/soundtouch.discovery.d.ts +2 -20
  240. package/dist_ts/integrations/soundtouch/soundtouch.discovery.js +23 -113
  241. package/dist_ts/integrations/soundtouch/soundtouch.mapper.d.ts +3 -12
  242. package/dist_ts/integrations/soundtouch/soundtouch.mapper.js +35 -189
  243. package/dist_ts/integrations/soundtouch/soundtouch.types.d.ts +57 -142
  244. package/dist_ts/integrations/soundtouch/soundtouch.types.js +18 -3
  245. package/dist_ts/integrations/soundtouch/soundtouch.validation.d.ts +22 -0
  246. package/dist_ts/integrations/soundtouch/soundtouch.validation.js +65 -0
  247. package/dist_ts/integrations/tasmota/tasmota.classes.integration.d.ts +5 -0
  248. package/dist_ts/integrations/tasmota/tasmota.classes.integration.js +23 -7
  249. package/dist_ts/integrations/tasmota/tasmota.classes.runtime.d.ts +19 -5
  250. package/dist_ts/integrations/tasmota/tasmota.classes.runtime.js +115 -41
  251. package/dist_ts/integrations/tasmota/tasmota.config.js +23 -1
  252. package/dist_ts/integrations/tasmota/tasmota.credentials.d.ts +11 -0
  253. package/dist_ts/integrations/tasmota/tasmota.credentials.js +97 -0
  254. package/dist_ts/integrations/tasmota/tasmota.discovery.d.ts +7 -1
  255. package/dist_ts/integrations/tasmota/tasmota.discovery.js +108 -30
  256. package/dist_ts/integrations/tasmota/tasmota.mapper.d.ts +2 -2
  257. package/dist_ts/integrations/tasmota/tasmota.mapper.js +7 -7
  258. package/dist_ts/integrations/tasmota/tasmota.types.d.ts +41 -2
  259. package/dist_ts/integrations/tuya/tuya.classes.integration.d.ts +4 -1
  260. package/dist_ts/integrations/tuya/tuya.classes.integration.js +79 -110
  261. package/dist_ts/integrations/tuya/tuya.classes.runtime.d.ts +5 -2
  262. package/dist_ts/integrations/tuya/tuya.classes.runtime.js +73 -26
  263. package/dist_ts/integrations/tuya/tuya.mapper.js +2 -2
  264. package/dist_ts/integrations/tuya/tuya.types.d.ts +3 -0
  265. package/dist_ts/integrations/tuya/tuya.validation.d.ts +2 -2
  266. package/dist_ts/integrations/tuya/tuya.validation.js +6 -11
  267. package/dist_ts/integrations/webostv/webostv.classes.client.d.ts +6 -5
  268. package/dist_ts/integrations/webostv/webostv.classes.client.js +147 -76
  269. package/dist_ts/integrations/webostv/webostv.classes.configflow.d.ts +7 -2
  270. package/dist_ts/integrations/webostv/webostv.classes.configflow.js +36 -20
  271. package/dist_ts/integrations/webostv/webostv.classes.integration.d.ts +4 -4
  272. package/dist_ts/integrations/webostv/webostv.classes.integration.js +46 -54
  273. package/dist_ts/integrations/webostv/webostv.mapper.js +5 -5
  274. package/dist_ts/integrations/webostv/webostv.types.d.ts +16 -3
  275. package/dist_ts/integrations/webostv/webostv.types.js +1 -1
  276. package/dist_ts/integrations/webostv/webostv.validation.d.ts +3 -1
  277. package/dist_ts/integrations/webostv/webostv.validation.js +23 -1
  278. package/dist_ts/integrations/wiz/index.d.ts +1 -0
  279. package/dist_ts/integrations/wiz/index.js +2 -1
  280. package/dist_ts/integrations/wiz/wiz.classes.client.d.ts +14 -28
  281. package/dist_ts/integrations/wiz/wiz.classes.client.js +93 -229
  282. package/dist_ts/integrations/wiz/wiz.classes.configflow.d.ts +6 -4
  283. package/dist_ts/integrations/wiz/wiz.classes.configflow.js +38 -67
  284. package/dist_ts/integrations/wiz/wiz.classes.integration.d.ts +10 -21
  285. package/dist_ts/integrations/wiz/wiz.classes.integration.js +204 -44
  286. package/dist_ts/integrations/wiz/wiz.discovery.d.ts +10 -31
  287. package/dist_ts/integrations/wiz/wiz.discovery.js +48 -249
  288. package/dist_ts/integrations/wiz/wiz.mapper.d.ts +7 -51
  289. package/dist_ts/integrations/wiz/wiz.mapper.js +82 -668
  290. package/dist_ts/integrations/wiz/wiz.types.d.ts +34 -208
  291. package/dist_ts/integrations/wiz/wiz.validation.d.ts +8 -0
  292. package/dist_ts/integrations/wiz/wiz.validation.js +72 -0
  293. package/dist_ts/integrations/yamaha/index.d.ts +1 -0
  294. package/dist_ts/integrations/yamaha/index.js +2 -1
  295. package/dist_ts/integrations/yamaha/yamaha.classes.client.d.ts +30 -4
  296. package/dist_ts/integrations/yamaha/yamaha.classes.client.js +162 -6
  297. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.d.ts +10 -3
  298. package/dist_ts/integrations/yamaha/yamaha.classes.configflow.js +106 -6
  299. package/dist_ts/integrations/yamaha/yamaha.classes.integration.d.ts +22 -4
  300. package/dist_ts/integrations/yamaha/yamaha.classes.integration.js +243 -8
  301. package/dist_ts/integrations/yamaha/yamaha.discovery.d.ts +13 -1
  302. package/dist_ts/integrations/yamaha/yamaha.discovery.js +57 -4
  303. package/dist_ts/integrations/yamaha/yamaha.mapper.d.ts +16 -8
  304. package/dist_ts/integrations/yamaha/yamaha.mapper.js +58 -12
  305. package/dist_ts/integrations/yamaha/yamaha.types.d.ts +73 -6
  306. package/dist_ts/integrations/yamaha/yamaha.types.js +8 -94
  307. package/dist_ts/integrations/yamaha/yamaha.validation.d.ts +12 -0
  308. package/dist_ts/integrations/yamaha/yamaha.validation.js +89 -0
  309. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.d.ts +6 -1
  310. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.js +18 -8
  311. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.d.ts +4 -1
  312. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.js +54 -6
  313. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.d.ts +12 -6
  314. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.js +73 -59
  315. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.d.ts +3 -2
  316. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.js +17 -12
  317. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.types.d.ts +31 -2
  318. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.d.ts +9 -1
  319. package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.js +57 -7
  320. package/dist_ts/integrations/zwave_js/zwave_js.classes.client.d.ts +10 -4
  321. package/dist_ts/integrations/zwave_js/zwave_js.classes.client.js +167 -31
  322. package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.d.ts +6 -1
  323. package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.js +57 -35
  324. package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.d.ts +3 -1
  325. package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.js +59 -37
  326. package/dist_ts/integrations/zwave_js/zwave_js.discovery.js +3 -3
  327. package/dist_ts/integrations/zwave_js/zwave_js.mapper.js +41 -24
  328. package/dist_ts/integrations/zwave_js/zwave_js.types.d.ts +25 -3
  329. package/dist_ts/integrations/zwave_js/zwave_js.validation.d.ts +6 -2
  330. package/dist_ts/integrations/zwave_js/zwave_js.validation.js +52 -17
  331. package/dist_ts/plugins.d.ts +2 -1
  332. package/dist_ts/plugins.js +3 -2
  333. package/package.json +9 -9
  334. package/readme.md +539 -8
  335. package/ts/00_commitinfo_data.ts +1 -1
  336. package/ts/core/classes.configstore.ts +84 -27
  337. package/ts/core/classes.nativehomeadmission.ts +96 -0
  338. package/ts/core/classes.nativeintegrationowner.ts +302 -0
  339. package/ts/core/classes.runtimemanager.ts +11 -2
  340. package/ts/core/index.ts +2 -0
  341. package/ts/core/types.ts +18 -0
  342. package/ts/integrations/braviatv/braviatv.classes.client.ts +245 -709
  343. package/ts/integrations/braviatv/braviatv.classes.configflow.ts +47 -76
  344. package/ts/integrations/braviatv/braviatv.classes.integration.ts +167 -147
  345. package/ts/integrations/braviatv/braviatv.discovery.ts +11 -10
  346. package/ts/integrations/braviatv/braviatv.mapper.ts +39 -142
  347. package/ts/integrations/braviatv/braviatv.types.ts +53 -173
  348. package/ts/integrations/braviatv/braviatv.validation.ts +68 -0
  349. package/ts/integrations/broadlink/broadlink.actions.ts +31 -0
  350. package/ts/integrations/broadlink/broadlink.classes.client.ts +170 -144
  351. package/ts/integrations/broadlink/broadlink.classes.configflow.ts +59 -89
  352. package/ts/integrations/broadlink/broadlink.classes.integration.ts +110 -64
  353. package/ts/integrations/broadlink/broadlink.mapper.ts +5 -4
  354. package/ts/integrations/broadlink/broadlink.selected.mapper.ts +46 -0
  355. package/ts/integrations/broadlink/broadlink.types.ts +48 -8
  356. package/ts/integrations/broadlink/broadlink.validation.ts +66 -0
  357. package/ts/integrations/daikin/daikin.classes.client.ts +52 -661
  358. package/ts/integrations/daikin/daikin.classes.configflow.ts +72 -152
  359. package/ts/integrations/daikin/daikin.classes.integration.ts +166 -71
  360. package/ts/integrations/daikin/daikin.discovery.ts +94 -346
  361. package/ts/integrations/daikin/daikin.mapper.ts +83 -166
  362. package/ts/integrations/daikin/daikin.types.ts +27 -48
  363. package/ts/integrations/daikin/daikin.validation.ts +51 -0
  364. package/ts/integrations/daikin/readme.md +83 -0
  365. package/ts/integrations/deconz/deconz.classes.client.ts +348 -252
  366. package/ts/integrations/deconz/deconz.classes.configflow.ts +57 -29
  367. package/ts/integrations/deconz/deconz.classes.integration.ts +69 -37
  368. package/ts/integrations/deconz/deconz.mapper.ts +22 -5
  369. package/ts/integrations/deconz/deconz.types.ts +49 -6
  370. package/ts/integrations/deconz/deconz.validation.ts +177 -4
  371. package/ts/integrations/deconz/readme.md +15 -1
  372. package/ts/integrations/denonavr/denonavr.classes.client.ts +132 -421
  373. package/ts/integrations/denonavr/denonavr.classes.configflow.ts +44 -37
  374. package/ts/integrations/denonavr/denonavr.classes.integration.ts +171 -117
  375. package/ts/integrations/denonavr/denonavr.discovery.ts +67 -220
  376. package/ts/integrations/denonavr/denonavr.mapper.ts +36 -182
  377. package/ts/integrations/denonavr/denonavr.types.ts +48 -111
  378. package/ts/integrations/denonavr/denonavr.validation.ts +87 -0
  379. package/ts/integrations/esphome/esphome.classes.client.ts +179 -94
  380. package/ts/integrations/esphome/esphome.classes.configflow.ts +40 -33
  381. package/ts/integrations/esphome/esphome.classes.integration.ts +63 -36
  382. package/ts/integrations/esphome/esphome.discovery.ts +3 -12
  383. package/ts/integrations/esphome/esphome.mapper.ts +9 -7
  384. package/ts/integrations/esphome/esphome.types.ts +35 -6
  385. package/ts/integrations/esphome/esphome.validation.ts +66 -0
  386. package/ts/integrations/esphome/readme.md +20 -6
  387. package/ts/integrations/homeassistant/homeassistant.classes.client.ts +353 -125
  388. package/ts/integrations/homeassistant/homeassistant.classes.integration.ts +35 -18
  389. package/ts/integrations/homeassistant/homeassistant.classes.runtime.ts +50 -63
  390. package/ts/integrations/homeassistant/homeassistant.types.ts +17 -1
  391. package/ts/integrations/homeassistant/readme.md +22 -6
  392. package/ts/integrations/hue/hue.classes.client.ts +110 -41
  393. package/ts/integrations/hue/hue.classes.configflow.ts +12 -6
  394. package/ts/integrations/hue/hue.classes.integration.ts +92 -26
  395. package/ts/integrations/hue/hue.validation.ts +13 -3
  396. package/ts/integrations/hue/readme.md +3 -3
  397. package/ts/integrations/kodi/kodi.classes.client.ts +72 -352
  398. package/ts/integrations/kodi/kodi.classes.configflow.ts +52 -55
  399. package/ts/integrations/kodi/kodi.classes.integration.ts +97 -178
  400. package/ts/integrations/kodi/kodi.discovery.ts +49 -28
  401. package/ts/integrations/kodi/kodi.mapper.ts +52 -10
  402. package/ts/integrations/kodi/kodi.types.ts +33 -58
  403. package/ts/integrations/kodi/kodi.validation.ts +60 -0
  404. package/ts/integrations/modbus/modbus.classes.client.ts +100 -842
  405. package/ts/integrations/modbus/modbus.classes.configflow.ts +70 -41
  406. package/ts/integrations/modbus/modbus.classes.integration.ts +115 -60
  407. package/ts/integrations/modbus/modbus.discovery.ts +38 -106
  408. package/ts/integrations/modbus/modbus.mapper.ts +64 -467
  409. package/ts/integrations/modbus/modbus.types.ts +64 -381
  410. package/ts/integrations/modbus/modbus.validation.ts +119 -0
  411. package/ts/integrations/mqtt/mqtt.classes.client.ts +136 -19
  412. package/ts/integrations/mqtt/mqtt.classes.configflow.ts +45 -32
  413. package/ts/integrations/mqtt/mqtt.classes.integration.ts +99 -38
  414. package/ts/integrations/mqtt/mqtt.classes.session.ts +118 -0
  415. package/ts/integrations/mqtt/mqtt.mapper.ts +43 -1
  416. package/ts/integrations/mqtt/mqtt.types.ts +52 -0
  417. package/ts/integrations/mqtt/mqtt.validation.ts +157 -0
  418. package/ts/integrations/mqtt/readme.md +12 -3
  419. package/ts/integrations/nanoleaf/nanoleaf.classes.client.ts +115 -249
  420. package/ts/integrations/nanoleaf/nanoleaf.classes.configflow.ts +90 -19
  421. package/ts/integrations/nanoleaf/nanoleaf.classes.integration.ts +130 -181
  422. package/ts/integrations/nanoleaf/nanoleaf.credentials.ts +39 -0
  423. package/ts/integrations/nanoleaf/nanoleaf.mapper.ts +47 -260
  424. package/ts/integrations/nanoleaf/nanoleaf.types.ts +29 -147
  425. package/ts/integrations/nanoleaf/nanoleaf.validation.ts +77 -0
  426. package/ts/integrations/onkyo/onkyo.classes.client.ts +148 -457
  427. package/ts/integrations/onkyo/onkyo.classes.configflow.ts +52 -6
  428. package/ts/integrations/onkyo/onkyo.classes.integration.ts +121 -12
  429. package/ts/integrations/onkyo/onkyo.discovery.ts +41 -3
  430. package/ts/integrations/onkyo/onkyo.mapper.ts +46 -19
  431. package/ts/integrations/onkyo/onkyo.types.ts +55 -110
  432. package/ts/integrations/onkyo/onkyo.validation.ts +85 -0
  433. package/ts/integrations/onkyo/readme.md +126 -0
  434. package/ts/integrations/panasonic_viera/panasonic_viera.classes.client.ts +101 -398
  435. package/ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.ts +69 -5
  436. package/ts/integrations/panasonic_viera/panasonic_viera.classes.integration.ts +141 -9
  437. package/ts/integrations/panasonic_viera/panasonic_viera.discovery.ts +32 -3
  438. package/ts/integrations/panasonic_viera/panasonic_viera.mapper.ts +34 -161
  439. package/ts/integrations/panasonic_viera/panasonic_viera.types.ts +52 -96
  440. package/ts/integrations/panasonic_viera/panasonic_viera.validation.ts +56 -0
  441. package/ts/integrations/pioneer/pioneer.classes.client.ts +132 -428
  442. package/ts/integrations/pioneer/pioneer.classes.configflow.ts +67 -5
  443. package/ts/integrations/pioneer/pioneer.classes.integration.ts +225 -11
  444. package/ts/integrations/pioneer/pioneer.discovery.ts +45 -3
  445. package/ts/integrations/pioneer/pioneer.mapper.ts +57 -13
  446. package/ts/integrations/pioneer/pioneer.types.ts +87 -85
  447. package/ts/integrations/pioneer/pioneer.validation.ts +84 -0
  448. package/ts/integrations/samsungtv/readme.md +11 -5
  449. package/ts/integrations/samsungtv/samsungtv.classes.client.ts +40 -15
  450. package/ts/integrations/samsungtv/samsungtv.classes.configflow.ts +21 -6
  451. package/ts/integrations/samsungtv/samsungtv.classes.integration.ts +53 -38
  452. package/ts/integrations/samsungtv/samsungtv.types.ts +4 -1
  453. package/ts/integrations/shelly/readme.md +22 -7
  454. package/ts/integrations/shelly/shelly.classes.client.ts +386 -269
  455. package/ts/integrations/shelly/shelly.classes.configflow.ts +43 -38
  456. package/ts/integrations/shelly/shelly.classes.integration.ts +92 -111
  457. package/ts/integrations/shelly/shelly.classes.subscription.ts +24 -1
  458. package/ts/integrations/shelly/shelly.mapper.ts +19 -20
  459. package/ts/integrations/shelly/shelly.rpc.ts +2 -0
  460. package/ts/integrations/shelly/shelly.types.ts +50 -2
  461. package/ts/integrations/shelly/shelly.validation.ts +76 -0
  462. package/ts/integrations/sonos/readme.md +117 -38
  463. package/ts/integrations/sonos/sonos.classes.client.ts +392 -123
  464. package/ts/integrations/sonos/sonos.classes.configflow.ts +25 -28
  465. package/ts/integrations/sonos/sonos.classes.integration.ts +211 -186
  466. package/ts/integrations/sonos/sonos.config.ts +24 -2
  467. package/ts/integrations/sonos/sonos.mapper.ts +13 -8
  468. package/ts/integrations/sonos/sonos.matchers.ts +2 -2
  469. package/ts/integrations/sonos/sonos.types.ts +30 -1
  470. package/ts/integrations/soundtouch/soundtouch.classes.client.ts +152 -571
  471. package/ts/integrations/soundtouch/soundtouch.classes.configflow.ts +47 -42
  472. package/ts/integrations/soundtouch/soundtouch.classes.integration.ts +95 -150
  473. package/ts/integrations/soundtouch/soundtouch.discovery.ts +21 -133
  474. package/ts/integrations/soundtouch/soundtouch.mapper.ts +36 -207
  475. package/ts/integrations/soundtouch/soundtouch.types.ts +50 -195
  476. package/ts/integrations/soundtouch/soundtouch.validation.ts +60 -0
  477. package/ts/integrations/tasmota/readme.md +16 -5
  478. package/ts/integrations/tasmota/tasmota.classes.integration.ts +24 -6
  479. package/ts/integrations/tasmota/tasmota.classes.runtime.ts +112 -43
  480. package/ts/integrations/tasmota/tasmota.config.ts +17 -0
  481. package/ts/integrations/tasmota/tasmota.credentials.ts +92 -0
  482. package/ts/integrations/tasmota/tasmota.discovery.ts +87 -24
  483. package/ts/integrations/tasmota/tasmota.mapper.ts +6 -6
  484. package/ts/integrations/tasmota/tasmota.types.ts +43 -2
  485. package/ts/integrations/tuya/readme.md +52 -10
  486. package/ts/integrations/tuya/tuya.classes.integration.ts +69 -98
  487. package/ts/integrations/tuya/tuya.classes.runtime.ts +70 -24
  488. package/ts/integrations/tuya/tuya.mapper.ts +1 -1
  489. package/ts/integrations/tuya/tuya.types.ts +4 -0
  490. package/ts/integrations/tuya/tuya.validation.ts +5 -8
  491. package/ts/integrations/webostv/readme.md +115 -22
  492. package/ts/integrations/webostv/webostv.classes.client.ts +123 -50
  493. package/ts/integrations/webostv/webostv.classes.configflow.ts +35 -15
  494. package/ts/integrations/webostv/webostv.classes.integration.ts +44 -37
  495. package/ts/integrations/webostv/webostv.mapper.ts +4 -4
  496. package/ts/integrations/webostv/webostv.types.ts +18 -3
  497. package/ts/integrations/webostv/webostv.validation.ts +22 -1
  498. package/ts/integrations/wiz/index.ts +1 -0
  499. package/ts/integrations/wiz/wiz.classes.client.ts +84 -254
  500. package/ts/integrations/wiz/wiz.classes.configflow.ts +39 -70
  501. package/ts/integrations/wiz/wiz.classes.integration.ts +189 -45
  502. package/ts/integrations/wiz/wiz.discovery.ts +48 -291
  503. package/ts/integrations/wiz/wiz.mapper.ts +81 -754
  504. package/ts/integrations/wiz/wiz.types.ts +24 -224
  505. package/ts/integrations/wiz/wiz.validation.ts +73 -0
  506. package/ts/integrations/yamaha/index.ts +1 -0
  507. package/ts/integrations/yamaha/yamaha.classes.client.ts +150 -6
  508. package/ts/integrations/yamaha/yamaha.classes.configflow.ts +100 -5
  509. package/ts/integrations/yamaha/yamaha.classes.integration.ts +236 -8
  510. package/ts/integrations/yamaha/yamaha.discovery.ts +52 -3
  511. package/ts/integrations/yamaha/yamaha.mapper.ts +65 -13
  512. package/ts/integrations/yamaha/yamaha.types.ts +77 -97
  513. package/ts/integrations/yamaha/yamaha.validation.ts +86 -0
  514. package/ts/integrations/zigbee2mqtt/readme.md +22 -27
  515. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.ts +16 -7
  516. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.ts +53 -6
  517. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.ts +62 -49
  518. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.ts +17 -11
  519. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.types.ts +35 -2
  520. package/ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.ts +53 -7
  521. package/ts/integrations/zwave_js/readme.md +42 -9
  522. package/ts/integrations/zwave_js/zwave_js.classes.client.ts +155 -31
  523. package/ts/integrations/zwave_js/zwave_js.classes.configflow.ts +55 -31
  524. package/ts/integrations/zwave_js/zwave_js.classes.integration.ts +57 -35
  525. package/ts/integrations/zwave_js/zwave_js.discovery.ts +2 -2
  526. package/ts/integrations/zwave_js/zwave_js.mapper.ts +37 -23
  527. package/ts/integrations/zwave_js/zwave_js.types.ts +26 -3
  528. package/ts/integrations/zwave_js/zwave_js.validation.ts +52 -18
  529. package/ts/plugins.ts +2 -1
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@smarthome.exchange/integrations',
6
- version: '1.0.0',
6
+ version: '3.0.0',
7
7
  description: 'TypeScript-native device integrations for smarthome.exchange.'
8
8
  }
@@ -28,6 +28,12 @@ export interface IConfigStoreInitializationOptions {
28
28
  signal?: AbortSignal;
29
29
  }
30
30
 
31
+ interface IConfigStoreOperationState {
32
+ keyLookups: Set<Promise<void>>;
33
+ settled: boolean;
34
+ providerFailed: boolean;
35
+ }
36
+
31
37
  const validateName = (valueArg: string, nameArg: string, maxLengthArg: number): string => {
32
38
  if (typeof valueArg !== 'string' || valueArg.length === 0 || valueArg.length > maxLengthArg || /[\p{Cc}\p{Cs}]/u.test(valueArg)) {
33
39
  throw new TypeError(`${nameArg} must be a non-empty string without control characters, at most ${maxLengthArg} characters.`);
@@ -102,9 +108,13 @@ export class NoSqlConfigStore implements IConfigStore {
102
108
  readonly #maxValueBytes: number;
103
109
  readonly #lifetime = new AbortController();
104
110
  readonly #preparations = new Set<Promise<void>>();
111
+ readonly #keyLookups = new Set<Promise<void>>();
112
+ readonly #retainedOperations = new Set<IConfigStoreOperationState>();
113
+ readonly #keyProviderFailures = new Set<Error>();
105
114
  #operationTail: Promise<void> = Promise.resolve();
106
115
  #pendingOperations = 0;
107
116
  #closePromise?: Promise<void>;
117
+ #retryPromise?: Promise<void>;
108
118
 
109
119
  constructor(optionsArg: INoSqlConfigStoreOptions) {
110
120
  this.#homeId = validateName(optionsArg.homeId, 'homeId', 256);
@@ -156,19 +166,34 @@ export class NoSqlConfigStore implements IConfigStore {
156
166
  await Promise.allSettled([
157
167
  ...this.#preparations,
158
168
  this.#operationTail,
169
+ ...this.#keyLookups,
159
170
  ]);
171
+ if (this.#keyProviderFailures.size) {
172
+ throw new AggregateError([...this.#keyProviderFailures], 'NoSqlConfigStore key-provider cleanup failed.');
173
+ }
160
174
  });
161
175
  this.#lifetime.abort(new Error('NoSqlConfigStore is closed.'));
162
176
  }
163
177
  return this.#closePromise;
164
178
  }
165
179
 
180
+ /** Acknowledges settled provider cleanup failures without repeating callbacks or reopening the store. */
181
+ public retryCleanup(): Promise<void> {
182
+ if (!this.#closePromise) return Promise.reject(new Error('NoSqlConfigStore cleanup requires terminal close.'));
183
+ this.#retryPromise ??= Promise.resolve().then(async () => {
184
+ await this.#closePromise!.catch(() => undefined);
185
+ this.#keyProviderFailures.clear();
186
+ this.#retainedOperations.clear();
187
+ });
188
+ return this.#retryPromise;
189
+ }
190
+
166
191
  public get<TValue>(keyArg: string, optionsArg: IConfigStoreOperationOptions = {}): Promise<TValue | undefined> {
167
192
  const key = validateName(keyArg, 'config key', 1024);
168
193
  const databaseOptions = this.#databaseOptions(optionsArg);
169
- return this.#runOperation(async () => {
194
+ return this.#runOperation(async (operationArg) => {
170
195
  const record = await this.#records.getInstance(this.#selector(key), databaseOptions);
171
- return record ? this.#decrypt<TValue>(key, record.encryptedValue) : undefined;
196
+ return record ? this.#decrypt<TValue>(key, record.encryptedValue, operationArg) : undefined;
172
197
  });
173
198
  }
174
199
 
@@ -176,8 +201,9 @@ export class NoSqlConfigStore implements IConfigStore {
176
201
  const key = validateName(keyArg, 'config key', 1024);
177
202
  const serialized = this.#serialize(valueArg);
178
203
  const databaseOptions = this.#databaseOptions(optionsArg);
179
- return this.#runOperation(async () => {
180
- const encryptedValue = await this.#encrypt(key, serialized);
204
+ return this.#runOperation(async (operationArg) => {
205
+ const encryptedValue = await this.#encrypt(key, serialized, operationArg);
206
+ this.#assertOpen();
181
207
  await this.#records.atomicUpdate({ id: this.#selector(key).id }, {
182
208
  $set: { homeId: this.#homeId, integrationDomain: this.#integrationDomain, key, encryptedValue },
183
209
  }, { upsert: true, ...databaseOptions });
@@ -189,10 +215,11 @@ export class NoSqlConfigStore implements IConfigStore {
189
215
  const key = validateName(keyArg, 'config key', 1024);
190
216
  const serialized = this.#serialize(valueArg);
191
217
  const databaseOptions = this.#databaseOptions(optionsArg);
192
- return this.#runOperation(async () => {
218
+ return this.#runOperation(async (operationArg) => {
193
219
  const record = Object.assign(new this.#records(), this.#selector(key), {
194
- encryptedValue: await this.#encrypt(key, serialized),
220
+ encryptedValue: await this.#encrypt(key, serialized, operationArg),
195
221
  });
222
+ this.#assertOpen();
196
223
  await this.#records.insert(record, databaseOptions);
197
224
  });
198
225
  }
@@ -253,8 +280,9 @@ export class NoSqlConfigStore implements IConfigStore {
253
280
  return serialized;
254
281
  }
255
282
 
256
- async #encrypt(keyArg: string, serializedArg: string): Promise<IEncryptedConfigValue> {
257
- const encryptionKey = await this.#lookupKey((signalArg) => this.#keyProvider.currentKey(signalArg));
283
+ async #encrypt(keyArg: string, serializedArg: string, operationArg: IConfigStoreOperationState): Promise<IEncryptedConfigValue> {
284
+ const encryptionKey = await this.#lookupKey((signalArg) => this.#keyProvider.currentKey(signalArg), operationArg);
285
+ this.#assertOpen();
258
286
  const keyId = validateName(encryptionKey.id, 'encryption key ID', 128);
259
287
  const key = copyKey(encryptionKey.key);
260
288
  try {
@@ -268,13 +296,15 @@ export class NoSqlConfigStore implements IConfigStore {
268
296
  }
269
297
  }
270
298
 
271
- async #decrypt<TValue>(keyArg: string, valueArg: IEncryptedConfigValue): Promise<TValue> {
299
+ async #decrypt<TValue>(keyArg: string, valueArg: IEncryptedConfigValue, operationArg: IConfigStoreOperationState): Promise<TValue> {
272
300
  if (!valueArg || valueArg.version !== 1) throw new Error('Stored config encryption envelope is invalid.');
273
301
  const keyId = validateName(valueArg.keyId, 'stored encryption key ID', 128);
274
302
  const nonce = decodeBase64(valueArg.nonce, 12, 12);
275
303
  const tag = decodeBase64(valueArg.tag, 16, 16);
276
304
  const ciphertext = decodeBase64(valueArg.ciphertext, this.#maxValueBytes);
277
- const key = copyKey(await this.#lookupKey((signalArg) => this.#keyProvider.keyForId(keyId, signalArg)));
305
+ const decryptionKey = await this.#lookupKey((signalArg) => this.#keyProvider.keyForId(keyId, signalArg), operationArg);
306
+ this.#assertOpen();
307
+ const key = copyKey(decryptionKey);
278
308
  try {
279
309
  const decipher = plugins.crypto.createDecipheriv('aes-256-gcm', key, nonce);
280
310
  decipher.setAAD(this.#aad(keyArg, keyId));
@@ -294,24 +324,46 @@ export class NoSqlConfigStore implements IConfigStore {
294
324
  }
295
325
  }
296
326
 
297
- async #lookupKey<TResult>(lookupArg: (signalArg: AbortSignal) => Promise<TResult>): Promise<TResult> {
327
+ async #lookupKey<TResult>(lookupArg: (signalArg: AbortSignal) => Promise<TResult>, operationArg: IConfigStoreOperationState): Promise<TResult> {
298
328
  this.#assertOpen();
299
329
  const controller = new AbortController();
300
- let timer: ReturnType<typeof setTimeout> | undefined;
301
- const deadline = new Promise<never>((resolveArg, rejectArg) => {
330
+ const signal = AbortSignal.any([controller.signal, this.#lifetime.signal]);
331
+ let resolveProvider!: (valueArg: TResult | PromiseLike<TResult>) => void;
332
+ let rejectProvider!: (errorArg: unknown) => void;
333
+ const provider = new Promise<TResult>((resolveArg, rejectArg) => {
334
+ resolveProvider = resolveArg;
335
+ rejectProvider = rejectArg;
336
+ });
337
+ let abortLookup!: () => void;
338
+ const settlement = provider.then(() => undefined, (errorArg: unknown) => {
339
+ if (signal.aborted && errorArg !== signal.reason) {
340
+ operationArg.providerFailed = true;
341
+ this.#keyProviderFailures.add(new Error('Config encryption key provider rejected after cancellation.'));
342
+ }
343
+ }).finally(() => {
344
+ this.#keyLookups.delete(settlement);
345
+ operationArg.keyLookups.delete(settlement);
346
+ if (operationArg.settled && !operationArg.keyLookups.size && !operationArg.providerFailed) {
347
+ this.#retainedOperations.delete(operationArg);
348
+ }
349
+ signal.removeEventListener('abort', abortLookup);
350
+ });
351
+ // Own the actual callback promise before invoking foreign code, including synchronous close reentry.
352
+ this.#keyLookups.add(settlement);
353
+ operationArg.keyLookups.add(settlement);
354
+ const cancellation = new Promise<never>((resolveArg, rejectArg) => {
302
355
  void resolveArg;
303
- timer = setTimeout(() => {
304
- const error = new Error('Config encryption key lookup timed out.');
305
- controller.abort(error);
306
- rejectArg(error);
307
- }, this.#timeoutMs);
308
- timer.unref();
356
+ abortLookup = () => {
357
+ rejectArg(signal.reason);
358
+ };
359
+ signal.addEventListener('abort', abortLookup, { once: true });
309
360
  });
361
+ const timer = setTimeout(() => controller.abort(new Error('Config encryption key lookup timed out.')), this.#timeoutMs);
362
+ timer.unref();
310
363
  try {
311
- const result = await Promise.race([
312
- lookupArg(AbortSignal.any([controller.signal, this.#lifetime.signal])),
313
- deadline,
314
- ]);
364
+ try { resolveProvider(lookupArg(signal)); } catch (errorArg) { rejectProvider(errorArg); }
365
+ const result = await Promise.race([provider, cancellation]);
366
+ signal.throwIfAborted();
315
367
  this.#assertOpen();
316
368
  return result;
317
369
  } finally {
@@ -319,18 +371,23 @@ export class NoSqlConfigStore implements IConfigStore {
319
371
  }
320
372
  }
321
373
 
322
- #runOperation<TResult>(operationArg: () => Promise<TResult>): Promise<TResult> {
374
+ #runOperation<TResult>(operationArg: (stateArg: IConfigStoreOperationState) => Promise<TResult>): Promise<TResult> {
323
375
  if (this.#lifetime.signal.aborted) {
324
376
  return Promise.reject(this.#lifetime.signal.reason);
325
377
  }
326
- if (this.#pendingOperations >= this.#maxPendingOperations) {
378
+ if (this.#pendingOperations + this.#retainedOperations.size >= this.#maxPendingOperations) {
327
379
  return Promise.reject(new Error(`NoSqlConfigStore operation capacity reached: ${this.#maxPendingOperations}.`));
328
380
  }
329
381
  this.#pendingOperations += 1;
382
+ const state: IConfigStoreOperationState = { keyLookups: new Set(), settled: false, providerFailed: false };
330
383
  const operation = this.#operationTail.then(() => {
331
384
  this.#assertOpen();
332
- return operationArg();
333
- }).finally(() => { this.#pendingOperations -= 1; });
385
+ return operationArg(state);
386
+ }).finally(() => {
387
+ state.settled = true;
388
+ this.#pendingOperations -= 1;
389
+ if (state.keyLookups.size || state.providerFailed) this.#retainedOperations.add(state);
390
+ });
334
391
  this.#operationTail = operation.then(() => undefined, () => undefined);
335
392
  return operation;
336
393
  }
@@ -0,0 +1,96 @@
1
+ import { NoSqlConfigStore } from './classes.configstore.js';
2
+ import type { IConfigFlowContext, IIntegrationSetupContext, ILogger } from './types.js';
3
+
4
+ export interface INativeHomeAdmissionOptions {
5
+ readonly homeId?: string;
6
+ }
7
+
8
+ /** A captured host selection, not an authentication credential. Persistence resources are borrowed. */
9
+ export interface INativeAdmissionContext {
10
+ readonly homeId: string;
11
+ readonly configStore?: NoSqlConfigStore;
12
+ readonly logger?: ILogger;
13
+ readonly abortSignal?: AbortSignal;
14
+ }
15
+
16
+ const identifier = (valueArg: unknown, nameArg: string, maximumArg: number): string => {
17
+ if (typeof valueArg !== 'string' || !valueArg || valueArg.length > maximumArg
18
+ || valueArg.trim() !== valueArg || /[\p{Cc}\p{Cs}]/u.test(valueArg)) {
19
+ throw new TypeError(`${nameArg} must be a non-empty identifier without surrounding whitespace or control characters, at most ${maximumArg} characters.`);
20
+ }
21
+ return valueArg;
22
+ };
23
+
24
+ const dataFields = (valueArg: unknown, allowedArg: readonly string[], nameArg: string): Record<string, unknown> => {
25
+ if (!valueArg || typeof valueArg !== 'object'
26
+ || (Object.getPrototypeOf(valueArg) !== Object.prototype && Object.getPrototypeOf(valueArg) !== null)) {
27
+ throw new TypeError(`${nameArg} must be a plain data object.`);
28
+ }
29
+ const captured: Record<string, unknown> = Object.create(null) as Record<string, unknown>;
30
+ for (const key of Reflect.ownKeys(valueArg)) {
31
+ const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
32
+ if (typeof key !== 'string' || !allowedArg.includes(key) || !descriptor
33
+ || !descriptor.enumerable || !('value' in descriptor)) {
34
+ throw new TypeError(`${nameArg} contains an unsupported field or accessor.`);
35
+ }
36
+ captured[key] = descriptor.value as unknown;
37
+ }
38
+ return captured;
39
+ };
40
+
41
+ const logger = (valueArg: unknown): ILogger | undefined => {
42
+ if (valueArg === undefined) return undefined;
43
+ if (!valueArg || typeof valueArg !== 'object') throw new TypeError('Native logger must expose a log method.');
44
+ let target: object | null = valueArg;
45
+ for (let depth = 0; target && depth < 16; depth += 1) {
46
+ const descriptor = Object.getOwnPropertyDescriptor(target, 'log');
47
+ if (descriptor) {
48
+ if ('value' in descriptor && typeof descriptor.value === 'function') return valueArg as ILogger;
49
+ break;
50
+ }
51
+ target = Object.getPrototypeOf(target) as object | null;
52
+ }
53
+ throw new TypeError('Native logger must expose a data-method log function.');
54
+ };
55
+
56
+ /** Binds to independently selected host context only after all synchronous checks pass. */
57
+ export class NativeHomeAdmission {
58
+ readonly #domain: string;
59
+ #homeId?: string;
60
+
61
+ constructor(domainArg: string, optionsArg: INativeHomeAdmissionOptions = {}) {
62
+ this.#domain = identifier(domainArg, 'integrationDomain', 128);
63
+ const options = dataFields(optionsArg, ['homeId'], 'Native home options');
64
+ if (options.homeId !== undefined) this.#homeId = identifier(options.homeId, 'homeId', 256);
65
+ }
66
+
67
+ public get homeId(): string | undefined { return this.#homeId; }
68
+
69
+ public admit(contextArg: IIntegrationSetupContext | IConfigFlowContext, configHomeIdArg?: string): INativeAdmissionContext {
70
+ const context = dataFields(contextArg, ['homeId', 'configStore', 'logger', 'abortSignal'], 'Native context');
71
+ const homeId = identifier(context.homeId, 'Host-selected homeId', 256);
72
+ if (this.#homeId !== undefined && this.#homeId !== homeId) throw new Error('Native owning home does not match the selected host home.');
73
+ if (configHomeIdArg !== undefined && identifier(configHomeIdArg, 'Configuration homeId', 256) !== homeId) {
74
+ throw new Error('Native configuration home does not match the selected host home.');
75
+ }
76
+ const configStore = context.configStore;
77
+ if (configStore !== undefined) {
78
+ if (!(configStore instanceof NoSqlConfigStore)) throw new TypeError('Native configStore must be an actual NoSqlConfigStore.');
79
+ // Call the owning implementation: a structural or overridden getScope cannot grant admission.
80
+ const scope = NoSqlConfigStore.prototype.getScope.call(configStore);
81
+ if (scope.homeId !== homeId || scope.integrationDomain !== this.#domain) {
82
+ throw new Error('Native configStore home/domain does not match the admitted owner.');
83
+ }
84
+ }
85
+ const capturedLogger = logger(context.logger);
86
+ const abortSignal = context.abortSignal;
87
+ if (abortSignal !== undefined) {
88
+ if (!(abortSignal instanceof AbortSignal)) throw new TypeError('Native abortSignal must be an AbortSignal.');
89
+ AbortSignal.prototype.throwIfAborted.call(abortSignal);
90
+ }
91
+ const captured = Object.freeze({ homeId, configStore: configStore as NoSqlConfigStore | undefined,
92
+ logger: capturedLogger, abortSignal: abortSignal as AbortSignal | undefined });
93
+ this.#homeId = homeId;
94
+ return captured;
95
+ }
96
+ }
@@ -0,0 +1,302 @@
1
+ import { NativeHomeAdmission, type INativeAdmissionContext, type INativeHomeAdmissionOptions } from './classes.nativehomeadmission.js';
2
+ import type { IConfigFlowContext, IIntegrationSetupContext } from './types.js';
3
+
4
+ export interface INativeIntegrationOwnerOptions extends INativeHomeAdmissionOptions {
5
+ /** Shared limit for pending operations, runtimes, and retained cleanup resources. Defaults to 16. */
6
+ readonly capacity?: number;
7
+ }
8
+
9
+ export interface INativeOperationLease {
10
+ readonly context: INativeAdmissionContext;
11
+ readonly signal: AbortSignal;
12
+ assertOpen(): void;
13
+ /** Register immediately after allocation, before invoking initialization or other foreign callbacks. */
14
+ own(cleanupArg: () => Promise<void>): void;
15
+ close(): Promise<void>;
16
+ }
17
+
18
+ export interface INativeSetupLease extends INativeOperationLease {
19
+ /** Transfers this operation's cleanup ownership to the returned runtime. */
20
+ promoteToRuntime(): void;
21
+ }
22
+
23
+ interface INativeResource {
24
+ readonly cleanup: () => Promise<void>;
25
+ status: 'idle' | 'pending' | 'closed' | 'failed';
26
+ promise?: Promise<void>;
27
+ }
28
+
29
+ interface INativeScopeState {
30
+ readonly lifetime: AbortController;
31
+ readonly leases: Set<NativeIntegrationLease>;
32
+ readonly operations: Set<Promise<unknown>>;
33
+ closed: boolean;
34
+ closePromise?: Promise<void>;
35
+ retryPromise?: Promise<void>;
36
+ }
37
+
38
+ const scopeState = (): INativeScopeState => ({ lifetime: new AbortController(), leases: new Set(), operations: new Set(), closed: false });
39
+
40
+ class NativeIntegrationLease implements INativeSetupLease {
41
+ readonly #state: NativeOwnerState;
42
+ readonly #scope: INativeScopeState;
43
+ readonly #runtimeAllowed: boolean;
44
+ readonly #lifetime = new AbortController();
45
+ readonly #resources = new Set<INativeResource>();
46
+ readonly context: INativeAdmissionContext;
47
+ readonly signal: AbortSignal;
48
+ #pending = true;
49
+ #promoted = false;
50
+ #closed = false;
51
+ #closePromise?: Promise<void>;
52
+
53
+ constructor(stateArg: NativeOwnerState, scopeArg: INativeScopeState, contextArg: INativeAdmissionContext, runtimeAllowedArg: boolean) {
54
+ this.#state = stateArg;
55
+ this.#scope = scopeArg;
56
+ this.#runtimeAllowed = runtimeAllowedArg;
57
+ this.context = contextArg;
58
+ this.signal = AbortSignal.any([stateArg.lifetime.signal, scopeArg.lifetime.signal, this.#lifetime.signal,
59
+ ...(contextArg.abortSignal ? [contextArg.abortSignal] : [])]);
60
+ // Abort owns cleanup as well as cancellation; rejected cleanup remains on this retained lease.
61
+ this.signal.addEventListener('abort', () => { void this.close().catch(() => undefined); }, { once: true });
62
+ }
63
+
64
+ public get promoted(): boolean { return this.#promoted; }
65
+ public get weight(): number { return Math.max(1, [...this.#resources].filter((resource) => resource.status !== 'closed').length); }
66
+ public get failed(): boolean { return [...this.#resources].some((resource) => resource.status === 'failed'); }
67
+
68
+ public assertOpen(): void {
69
+ if (this.#closed || this.#state.closed || this.#scope.closed) throw new Error('Native admission lease is closed.');
70
+ this.signal.throwIfAborted();
71
+ }
72
+
73
+ public own(cleanupArg: () => Promise<void>): void {
74
+ this.assertOpen();
75
+ if (typeof cleanupArg !== 'function') throw new TypeError('Native cleanup must be a function.');
76
+ if ([...this.#resources].filter((resource) => resource.status !== 'closed').length > 0) this.#state.assertCapacity();
77
+ this.#resources.add({ cleanup: cleanupArg, status: 'idle' });
78
+ }
79
+
80
+ public promoteToRuntime(): void {
81
+ this.assertOpen();
82
+ if (!this.#runtimeAllowed || !this.#pending) throw new Error('Only an admitted pending setup can become a runtime.');
83
+ if (!this.#resources.size) throw new Error('A native runtime must retain an owned cleanup handle.');
84
+ this.#promoted = true;
85
+ }
86
+
87
+ public finish(): void { this.#pending = false; this.#releaseIfClosed(); }
88
+
89
+ public close(): Promise<void> {
90
+ if (!this.#closePromise) {
91
+ this.#closed = true;
92
+ this.#closePromise = Promise.resolve().then(() => this.#drain(false));
93
+ this.#lifetime.abort(new Error('Native admission lease is closed.'));
94
+ }
95
+ return this.#closePromise;
96
+ }
97
+
98
+ public retryCleanup(): Promise<void> { return this.#drain(true); }
99
+
100
+ async #drain(retryArg: boolean): Promise<void> {
101
+ const results = await Promise.allSettled([...this.#resources].map((resource) => {
102
+ if (retryArg && resource.status === 'failed') resource.promise = undefined;
103
+ if (!resource.promise) {
104
+ resource.status = 'pending';
105
+ resource.promise = Promise.resolve().then(() => resource.cleanup()).then(() => {
106
+ resource.status = 'closed';
107
+ }, (errorArg: unknown) => {
108
+ resource.status = 'failed';
109
+ throw errorArg;
110
+ });
111
+ }
112
+ return resource.promise;
113
+ }));
114
+ this.#releaseIfClosed();
115
+ const failures = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected').map((result) => result.reason as unknown);
116
+ if (failures.length) throw new AggregateError(failures, 'Native resource cleanup failed; handles remain owned for explicit retry.');
117
+ }
118
+
119
+ #releaseIfClosed(): void {
120
+ if (!this.#pending && this.#closed && [...this.#resources].every((resource) => resource.status === 'closed')) {
121
+ this.#state.release(this, this.#scope);
122
+ }
123
+ }
124
+ }
125
+
126
+ class NativeOwnerState {
127
+ readonly admission: NativeHomeAdmission;
128
+ readonly lifetime = new AbortController();
129
+ readonly root = scopeState();
130
+ readonly #capacity: number;
131
+ readonly #leases = new Set<NativeIntegrationLease>();
132
+ readonly #scopes = new Set<INativeScopeState>([this.root]);
133
+ closed = false;
134
+ #closePromise?: Promise<void>;
135
+ #retryPromise?: Promise<void>;
136
+
137
+ constructor(domainArg: string, optionsArg: INativeIntegrationOwnerOptions) {
138
+ if (!optionsArg || (Object.getPrototypeOf(optionsArg) !== Object.prototype && Object.getPrototypeOf(optionsArg) !== null)) {
139
+ throw new TypeError('Native owner options must be a plain data object.');
140
+ }
141
+ for (const key of Reflect.ownKeys(optionsArg)) {
142
+ const descriptor = Object.getOwnPropertyDescriptor(optionsArg, key);
143
+ if ((key !== 'homeId' && key !== 'capacity') || !descriptor || !descriptor.enumerable || !('value' in descriptor)) {
144
+ throw new TypeError('Native owner options contain an unsupported field or accessor.');
145
+ }
146
+ }
147
+ this.#capacity = optionsArg.capacity ?? 16;
148
+ if (!Number.isSafeInteger(this.#capacity) || this.#capacity < 1) throw new RangeError('Native owner capacity must be a positive safe integer.');
149
+ this.admission = new NativeHomeAdmission(domainArg, { homeId: optionsArg.homeId });
150
+ }
151
+
152
+ public assertOpen(scopeArg: INativeScopeState): void {
153
+ if (this.closed || scopeArg.closed) throw new Error('Native integration owner/scope is closed.');
154
+ }
155
+
156
+ public assertCapacity(): void {
157
+ if ([...this.#leases].reduce((count, lease) => count + lease.weight, 0) >= this.#capacity) {
158
+ throw new Error(`Native integration ownership capacity reached: ${this.#capacity}.`);
159
+ }
160
+ }
161
+
162
+ public createScope(): INativeScopeState {
163
+ this.assertOpen(this.root);
164
+ if (this.#scopes.size > this.#capacity) throw new Error('Native config-flow scope capacity reached.');
165
+ const scope = scopeState();
166
+ this.#scopes.add(scope);
167
+ return scope;
168
+ }
169
+
170
+ public admit(scopeArg: INativeScopeState, contextArg: IIntegrationSetupContext | IConfigFlowContext, configHomeIdArg?: string): INativeAdmissionContext {
171
+ this.assertOpen(scopeArg);
172
+ return this.admission.admit(contextArg, configHomeIdArg);
173
+ }
174
+
175
+ public run<TResult>(scopeArg: INativeScopeState, contextArg: IIntegrationSetupContext | IConfigFlowContext,
176
+ configHomeIdArg: string | undefined, workArg: (leaseArg: INativeSetupLease) => Promise<TResult>, runtimeArg: boolean): Promise<TResult> {
177
+ this.assertOpen(scopeArg);
178
+ if (typeof workArg !== 'function') throw new TypeError('Native operation must be a function.');
179
+ if (runtimeArg && typeof configHomeIdArg !== 'string') throw new TypeError('Native setup requires configuration homeId.');
180
+ this.assertCapacity();
181
+ const context = this.admission.admit(contextArg, configHomeIdArg);
182
+ const lease = new NativeIntegrationLease(this, scopeArg, context, runtimeArg);
183
+ this.#leases.add(lease);
184
+ scopeArg.leases.add(lease);
185
+ // Publish ownership and work before invoking any caller callback.
186
+ const operation = Promise.resolve().then(async () => {
187
+ try {
188
+ lease.assertOpen();
189
+ const result = await workArg(lease);
190
+ if (runtimeArg) {
191
+ // Returning a live runtime is a new admission; a completed flow effect is already accepted.
192
+ lease.assertOpen();
193
+ if (!lease.promoted) throw new Error('Native setup must transfer its cleanup lease to the returned runtime.');
194
+ }
195
+ if (!runtimeArg) await lease.close();
196
+ return result;
197
+ } catch (errorArg) {
198
+ try { await lease.close(); }
199
+ catch (cleanupErrorArg) { throw new AggregateError([errorArg, cleanupErrorArg], 'Native operation and cleanup failed.'); }
200
+ throw errorArg;
201
+ } finally { lease.finish(); }
202
+ });
203
+ scopeArg.operations.add(operation);
204
+ void operation.then(() => scopeArg.operations.delete(operation), () => scopeArg.operations.delete(operation));
205
+ return operation;
206
+ }
207
+
208
+ public release(leaseArg: NativeIntegrationLease, scopeArg: INativeScopeState): void {
209
+ this.#leases.delete(leaseArg);
210
+ scopeArg.leases.delete(leaseArg);
211
+ }
212
+
213
+ public closeScope(scopeArg: INativeScopeState): Promise<void> {
214
+ if (!scopeArg.closePromise) {
215
+ scopeArg.closed = true;
216
+ scopeArg.closePromise = Promise.resolve().then(async () => {
217
+ await this.#drain([scopeArg]);
218
+ this.#scopes.delete(scopeArg);
219
+ });
220
+ for (const lease of scopeArg.leases) void lease.close().catch(() => undefined);
221
+ scopeArg.lifetime.abort(new Error('Native config-flow scope is closed.'));
222
+ }
223
+ return scopeArg.closePromise;
224
+ }
225
+
226
+ public close(): Promise<void> {
227
+ if (!this.#closePromise) {
228
+ this.closed = true;
229
+ for (const scope of this.#scopes) scope.closed = true;
230
+ this.#closePromise = Promise.resolve().then(() => this.#drain([...this.#scopes]));
231
+ for (const lease of this.#leases) void lease.close().catch(() => undefined);
232
+ this.lifetime.abort(new Error('Native integration owner is closed.'));
233
+ for (const scope of this.#scopes) scope.lifetime.abort(this.lifetime.signal.reason);
234
+ }
235
+ return this.#closePromise;
236
+ }
237
+
238
+ public retryCleanup(scopeArg?: INativeScopeState): Promise<void> {
239
+ const existing = scopeArg ? scopeArg.retryPromise : this.#retryPromise;
240
+ if (existing) return existing;
241
+ const retry = Promise.resolve().then(async () => {
242
+ const leases = [...(scopeArg ? scopeArg.leases : this.#leases)].filter((lease) => lease.failed);
243
+ const results = await Promise.allSettled(leases.map((lease) => lease.retryCleanup()));
244
+ const errors = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected').map((result) => result.reason as unknown);
245
+ if (errors.length) throw new AggregateError(errors, 'Explicit native cleanup retry failed.');
246
+ for (const scope of this.#scopes) {
247
+ if (scope !== this.root && scope.closed && !scope.leases.size && !scope.operations.size) this.#scopes.delete(scope);
248
+ }
249
+ });
250
+ if (scopeArg) scopeArg.retryPromise = retry;
251
+ else this.#retryPromise = retry;
252
+ const settled = (): void => { if (scopeArg) scopeArg.retryPromise = undefined; else this.#retryPromise = undefined; };
253
+ void retry.then(settled, settled);
254
+ return retry;
255
+ }
256
+
257
+ async #drain(scopesArg: INativeScopeState[]): Promise<void> {
258
+ const cleanup = scopesArg.flatMap((scope) => [...scope.leases].map((lease) => lease.close()));
259
+ const operations = scopesArg.flatMap((scope) => [...scope.operations]);
260
+ const [results] = await Promise.all([Promise.allSettled(cleanup), Promise.allSettled(operations)]);
261
+ // Operation failures are delivered to their callers; owner close owns joining and cleanup failures.
262
+ const finalResults = await Promise.allSettled(scopesArg.flatMap((scope) => [...scope.leases].map((lease) => lease.close())));
263
+ const errors = [...results, ...finalResults].filter((result): result is PromiseRejectedResult => result.status === 'rejected').map((result) => result.reason as unknown);
264
+ if (errors.length) throw new AggregateError([...new Set(errors)], 'Native owner cleanup failed; resources remain retained.');
265
+ }
266
+ }
267
+
268
+ /** A terminal child scope shares owner home admission and resource capacity. */
269
+ export class NativeIntegrationScope {
270
+ readonly #state: NativeOwnerState;
271
+ readonly #scope: INativeScopeState;
272
+ constructor(stateArg: NativeOwnerState, scopeArg: INativeScopeState) { this.#state = stateArg; this.#scope = scopeArg; }
273
+ public admit(contextArg: IConfigFlowContext, configHomeIdArg?: string): INativeAdmissionContext {
274
+ return this.#state.admit(this.#scope, contextArg, configHomeIdArg);
275
+ }
276
+ public run<TResult>(contextArg: IConfigFlowContext, configHomeIdArg: string | undefined,
277
+ workArg: (leaseArg: INativeOperationLease) => Promise<TResult>): Promise<TResult> {
278
+ return this.#state.run(this.#scope, contextArg, configHomeIdArg, workArg, false);
279
+ }
280
+ public close(): Promise<void> { return this.#state.closeScope(this.#scope); }
281
+ public retryCleanup(): Promise<void> { return this.#state.retryCleanup(this.#scope); }
282
+ }
283
+
284
+ /** Owns native admission, in-flight work, runtimes and failed cleanup without replaying transport work. */
285
+ export class NativeIntegrationOwner {
286
+ readonly #state: NativeOwnerState;
287
+ constructor(domainArg: string, optionsArg: INativeIntegrationOwnerOptions = {}) { this.#state = new NativeOwnerState(domainArg, optionsArg); }
288
+ public get admission(): NativeHomeAdmission { return this.#state.admission; }
289
+ public get signal(): AbortSignal { return this.#state.lifetime.signal; }
290
+ public runSetup<TResult>(contextArg: IIntegrationSetupContext, configHomeIdArg: string,
291
+ workArg: (leaseArg: INativeSetupLease) => Promise<TResult>): Promise<TResult> {
292
+ return this.#state.run(this.#state.root, contextArg, configHomeIdArg, workArg, true);
293
+ }
294
+ /** Admits finite work against the shared capacity without allocating a child scope or runtime. */
295
+ public runOperation<TResult>(contextArg: IIntegrationSetupContext | IConfigFlowContext, configHomeIdArg: string | undefined,
296
+ workArg: (leaseArg: INativeOperationLease) => Promise<TResult>): Promise<TResult> {
297
+ return this.#state.run(this.#state.root, contextArg, configHomeIdArg, workArg, false);
298
+ }
299
+ public createFlowScope(): NativeIntegrationScope { return new NativeIntegrationScope(this.#state, this.#state.createScope()); }
300
+ public close(): Promise<void> { return this.#state.close(); }
301
+ public retryCleanup(): Promise<void> { return this.#state.retryCleanup(); }
302
+ }
@@ -11,6 +11,7 @@ export class IntegrationRuntimeManager {
11
11
  private readonly runtimes = new Map<string, IIntegrationRuntime>();
12
12
  private readonly runtimesPendingCleanup = new Map<IIntegrationRuntime, string>();
13
13
  private readonly runtimeDestructionPromises = new Map<IIntegrationRuntime, Promise<void>>();
14
+ private readonly runtimeCleanupAttempted = new WeakSet<IIntegrationRuntime>();
14
15
  private readonly destroyedRuntimes = new WeakSet<IIntegrationRuntime>();
15
16
  private readonly setupDomainsInProgress = new Set<string>();
16
17
  private destroyAllPromise?: Promise<void>;
@@ -102,6 +103,10 @@ export class IntegrationRuntimeManager {
102
103
  runtimeArg: IIntegrationRuntime,
103
104
  timeoutMsArg?: number
104
105
  ): Promise<void> {
106
+ return this.closeRuntime(runtimeArg, timeoutMsArg, false);
107
+ }
108
+
109
+ private closeRuntime(runtimeArg: IIntegrationRuntime, timeoutMsArg: number | undefined, retryArg: boolean): Promise<void> {
105
110
  this.retireRuntime(runtimeArg.domain, runtimeArg);
106
111
  if (this.destroyedRuntimes.has(runtimeArg)) {
107
112
  this.runtimesPendingCleanup.delete(runtimeArg);
@@ -112,7 +117,11 @@ export class IntegrationRuntimeManager {
112
117
  }
113
118
  let destructionPromise = this.runtimeDestructionPromises.get(runtimeArg);
114
119
  if (!destructionPromise) {
115
- const destroyAttempt = Promise.resolve().then(() => runtimeArg.destroy());
120
+ const destroyAttempt = Promise.resolve().then(() => {
121
+ const retry = retryArg && this.runtimeCleanupAttempted.has(runtimeArg);
122
+ this.runtimeCleanupAttempted.add(runtimeArg);
123
+ return retry && runtimeArg.retryCleanup ? runtimeArg.retryCleanup() : runtimeArg.destroy();
124
+ });
116
125
  destructionPromise = destroyAttempt
117
126
  .then(() => {
118
127
  this.destroyedRuntimes.add(runtimeArg);
@@ -155,7 +164,7 @@ export class IntegrationRuntimeManager {
155
164
  return;
156
165
  }
157
166
  const results = await Promise.allSettled(
158
- runtimes.map((runtimeArg) => this.destroyRuntime(runtimeArg))
167
+ runtimes.map((runtimeArg) => this.closeRuntime(runtimeArg, undefined, true))
159
168
  );
160
169
  const errors = results
161
170
  .filter((resultArg): resultArg is PromiseRejectedResult => resultArg.status === 'rejected')
package/ts/core/index.ts CHANGED
@@ -9,3 +9,5 @@ export * from './classes.runtimemanager.js';
9
9
  export * from './classes.simplelocalintegration.js';
10
10
  export * from './errors.js';
11
11
  export * from './types.js';
12
+ export * from './classes.nativehomeadmission.js';
13
+ export * from './classes.nativeintegrationowner.js';