@matter/react-native 0.11.0-alpha.0-20241005-e3e4e4a7a

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 (161) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +56 -0
  3. package/dist/cjs/ble/BleReactNative.d.ts +17 -0
  4. package/dist/cjs/ble/BleReactNative.d.ts.map +1 -0
  5. package/dist/cjs/ble/BleReactNative.js +61 -0
  6. package/dist/cjs/ble/BleReactNative.js.map +6 -0
  7. package/dist/cjs/ble/BleScanner.d.ts +52 -0
  8. package/dist/cjs/ble/BleScanner.d.ts.map +1 -0
  9. package/dist/cjs/ble/BleScanner.js +239 -0
  10. package/dist/cjs/ble/BleScanner.js.map +6 -0
  11. package/dist/cjs/ble/ReactNativeBleChannel.d.ts +33 -0
  12. package/dist/cjs/ble/ReactNativeBleChannel.d.ts.map +1 -0
  13. package/dist/cjs/ble/ReactNativeBleChannel.js +252 -0
  14. package/dist/cjs/ble/ReactNativeBleChannel.js.map +6 -0
  15. package/dist/cjs/ble/ReactNativeBleClient.d.ts +25 -0
  16. package/dist/cjs/ble/ReactNativeBleClient.d.ts.map +1 -0
  17. package/dist/cjs/ble/ReactNativeBleClient.js +143 -0
  18. package/dist/cjs/ble/ReactNativeBleClient.js.map +6 -0
  19. package/dist/cjs/ble/export.d.ts +8 -0
  20. package/dist/cjs/ble/export.d.ts.map +1 -0
  21. package/dist/cjs/ble/export.js +25 -0
  22. package/dist/cjs/ble/export.js.map +6 -0
  23. package/dist/cjs/crypto/ReactNativeCrypto.d.ts +12 -0
  24. package/dist/cjs/crypto/ReactNativeCrypto.d.ts.map +1 -0
  25. package/dist/cjs/crypto/ReactNativeCrypto.js +95 -0
  26. package/dist/cjs/crypto/ReactNativeCrypto.js.map +6 -0
  27. package/dist/cjs/crypto/export.d.ts +7 -0
  28. package/dist/cjs/crypto/export.d.ts.map +1 -0
  29. package/dist/cjs/crypto/export.js +24 -0
  30. package/dist/cjs/crypto/export.js.map +6 -0
  31. package/dist/cjs/crypto/register.d.ts +7 -0
  32. package/dist/cjs/crypto/register.d.ts.map +1 -0
  33. package/dist/cjs/crypto/register.js +15 -0
  34. package/dist/cjs/crypto/register.js.map +6 -0
  35. package/dist/cjs/environment/ReactNativeEnvironment.d.ts +16 -0
  36. package/dist/cjs/environment/ReactNativeEnvironment.d.ts.map +1 -0
  37. package/dist/cjs/environment/ReactNativeEnvironment.js +60 -0
  38. package/dist/cjs/environment/ReactNativeEnvironment.js.map +6 -0
  39. package/dist/cjs/environment/export.d.ts +7 -0
  40. package/dist/cjs/environment/export.d.ts.map +1 -0
  41. package/dist/cjs/environment/export.js +24 -0
  42. package/dist/cjs/environment/export.js.map +6 -0
  43. package/dist/cjs/environment/register.d.ts +7 -0
  44. package/dist/cjs/environment/register.d.ts.map +1 -0
  45. package/dist/cjs/environment/register.js +12 -0
  46. package/dist/cjs/environment/register.js.map +6 -0
  47. package/dist/cjs/export.d.ts +9 -0
  48. package/dist/cjs/export.d.ts.map +1 -0
  49. package/dist/cjs/export.js +10 -0
  50. package/dist/cjs/export.js.map +6 -0
  51. package/dist/cjs/net/NetworkReactNative.d.ts +20 -0
  52. package/dist/cjs/net/NetworkReactNative.d.ts.map +1 -0
  53. package/dist/cjs/net/NetworkReactNative.js +173 -0
  54. package/dist/cjs/net/NetworkReactNative.js.map +6 -0
  55. package/dist/cjs/net/UdpChannelReactNative.d.ts +39 -0
  56. package/dist/cjs/net/UdpChannelReactNative.d.ts.map +1 -0
  57. package/dist/cjs/net/UdpChannelReactNative.js +184 -0
  58. package/dist/cjs/net/UdpChannelReactNative.js.map +6 -0
  59. package/dist/cjs/net/export.d.ts +8 -0
  60. package/dist/cjs/net/export.d.ts.map +1 -0
  61. package/dist/cjs/net/export.js +35 -0
  62. package/dist/cjs/net/export.js.map +6 -0
  63. package/dist/cjs/net/register.d.ts +7 -0
  64. package/dist/cjs/net/register.d.ts.map +1 -0
  65. package/dist/cjs/net/register.js +15 -0
  66. package/dist/cjs/net/register.js.map +6 -0
  67. package/dist/cjs/package.json +10 -0
  68. package/dist/cjs/storage/StorageBackendAsyncStorage.d.ts +31 -0
  69. package/dist/cjs/storage/StorageBackendAsyncStorage.d.ts.map +1 -0
  70. package/dist/cjs/storage/StorageBackendAsyncStorage.js +142 -0
  71. package/dist/cjs/storage/StorageBackendAsyncStorage.js.map +6 -0
  72. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  73. package/dist/esm/ble/BleReactNative.d.ts +17 -0
  74. package/dist/esm/ble/BleReactNative.d.ts.map +1 -0
  75. package/dist/esm/ble/BleReactNative.js +41 -0
  76. package/dist/esm/ble/BleReactNative.js.map +6 -0
  77. package/dist/esm/ble/BleScanner.d.ts +52 -0
  78. package/dist/esm/ble/BleScanner.d.ts.map +1 -0
  79. package/dist/esm/ble/BleScanner.js +219 -0
  80. package/dist/esm/ble/BleScanner.js.map +6 -0
  81. package/dist/esm/ble/ReactNativeBleChannel.d.ts +33 -0
  82. package/dist/esm/ble/ReactNativeBleChannel.d.ts.map +1 -0
  83. package/dist/esm/ble/ReactNativeBleChannel.js +257 -0
  84. package/dist/esm/ble/ReactNativeBleChannel.js.map +6 -0
  85. package/dist/esm/ble/ReactNativeBleClient.d.ts +25 -0
  86. package/dist/esm/ble/ReactNativeBleClient.d.ts.map +1 -0
  87. package/dist/esm/ble/ReactNativeBleClient.js +123 -0
  88. package/dist/esm/ble/ReactNativeBleClient.js.map +6 -0
  89. package/dist/esm/ble/export.d.ts +8 -0
  90. package/dist/esm/ble/export.d.ts.map +1 -0
  91. package/dist/esm/ble/export.js +8 -0
  92. package/dist/esm/ble/export.js.map +6 -0
  93. package/dist/esm/crypto/ReactNativeCrypto.d.ts +12 -0
  94. package/dist/esm/crypto/ReactNativeCrypto.d.ts.map +1 -0
  95. package/dist/esm/crypto/ReactNativeCrypto.js +65 -0
  96. package/dist/esm/crypto/ReactNativeCrypto.js.map +6 -0
  97. package/dist/esm/crypto/export.d.ts +7 -0
  98. package/dist/esm/crypto/export.d.ts.map +1 -0
  99. package/dist/esm/crypto/export.js +7 -0
  100. package/dist/esm/crypto/export.js.map +6 -0
  101. package/dist/esm/crypto/register.d.ts +7 -0
  102. package/dist/esm/crypto/register.d.ts.map +1 -0
  103. package/dist/esm/crypto/register.js +14 -0
  104. package/dist/esm/crypto/register.js.map +6 -0
  105. package/dist/esm/environment/ReactNativeEnvironment.d.ts +16 -0
  106. package/dist/esm/environment/ReactNativeEnvironment.d.ts.map +1 -0
  107. package/dist/esm/environment/ReactNativeEnvironment.js +40 -0
  108. package/dist/esm/environment/ReactNativeEnvironment.js.map +6 -0
  109. package/dist/esm/environment/export.d.ts +7 -0
  110. package/dist/esm/environment/export.d.ts.map +1 -0
  111. package/dist/esm/environment/export.js +7 -0
  112. package/dist/esm/environment/export.js.map +6 -0
  113. package/dist/esm/environment/register.d.ts +7 -0
  114. package/dist/esm/environment/register.d.ts.map +1 -0
  115. package/dist/esm/environment/register.js +11 -0
  116. package/dist/esm/environment/register.js.map +6 -0
  117. package/dist/esm/export.d.ts +9 -0
  118. package/dist/esm/export.d.ts.map +1 -0
  119. package/dist/esm/export.js +9 -0
  120. package/dist/esm/export.js.map +6 -0
  121. package/dist/esm/net/NetworkReactNative.d.ts +20 -0
  122. package/dist/esm/net/NetworkReactNative.d.ts.map +1 -0
  123. package/dist/esm/net/NetworkReactNative.js +151 -0
  124. package/dist/esm/net/NetworkReactNative.js.map +6 -0
  125. package/dist/esm/net/UdpChannelReactNative.d.ts +39 -0
  126. package/dist/esm/net/UdpChannelReactNative.d.ts.map +1 -0
  127. package/dist/esm/net/UdpChannelReactNative.js +162 -0
  128. package/dist/esm/net/UdpChannelReactNative.js.map +6 -0
  129. package/dist/esm/net/export.d.ts +8 -0
  130. package/dist/esm/net/export.d.ts.map +1 -0
  131. package/dist/esm/net/export.js +14 -0
  132. package/dist/esm/net/export.js.map +6 -0
  133. package/dist/esm/net/register.d.ts +7 -0
  134. package/dist/esm/net/register.d.ts.map +1 -0
  135. package/dist/esm/net/register.js +14 -0
  136. package/dist/esm/net/register.js.map +6 -0
  137. package/dist/esm/package.json +10 -0
  138. package/dist/esm/storage/StorageBackendAsyncStorage.d.ts +31 -0
  139. package/dist/esm/storage/StorageBackendAsyncStorage.d.ts.map +1 -0
  140. package/dist/esm/storage/StorageBackendAsyncStorage.js +112 -0
  141. package/dist/esm/storage/StorageBackendAsyncStorage.js.map +6 -0
  142. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  143. package/package.json +105 -0
  144. package/src/ble/BleReactNative.ts +45 -0
  145. package/src/ble/BleScanner.ts +277 -0
  146. package/src/ble/ReactNativeBleChannel.ts +313 -0
  147. package/src/ble/ReactNativeBleClient.ts +132 -0
  148. package/src/ble/export.ts +8 -0
  149. package/src/crypto/ReactNativeCrypto.ts +108 -0
  150. package/src/crypto/export.ts +7 -0
  151. package/src/crypto/register.ts +16 -0
  152. package/src/environment/ReactNativeEnvironment.ts +53 -0
  153. package/src/environment/export.ts +7 -0
  154. package/src/environment/register.ts +16 -0
  155. package/src/export.ts +11 -0
  156. package/src/net/NetworkReactNative.ts +190 -0
  157. package/src/net/UdpChannelReactNative.ts +219 -0
  158. package/src/net/export.ts +13 -0
  159. package/src/net/register.ts +17 -0
  160. package/src/storage/StorageBackendAsyncStorage.ts +145 -0
  161. package/src/tsconfig.json +17 -0
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import dgram from "react-native-udp";
7
+
8
+ // @ts-expect-error globalThis is no index structure
9
+ global.dgram = dgram;
10
+
11
+ // @ts-expect-error globalThis is no index structure
12
+ const rnDGramCreateSocket = dgram.createSocket;
13
+ // @ts-expect-error globalThis is no index structure
14
+ // Work around because React-Native UDP lib is not providing the setMulticastInterface method
15
+ dgram.createSocket = (...args: any[]) => {
16
+ const socket = rnDGramCreateSocket(...args);
17
+ socket.setMulticastInterface = () => {}; // Stub for now
18
+
19
+ const originalSend = socket.send;
20
+ socket.send = (
21
+ buffer: Uint8Array,
22
+ port: number,
23
+ address: string,
24
+ callback: (error: Error | null, bytes: number) => void,
25
+ ) => originalSend(buffer, 0, buffer.length, port, address, callback);
26
+ return socket;
27
+ };
28
+
29
+ import {
30
+ AsyncCache,
31
+ InterfaceType,
32
+ isIPv6,
33
+ Logger,
34
+ Network,
35
+ NetworkError,
36
+ NetworkInterface,
37
+ NetworkInterfaceDetails,
38
+ onSameNetwork,
39
+ UdpChannel,
40
+ UdpChannelOptions,
41
+ } from "#general";
42
+ import { fetch as fetchNetworkInfo } from "@react-native-community/netinfo";
43
+ import { UdpChannelReactNative } from "./UdpChannelReactNative.js";
44
+
45
+ const logger = Logger.get("NetworkNode");
46
+
47
+ type NetworkInterfaceInfo = {
48
+ address: string;
49
+ family: string;
50
+ netmask: string;
51
+ internal: boolean;
52
+ };
53
+
54
+ async function networkInterfaces() {
55
+ const netInfo = [await fetchNetworkInfo("wifi"), await fetchNetworkInfo("ethernet")];
56
+ const networkInterfaces: Record<string, NetworkInterfaceInfo[]> = {};
57
+ netInfo.forEach(({ type, isConnected, details }) => {
58
+ if (!details || !isConnected) return;
59
+ const ipAddress = "ipAddress" in details ? (details.ipAddress as string | null) : undefined;
60
+ const subnet = "subnet" in details ? (details.subnet as string | null) : undefined;
61
+ if (!ipAddress || !subnet) return;
62
+ networkInterfaces[type] = [
63
+ {
64
+ address: ipAddress,
65
+ family: isIPv6(ipAddress) ? "IPv6" : "IPv4",
66
+ netmask: subnet,
67
+ internal: false,
68
+ },
69
+ ];
70
+ });
71
+ return networkInterfaces;
72
+ }
73
+
74
+ export class NetworkReactNative extends Network {
75
+ static async getMulticastInterfaceIpv4(netInterface: string): Promise<string | undefined> {
76
+ const netInterfaceInfo = (await networkInterfaces())[netInterface];
77
+ if (netInterfaceInfo === undefined) throw new NetworkError(`Unknown interface: ${netInterface}`);
78
+ for (const { address, family } of netInterfaceInfo) {
79
+ if (family === "IPv4") {
80
+ return address;
81
+ }
82
+ }
83
+ return undefined;
84
+ }
85
+
86
+ static async getMembershipMulticastInterfaces(
87
+ netInterface: string | undefined,
88
+ ipv4: boolean,
89
+ ): Promise<(string | undefined)[]> {
90
+ if (ipv4) {
91
+ return [undefined];
92
+ } else {
93
+ let networkInterfaceEntries = Object.entries(await networkInterfaces());
94
+ if (netInterface !== undefined) {
95
+ networkInterfaceEntries = networkInterfaceEntries.filter(([name]) => name === netInterface);
96
+ }
97
+ const multicastInterfaces = networkInterfaceEntries.flatMap(([netInterface, netInterfaceInfo]) => {
98
+ if (netInterfaceInfo === undefined) return [];
99
+ const zone = netInterface;
100
+ return zone === undefined ? [] : [`::%${zone}`];
101
+ });
102
+ if (multicastInterfaces.length === 0) {
103
+ logger.warn(
104
+ `No IPv6 multicast interface found${
105
+ netInterface !== undefined ? ` for interface ${netInterface}` : ""
106
+ }.`,
107
+ );
108
+ }
109
+ return multicastInterfaces;
110
+ }
111
+ }
112
+
113
+ static getNetInterfaceForIp(ip: string) {
114
+ // Finding the local interface on the same interface is complex and won't change
115
+ // So let's cache the results for 5mn
116
+ return this.netInterfaces.get(ip);
117
+ }
118
+
119
+ private static readonly netInterfaces = new AsyncCache<string | undefined>(
120
+ "Network interface",
121
+ (ip: string) => this.getNetInterfaceForRemoveAddress(ip),
122
+ 5 * 60 * 1000 /* 5mn */,
123
+ );
124
+
125
+ override async close() {
126
+ await NetworkReactNative.netInterfaces.close();
127
+ }
128
+
129
+ private static async getNetInterfaceForRemoveAddress(ip: string) {
130
+ if (ip.includes("%")) {
131
+ // IPv6 address with scope
132
+ return ip.split("%")[1];
133
+ } else {
134
+ const interfaces = await networkInterfaces();
135
+ for (const name in interfaces) {
136
+ const netInterfaces = interfaces[name];
137
+ for (const { address, netmask } of netInterfaces) {
138
+ if (onSameNetwork(ip, address, netmask)) {
139
+ return name;
140
+ }
141
+ }
142
+ }
143
+ if (isIPv6(ip)) {
144
+ if (ip.startsWith("fd")) {
145
+ // IPv6 address is an ULA
146
+ return ""; // consider it as being ok and using the "Default interface"
147
+ }
148
+ }
149
+ return undefined;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Get all network interfaces.
155
+ * The optional configuration parameter allows to map interface names to types if this mapping is known.
156
+ * Each network interface which has no mapped type is returned as Ethernet for now.
157
+ *
158
+ * @param configuration - An array of objects with the name and type properties.
159
+ */
160
+ async getNetInterfaces(configuration: NetworkInterface[] = []): Promise<NetworkInterface[]> {
161
+ const result = new Array<NetworkInterface>();
162
+ const interfaces = await networkInterfaces();
163
+ for (const name in interfaces) {
164
+ const netInterfaces = interfaces[name];
165
+ if (netInterfaces.length === 0) continue;
166
+ if (netInterfaces[0].internal) continue;
167
+ let type = InterfaceType.Ethernet;
168
+ if (configuration.length > 0) {
169
+ const nameType = configuration.find(({ name: mapName }) => name === mapName);
170
+ if (nameType !== undefined && nameType.type !== undefined) {
171
+ type = nameType.type;
172
+ }
173
+ }
174
+ result.push({ name, type });
175
+ }
176
+ return result;
177
+ }
178
+
179
+ async getIpMac(netInterface: string): Promise<NetworkInterfaceDetails | undefined> {
180
+ const netInterfaceInfo = (await networkInterfaces())[netInterface];
181
+ if (netInterfaceInfo === undefined) return undefined;
182
+ const ipV4 = netInterfaceInfo.filter(({ family }) => family === "IPv4").map(({ address }) => address);
183
+ const ipV6 = netInterfaceInfo.filter(({ family }) => family === "IPv6").map(({ address }) => address);
184
+ return { mac: "00:00:00:00:00:00", ipV4, ipV6 };
185
+ }
186
+
187
+ override createUdpChannel(options: UdpChannelOptions): Promise<UdpChannel> {
188
+ return UdpChannelReactNative.create(options);
189
+ }
190
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import dgram from "react-native-udp";
7
+
8
+ import {
9
+ ChannelType,
10
+ Diagnostic,
11
+ isIPv4,
12
+ isIPv6,
13
+ Logger,
14
+ MAX_UDP_MESSAGE_SIZE,
15
+ NetworkError,
16
+ UdpChannel,
17
+ UdpChannelOptions,
18
+ } from "#general";
19
+ import { NetworkReactNative } from "./NetworkReactNative.js";
20
+
21
+ const logger = Logger.get("UdpChannelNode");
22
+
23
+ // Move out some dram types not available in react-native-udp
24
+ // TODO find a way to clean that up once anything is working
25
+ interface RemoteInfo {
26
+ address: string;
27
+ family: "IPv4" | "IPv6";
28
+ port: number;
29
+ size: number;
30
+ }
31
+ type SocketType = "udp4" | "udp6";
32
+ interface SocketOptions {
33
+ type: SocketType;
34
+ reuseAddr?: boolean | undefined;
35
+ /**
36
+ * @default false
37
+ */
38
+ ipv6Only?: boolean | undefined;
39
+ recvBufferSize?: number | undefined;
40
+ sendBufferSize?: number | undefined;
41
+ lookup?:
42
+ | ((
43
+ hostname: string,
44
+ options: any,
45
+ callback: (err: Error | null, address: string, family: number) => void,
46
+ ) => void)
47
+ | undefined;
48
+ }
49
+ interface Socket {
50
+ setBroadcast(flag: boolean): void;
51
+ setMulticastInterface(interfaceAddress: string): void;
52
+ addMembership(multicastAddress: string, multicastInterface?: string): void;
53
+ on(event: "message", listener: (msg: Uint8Array, rinfo: RemoteInfo) => void): void;
54
+ on(event: "error", listener: (error: Error) => void): void;
55
+ removeListener(event: "message", listener: (msg: Uint8Array, rinfo: RemoteInfo) => void): void;
56
+ removeListener(event: "error", listener: (error: Error) => void): void;
57
+ send(msg: Uint8Array, port: number, address: string, callback: (error: Error | null) => void): void;
58
+ close(): void;
59
+ address(): { address: string; port: number };
60
+ }
61
+
62
+ function createDgramSocket(host: string | undefined, port: number | undefined, options: SocketOptions) {
63
+ // @ts-expect-error default types are strange
64
+ const socket = dgram.createSocket({
65
+ ...options,
66
+ reusePort: options.reuseAddr,
67
+ });
68
+ return new Promise<Socket>((resolve, reject) => {
69
+ const handleBindError = (error: Error) => {
70
+ try {
71
+ socket.close();
72
+ } catch (error) {
73
+ logger.debug("Error on closing socket", error);
74
+ }
75
+ reject(error);
76
+ };
77
+ socket.on("error", handleBindError);
78
+ socket.bind(port, host, (error: any) => {
79
+ if (error) return;
80
+ const { address: localHost, port: localPort } = socket.address();
81
+ logger.debug(
82
+ "Socket created and bound ",
83
+ Diagnostic.dict({
84
+ remoteAddress: `${host}:${port}`,
85
+ localAddress: `${localHost}:${localPort}`,
86
+ }),
87
+ );
88
+ socket.removeListener("error", handleBindError);
89
+ socket.on("error", (error: Error) => logger.error(error));
90
+ resolve(socket);
91
+ });
92
+ });
93
+ }
94
+
95
+ export class UdpChannelReactNative implements UdpChannel {
96
+ static async create({
97
+ listeningPort,
98
+ type,
99
+ listeningAddress,
100
+ netInterface,
101
+ membershipAddresses,
102
+ }: UdpChannelOptions) {
103
+ const socketOptions: SocketOptions = { type, reuseAddr: true };
104
+ if (type === "udp6") {
105
+ socketOptions.ipv6Only = true;
106
+ }
107
+ const socket = await createDgramSocket(listeningAddress, listeningPort, socketOptions);
108
+ socket.setBroadcast(true);
109
+ let netInterfaceZone: string | undefined;
110
+ if (netInterface !== undefined) {
111
+ netInterfaceZone = netInterface;
112
+ let multicastInterface: string | undefined;
113
+ if (type === "udp4") {
114
+ multicastInterface = await NetworkReactNative.getMulticastInterfaceIpv4(netInterface);
115
+ if (multicastInterface === undefined) {
116
+ throw new NetworkError(`No IPv4 addresses on interface: ${netInterface}`);
117
+ }
118
+ } else {
119
+ multicastInterface = `::%${netInterfaceZone}`;
120
+ }
121
+ logger.debug(
122
+ "Initialize multicast",
123
+ Diagnostic.dict({
124
+ address: `${multicastInterface}:${listeningPort}`,
125
+ interface: netInterface,
126
+ type: type,
127
+ }),
128
+ );
129
+ socket.setMulticastInterface(multicastInterface);
130
+ }
131
+ if (membershipAddresses !== undefined) {
132
+ const multicastInterfaces = await NetworkReactNative.getMembershipMulticastInterfaces(
133
+ netInterface,
134
+ type === "udp4",
135
+ );
136
+ for (const address of membershipAddresses) {
137
+ for (const multicastInterface of multicastInterfaces) {
138
+ try {
139
+ socket.addMembership(address, multicastInterface);
140
+ } catch (error) {
141
+ logger.warn(
142
+ `Error adding membership for address ${address}${
143
+ multicastInterface ? ` with interface ${multicastInterface}` : ""
144
+ }: ${error}`,
145
+ );
146
+ }
147
+ }
148
+ }
149
+ }
150
+ return new UdpChannelReactNative(type, socket, netInterfaceZone);
151
+ }
152
+
153
+ readonly maxPayloadSize = MAX_UDP_MESSAGE_SIZE;
154
+
155
+ constructor(
156
+ private readonly type: "udp4" | "udp6",
157
+ private readonly socket: Socket,
158
+ private readonly netInterface?: string,
159
+ ) {}
160
+
161
+ onData(listener: (netInterface: string, peerAddress: string, peerPort: number, data: Uint8Array) => void) {
162
+ const messageListener = async (data: Uint8Array, { address, port }: RemoteInfo) => {
163
+ const netInterface = this.netInterface ?? (await NetworkReactNative.getNetInterfaceForIp(address));
164
+ if (netInterface === undefined) return;
165
+ listener(netInterface, address, port, data);
166
+ };
167
+
168
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
169
+ this.socket.on("message", messageListener);
170
+ return {
171
+ close: async () => {
172
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
173
+ this.socket.removeListener("message", messageListener);
174
+ },
175
+ };
176
+ }
177
+
178
+ async send(host: string, port: number, data: Uint8Array) {
179
+ return new Promise<void>((resolve, reject) => {
180
+ this.socket.send(data, port, host, error => {
181
+ if (error !== null) {
182
+ const netError = new NetworkError(error.message);
183
+ netError.stack = error.stack;
184
+ reject(netError);
185
+ return;
186
+ }
187
+ resolve();
188
+ });
189
+ });
190
+ }
191
+
192
+ async close() {
193
+ try {
194
+ this.socket.close();
195
+ } catch (error) {
196
+ logger.debug("Error on closing socket", error);
197
+ }
198
+ }
199
+
200
+ get port() {
201
+ return this.socket.address().port;
202
+ }
203
+
204
+ supports(type: ChannelType, address?: string) {
205
+ if (type !== ChannelType.UDP) {
206
+ return false;
207
+ }
208
+
209
+ if (address === undefined) {
210
+ return true;
211
+ }
212
+
213
+ if (this.type === "udp4") {
214
+ return isIPv4(address);
215
+ }
216
+
217
+ return isIPv6(address);
218
+ }
219
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Network } from "@matter/general";
8
+
9
+ export * from "./NetworkReactNative.js";
10
+
11
+ export async function closeNetwork() {
12
+ return Network.get().close();
13
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Network, NoProviderError, singleton } from "@matter/general";
8
+ import { NetworkReactNative } from "./NetworkReactNative.js";
9
+
10
+ // Check if Network singleton is already registered and auto register if not
11
+ try {
12
+ Network.get();
13
+ } catch (error) {
14
+ NoProviderError.accept(error);
15
+ Network.get = singleton(() => new NetworkReactNative());
16
+ // use net export closeNetwork() to close the network
17
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { MaybeAsyncStorage, StorageError, SupportedStorageTypes, fromJson, toJson } from "#general";
8
+ import AsyncStorage from "@react-native-async-storage/async-storage";
9
+
10
+ export class StorageBackendAsyncStorage extends MaybeAsyncStorage {
11
+ #namespace: string;
12
+ protected isInitialized = false;
13
+
14
+ /**
15
+ * Creates a new instance of the AsyncStorage storage backend. In a "namespace" is provided then the keys will be
16
+ * prefixed with the namespace (separated with a # which is normally not used in matter.js keys).
17
+ */
18
+ constructor(namespace?: string) {
19
+ super();
20
+ this.#namespace = namespace ?? "";
21
+ }
22
+
23
+ get initialized() {
24
+ return this.isInitialized;
25
+ }
26
+
27
+ initialize() {
28
+ this.isInitialized = true;
29
+ }
30
+
31
+ close() {
32
+ this.isInitialized = false;
33
+ }
34
+
35
+ clear() {
36
+ // @ts-expect-error AsyncStorage types are not correct
37
+ return AsyncStorage.clear();
38
+ }
39
+
40
+ getContextBaseKey(contexts: string[], allowEmptyContext = false) {
41
+ const contextKey = contexts.join(".");
42
+ if (
43
+ (!contextKey.length && !allowEmptyContext) ||
44
+ contextKey.includes("..") ||
45
+ contextKey.startsWith(".") ||
46
+ contextKey.endsWith(".")
47
+ )
48
+ throw new StorageError("Context must not be an empty and not contain dots.");
49
+ return `${this.#namespace.length ? `${this.#namespace}#` : ""}${contextKey}`;
50
+ }
51
+
52
+ buildStorageKey(contexts: string[], key: string) {
53
+ if (!key.length) {
54
+ throw new StorageError("Key must not be an empty string.");
55
+ }
56
+ const contextKey = this.getContextBaseKey(contexts);
57
+ return `${contextKey}.${key}`;
58
+ }
59
+
60
+ async get<T extends SupportedStorageTypes>(contexts: string[], key: string): Promise<T | undefined> {
61
+ // @ts-expect-error AsyncStorage types are not correct
62
+ const value = await AsyncStorage.getItem(this.buildStorageKey(contexts, key));
63
+ if (value === null) return undefined;
64
+ return fromJson(value) as T;
65
+ }
66
+
67
+ set(contexts: string[], key: string, value: SupportedStorageTypes): Promise<void>;
68
+ set(contexts: string[], values: Record<string, SupportedStorageTypes>): Promise<void>;
69
+ async set(
70
+ contexts: string[],
71
+ keyOrValues: string | Record<string, SupportedStorageTypes>,
72
+ value?: SupportedStorageTypes,
73
+ ) {
74
+ if (typeof keyOrValues === "string") {
75
+ // @ts-expect-error AsyncStorage types are not correct
76
+ await AsyncStorage.setItem(this.buildStorageKey(contexts, keyOrValues), toJson(value));
77
+ } else {
78
+ for (const [key, value] of Object.entries(keyOrValues)) {
79
+ // @ts-expect-error AsyncStorage types are not correct
80
+ await AsyncStorage.setItem(this.buildStorageKey(contexts, key), toJson(value));
81
+ }
82
+ }
83
+ }
84
+
85
+ delete(contexts: string[], key: string) {
86
+ // @ts-expect-error AsyncStorage types are not correct
87
+ return AsyncStorage.removeItem(this.buildStorageKey(contexts, key));
88
+ }
89
+
90
+ /** Returns all keys of a storage context without keys of sub-contexts */
91
+ async keys(contexts: string[]) {
92
+ const contextKey = this.getContextBaseKey(contexts);
93
+ const keys = [];
94
+ const contextKeyStart = `${contextKey}.`;
95
+ // @ts-expect-error AsyncStorage types are not correct
96
+ const allKeys = await AsyncStorage.getAllKeys();
97
+ for (const key of allKeys) {
98
+ if (key.startsWith(contextKeyStart) && !key.includes(".", contextKeyStart.length)) {
99
+ keys.push(key.substring(contextKeyStart.length));
100
+ }
101
+ }
102
+ return keys;
103
+ }
104
+
105
+ async values(contexts: string[]) {
106
+ // Initialize and context checks are done by keys method
107
+ const values = {} as Record<string, SupportedStorageTypes>;
108
+ for (const key of await this.keys(contexts)) {
109
+ values[key] = await this.get(contexts, key);
110
+ }
111
+ return values;
112
+ }
113
+
114
+ async contexts(contexts: string[]) {
115
+ const contextKey = this.getContextBaseKey(contexts, true);
116
+ const startContextKey = contextKey.length ? `${contextKey}.` : "";
117
+ const foundContexts = new Array<string>();
118
+ // @ts-expect-error AsyncStorage types are not correct
119
+ const allKeys = await AsyncStorage.getAllKeys();
120
+ for (const key of allKeys) {
121
+ if (key.startsWith(startContextKey)) {
122
+ const subKeys = key.substring(startContextKey.length).split(".");
123
+ if (subKeys.length === 1) continue; // found leaf key
124
+ const context = subKeys[0];
125
+ if (!foundContexts.includes(context)) {
126
+ foundContexts.push(context);
127
+ }
128
+ }
129
+ }
130
+ return foundContexts;
131
+ }
132
+
133
+ async clearAll(contexts: string[]) {
134
+ const contextKey = this.getContextBaseKey(contexts, true);
135
+ const startContextKey = contextKey.length ? `${contextKey}.` : "";
136
+ // @ts-expect-error AsyncStorage types are not correct
137
+ const allKeys = await AsyncStorage.getAllKeys();
138
+ for (const key of allKeys) {
139
+ if (key.startsWith(startContextKey)) {
140
+ // @ts-expect-error AsyncStorage types are not correct
141
+ await AsyncStorage.removeItem(key);
142
+ }
143
+ }
144
+ }
145
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../../tools/tsc/tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "types": []
5
+ },
6
+ "references": [
7
+ {
8
+ "path": "../../nodejs/src"
9
+ },
10
+ {
11
+ "path": "../../general/src"
12
+ },
13
+ {
14
+ "path": "../../protocol/src"
15
+ }
16
+ ]
17
+ }