@matter/general 0.16.0-alpha.0-20251205-dfb1e1556 → 0.16.0-alpha.0-20251207-37e501b18

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 (260) hide show
  1. package/dist/cjs/MatterError.js +2 -2
  2. package/dist/cjs/MatterError.js.map +1 -1
  3. package/dist/cjs/codec/DerTypes.d.ts.map +1 -1
  4. package/dist/cjs/codec/DerTypes.js +2 -1
  5. package/dist/cjs/codec/DerTypes.js.map +1 -1
  6. package/dist/cjs/environment/Environment.d.ts +4 -1
  7. package/dist/cjs/environment/Environment.d.ts.map +1 -1
  8. package/dist/cjs/environment/Environment.js +83 -3
  9. package/dist/cjs/environment/Environment.js.map +1 -1
  10. package/dist/cjs/environment/RuntimeService.d.ts +3 -44
  11. package/dist/cjs/environment/RuntimeService.d.ts.map +1 -1
  12. package/dist/cjs/environment/RuntimeService.js +65 -10
  13. package/dist/cjs/environment/RuntimeService.js.map +2 -2
  14. package/dist/cjs/environment/Worker.d.ts +56 -0
  15. package/dist/cjs/environment/Worker.d.ts.map +1 -0
  16. package/dist/cjs/environment/Worker.js +44 -0
  17. package/dist/cjs/environment/Worker.js.map +6 -0
  18. package/dist/cjs/environment/index.d.ts +1 -0
  19. package/dist/cjs/environment/index.d.ts.map +1 -1
  20. package/dist/cjs/environment/index.js +1 -0
  21. package/dist/cjs/environment/index.js.map +1 -1
  22. package/dist/cjs/log/Diagnostic.d.ts +11 -1
  23. package/dist/cjs/log/Diagnostic.d.ts.map +1 -1
  24. package/dist/cjs/log/Diagnostic.js +3 -0
  25. package/dist/cjs/log/Diagnostic.js.map +1 -1
  26. package/dist/cjs/log/DiagnosticPresentation.d.ts +4 -0
  27. package/dist/cjs/log/DiagnosticPresentation.d.ts.map +1 -1
  28. package/dist/cjs/log/DiagnosticPresentation.js +3 -2
  29. package/dist/cjs/log/DiagnosticPresentation.js.map +1 -1
  30. package/dist/cjs/log/DiagnosticSource.d.ts +1 -1
  31. package/dist/cjs/log/DiagnosticSource.d.ts.map +1 -1
  32. package/dist/cjs/log/DiagnosticSource.js +5 -1
  33. package/dist/cjs/log/DiagnosticSource.js.map +1 -1
  34. package/dist/cjs/log/LogFormat.js +2 -2
  35. package/dist/cjs/log/LogFormat.js.map +1 -1
  36. package/dist/cjs/net/Channel.d.ts +1 -1
  37. package/dist/cjs/net/Channel.d.ts.map +1 -1
  38. package/dist/cjs/net/Channel.js +1 -1
  39. package/dist/cjs/net/Channel.js.map +1 -1
  40. package/dist/cjs/net/mock/MockUdpChannel.d.ts +1 -1
  41. package/dist/cjs/net/mock/MockUdpChannel.d.ts.map +1 -1
  42. package/dist/cjs/net/mock/NetworkSimulator.d.ts.map +1 -1
  43. package/dist/cjs/net/mock/NetworkSimulator.js +2 -1
  44. package/dist/cjs/net/mock/NetworkSimulator.js.map +1 -1
  45. package/dist/cjs/net/udp/UdpChannel.d.ts +5 -1
  46. package/dist/cjs/net/udp/UdpChannel.d.ts.map +1 -1
  47. package/dist/cjs/net/udp/UdpMulticastServer.d.ts +4 -6
  48. package/dist/cjs/net/udp/UdpMulticastServer.d.ts.map +1 -1
  49. package/dist/cjs/net/udp/UdpMulticastServer.js +114 -37
  50. package/dist/cjs/net/udp/UdpMulticastServer.js.map +1 -1
  51. package/dist/cjs/time/StandardTime.d.ts +38 -0
  52. package/dist/cjs/time/StandardTime.d.ts.map +1 -0
  53. package/dist/cjs/time/StandardTime.js +172 -0
  54. package/dist/cjs/time/StandardTime.js.map +6 -0
  55. package/dist/cjs/time/Time.d.ts +4 -23
  56. package/dist/cjs/time/Time.d.ts.map +1 -1
  57. package/dist/cjs/time/Time.js +12 -88
  58. package/dist/cjs/time/Time.js.map +1 -1
  59. package/dist/cjs/time/Timestamp.d.ts +5 -0
  60. package/dist/cjs/time/Timestamp.d.ts.map +1 -1
  61. package/dist/cjs/time/Timestamp.js +4 -0
  62. package/dist/cjs/time/Timestamp.js.map +1 -1
  63. package/dist/cjs/time/index.d.ts +1 -0
  64. package/dist/cjs/time/index.d.ts.map +1 -1
  65. package/dist/cjs/time/index.js +1 -0
  66. package/dist/cjs/time/index.js.map +1 -1
  67. package/dist/cjs/transaction/Transaction.d.ts +12 -4
  68. package/dist/cjs/transaction/Transaction.d.ts.map +1 -1
  69. package/dist/cjs/transaction/Transaction.js +4 -4
  70. package/dist/cjs/transaction/Transaction.js.map +1 -1
  71. package/dist/cjs/transaction/Tx.d.ts +2 -1
  72. package/dist/cjs/transaction/Tx.d.ts.map +1 -1
  73. package/dist/cjs/transaction/Tx.js +92 -6
  74. package/dist/cjs/transaction/Tx.js.map +1 -1
  75. package/dist/cjs/util/Abort.js +1 -1
  76. package/dist/cjs/util/Abort.js.map +1 -1
  77. package/dist/cjs/util/Construction.d.ts +2 -1
  78. package/dist/cjs/util/Construction.d.ts.map +1 -1
  79. package/dist/cjs/util/Construction.js +60 -16
  80. package/dist/cjs/util/Construction.js.map +2 -2
  81. package/dist/cjs/util/DataReadQueue.d.ts +1 -5
  82. package/dist/cjs/util/DataReadQueue.d.ts.map +1 -1
  83. package/dist/cjs/util/DataReadQueue.js +12 -11
  84. package/dist/cjs/util/DataReadQueue.js.map +1 -1
  85. package/dist/cjs/util/FormattedText.d.ts +1 -1
  86. package/dist/cjs/util/FormattedText.d.ts.map +1 -1
  87. package/dist/cjs/util/FormattedText.js +1 -3
  88. package/dist/cjs/util/FormattedText.js.map +1 -1
  89. package/dist/cjs/util/Lifetime.d.ts +97 -0
  90. package/dist/cjs/util/Lifetime.d.ts.map +1 -0
  91. package/dist/cjs/util/Lifetime.js +166 -0
  92. package/dist/cjs/util/Lifetime.js.map +6 -0
  93. package/dist/cjs/util/Multiplex.js +2 -2
  94. package/dist/cjs/util/Multiplex.js.map +1 -1
  95. package/dist/cjs/util/Observable.d.ts +27 -26
  96. package/dist/cjs/util/Observable.d.ts.map +1 -1
  97. package/dist/cjs/util/Observable.js +76 -35
  98. package/dist/cjs/util/Observable.js.map +2 -2
  99. package/dist/cjs/util/String.d.ts +5 -29
  100. package/dist/cjs/util/String.d.ts.map +1 -1
  101. package/dist/cjs/util/String.js +16 -158
  102. package/dist/cjs/util/String.js.map +2 -2
  103. package/dist/cjs/util/identifier-case.d.ts +19 -0
  104. package/dist/cjs/util/identifier-case.d.ts.map +1 -0
  105. package/dist/cjs/util/identifier-case.js +99 -0
  106. package/dist/cjs/util/identifier-case.js.map +6 -0
  107. package/dist/cjs/util/index.d.ts +3 -0
  108. package/dist/cjs/util/index.d.ts.map +1 -1
  109. package/dist/cjs/util/index.js +3 -0
  110. package/dist/cjs/util/index.js.map +1 -1
  111. package/dist/cjs/util/serialize.d.ts +25 -0
  112. package/dist/cjs/util/serialize.d.ts.map +1 -0
  113. package/dist/cjs/util/serialize.js +116 -0
  114. package/dist/cjs/util/serialize.js.map +6 -0
  115. package/dist/esm/MatterError.js +1 -1
  116. package/dist/esm/codec/DerTypes.d.ts.map +1 -1
  117. package/dist/esm/codec/DerTypes.js +2 -1
  118. package/dist/esm/codec/DerTypes.js.map +1 -1
  119. package/dist/esm/environment/Environment.d.ts +4 -1
  120. package/dist/esm/environment/Environment.d.ts.map +1 -1
  121. package/dist/esm/environment/Environment.js +83 -3
  122. package/dist/esm/environment/Environment.js.map +1 -1
  123. package/dist/esm/environment/RuntimeService.d.ts +3 -44
  124. package/dist/esm/environment/RuntimeService.d.ts.map +1 -1
  125. package/dist/esm/environment/RuntimeService.js +65 -10
  126. package/dist/esm/environment/RuntimeService.js.map +2 -2
  127. package/dist/esm/environment/Worker.d.ts +56 -0
  128. package/dist/esm/environment/Worker.d.ts.map +1 -0
  129. package/dist/esm/environment/Worker.js +24 -0
  130. package/dist/esm/environment/Worker.js.map +6 -0
  131. package/dist/esm/environment/index.d.ts +1 -0
  132. package/dist/esm/environment/index.d.ts.map +1 -1
  133. package/dist/esm/environment/index.js +1 -0
  134. package/dist/esm/environment/index.js.map +1 -1
  135. package/dist/esm/log/Diagnostic.d.ts +11 -1
  136. package/dist/esm/log/Diagnostic.d.ts.map +1 -1
  137. package/dist/esm/log/Diagnostic.js +3 -0
  138. package/dist/esm/log/Diagnostic.js.map +1 -1
  139. package/dist/esm/log/DiagnosticPresentation.d.ts +4 -0
  140. package/dist/esm/log/DiagnosticPresentation.d.ts.map +1 -1
  141. package/dist/esm/log/DiagnosticPresentation.js +3 -2
  142. package/dist/esm/log/DiagnosticPresentation.js.map +1 -1
  143. package/dist/esm/log/DiagnosticSource.d.ts +1 -1
  144. package/dist/esm/log/DiagnosticSource.d.ts.map +1 -1
  145. package/dist/esm/log/DiagnosticSource.js +5 -1
  146. package/dist/esm/log/DiagnosticSource.js.map +1 -1
  147. package/dist/esm/log/LogFormat.js +1 -1
  148. package/dist/esm/log/LogFormat.js.map +1 -1
  149. package/dist/esm/net/Channel.d.ts +1 -1
  150. package/dist/esm/net/Channel.d.ts.map +1 -1
  151. package/dist/esm/net/Channel.js +1 -1
  152. package/dist/esm/net/Channel.js.map +1 -1
  153. package/dist/esm/net/mock/MockUdpChannel.d.ts +1 -1
  154. package/dist/esm/net/mock/MockUdpChannel.d.ts.map +1 -1
  155. package/dist/esm/net/mock/NetworkSimulator.d.ts.map +1 -1
  156. package/dist/esm/net/mock/NetworkSimulator.js +2 -1
  157. package/dist/esm/net/mock/NetworkSimulator.js.map +1 -1
  158. package/dist/esm/net/udp/UdpChannel.d.ts +5 -1
  159. package/dist/esm/net/udp/UdpChannel.d.ts.map +1 -1
  160. package/dist/esm/net/udp/UdpMulticastServer.d.ts +4 -6
  161. package/dist/esm/net/udp/UdpMulticastServer.d.ts.map +1 -1
  162. package/dist/esm/net/udp/UdpMulticastServer.js +114 -37
  163. package/dist/esm/net/udp/UdpMulticastServer.js.map +1 -1
  164. package/dist/esm/time/StandardTime.d.ts +38 -0
  165. package/dist/esm/time/StandardTime.d.ts.map +1 -0
  166. package/dist/esm/time/StandardTime.js +152 -0
  167. package/dist/esm/time/StandardTime.js.map +6 -0
  168. package/dist/esm/time/Time.d.ts +4 -23
  169. package/dist/esm/time/Time.d.ts.map +1 -1
  170. package/dist/esm/time/Time.js +12 -88
  171. package/dist/esm/time/Time.js.map +1 -1
  172. package/dist/esm/time/Timestamp.d.ts +5 -0
  173. package/dist/esm/time/Timestamp.d.ts.map +1 -1
  174. package/dist/esm/time/Timestamp.js +4 -0
  175. package/dist/esm/time/Timestamp.js.map +1 -1
  176. package/dist/esm/time/index.d.ts +1 -0
  177. package/dist/esm/time/index.d.ts.map +1 -1
  178. package/dist/esm/time/index.js +1 -0
  179. package/dist/esm/time/index.js.map +1 -1
  180. package/dist/esm/transaction/Transaction.d.ts +12 -4
  181. package/dist/esm/transaction/Transaction.d.ts.map +1 -1
  182. package/dist/esm/transaction/Transaction.js +4 -4
  183. package/dist/esm/transaction/Transaction.js.map +1 -1
  184. package/dist/esm/transaction/Tx.d.ts +2 -1
  185. package/dist/esm/transaction/Tx.d.ts.map +1 -1
  186. package/dist/esm/transaction/Tx.js +92 -6
  187. package/dist/esm/transaction/Tx.js.map +1 -1
  188. package/dist/esm/util/Abort.js +1 -1
  189. package/dist/esm/util/Abort.js.map +1 -1
  190. package/dist/esm/util/Construction.d.ts +2 -1
  191. package/dist/esm/util/Construction.d.ts.map +1 -1
  192. package/dist/esm/util/Construction.js +60 -16
  193. package/dist/esm/util/Construction.js.map +2 -2
  194. package/dist/esm/util/DataReadQueue.d.ts +1 -5
  195. package/dist/esm/util/DataReadQueue.d.ts.map +1 -1
  196. package/dist/esm/util/DataReadQueue.js +12 -11
  197. package/dist/esm/util/DataReadQueue.js.map +1 -1
  198. package/dist/esm/util/FormattedText.d.ts +1 -1
  199. package/dist/esm/util/FormattedText.d.ts.map +1 -1
  200. package/dist/esm/util/FormattedText.js +2 -4
  201. package/dist/esm/util/FormattedText.js.map +1 -1
  202. package/dist/esm/util/Lifetime.d.ts +97 -0
  203. package/dist/esm/util/Lifetime.d.ts.map +1 -0
  204. package/dist/esm/util/Lifetime.js +146 -0
  205. package/dist/esm/util/Lifetime.js.map +6 -0
  206. package/dist/esm/util/Multiplex.js +2 -2
  207. package/dist/esm/util/Multiplex.js.map +1 -1
  208. package/dist/esm/util/Observable.d.ts +27 -26
  209. package/dist/esm/util/Observable.d.ts.map +1 -1
  210. package/dist/esm/util/Observable.js +76 -35
  211. package/dist/esm/util/Observable.js.map +2 -2
  212. package/dist/esm/util/String.d.ts +5 -29
  213. package/dist/esm/util/String.d.ts.map +1 -1
  214. package/dist/esm/util/String.js +16 -158
  215. package/dist/esm/util/String.js.map +2 -2
  216. package/dist/esm/util/identifier-case.d.ts +19 -0
  217. package/dist/esm/util/identifier-case.d.ts.map +1 -0
  218. package/dist/esm/util/identifier-case.js +79 -0
  219. package/dist/esm/util/identifier-case.js.map +6 -0
  220. package/dist/esm/util/index.d.ts +3 -0
  221. package/dist/esm/util/index.d.ts.map +1 -1
  222. package/dist/esm/util/index.js +3 -0
  223. package/dist/esm/util/index.js.map +1 -1
  224. package/dist/esm/util/serialize.d.ts +25 -0
  225. package/dist/esm/util/serialize.d.ts.map +1 -0
  226. package/dist/esm/util/serialize.js +96 -0
  227. package/dist/esm/util/serialize.js.map +6 -0
  228. package/package.json +2 -2
  229. package/src/MatterError.ts +1 -1
  230. package/src/codec/DerTypes.ts +2 -1
  231. package/src/environment/Environment.ts +29 -4
  232. package/src/environment/RuntimeService.ts +19 -55
  233. package/src/environment/Worker.ts +75 -0
  234. package/src/environment/index.ts +1 -0
  235. package/src/log/Diagnostic.ts +15 -1
  236. package/src/log/DiagnosticPresentation.ts +7 -2
  237. package/src/log/DiagnosticSource.ts +5 -1
  238. package/src/log/LogFormat.ts +1 -1
  239. package/src/net/Channel.ts +2 -2
  240. package/src/net/mock/MockUdpChannel.ts +1 -1
  241. package/src/net/mock/NetworkSimulator.ts +2 -1
  242. package/src/net/udp/UdpChannel.ts +6 -1
  243. package/src/net/udp/UdpMulticastServer.ts +72 -34
  244. package/src/time/StandardTime.ts +122 -0
  245. package/src/time/Time.ts +13 -105
  246. package/src/time/Timestamp.ts +8 -0
  247. package/src/time/index.ts +1 -0
  248. package/src/transaction/Transaction.ts +19 -6
  249. package/src/transaction/Tx.ts +49 -2
  250. package/src/util/Abort.ts +1 -1
  251. package/src/util/Construction.ts +76 -18
  252. package/src/util/DataReadQueue.ts +14 -12
  253. package/src/util/FormattedText.ts +1 -1
  254. package/src/util/Lifetime.ts +284 -0
  255. package/src/util/Multiplex.ts +2 -2
  256. package/src/util/Observable.ts +132 -53
  257. package/src/util/String.ts +14 -215
  258. package/src/util/identifier-case.ts +101 -0
  259. package/src/util/index.ts +3 -0
  260. package/src/util/serialize.ts +129 -0
@@ -7,6 +7,7 @@
7
7
  import { MatterAggregateError } from "#MatterError.js";
8
8
  import { Minutes } from "#time/TimeUnit.js";
9
9
  import { Bytes } from "#util/Bytes.js";
10
+ import { Lifetime } from "#util/Lifetime.js";
10
11
  import { Logger } from "../../log/Logger.js";
11
12
  import { Cache } from "../../util/Cache.js";
12
13
  import { asError } from "../../util/Error.js";
@@ -22,41 +23,67 @@ export interface UdpMulticastServerOptions {
22
23
  broadcastAddressIpv6: string;
23
24
  broadcastAddressIpv4?: string;
24
25
  netInterface?: string;
26
+ lifetime?: Lifetime.Owner;
25
27
  }
26
28
 
27
29
  export class UdpMulticastServer {
30
+ #lifetime: Lifetime;
31
+ readonly network: Network;
32
+ readonly netInterface: string | undefined;
33
+ readonly #broadcastAddressIpv4: string | undefined;
34
+ readonly #broadcastAddressIpv6: string;
35
+ readonly #broadcastPort: number;
36
+ readonly #serverIpv4: UdpChannel | undefined;
37
+ readonly #serverIpv6: UdpChannel;
38
+
28
39
  static async create({
29
40
  netInterface,
30
41
  broadcastAddressIpv4,
31
42
  broadcastAddressIpv6,
32
43
  listeningPort,
33
44
  network,
45
+ lifetime: lifetimeOwner,
34
46
  }: UdpMulticastServerOptions) {
35
- let ipv4UdpChannel: UdpChannel | undefined = undefined;
36
- if (broadcastAddressIpv4 !== undefined) {
47
+ const lifetime = (lifetimeOwner || Lifetime.process)?.join("multicast server");
48
+
49
+ try {
50
+ let ipv4UdpChannel: UdpChannel | undefined = undefined;
51
+ if (broadcastAddressIpv4 !== undefined) {
52
+ try {
53
+ ipv4UdpChannel = await network.createUdpChannel({
54
+ lifetime,
55
+ type: "udp4",
56
+ netInterface,
57
+ listeningPort,
58
+ reuseAddress: true,
59
+ });
60
+ await ipv4UdpChannel.addMembership(broadcastAddressIpv4);
61
+ } catch (error) {
62
+ NoAddressAvailableError.accept(error);
63
+ logger.info(
64
+ `IPv4 UDP channel not created because IPv4 is not available: ${asError(error).message}`,
65
+ );
66
+ }
67
+ }
68
+
69
+ let ipv6UdpChannel;
37
70
  try {
38
- ipv4UdpChannel = await network.createUdpChannel({
39
- type: "udp4",
71
+ ipv6UdpChannel = await network.createUdpChannel({
72
+ lifetime,
73
+ type: "udp6",
40
74
  netInterface,
41
75
  listeningPort,
42
76
  reuseAddress: true,
43
77
  });
44
- await ipv4UdpChannel.addMembership(broadcastAddressIpv4);
78
+ await ipv6UdpChannel.addMembership(broadcastAddressIpv6);
45
79
  } catch (error) {
46
80
  NoAddressAvailableError.accept(error);
47
- logger.info(`IPv4 UDP channel not created because IPv4 is not available: ${asError(error).message}`);
81
+ logger.info(`IPv6 UDP interface not created because IPv6 is not available, but required my Matter`);
82
+ throw error;
48
83
  }
49
- }
50
84
 
51
- try {
52
- const ipv6UdpChannel = await network.createUdpChannel({
53
- type: "udp6",
54
- netInterface,
55
- listeningPort,
56
- reuseAddress: true,
57
- });
58
- await ipv6UdpChannel.addMembership(broadcastAddressIpv6);
59
85
  return new UdpMulticastServer(
86
+ lifetime,
60
87
  network,
61
88
  broadcastAddressIpv4,
62
89
  broadcastAddressIpv6,
@@ -66,8 +93,7 @@ export class UdpMulticastServer {
66
93
  netInterface,
67
94
  );
68
95
  } catch (error) {
69
- NoAddressAvailableError.accept(error);
70
- logger.info(`IPv6 UDP interface not created because IPv6 is not available, but required my Matter.`);
96
+ lifetime[Symbol.dispose]();
71
97
  throw error;
72
98
  }
73
99
  }
@@ -80,28 +106,38 @@ export class UdpMulticastServer {
80
106
  );
81
107
 
82
108
  private constructor(
83
- readonly network: Network,
84
- private readonly broadcastAddressIpv4: string | undefined,
85
- private readonly broadcastAddressIpv6: string,
86
- private readonly broadcastPort: number,
87
- private readonly serverIpv4: UdpChannel | undefined,
88
- private readonly serverIpv6: UdpChannel,
89
- readonly netInterface: string | undefined,
90
- ) {}
109
+ lifetime: Lifetime,
110
+ network: Network,
111
+ broadcastAddressIpv4: string | undefined,
112
+ broadcastAddressIpv6: string,
113
+ broadcastPort: number,
114
+ serverIpv4: UdpChannel | undefined,
115
+ serverIpv6: UdpChannel,
116
+ netInterface: string | undefined,
117
+ ) {
118
+ this.#lifetime = lifetime;
119
+ this.network = network;
120
+ this.#broadcastAddressIpv4 = broadcastAddressIpv4;
121
+ this.#broadcastAddressIpv6 = broadcastAddressIpv6;
122
+ this.#broadcastPort = broadcastPort;
123
+ this.#serverIpv4 = serverIpv4;
124
+ this.#serverIpv6 = serverIpv6;
125
+ this.netInterface = netInterface;
126
+ }
91
127
 
92
128
  get supportsIpv4() {
93
- return this.serverIpv4 !== undefined;
129
+ return this.#serverIpv4 !== undefined;
94
130
  }
95
131
 
96
132
  onMessage(listener: (message: Bytes, peerAddress: string, netInterface: string) => void) {
97
- this.serverIpv4?.onData((netInterface, peerAddress, _port, message) => {
133
+ this.#serverIpv4?.onData((netInterface, peerAddress, _port, message) => {
98
134
  if (netInterface === undefined) {
99
135
  // Ignore Network packages not coming over any known interface
100
136
  return;
101
137
  }
102
138
  listener(message, peerAddress, netInterface);
103
139
  });
104
- this.serverIpv6.onData((netInterface, peerAddress, _port, message) => {
140
+ this.#serverIpv6.onData((netInterface, peerAddress, _port, message) => {
105
141
  if (netInterface === undefined) {
106
142
  // Ignore Network packages not coming over any known interface
107
143
  return;
@@ -118,7 +154,7 @@ export class UdpMulticastServer {
118
154
  try {
119
155
  await (
120
156
  await this.broadcastChannels.get(netInterface, isIPv4(uniCastTarget))
121
- ).send(uniCastTarget, this.broadcastPort, message);
157
+ ).send(uniCastTarget, this.#broadcastPort, message);
122
158
  } catch (error) {
123
159
  logger.info(`${netInterface} ${uniCastTarget}: ${asError(error).message}`);
124
160
  }
@@ -136,7 +172,7 @@ export class UdpMulticastServer {
136
172
  await MatterAggregateError.allSettled(
137
173
  ips.map(async ip => {
138
174
  const iPv4 = ipV4.includes(ip);
139
- const broadcastTarget = iPv4 ? this.broadcastAddressIpv4 : this.broadcastAddressIpv6;
175
+ const broadcastTarget = iPv4 ? this.#broadcastAddressIpv4 : this.#broadcastAddressIpv6;
140
176
  if (broadcastTarget === undefined) {
141
177
  // IPv4 but disabled, so just resolve
142
178
  return;
@@ -144,7 +180,7 @@ export class UdpMulticastServer {
144
180
  try {
145
181
  await (
146
182
  await this.broadcastChannels.get(netInterface, iPv4)
147
- ).send(broadcastTarget, this.broadcastPort, message);
183
+ ).send(broadcastTarget, this.#broadcastPort, message);
148
184
  } catch (error) {
149
185
  logger.info(`${netInterface}: ${asError(error).message}`);
150
186
  }
@@ -159,16 +195,18 @@ export class UdpMulticastServer {
159
195
 
160
196
  private async createBroadcastChannel(netInterface: string, iPv4: string): Promise<UdpChannel> {
161
197
  return await this.network.createUdpChannel({
198
+ lifetime: this.#lifetime,
162
199
  type: iPv4 ? "udp4" : "udp6",
163
- listeningPort: this.broadcastPort,
200
+ listeningPort: this.#broadcastPort,
164
201
  netInterface,
165
202
  reuseAddress: true,
166
203
  });
167
204
  }
168
205
 
169
206
  async close() {
170
- await this.serverIpv4?.close();
171
- await this.serverIpv6.close();
207
+ using _closing = this.#lifetime.closing();
208
+ await this.#serverIpv4?.close();
209
+ await this.#serverIpv6.close();
172
210
  await this.broadcastChannels.close();
173
211
  }
174
212
  }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { ImplementationError } from "#MatterError.js";
8
+ import { Boot } from "#util/Boot.js";
9
+ import { decamelize } from "#util/identifier-case.js";
10
+ import { Lifetime } from "#util/Lifetime.js";
11
+ import { Duration } from "./Duration.js";
12
+ import { Time, Timer } from "./Time.js";
13
+ import { Instant } from "./TimeUnit.js";
14
+
15
+ /**
16
+ * A {@link Timer} implementation that uses standard JavaScript timers.
17
+ */
18
+ export class StandardTime extends Time {
19
+ override getTimer(name: string, duration: Duration, callback: Timer.Callback) {
20
+ return new StandardTimer(name, duration, callback, false);
21
+ }
22
+
23
+ override getPeriodicTimer(name: string, duration: Duration, callback: Timer.Callback) {
24
+ return new StandardTimer(name, duration, callback, true);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * A {@link Timer} implementation that uses standard JavaScript functions.
30
+ */
31
+ export class StandardTimer implements Timer {
32
+ #timerId: unknown;
33
+ #utility = false;
34
+ #interval = Instant; // Real value installed in constructor
35
+ isRunning = false;
36
+
37
+ get systemId() {
38
+ return Number(this.#timerId);
39
+ }
40
+
41
+ constructor(
42
+ readonly name: string,
43
+ duration: Duration,
44
+ private readonly callback: Timer.Callback,
45
+ readonly isPeriodic: boolean,
46
+ ) {
47
+ this.interval = duration;
48
+ }
49
+
50
+ /**
51
+ * The timer's interval.
52
+ *
53
+ * You can change this value but changes have no effect until the timer restarts.
54
+ */
55
+ set interval(interval: Duration) {
56
+ if (interval < 0 || interval > 2147483647) {
57
+ throw new ImplementationError(
58
+ `Invalid intervalMs: ${interval}. The value must be between 0 and 32-bit maximum value (2147483647)`,
59
+ );
60
+ }
61
+ this.#interval = interval;
62
+ }
63
+
64
+ get interval() {
65
+ return this.#interval;
66
+ }
67
+
68
+ get utility() {
69
+ return this.#utility;
70
+ }
71
+
72
+ set utility(utility: boolean) {
73
+ if (utility === this.#utility) {
74
+ return;
75
+ }
76
+
77
+ // Support node.js-style environments to control whether the timer blocks process exit
78
+ if (this.#timerId !== undefined) {
79
+ const timerId = this.#timerId as { ref?: () => void; unref?: () => void };
80
+ if (utility) {
81
+ timerId.unref?.();
82
+ } else {
83
+ timerId.ref?.();
84
+ }
85
+ }
86
+
87
+ this.#utility = utility;
88
+ }
89
+
90
+ start() {
91
+ if (this.isRunning) this.stop();
92
+ Time.register(this);
93
+ this.isRunning = true;
94
+ this.#timerId = (this.isPeriodic ? setInterval : setTimeout)(() => {
95
+ using lifetime = Lifetime(decamelize(this.name, " "));
96
+ if (!this.isPeriodic) {
97
+ Time.unregister(this);
98
+ this.isRunning = false;
99
+ }
100
+ this.callback(lifetime);
101
+ }, this.interval);
102
+ return this;
103
+ }
104
+
105
+ stop() {
106
+ (this.isPeriodic ? clearInterval : clearTimeout)(this.#timerId as ReturnType<typeof setTimeout>);
107
+ Time.unregister(this);
108
+ this.isRunning = false;
109
+ return this;
110
+ }
111
+ }
112
+
113
+ Boot.init(() => {
114
+ Time.default = new StandardTime();
115
+
116
+ Time.startup.systemMs = Time.startup.processMs = Time.nowMs;
117
+
118
+ // Hook for testing frameworks
119
+ if (typeof MatterHooks !== "undefined") {
120
+ MatterHooks?.timeSetup?.(Time);
121
+ }
122
+ });
package/src/time/Time.ts CHANGED
@@ -4,13 +4,13 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Boot } from "#util/Boot.js";
7
+ import { DiagnosticPresentation } from "#log/DiagnosticPresentation.js";
8
+ import { NoProviderError } from "#MatterError.js";
8
9
  import { CancelablePromise } from "#util/Cancelable.js";
9
- import { ImplementationError } from "../MatterError.js";
10
+ import type { Lifetime } from "#util/Lifetime.js";
10
11
  import { Diagnostic } from "../log/Diagnostic.js";
11
12
  import { DiagnosticSource } from "../log/DiagnosticSource.js";
12
13
  import { Duration } from "./Duration.js";
13
- import { Instant } from "./TimeUnit.js";
14
14
  import type { Timestamp } from "./Timestamp.js";
15
15
 
16
16
  const registry = new Set<Timer>();
@@ -53,8 +53,8 @@ export class Time {
53
53
  /**
54
54
  * Create a timer that will call callback after durationMs has passed.
55
55
  */
56
- getTimer(name: string, duration: Duration, callback: Timer.Callback): Timer {
57
- return new StandardTimer(name, duration, callback, false);
56
+ getTimer(_name: string, _duration: Duration, _callback: Timer.Callback): Timer {
57
+ throw new NoProviderError();
58
58
  }
59
59
  static readonly getTimer = (name: string, duration: Duration, callback: Timer.Callback): Timer =>
60
60
  Time.default.getTimer(name, duration, callback);
@@ -62,8 +62,8 @@ export class Time {
62
62
  /**
63
63
  * Create a timer that will periodically call callback at intervalMs intervals.
64
64
  */
65
- getPeriodicTimer(name: string, duration: Duration, callback: Timer.Callback): Timer {
66
- return new StandardTimer(name, duration, callback, true);
65
+ getPeriodicTimer(_name: string, _duration: Duration, _callback: Timer.Callback): Timer {
66
+ throw new NoProviderError();
67
67
  }
68
68
  static readonly getPeriodicTimer = (name: string, duration: Duration, callback: Timer.Callback): Timer =>
69
69
  Time.default.getPeriodicTimer(name, duration, callback);
@@ -77,7 +77,7 @@ export class Time {
77
77
  return new CancelablePromise(
78
78
  resolve => {
79
79
  resolver = resolve;
80
- timer = Time.getTimer(name, duration, resolve);
80
+ timer = Time.getTimer(name, duration, () => resolve());
81
81
  timer.start();
82
82
  },
83
83
 
@@ -144,113 +144,21 @@ export interface Timer {
144
144
  }
145
145
 
146
146
  export namespace Timer {
147
- export type Callback = () => any;
148
- }
149
-
150
- export class StandardTimer implements Timer {
151
- #timerId: unknown;
152
- #utility = false;
153
- #interval = Instant; // Real value installed in constructor
154
- isRunning = false;
155
-
156
- get systemId() {
157
- return Number(this.#timerId);
158
- }
159
-
160
- constructor(
161
- readonly name: string,
162
- duration: Duration,
163
- private readonly callback: Timer.Callback,
164
- readonly isPeriodic: boolean,
165
- ) {
166
- this.interval = duration;
167
- }
168
-
169
- /**
170
- * The timer's interval.
171
- *
172
- * You can change this value but changes have no effect until the timer restarts.
173
- */
174
- set interval(interval: Duration) {
175
- if (interval < 0 || interval > 2147483647) {
176
- throw new ImplementationError(
177
- `Invalid intervalMs: ${interval}. The value must be between 0 and 32-bit maximum value (2147483647)`,
178
- );
179
- }
180
- this.#interval = interval;
181
- }
182
-
183
- get interval() {
184
- return this.#interval;
185
- }
186
-
187
- get utility() {
188
- return this.#utility;
189
- }
190
-
191
- set utility(utility: boolean) {
192
- if (utility === this.#utility) {
193
- return;
194
- }
195
-
196
- // Support node.js-style environments to control whether the timer blocks process exit
197
- if (this.#timerId !== undefined) {
198
- const timerId = this.#timerId as { ref?: () => void; unref?: () => void };
199
- if (utility) {
200
- timerId.unref?.();
201
- } else {
202
- timerId.ref?.();
203
- }
204
- }
205
-
206
- this.#utility = utility;
207
- }
208
-
209
- start() {
210
- if (this.isRunning) this.stop();
211
- Time.register(this);
212
- this.isRunning = true;
213
- this.#timerId = (this.isPeriodic ? setInterval : setTimeout)(() => {
214
- if (!this.isPeriodic) {
215
- Time.unregister(this);
216
- this.isRunning = false;
217
- }
218
- this.callback();
219
- }, this.interval);
220
- return this;
221
- }
222
-
223
- stop() {
224
- (this.isPeriodic ? clearInterval : clearTimeout)(this.#timerId as ReturnType<typeof setTimeout>);
225
- Time.unregister(this);
226
- this.isRunning = false;
227
- return this;
228
- }
147
+ export type Callback = (lifetime: Lifetime) => any;
229
148
  }
230
149
 
231
150
  DiagnosticSource.add({
232
- get [Diagnostic.value]() {
151
+ get [DiagnosticPresentation.value]() {
233
152
  return Diagnostic.node("⏱", "Timers", {
234
153
  children: [...registry].map(timer => [
235
154
  timer.name,
236
155
  Diagnostic.dict({
237
156
  periodic: timer.isPeriodic,
238
- interval: timer.interval,
239
- system: timer.systemId,
240
- elapsed: timer.elapsed,
157
+ up: timer.elapsed,
158
+ interval: Duration.format(timer.interval),
159
+ "system#": timer.systemId,
241
160
  }),
242
161
  ]),
243
162
  });
244
163
  },
245
164
  });
246
-
247
- Boot.init(() => {
248
- Time.default = new Time();
249
-
250
- Time.startup.systemMs = Time.startup.processMs = Time.nowMs;
251
-
252
- // Hook for testing frameworks
253
- if (typeof MatterHooks !== "undefined") {
254
- MatterHooks?.timeSetup?.(Time);
255
- }
256
- });
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { UnexpectedDataError } from "#MatterError.js";
8
8
  import { Branded } from "#util/Type.js";
9
+ import type { Duration } from "./Duration.js";
9
10
  import { Time } from "./Time.js";
10
11
 
11
12
  /**
@@ -91,4 +92,11 @@ export namespace Timestamp {
91
92
  export function dateOf(timestamp: Timestamp) {
92
93
  return new Date(timestamp);
93
94
  }
95
+
96
+ /**
97
+ * Compute the duration between two timestamps.
98
+ */
99
+ export function delta(from: Timestamp, to: Timestamp) {
100
+ return (to - from) as Duration;
101
+ }
94
102
  }
package/src/time/index.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  export * from "./Duration.js";
8
+ export * from "./StandardTime.js";
8
9
  export * from "./Time.js";
9
10
  export * from "./Timespan.js";
10
11
  export * from "./Timestamp.js";
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import { Lifetime } from "#util/Lifetime.js";
7
8
  import { MaybePromise } from "#util/Promises.js";
8
9
  import { Participant } from "./Participant.js";
9
10
  import { Resource } from "./Resource.js";
@@ -27,7 +28,7 @@ import { open } from "./Tx.js";
27
28
  *
28
29
  * TODO - does prevent deadlock but we should probably add a timeout for resource locking
29
30
  */
30
- export interface Transaction {
31
+ export interface Transaction extends Lifetime.Owner {
31
32
  /**
32
33
  * Diagnostic description of the transaction's source.
33
34
  */
@@ -144,6 +145,14 @@ export interface Transaction {
144
145
  */
145
146
  rollback(): MaybePromise;
146
147
 
148
+ /**
149
+ * Destroy the transaction without proper commit or rollback.
150
+ *
151
+ * This guarantees a synchronous resolution but will result in an error if {@link Transaction#status} is not shared,
152
+ * read-only or destroyed.
153
+ */
154
+ [Symbol.dispose](): void;
155
+
147
156
  /**
148
157
  * Wait for a set of transactions to complete.
149
158
  *
@@ -168,8 +177,12 @@ export const Transaction = {
168
177
  * The transaction is destroyed when {@link act} returns. You will receive an error if you access it after it is
169
178
  * destroyed.
170
179
  */
171
- act<T>(via: string, actor: (transaction: Transaction) => MaybePromise<T>): MaybePromise<T> {
172
- const tx = open(via);
180
+ act<T>(
181
+ via: string,
182
+ lifetime: Lifetime.Owner,
183
+ actor: (transaction: Transaction) => MaybePromise<T>,
184
+ ): MaybePromise<T> {
185
+ const tx = open(via, lifetime);
173
186
 
174
187
  let result;
175
188
  try {
@@ -188,9 +201,9 @@ export const Transaction = {
188
201
  *
189
202
  * When closed the transaction commits automatically if exclusive.
190
203
  */
191
- open(via: string, isolation: Transaction.IsolationLevel = "rw") {
204
+ open(via: string, lifetime: Lifetime.Owner, isolation: Transaction.IsolationLevel = "rw") {
192
205
  // This function is replaced below so do not edit
193
- return open(via, isolation);
206
+ return open(via, lifetime, isolation);
194
207
  },
195
208
 
196
209
  Status,
@@ -218,7 +231,7 @@ export namespace Transaction {
218
231
 
219
232
  export interface Finalization {
220
233
  /**
221
- * Finish the transaction. If {@link result} is a promise this may result on commit or rollback.
234
+ * Finish the transaction. If {@link result} is a promise this may result in commit or rollback.
222
235
  */
223
236
  resolve<T>(result: T): MaybePromise<Awaited<T>>;
224
237
 
@@ -11,6 +11,7 @@ import { Time, Timer } from "#time/Time.js";
11
11
  import { Timestamp } from "#time/Timestamp.js";
12
12
  import { Millis } from "#time/TimeUnit.js";
13
13
  import { asError } from "#util/Error.js";
14
+ import { Lifetime } from "#util/Lifetime.js";
14
15
  import { Observable } from "#util/Observable.js";
15
16
  import { MaybePromise } from "#util/Promises.js";
16
17
  import { describeList } from "#util/String.js";
@@ -34,9 +35,10 @@ const MAX_CHAINED_COMMITS = 5;
34
35
  */
35
36
  export function open(
36
37
  via: string,
38
+ lifetime: Lifetime.Owner,
37
39
  isolation: Transaction.IsolationLevel = "rw",
38
40
  ): Transaction & Transaction.Finalization {
39
- return new Tx(via, isolation);
41
+ return new Tx(via, lifetime, isolation);
40
42
  }
41
43
 
42
44
  /**
@@ -50,14 +52,35 @@ class Tx implements Transaction, Transaction.Finalization {
50
52
  #status;
51
53
  #waitingOn?: Iterable<Transaction>;
52
54
  #via: string;
55
+ #lifetime: Lifetime;
53
56
  #shared?: Observable<[]>;
54
57
  #closed?: Observable<[]>;
55
58
  #isAsync = false;
56
59
  #reportingLocks = false;
57
60
 
58
- constructor(via: string, isolation: Transaction.IsolationLevel) {
61
+ constructor(via: string, lifetime: Lifetime.Owner, isolation: Transaction.IsolationLevel) {
59
62
  this.#via = Diagnostic.via(via);
63
+
64
+ this.#lifetime = lifetime.join("tx", via);
65
+ Object.defineProperties(this.#lifetime.details, {
66
+ status: {
67
+ get: () => this.#status,
68
+ enumerable: true,
69
+ },
70
+
71
+ resources: {
72
+ get: () => {
73
+ if (!this.#resources.size) {
74
+ return;
75
+ }
76
+
77
+ return [...this.#resources].join("+");
78
+ },
79
+ },
80
+ });
81
+
60
82
  this.#isolation = isolation;
83
+
61
84
  if (isolation === "rw") {
62
85
  this.#status = Status.Shared;
63
86
  } else {
@@ -66,6 +89,18 @@ class Tx implements Transaction, Transaction.Finalization {
66
89
  }
67
90
 
68
91
  [Symbol.dispose]() {
92
+ switch (this.status) {
93
+ case Status.ReadOnly:
94
+ case Status.Shared:
95
+ case Status.Destroyed:
96
+ break;
97
+
98
+ default:
99
+ logger.error(this.via, "Disposed", this.via, "while", this.status);
100
+ break;
101
+ }
102
+
103
+ using _closing = this.#lifetime.closing();
69
104
  this.#reset("dropped");
70
105
  this.#status = Status.Destroyed;
71
106
  this.#closed?.emit();
@@ -253,6 +288,8 @@ class Tx implements Transaction, Transaction.Finalization {
253
288
  }
254
289
 
255
290
  resolve<T>(result: T): MaybePromise<Awaited<T>> {
291
+ this.#lifetime.closing();
292
+
256
293
  // If result is a promise, we wait for resolution and then commit (success) or roll back (error)
257
294
  if (MaybePromise.is(result)) {
258
295
  this.isAsync = true;
@@ -292,6 +329,8 @@ class Tx implements Transaction, Transaction.Finalization {
292
329
  }
293
330
 
294
331
  reject(cause: unknown): MaybePromise<never> {
332
+ this.#lifetime.closing();
333
+
295
334
  if (this.#status === Status.Shared) {
296
335
  try {
297
336
  this.#reset("released");
@@ -331,6 +370,14 @@ class Tx implements Transaction, Transaction.Finalization {
331
370
  throw cause;
332
371
  }
333
372
 
373
+ join(...name: unknown[]) {
374
+ if (this.#isolation === "ro") {
375
+ throw new ReadOnlyError();
376
+ }
377
+
378
+ return this.#lifetime.join(...name);
379
+ }
380
+
334
381
  /**
335
382
  * Execute commit logic for a single commit cycle.
336
383
  *