@matter/general 0.16.0-alpha.0-20251006-3fe1e7c57 → 0.16.0-alpha.0-20251013-89bb7099d

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 (190) hide show
  1. package/dist/cjs/codec/Base64Codec.js +2 -2
  2. package/dist/cjs/codec/Base64Codec.js.map +1 -1
  3. package/dist/cjs/environment/Environment.d.ts +12 -18
  4. package/dist/cjs/environment/Environment.d.ts.map +1 -1
  5. package/dist/cjs/environment/Environment.js +17 -32
  6. package/dist/cjs/environment/Environment.js.map +1 -1
  7. package/dist/cjs/environment/Environmental.d.ts +16 -2
  8. package/dist/cjs/environment/Environmental.d.ts.map +1 -1
  9. package/dist/cjs/log/Logger.js +1 -1
  10. package/dist/cjs/net/Network.d.ts +4 -0
  11. package/dist/cjs/net/Network.d.ts.map +1 -1
  12. package/dist/cjs/net/Network.js +6 -0
  13. package/dist/cjs/net/Network.js.map +1 -1
  14. package/dist/cjs/net/ServerAddress.d.ts +1 -0
  15. package/dist/cjs/net/ServerAddress.d.ts.map +1 -1
  16. package/dist/cjs/net/ServerAddress.js +13 -0
  17. package/dist/cjs/net/ServerAddress.js.map +1 -1
  18. package/dist/cjs/net/http/HttpEndpoint.d.ts +53 -0
  19. package/dist/cjs/net/http/HttpEndpoint.d.ts.map +1 -0
  20. package/dist/cjs/net/http/HttpEndpoint.js +22 -0
  21. package/dist/cjs/net/http/HttpEndpoint.js.map +6 -0
  22. package/dist/cjs/net/http/HttpEndpointFactory.d.ts +23 -0
  23. package/dist/cjs/net/http/HttpEndpointFactory.d.ts.map +1 -0
  24. package/dist/cjs/net/http/HttpEndpointFactory.js +39 -0
  25. package/dist/cjs/net/http/HttpEndpointFactory.js.map +6 -0
  26. package/dist/cjs/net/http/HttpEndpointGroup.d.ts +17 -0
  27. package/dist/cjs/net/http/HttpEndpointGroup.d.ts.map +1 -0
  28. package/dist/cjs/net/http/HttpEndpointGroup.js +56 -0
  29. package/dist/cjs/net/http/HttpEndpointGroup.js.map +6 -0
  30. package/dist/cjs/net/http/HttpService.d.ts +20 -0
  31. package/dist/cjs/net/http/HttpService.d.ts.map +1 -0
  32. package/dist/cjs/net/http/HttpService.js +83 -0
  33. package/dist/cjs/net/http/HttpService.js.map +6 -0
  34. package/dist/cjs/net/http/HttpSharedEndpoint.d.ts +16 -0
  35. package/dist/cjs/net/http/HttpSharedEndpoint.d.ts.map +1 -0
  36. package/dist/cjs/net/http/HttpSharedEndpoint.js +92 -0
  37. package/dist/cjs/net/http/HttpSharedEndpoint.js.map +6 -0
  38. package/dist/cjs/net/http/index.d.ts +11 -0
  39. package/dist/cjs/net/http/index.d.ts.map +1 -0
  40. package/dist/cjs/net/http/index.js +28 -0
  41. package/dist/cjs/net/http/index.js.map +6 -0
  42. package/dist/cjs/net/index.d.ts +2 -0
  43. package/dist/cjs/net/index.d.ts.map +1 -1
  44. package/dist/cjs/net/index.js +2 -0
  45. package/dist/cjs/net/index.js.map +1 -1
  46. package/dist/cjs/net/mqtt/MqttEndpoint.d.ts +39 -0
  47. package/dist/cjs/net/mqtt/MqttEndpoint.d.ts.map +1 -0
  48. package/dist/cjs/net/mqtt/MqttEndpoint.js +36 -0
  49. package/dist/cjs/net/mqtt/MqttEndpoint.js.map +6 -0
  50. package/dist/cjs/net/mqtt/MqttEndpointFactory.d.ts +15 -0
  51. package/dist/cjs/net/mqtt/MqttEndpointFactory.d.ts.map +1 -0
  52. package/dist/cjs/net/mqtt/MqttEndpointFactory.js +31 -0
  53. package/dist/cjs/net/mqtt/MqttEndpointFactory.js.map +6 -0
  54. package/dist/cjs/net/mqtt/MqttService.d.ts +19 -0
  55. package/dist/cjs/net/mqtt/MqttService.d.ts.map +1 -0
  56. package/dist/cjs/net/mqtt/MqttService.js +52 -0
  57. package/dist/cjs/net/mqtt/MqttService.js.map +6 -0
  58. package/dist/cjs/net/mqtt/index.d.ts +9 -0
  59. package/dist/cjs/net/mqtt/index.d.ts.map +1 -0
  60. package/dist/cjs/net/mqtt/index.js +26 -0
  61. package/dist/cjs/net/mqtt/index.js.map +6 -0
  62. package/dist/cjs/net/udp/UdpChannel.d.ts +31 -2
  63. package/dist/cjs/net/udp/UdpChannel.d.ts.map +1 -1
  64. package/dist/cjs/net/udp/UdpMulticastServer.d.ts.map +1 -1
  65. package/dist/cjs/net/udp/UdpMulticastServer.js +6 -3
  66. package/dist/cjs/net/udp/UdpMulticastServer.js.map +1 -1
  67. package/dist/cjs/storage/StorageService.d.ts +5 -1
  68. package/dist/cjs/storage/StorageService.d.ts.map +1 -1
  69. package/dist/cjs/storage/StorageService.js +6 -1
  70. package/dist/cjs/storage/StorageService.js.map +1 -1
  71. package/dist/cjs/time/Duration.d.ts +1 -1
  72. package/dist/cjs/util/Abort.d.ts +4 -1
  73. package/dist/cjs/util/Abort.d.ts.map +1 -1
  74. package/dist/cjs/util/Abort.js +19 -3
  75. package/dist/cjs/util/Abort.js.map +1 -1
  76. package/dist/cjs/util/Construction.d.ts +1 -1
  77. package/dist/cjs/util/Multiplex.js +1 -1
  78. package/dist/cjs/util/Multiplex.js.map +1 -1
  79. package/dist/cjs/util/String.d.ts +5 -1
  80. package/dist/cjs/util/String.d.ts.map +1 -1
  81. package/dist/cjs/util/String.js +16 -0
  82. package/dist/cjs/util/String.js.map +1 -1
  83. package/dist/esm/codec/Base64Codec.js +2 -2
  84. package/dist/esm/codec/Base64Codec.js.map +1 -1
  85. package/dist/esm/environment/Environment.d.ts +12 -18
  86. package/dist/esm/environment/Environment.d.ts.map +1 -1
  87. package/dist/esm/environment/Environment.js +17 -32
  88. package/dist/esm/environment/Environment.js.map +1 -1
  89. package/dist/esm/environment/Environmental.d.ts +16 -2
  90. package/dist/esm/environment/Environmental.d.ts.map +1 -1
  91. package/dist/esm/log/Logger.js +1 -1
  92. package/dist/esm/net/Network.d.ts +4 -0
  93. package/dist/esm/net/Network.d.ts.map +1 -1
  94. package/dist/esm/net/Network.js +6 -0
  95. package/dist/esm/net/Network.js.map +1 -1
  96. package/dist/esm/net/ServerAddress.d.ts +1 -0
  97. package/dist/esm/net/ServerAddress.d.ts.map +1 -1
  98. package/dist/esm/net/ServerAddress.js +13 -0
  99. package/dist/esm/net/ServerAddress.js.map +1 -1
  100. package/dist/esm/net/http/HttpEndpoint.d.ts +53 -0
  101. package/dist/esm/net/http/HttpEndpoint.d.ts.map +1 -0
  102. package/dist/esm/net/http/HttpEndpoint.js +6 -0
  103. package/dist/esm/net/http/HttpEndpoint.js.map +6 -0
  104. package/dist/esm/net/http/HttpEndpointFactory.d.ts +23 -0
  105. package/dist/esm/net/http/HttpEndpointFactory.d.ts.map +1 -0
  106. package/dist/esm/net/http/HttpEndpointFactory.js +19 -0
  107. package/dist/esm/net/http/HttpEndpointFactory.js.map +6 -0
  108. package/dist/esm/net/http/HttpEndpointGroup.d.ts +17 -0
  109. package/dist/esm/net/http/HttpEndpointGroup.d.ts.map +1 -0
  110. package/dist/esm/net/http/HttpEndpointGroup.js +36 -0
  111. package/dist/esm/net/http/HttpEndpointGroup.js.map +6 -0
  112. package/dist/esm/net/http/HttpService.d.ts +20 -0
  113. package/dist/esm/net/http/HttpService.d.ts.map +1 -0
  114. package/dist/esm/net/http/HttpService.js +63 -0
  115. package/dist/esm/net/http/HttpService.js.map +6 -0
  116. package/dist/esm/net/http/HttpSharedEndpoint.d.ts +16 -0
  117. package/dist/esm/net/http/HttpSharedEndpoint.d.ts.map +1 -0
  118. package/dist/esm/net/http/HttpSharedEndpoint.js +72 -0
  119. package/dist/esm/net/http/HttpSharedEndpoint.js.map +6 -0
  120. package/dist/esm/net/http/index.d.ts +11 -0
  121. package/dist/esm/net/http/index.d.ts.map +1 -0
  122. package/dist/esm/net/http/index.js +11 -0
  123. package/dist/esm/net/http/index.js.map +6 -0
  124. package/dist/esm/net/index.d.ts +2 -0
  125. package/dist/esm/net/index.d.ts.map +1 -1
  126. package/dist/esm/net/index.js +2 -0
  127. package/dist/esm/net/index.js.map +1 -1
  128. package/dist/esm/net/mqtt/MqttEndpoint.d.ts +39 -0
  129. package/dist/esm/net/mqtt/MqttEndpoint.d.ts.map +1 -0
  130. package/dist/esm/net/mqtt/MqttEndpoint.js +16 -0
  131. package/dist/esm/net/mqtt/MqttEndpoint.js.map +6 -0
  132. package/dist/esm/net/mqtt/MqttEndpointFactory.d.ts +15 -0
  133. package/dist/esm/net/mqtt/MqttEndpointFactory.d.ts.map +1 -0
  134. package/dist/esm/net/mqtt/MqttEndpointFactory.js +11 -0
  135. package/dist/esm/net/mqtt/MqttEndpointFactory.js.map +6 -0
  136. package/dist/esm/net/mqtt/MqttService.d.ts +19 -0
  137. package/dist/esm/net/mqtt/MqttService.d.ts.map +1 -0
  138. package/dist/esm/net/mqtt/MqttService.js +32 -0
  139. package/dist/esm/net/mqtt/MqttService.js.map +6 -0
  140. package/dist/esm/net/mqtt/index.d.ts +9 -0
  141. package/dist/esm/net/mqtt/index.d.ts.map +1 -0
  142. package/dist/esm/net/mqtt/index.js +9 -0
  143. package/dist/esm/net/mqtt/index.js.map +6 -0
  144. package/dist/esm/net/udp/UdpChannel.d.ts +31 -2
  145. package/dist/esm/net/udp/UdpChannel.d.ts.map +1 -1
  146. package/dist/esm/net/udp/UdpMulticastServer.d.ts.map +1 -1
  147. package/dist/esm/net/udp/UdpMulticastServer.js +6 -3
  148. package/dist/esm/net/udp/UdpMulticastServer.js.map +1 -1
  149. package/dist/esm/storage/StorageService.d.ts +5 -1
  150. package/dist/esm/storage/StorageService.d.ts.map +1 -1
  151. package/dist/esm/storage/StorageService.js +6 -1
  152. package/dist/esm/storage/StorageService.js.map +1 -1
  153. package/dist/esm/time/Duration.d.ts +1 -1
  154. package/dist/esm/util/Abort.d.ts +4 -1
  155. package/dist/esm/util/Abort.d.ts.map +1 -1
  156. package/dist/esm/util/Abort.js +19 -3
  157. package/dist/esm/util/Abort.js.map +1 -1
  158. package/dist/esm/util/Construction.d.ts +1 -1
  159. package/dist/esm/util/Multiplex.js +1 -1
  160. package/dist/esm/util/Multiplex.js.map +1 -1
  161. package/dist/esm/util/String.d.ts +5 -1
  162. package/dist/esm/util/String.d.ts.map +1 -1
  163. package/dist/esm/util/String.js +16 -0
  164. package/dist/esm/util/String.js.map +1 -1
  165. package/package.json +2 -2
  166. package/src/codec/Base64Codec.ts +2 -2
  167. package/src/environment/Environment.ts +35 -52
  168. package/src/environment/Environmental.ts +17 -2
  169. package/src/log/Logger.ts +1 -1
  170. package/src/net/Network.ts +4 -0
  171. package/src/net/ServerAddress.ts +16 -0
  172. package/src/net/http/HttpEndpoint.ts +60 -0
  173. package/src/net/http/HttpEndpointFactory.ts +27 -0
  174. package/src/net/http/HttpEndpointGroup.ts +43 -0
  175. package/src/net/http/HttpService.ts +75 -0
  176. package/src/net/http/HttpSharedEndpoint.ts +88 -0
  177. package/src/net/http/index.ts +11 -0
  178. package/src/net/index.ts +2 -0
  179. package/src/net/mqtt/MqttEndpoint.ts +50 -0
  180. package/src/net/mqtt/MqttEndpointFactory.ts +17 -0
  181. package/src/net/mqtt/MqttService.ts +40 -0
  182. package/src/net/mqtt/index.ts +9 -0
  183. package/src/net/udp/UdpChannel.ts +35 -2
  184. package/src/net/udp/UdpMulticastServer.ts +3 -0
  185. package/src/storage/StorageService.ts +14 -1
  186. package/src/time/Duration.ts +1 -1
  187. package/src/util/Abort.ts +28 -4
  188. package/src/util/Construction.ts +2 -2
  189. package/src/util/Multiplex.ts +1 -1
  190. package/src/util/String.ts +25 -6
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Logger } from "#log/Logger.js";
8
+ import { HttpEndpoint } from "./HttpEndpoint.js";
9
+
10
+ const logger = Logger.get("HttpSharedEntpoint");
11
+
12
+ /**
13
+ * Shared access to a single underlying HTTP endpoint.
14
+ */
15
+ export class HttpSharedEndpoint {
16
+ #create: () => Promise<HttpEndpoint>;
17
+ #target?: Promise<HttpEndpoint>;
18
+ #instances = new Set<HttpEndpoint>();
19
+ #isTls: boolean;
20
+
21
+ constructor(isTls: boolean, create: () => Promise<HttpEndpoint>) {
22
+ this.#isTls = isTls;
23
+ this.#create = create;
24
+ }
25
+
26
+ get isTls() {
27
+ return this.#isTls;
28
+ }
29
+
30
+ async use() {
31
+ if (this.#target === undefined) {
32
+ this.#target = this.#createTarget();
33
+ }
34
+
35
+ await this.#target;
36
+
37
+ const instance: HttpEndpoint = {
38
+ http: undefined,
39
+ ws: undefined,
40
+
41
+ close: async () => {
42
+ this.#instances.delete(instance);
43
+ if (!this.#instances.size) {
44
+ try {
45
+ await (await this.#target)?.close();
46
+ } catch (e) {
47
+ logger.error("Error closing HTTP endpoint", e);
48
+ } finally {
49
+ this.#target = undefined;
50
+ }
51
+ }
52
+ },
53
+ };
54
+
55
+ this.#instances.add(instance);
56
+
57
+ return instance;
58
+ }
59
+
60
+ async #createTarget() {
61
+ const target = await this.#create();
62
+
63
+ target.http = async request => {
64
+ for (const instance of this.#instances) {
65
+ const response = await instance.http?.(request);
66
+ if (response) {
67
+ return response;
68
+ }
69
+ }
70
+ };
71
+
72
+ target.ws = async (request, connect) => {
73
+ let connected = false;
74
+ for (const instance of this.#instances) {
75
+ await instance.ws?.(request, async () => {
76
+ connected = true;
77
+ return connect();
78
+ });
79
+
80
+ if (connected) {
81
+ break;
82
+ }
83
+ }
84
+ };
85
+
86
+ return target;
87
+ }
88
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./HttpEndpoint.js";
8
+ export * from "./HttpEndpointFactory.js";
9
+ export * from "./HttpEndpointGroup.js";
10
+ export * from "./HttpService.js";
11
+ export * from "./HttpSharedEndpoint.js";
package/src/net/index.ts CHANGED
@@ -7,7 +7,9 @@
7
7
  export * from "./AppAddress.js";
8
8
  export * from "./Channel.js";
9
9
  export * from "./ConnectionlessTransport.js";
10
+ export * from "./http/index.js";
10
11
  export * from "./mock/index.js";
12
+ export * from "./mqtt/index.js";
11
13
  export * from "./Network.js";
12
14
  export * from "./RetrySchedule.js";
13
15
  export * from "./ServerAddress.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Environment } from "#environment/Environment.js";
8
+ import { AppAddress } from "#net/AppAddress.js";
9
+ import { Abort } from "#util/Abort.js";
10
+ import { Bytes } from "#util/Bytes.js";
11
+ import { MaybePromise } from "#util/Promises.js";
12
+
13
+ export interface MqttEndpoint {
14
+ subscribe(
15
+ topic: string,
16
+ options?: MqttEndpoint.SubscriptionOptions,
17
+ ): AsyncIterableIterator<MqttEndpoint.Message, void, void>;
18
+ publish(message: MqttEndpoint.Message): Promise<void>;
19
+
20
+ close(): Promise<void>;
21
+ }
22
+
23
+ export namespace MqttEndpoint {
24
+ export interface Message {
25
+ topic: string;
26
+ payload: Bytes | string | null;
27
+ retain?: boolean;
28
+ contentType?: string;
29
+ responseTopic?: string;
30
+ correlationData?: Bytes;
31
+ qos?: 0 | 1 | 2;
32
+ }
33
+
34
+ export function Message(message: Message) {
35
+ return message;
36
+ }
37
+
38
+ export interface ConnectionOptions {
39
+ address: AppAddress.Definition;
40
+ environment?: Environment;
41
+ will?: Message;
42
+ onUp?: (endpoint: MqttEndpoint) => MaybePromise<void>;
43
+ onDown?: (endpoint: MqttEndpoint) => MaybePromise<void>;
44
+ }
45
+
46
+ export interface SubscriptionOptions {
47
+ noLocal?: boolean;
48
+ abort?: Abort.Signal;
49
+ }
50
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { MqttEndpoint } from "./MqttEndpoint.js";
8
+ import type { MqttService } from "./MqttService.js";
9
+
10
+ /**
11
+ * Connects to an MQTT broker.
12
+ *
13
+ * MQTT adapters should implement this class. Clients should access via {@link MqttService}.
14
+ */
15
+ export abstract class MqttEndpointFactory {
16
+ abstract connect(options: MqttEndpoint.ConnectionOptions): Promise<MqttEndpoint>;
17
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Environment } from "#environment/Environment.js";
8
+ import { Environmental } from "#environment/Environmental.js";
9
+ import { ImplementationError } from "#MatterError.js";
10
+ import { AppAddress } from "#net/AppAddress.js";
11
+ import { MqttEndpoint } from "./MqttEndpoint.js";
12
+ import { MqttEndpointFactory } from "./MqttEndpointFactory.js";
13
+
14
+ /**
15
+ * Provides connections to MQTT brokers.
16
+ */
17
+ export class MqttService {
18
+ #factory: MqttEndpointFactory;
19
+
20
+ constructor(factory: MqttEndpointFactory) {
21
+ this.#factory = factory;
22
+ }
23
+
24
+ static [Environmental.create](env: Environment) {
25
+ const factory = env.get(MqttEndpointFactory);
26
+ const instance = new this(factory);
27
+ env.set(MqttService, instance);
28
+ return instance;
29
+ }
30
+
31
+ connect(options: MqttEndpoint.ConnectionOptions) {
32
+ const addr = AppAddress.for(options.address);
33
+
34
+ if (addr.appProtocol !== "mqtt") {
35
+ throw new ImplementationError(`Unsupported address ${addr} for MQTT connection`);
36
+ }
37
+
38
+ return this.#factory.connect(options);
39
+ }
40
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./MqttEndpoint.js";
8
+ export * from "./MqttEndpointFactory.js";
9
+ export * from "./MqttService.js";
@@ -11,13 +11,46 @@ import { ConnectionlessTransport } from "../ConnectionlessTransport.js";
11
11
  /** @see {@link MatterSpecification.v12.Core} § 4.4.4 */
12
12
  export const MAX_UDP_MESSAGE_SIZE = 1280 - 48; // 48 bytes IP and UDP header size for IPv6
13
13
 
14
- export type UdpSocketType = "udp4" | "udp6";
14
+ /**
15
+ * UDP socket address type.
16
+ *
17
+ * "udp4" and "udp6" are IPv4 and IPv6 exclusively. "udp" binds both IPv4 and IPv6 addresses.
18
+ */
19
+ export type UdpSocketType = "udp" | "udp4" | "udp6";
15
20
 
16
21
  export interface UdpChannelOptions {
17
- listeningPort?: number;
22
+ /**
23
+ * UDP channel type. "udp4" and "udp6" mean IPv4 and IPv6 respectively. "udp" is dual-mode IPv4/IPv6.
24
+ */
18
25
  type: UdpSocketType;
26
+
27
+ /**
28
+ * The port to listen on. undefined or 0 directs the operating system to select an open port.
29
+ */
30
+ listeningPort?: number;
31
+
32
+ /**
33
+ * The address to listen on, either a hostname or IP address in correct format based on {@link type}.
34
+ *
35
+ * undefined directs the operating system to listen on all addresses on the port. "0.0.0.0" is wildcard IPv4 and
36
+ * "::" is wildcard IPv6.
37
+ *
38
+ * "0.0.0.0" is not allowed if {@link type} is "udp".
39
+ */
19
40
  listeningAddress?: string;
41
+
42
+ /**
43
+ * Specifies a specific network interface.
44
+ *
45
+ * This is required for multicast sockets.
46
+ */
20
47
  netInterface?: string;
48
+
49
+ /**
50
+ * Address+port pairs are normally may normally only be opened by a single socket. This allows shared access to a
51
+ * port.
52
+ */
53
+ reuseAddress?: boolean;
21
54
  }
22
55
 
23
56
  export interface UdpChannel {
@@ -39,6 +39,7 @@ export class UdpMulticastServer {
39
39
  type: "udp4",
40
40
  netInterface,
41
41
  listeningPort,
42
+ reuseAddress: true,
42
43
  });
43
44
  await ipv4UdpChannel.addMembership(broadcastAddressIpv4);
44
45
  } catch (error) {
@@ -52,6 +53,7 @@ export class UdpMulticastServer {
52
53
  type: "udp6",
53
54
  netInterface,
54
55
  listeningPort,
56
+ reuseAddress: true,
55
57
  });
56
58
  await ipv6UdpChannel.addMembership(broadcastAddressIpv6);
57
59
  return new UdpMulticastServer(
@@ -160,6 +162,7 @@ export class UdpMulticastServer {
160
162
  type: iPv4 ? "udp4" : "udp6",
161
163
  listeningPort: this.broadcastPort,
162
164
  netInterface,
165
+ reuseAddress: true,
163
166
  });
164
167
  }
165
168
 
@@ -18,9 +18,17 @@ export class StorageService {
18
18
  #factory?: (namespace: string) => Storage;
19
19
  #location?: string;
20
20
 
21
- constructor(environment: Environment, factory?: (namespace: string) => Storage) {
21
+ constructor(
22
+ environment: Environment,
23
+ factory?: (namespace: string) => Storage,
24
+ resolver?: (...paths: string[]) => string,
25
+ ) {
22
26
  environment.set(StorageService, this);
23
27
  this.#factory = factory;
28
+
29
+ // Fallback resolver is dumb and probably not useful; expected to be replaced by platform implementation if
30
+ // file resolution is necessary
31
+ this.resolve = resolver ?? ((...paths: []) => paths.join("/"));
24
32
  }
25
33
 
26
34
  static [Environmental.create](environment: Environment) {
@@ -61,6 +69,11 @@ export class StorageService {
61
69
  this.#location = location;
62
70
  }
63
71
 
72
+ /**
73
+ * Join one or more relative paths to some platform-dependent notion of an absolute storage path.
74
+ */
75
+ resolve: (...paths: string[]) => string;
76
+
64
77
  [Diagnostic.value]() {
65
78
  return [
66
79
  "Persistence",
@@ -19,7 +19,7 @@ import type { Millis, Seconds, TimeUnit } from "./TimeUnit.js";
19
19
  * Math operators always result in millisecond values and can thus be converted back to an interval using
20
20
  * {@link Millis}. For example, `Millisecs(Hours(1) + Minutes(30))` would produce a 90 minute {@link Duration}.
21
21
  */
22
- export type Duration = Branded<number, "Interval"> | 0;
22
+ export type Duration = Branded<number, "Duration"> | 0;
23
23
 
24
24
  /**
25
25
  * Create an interval from a number or string.
package/src/util/Abort.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import { TimeoutError } from "#MatterError.js";
8
+ import { Duration } from "#time/Duration.js";
9
+ import { Time, Timer } from "#time/Time.js";
10
+
7
11
  /**
8
12
  * Utilities for implementing abort logic.
9
13
  */
@@ -72,20 +76,40 @@ export namespace Abort {
72
76
  * aborts.
73
77
  *
74
78
  * Closing the returned controller unregisters with the input controller. It does not perform an abort.
79
+ *
80
+ * {@link timeout} is a convenience for adding a timeout.
75
81
  */
76
- export function subtask(signal: Signal | undefined): DisposableController {
82
+ export function subtask(signal: Signal | undefined, timeout?: Duration): DisposableController {
83
+ let timer: Timer | undefined;
84
+
77
85
  if (signal && "signal" in signal) {
78
86
  signal = signal.signal;
79
87
  }
80
88
 
81
89
  const controller = new AbortController() as DisposableController;
82
90
 
91
+ if (timeout) {
92
+ timer = Time.getPeriodicTimer("subtask timeout", timeout, () => {
93
+ if (controller.signal.aborted) {
94
+ return;
95
+ }
96
+
97
+ controller.abort(new TimeoutError());
98
+ });
99
+ timer.start();
100
+ }
101
+
83
102
  if (signal) {
84
- const outerHandler = () => controller.abort();
103
+ const outerHandler = () => controller.abort(signal.reason);
85
104
  signal.addEventListener("abort", outerHandler);
86
- controller[Symbol.dispose] = () => signal.removeEventListener("abort", outerHandler);
105
+ controller[Symbol.dispose] = () => {
106
+ signal.removeEventListener("abort", outerHandler);
107
+ timer?.stop();
108
+ };
87
109
  } else {
88
- controller[Symbol.dispose] = () => {};
110
+ controller[Symbol.dispose] = () => {
111
+ timer?.stop();
112
+ };
89
113
  }
90
114
 
91
115
  return controller;
@@ -39,7 +39,7 @@ export async function asyncNew<const A extends any[], const C extends new (...ar
39
39
  * Construction happens in the initializer parameter of {@link Construction} or via {@link Construction.construct} on
40
40
  * the subject. You invoke in your constructor and place in a property called "construction".
41
41
  *
42
- * Destruciton is optional and happens in the destructor parameter of {@link Construction#close} or via
42
+ * Destruction is optional and happens in the destructor parameter of {@link Construction#close} or via
43
43
  * {@link Construction.destruct} on the subject. Typically you invoke in a "close" method of the subject.
44
44
  *
45
45
  * If construction or destruction is not asynchronous (does not return a Promise) then they complete synchronously,
@@ -430,7 +430,7 @@ export function Construction<const T extends Constructable>(
430
430
  }
431
431
  : invokeDestruct;
432
432
 
433
- // Destruction phase 1 - move to destroyed state
433
+ // Destruction phase 1 - move to destroying state
434
434
  function beginDestruction() {
435
435
  if (status === Lifecycle.Status.Destroying || status === Lifecycle.Status.Destroyed) {
436
436
  return self.closed;
@@ -43,7 +43,7 @@ export class BasicMultiplex implements PromiseLike<void> {
43
43
  if (description) {
44
44
  message = `${message} in ${description}`;
45
45
  }
46
- logger.error(message, e);
46
+ logger.error(`${message}:`, e);
47
47
  })
48
48
  .finally(() => this.#workers.delete(entry)),
49
49
  description,
@@ -104,7 +104,7 @@ export function decamelize(name: string, separator = "-") {
104
104
  /**
105
105
  * Like JSON.stringify but targets well-formed JS and is slightly more readable.
106
106
  */
107
- export function serialize(value: any) {
107
+ export function serialize(value: unknown) {
108
108
  const visited = new Set();
109
109
 
110
110
  function asValidKey(key: string) {
@@ -114,15 +114,15 @@ export function serialize(value: any) {
114
114
  return JSON.stringify(key);
115
115
  }
116
116
 
117
- function serializeOne(value: any): string | undefined {
117
+ function serializeOne(value: unknown): string | undefined {
118
118
  if (value === undefined) {
119
119
  return;
120
120
  }
121
121
  if (value === null) {
122
122
  return "null";
123
123
  }
124
- if (value[serialize.SERIALIZE]) {
125
- return value[serialize.SERIALIZE]();
124
+ if ((value as { [serialize.SERIALIZE](): string })[serialize.SERIALIZE]) {
125
+ return (value as { [serialize.SERIALIZE](): string })[serialize.SERIALIZE]();
126
126
  }
127
127
  if (typeof value === "function") {
128
128
  return;
@@ -147,7 +147,7 @@ export function serialize(value: any) {
147
147
  if (visited.has(value)) {
148
148
  return;
149
149
  }
150
- if (value.toJSON) {
150
+ if ((value as { toJSON(): string }).toJSON) {
151
151
  value = JSON.parse(JSON.stringify(value));
152
152
  }
153
153
 
@@ -161,7 +161,7 @@ export function serialize(value: any) {
161
161
  return "[]";
162
162
  }
163
163
 
164
- const entries = Object.entries(value)
164
+ const entries = Object.entries(value as {})
165
165
  .map(([k, v]) => [k, serializeOne(v)])
166
166
  .filter(([_k, v]) => v !== undefined)
167
167
  .map(([k, v]) => `${asValidKey(k ?? "")}: ${v}`);
@@ -179,6 +179,25 @@ export function serialize(value: any) {
179
179
  return serializeOne(value);
180
180
  }
181
181
 
182
+ /**
183
+ * Like {@link JSON.stringify} but with support for additional standard objects.
184
+ */
185
+ export function asJson(value: unknown, space?: number) {
186
+ return JSON.stringify(
187
+ value,
188
+ (_key, value) => {
189
+ if (typeof value === "bigint") {
190
+ return value.toString();
191
+ }
192
+ if (Bytes.isBytes(value)) {
193
+ return Bytes.toHex(value);
194
+ }
195
+ return value;
196
+ },
197
+ space,
198
+ );
199
+ }
200
+
182
201
  export namespace serialize {
183
202
  /**
184
203
  * Custom serialization function key.