react-native-ble-mesh 1.1.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +288 -172
  2. package/docs/IOS-BACKGROUND-BLE.md +231 -0
  3. package/docs/OPTIMIZATION.md +183 -0
  4. package/docs/SPEC-v2.1.md +308 -0
  5. package/package.json +1 -1
  6. package/src/MeshNetwork.js +667 -465
  7. package/src/constants/index.js +1 -0
  8. package/src/crypto/AutoCrypto.js +90 -0
  9. package/src/crypto/CryptoProvider.js +99 -0
  10. package/src/crypto/index.js +15 -63
  11. package/src/crypto/providers/ExpoCryptoProvider.js +126 -0
  12. package/src/crypto/providers/QuickCryptoProvider.js +158 -0
  13. package/src/crypto/providers/TweetNaClProvider.js +124 -0
  14. package/src/crypto/providers/index.js +11 -0
  15. package/src/errors/MeshError.js +2 -1
  16. package/src/expo/withBLEMesh.js +102 -0
  17. package/src/hooks/AppStateManager.js +9 -1
  18. package/src/hooks/useMesh.js +47 -13
  19. package/src/hooks/useMessages.js +6 -4
  20. package/src/hooks/usePeers.js +13 -9
  21. package/src/index.js +23 -8
  22. package/src/mesh/dedup/BloomFilter.js +44 -57
  23. package/src/mesh/dedup/DedupManager.js +67 -10
  24. package/src/mesh/fragment/Assembler.js +5 -0
  25. package/src/mesh/fragment/Fragmenter.js +1 -1
  26. package/src/mesh/index.js +1 -1
  27. package/src/mesh/monitor/ConnectionQuality.js +433 -0
  28. package/src/mesh/monitor/NetworkMonitor.js +376 -320
  29. package/src/mesh/monitor/index.js +7 -3
  30. package/src/mesh/peer/Peer.js +5 -2
  31. package/src/mesh/peer/PeerManager.js +21 -4
  32. package/src/mesh/router/MessageRouter.js +38 -19
  33. package/src/mesh/router/RouteTable.js +24 -8
  34. package/src/mesh/store/StoreAndForwardManager.js +305 -296
  35. package/src/mesh/store/index.js +1 -1
  36. package/src/protocol/deserializer.js +9 -10
  37. package/src/protocol/header.js +13 -7
  38. package/src/protocol/message.js +15 -3
  39. package/src/protocol/serializer.js +7 -10
  40. package/src/protocol/validator.js +23 -5
  41. package/src/service/BatteryOptimizer.js +285 -278
  42. package/src/service/EmergencyManager.js +224 -214
  43. package/src/service/HandshakeManager.js +163 -13
  44. package/src/service/MeshService.js +72 -6
  45. package/src/service/SessionManager.js +79 -2
  46. package/src/service/audio/AudioManager.js +8 -2
  47. package/src/service/file/FileAssembler.js +106 -0
  48. package/src/service/file/FileChunker.js +79 -0
  49. package/src/service/file/FileManager.js +307 -0
  50. package/src/service/file/FileMessage.js +122 -0
  51. package/src/service/file/index.js +15 -0
  52. package/src/service/text/TextManager.js +21 -15
  53. package/src/service/text/broadcast/BroadcastManager.js +16 -0
  54. package/src/storage/MessageStore.js +55 -2
  55. package/src/transport/BLETransport.js +141 -10
  56. package/src/transport/MockTransport.js +1 -1
  57. package/src/transport/MultiTransport.js +330 -0
  58. package/src/transport/WiFiDirectTransport.js +296 -0
  59. package/src/transport/adapters/NodeBLEAdapter.js +34 -0
  60. package/src/transport/adapters/RNBLEAdapter.js +56 -1
  61. package/src/transport/index.js +6 -0
  62. package/src/utils/EventEmitter.js +6 -9
  63. package/src/utils/bytes.js +12 -10
  64. package/src/utils/compression.js +293 -291
  65. package/src/utils/encoding.js +33 -8
  66. package/src/crypto/aead.js +0 -189
  67. package/src/crypto/chacha20.js +0 -181
  68. package/src/crypto/hkdf.js +0 -187
  69. package/src/crypto/hmac.js +0 -143
  70. package/src/crypto/keys/KeyManager.js +0 -271
  71. package/src/crypto/keys/KeyPair.js +0 -216
  72. package/src/crypto/keys/SecureStorage.js +0 -219
  73. package/src/crypto/keys/index.js +0 -32
  74. package/src/crypto/noise/handshake.js +0 -410
  75. package/src/crypto/noise/index.js +0 -27
  76. package/src/crypto/noise/session.js +0 -253
  77. package/src/crypto/noise/state.js +0 -268
  78. package/src/crypto/poly1305.js +0 -113
  79. package/src/crypto/sha256.js +0 -240
  80. package/src/crypto/x25519.js +0 -154
@@ -0,0 +1,296 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview Wi-Fi Direct transport for high-bandwidth mesh communication
5
+ * @module transport/WiFiDirectTransport
6
+ *
7
+ * Provides ~250Mbps throughput and ~200m range via Wi-Fi Direct (P2P).
8
+ * Requires: react-native-wifi-p2p (optional peer dependency)
9
+ */
10
+
11
+ const Transport = require('./Transport');
12
+ const { ConnectionError } = require('../errors');
13
+
14
+ /**
15
+ * Wi-Fi Direct transport states
16
+ * @constant {Object}
17
+ */
18
+ const WIFI_DIRECT_STATE = Object.freeze({
19
+ AVAILABLE: 'available',
20
+ UNAVAILABLE: 'unavailable',
21
+ DISCOVERING: 'discovering',
22
+ CONNECTED: 'connected'
23
+ });
24
+
25
+ /**
26
+ * Wi-Fi Direct transport implementation.
27
+ * Uses react-native-wifi-p2p for peer discovery and data transfer.
28
+ *
29
+ * @class WiFiDirectTransport
30
+ * @extends Transport
31
+ */
32
+ class WiFiDirectTransport extends Transport {
33
+ /**
34
+ * @param {Object} [options={}]
35
+ * @param {Object} [options.wifiP2p] - Injected react-native-wifi-p2p module
36
+ * @param {number} [options.port=8988] - Server port for socket communication
37
+ * @param {number} [options.connectTimeoutMs=15000] - Connection timeout
38
+ * @param {number} [options.maxPeers=4] - Max simultaneous Wi-Fi Direct peers
39
+ */
40
+ constructor(options = {}) {
41
+ super({ maxPeers: options.maxPeers || 4, ...options });
42
+
43
+ this._wifiP2p = options.wifiP2p || null;
44
+ this._port = options.port || 8988;
45
+ this._connectTimeoutMs = options.connectTimeoutMs || 15000;
46
+ this._isDiscovering = false;
47
+ this._isGroupOwner = false;
48
+ this._groupInfo = null;
49
+ this._subscriptions = [];
50
+ }
51
+
52
+ /**
53
+ * Whether discovery is active
54
+ * @returns {boolean}
55
+ */
56
+ get isDiscovering() {
57
+ return this._isDiscovering;
58
+ }
59
+
60
+ /**
61
+ * Whether this device is the Wi-Fi Direct group owner
62
+ * @returns {boolean}
63
+ */
64
+ get isGroupOwner() {
65
+ return this._isGroupOwner;
66
+ }
67
+
68
+ /**
69
+ * Starts the Wi-Fi Direct transport
70
+ * @returns {Promise<void>}
71
+ */
72
+ async start() {
73
+ if (this.isRunning) { return; }
74
+ this._setState(Transport.STATE.STARTING);
75
+
76
+ try {
77
+ const p2p = this._getWifiP2p();
78
+ await p2p.initialize();
79
+
80
+ // Check if Wi-Fi Direct is supported
81
+ const isAvailable = await p2p.isSuccessfulInitialize();
82
+ if (!isAvailable) {
83
+ throw new ConnectionError('Wi-Fi Direct is not available on this device', 'E100');
84
+ }
85
+
86
+ this._setState(Transport.STATE.RUNNING);
87
+ } catch (error) {
88
+ this._setState(Transport.STATE.ERROR);
89
+ throw error;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Stops the Wi-Fi Direct transport
95
+ * @returns {Promise<void>}
96
+ */
97
+ async stop() {
98
+ if (this._state === Transport.STATE.STOPPED) { return; }
99
+ this._setState(Transport.STATE.STOPPING);
100
+
101
+ try {
102
+ if (this._isDiscovering) {
103
+ await this.stopDiscovery();
104
+ }
105
+
106
+ const p2p = this._getWifiP2p();
107
+
108
+ // Disconnect from group
109
+ try {
110
+ await p2p.removeGroup();
111
+ } catch (e) {
112
+ // Ignore — may not be in a group
113
+ }
114
+
115
+ // Cleanup subscriptions
116
+ this._subscriptions.forEach(sub => {
117
+ if (sub && typeof sub.remove === 'function') { sub.remove(); }
118
+ });
119
+ this._subscriptions = [];
120
+
121
+ this._peers.clear();
122
+ } finally {
123
+ this._setState(Transport.STATE.STOPPED);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Starts discovering nearby Wi-Fi Direct devices
129
+ * @returns {Promise<void>}
130
+ */
131
+ async startDiscovery() {
132
+ if (!this.isRunning || this._isDiscovering) { return; }
133
+
134
+ const p2p = this._getWifiP2p();
135
+ await p2p.discoverPeers();
136
+ this._isDiscovering = true;
137
+ this.emit('discoveryStarted');
138
+ }
139
+
140
+ /**
141
+ * Stops device discovery
142
+ * @returns {Promise<void>}
143
+ */
144
+ async stopDiscovery() {
145
+ if (!this._isDiscovering) { return; }
146
+
147
+ const p2p = this._getWifiP2p();
148
+ try {
149
+ await p2p.stopDiscoveringPeers();
150
+ } catch (e) {
151
+ // Ignore
152
+ }
153
+ this._isDiscovering = false;
154
+ this.emit('discoveryStopped');
155
+ }
156
+
157
+ /**
158
+ * Connects to a Wi-Fi Direct peer
159
+ * @param {string} peerId - Device address
160
+ * @returns {Promise<void>}
161
+ */
162
+ async connectToPeer(peerId) {
163
+ if (!this.isRunning) {
164
+ throw new Error('Transport is not running');
165
+ }
166
+ if (this._peers.has(peerId)) {
167
+ throw ConnectionError.fromCode('E206', peerId);
168
+ }
169
+ if (!this.canAcceptPeer()) {
170
+ throw ConnectionError.fromCode('E203', peerId);
171
+ }
172
+
173
+ const p2p = this._getWifiP2p();
174
+
175
+ try {
176
+ await p2p.connect(peerId);
177
+
178
+ const connectionInfo = await p2p.getConnectionInfo();
179
+ this._isGroupOwner = connectionInfo.isGroupOwner || false;
180
+ this._groupInfo = connectionInfo;
181
+
182
+ this._peers.set(peerId, {
183
+ peerId,
184
+ connectedAt: Date.now(),
185
+ isGroupOwner: this._isGroupOwner,
186
+ groupOwnerAddress: connectionInfo.groupOwnerAddress
187
+ });
188
+
189
+ this.emit('peerConnected', { peerId, transport: 'wifi-direct' });
190
+ } catch (error) {
191
+ throw ConnectionError.connectionFailed(peerId, { cause: error.message });
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Disconnects from a peer
197
+ * @param {string} peerId
198
+ * @returns {Promise<void>}
199
+ */
200
+ async disconnectFromPeer(peerId) {
201
+ if (!this._peers.has(peerId)) { return; }
202
+
203
+ const p2p = this._getWifiP2p();
204
+ try {
205
+ await p2p.removeGroup();
206
+ } catch (e) {
207
+ // Ignore
208
+ }
209
+
210
+ this._peers.delete(peerId);
211
+ this.emit('peerDisconnected', { peerId, reason: 'user_request' });
212
+ }
213
+
214
+ /**
215
+ * Sends data to a peer via Wi-Fi Direct socket
216
+ * @param {string} peerId - Target peer
217
+ * @param {Uint8Array} data - Data to send
218
+ * @returns {Promise<void>}
219
+ */
220
+ async send(peerId, data) {
221
+ if (!this.isRunning) { throw new Error('Transport is not running'); }
222
+ if (!this._peers.has(peerId)) {
223
+ throw ConnectionError.fromCode('E207', peerId);
224
+ }
225
+
226
+ const p2p = this._getWifiP2p();
227
+ const peerInfo = this._peers.get(peerId);
228
+
229
+ // Convert Uint8Array to base64 for transfer
230
+ const base64 = this._uint8ArrayToBase64(data);
231
+
232
+ if (this._isGroupOwner) {
233
+ // Group owner sends via server socket
234
+ await p2p.sendMessage(base64);
235
+ } else {
236
+ // Client sends to group owner address
237
+ await p2p.sendMessageTo(peerInfo.groupOwnerAddress, this._port, base64);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Broadcasts to all connected peers
243
+ * @param {Uint8Array} data
244
+ * @returns {Promise<string[]>}
245
+ */
246
+ async broadcast(data) {
247
+ if (!this.isRunning) { throw new Error('Transport is not running'); }
248
+
249
+ const peerIds = this.getConnectedPeers();
250
+ const results = await Promise.allSettled(
251
+ peerIds.map(id => this.send(id, data))
252
+ );
253
+ return peerIds.filter((_, i) => results[i].status === 'fulfilled');
254
+ }
255
+
256
+ /**
257
+ * Gets available Wi-Fi Direct peers (discovered but not connected)
258
+ * @returns {Promise<Object[]>}
259
+ */
260
+ async getAvailablePeers() {
261
+ if (!this.isRunning) { return []; }
262
+ const p2p = this._getWifiP2p();
263
+ try {
264
+ return await p2p.getAvailablePeers();
265
+ } catch (e) {
266
+ return [];
267
+ }
268
+ }
269
+
270
+ /** @private */
271
+ _getWifiP2p() {
272
+ if (!this._wifiP2p) {
273
+ try {
274
+ this._wifiP2p = require('react-native-wifi-p2p');
275
+ } catch (e) {
276
+ throw new Error(
277
+ 'react-native-wifi-p2p is required for WiFiDirectTransport. ' +
278
+ 'Install: npm install react-native-wifi-p2p'
279
+ );
280
+ }
281
+ }
282
+ return this._wifiP2p;
283
+ }
284
+
285
+ /** @private */
286
+ _uint8ArrayToBase64(bytes) {
287
+ const chunks = [];
288
+ for (let i = 0; i < bytes.length; i += 8192) {
289
+ chunks.push(String.fromCharCode.apply(null, bytes.subarray(i, Math.min(i + 8192, bytes.length))));
290
+ }
291
+ const binary = chunks.join('');
292
+ return typeof btoa !== 'undefined' ? btoa(binary) : Buffer.from(bytes).toString('base64');
293
+ }
294
+ }
295
+
296
+ module.exports = { WiFiDirectTransport, WIFI_DIRECT_STATE };
@@ -60,6 +60,13 @@ class NodeBLEAdapter extends BLEAdapter {
60
60
  * @private
61
61
  */
62
62
  this._scanCallback = null;
63
+
64
+ /**
65
+ * Disconnect callback
66
+ * @type {Function|null}
67
+ * @private
68
+ */
69
+ this._disconnectCallback = null;
63
70
  }
64
71
 
65
72
  /**
@@ -181,6 +188,18 @@ class NodeBLEAdapter extends BLEAdapter {
181
188
  // Monitor disconnection
182
189
  peripheral.once('disconnect', () => {
183
190
  this._peripherals.delete(deviceId);
191
+
192
+ // Clean up subscriptions for this device
193
+ for (const [key] of this._subscriptions.entries()) {
194
+ if (key.startsWith(`${deviceId}:`)) {
195
+ this._subscriptions.delete(key);
196
+ }
197
+ }
198
+
199
+ // Notify transport
200
+ if (this._disconnectCallback) {
201
+ this._disconnectCallback(deviceId);
202
+ }
184
203
  });
185
204
 
186
205
  return {
@@ -196,6 +215,13 @@ class NodeBLEAdapter extends BLEAdapter {
196
215
  * @returns {Promise<void>}
197
216
  */
198
217
  async disconnect(deviceId) {
218
+ // Clean up subscriptions first
219
+ for (const [key] of this._subscriptions.entries()) {
220
+ if (key.startsWith(`${deviceId}:`)) {
221
+ this._subscriptions.delete(key);
222
+ }
223
+ }
224
+
199
225
  const peripheral = this._peripherals.get(deviceId);
200
226
  if (peripheral) {
201
227
  await this._disconnectPeripheral(peripheral);
@@ -410,6 +436,14 @@ class NodeBLEAdapter extends BLEAdapter {
410
436
  });
411
437
  }
412
438
 
439
+ /**
440
+ * Registers a callback for device disconnection events
441
+ * @param {Function} callback - Callback function receiving peerId
442
+ */
443
+ onDeviceDisconnected(callback) {
444
+ this._disconnectCallback = callback;
445
+ }
446
+
413
447
  /**
414
448
  * Ensures the adapter is initialized
415
449
  * @throws {Error} If not initialized
@@ -40,6 +40,13 @@ class RNBLEAdapter extends BLEAdapter {
40
40
  */
41
41
  this._BleManager = options.BleManager || null;
42
42
 
43
+ /**
44
+ * iOS state restoration identifier
45
+ * @type {string|null}
46
+ * @private
47
+ */
48
+ this._restoreIdentifier = options.restoreIdentifier || null;
49
+
43
50
  /**
44
51
  * Connected devices map
45
52
  * @type {Map<string, Object>}
@@ -67,6 +74,13 @@ class RNBLEAdapter extends BLEAdapter {
67
74
  * @private
68
75
  */
69
76
  this._stateSubscription = null;
77
+
78
+ /**
79
+ * Disconnect callback
80
+ * @type {Function|null}
81
+ * @private
82
+ */
83
+ this._disconnectCallback = null;
70
84
  }
71
85
 
72
86
  /**
@@ -91,7 +105,19 @@ class RNBLEAdapter extends BLEAdapter {
91
105
  }
92
106
  }
93
107
 
94
- this._manager = new this._BleManager();
108
+ const managerOptions = {};
109
+ if (this._restoreIdentifier) {
110
+ managerOptions.restoreStateIdentifier = this._restoreIdentifier;
111
+ managerOptions.restoreStateFunction = (restoredState) => {
112
+ // Re-populate devices from restored state
113
+ if (restoredState && restoredState.connectedPeripherals) {
114
+ for (const peripheral of restoredState.connectedPeripherals) {
115
+ this._devices.set(peripheral.id, peripheral);
116
+ }
117
+ }
118
+ };
119
+ }
120
+ this._manager = new this._BleManager(managerOptions);
95
121
 
96
122
  // Subscribe to state changes
97
123
  this._stateSubscription = this._manager.onStateChange((state) => {
@@ -187,6 +213,19 @@ class RNBLEAdapter extends BLEAdapter {
187
213
  // Monitor disconnection
188
214
  device.onDisconnected(() => {
189
215
  this._devices.delete(deviceId);
216
+
217
+ // Clean up subscriptions for this device
218
+ for (const [key, subscription] of this._subscriptions.entries()) {
219
+ if (key.startsWith(`${deviceId}:`)) {
220
+ subscription.remove();
221
+ this._subscriptions.delete(key);
222
+ }
223
+ }
224
+
225
+ // Notify transport
226
+ if (this._disconnectCallback) {
227
+ this._disconnectCallback(deviceId);
228
+ }
190
229
  });
191
230
 
192
231
  return {
@@ -202,6 +241,14 @@ class RNBLEAdapter extends BLEAdapter {
202
241
  * @returns {Promise<void>}
203
242
  */
204
243
  async disconnect(deviceId) {
244
+ // Clean up subscriptions first
245
+ for (const [key, subscription] of this._subscriptions.entries()) {
246
+ if (key.startsWith(`${deviceId}:`)) {
247
+ subscription.remove();
248
+ this._subscriptions.delete(key);
249
+ }
250
+ }
251
+
205
252
  const device = this._devices.get(deviceId);
206
253
  if (device) {
207
254
  await this._manager.cancelDeviceConnection(deviceId);
@@ -284,6 +331,14 @@ class RNBLEAdapter extends BLEAdapter {
284
331
  return stateMap[state] || BLEAdapter.STATE.UNKNOWN;
285
332
  }
286
333
 
334
+ /**
335
+ * Registers a callback for device disconnection events
336
+ * @param {Function} callback - Callback function receiving peerId
337
+ */
338
+ onDeviceDisconnected(callback) {
339
+ this._disconnectCallback = callback;
340
+ }
341
+
287
342
  /**
288
343
  * Ensures the adapter is initialized
289
344
  * @throws {Error} If not initialized
@@ -8,6 +8,8 @@
8
8
  const Transport = require('./Transport');
9
9
  const MockTransport = require('./MockTransport');
10
10
  const BLETransport = require('./BLETransport');
11
+ const { WiFiDirectTransport, WIFI_DIRECT_STATE } = require('./WiFiDirectTransport');
12
+ const { MultiTransport, STRATEGY: MULTI_TRANSPORT_STRATEGY } = require('./MultiTransport');
11
13
 
12
14
  // React Native compatible adapters (from adapters subdirectory)
13
15
  const BLEAdapter = require('./adapters/BLEAdapter');
@@ -17,6 +19,10 @@ module.exports = {
17
19
  Transport,
18
20
  MockTransport,
19
21
  BLETransport,
22
+ WiFiDirectTransport,
23
+ WIFI_DIRECT_STATE,
24
+ MultiTransport,
25
+ MULTI_TRANSPORT_STRATEGY,
20
26
  BLEAdapter,
21
27
  RNBLEAdapter,
22
28
 
@@ -116,10 +116,11 @@ class EventEmitter {
116
116
  return false;
117
117
  }
118
118
 
119
- const listeners = this._events.get(event).slice();
120
- const toRemove = [];
119
+ const listeners = this._events.get(event);
120
+ const hasOnce = listeners.some(e => e.once);
121
+ const iterList = hasOnce ? listeners.slice() : listeners;
121
122
 
122
- for (const entry of listeners) {
123
+ for (const entry of iterList) {
123
124
  try {
124
125
  entry.listener.apply(this, args);
125
126
  } catch (error) {
@@ -130,15 +131,11 @@ class EventEmitter {
130
131
  console.error('Error in error handler:', error);
131
132
  }
132
133
  }
133
-
134
- if (entry.once) {
135
- toRemove.push(entry);
136
- }
137
134
  }
138
135
 
139
136
  // Remove one-time listeners
140
- if (toRemove.length > 0) {
141
- const remaining = this._events.get(event).filter(e => !toRemove.includes(e));
137
+ if (hasOnce) {
138
+ const remaining = listeners.filter(e => !e.once);
142
139
  if (remaining.length === 0) {
143
140
  this._events.delete(event);
144
141
  } else {
@@ -11,9 +11,16 @@
11
11
  * @returns {Uint8Array} Concatenated array
12
12
  */
13
13
  function concat(...arrays) {
14
- // Filter out undefined/null and calculate total length
15
- const validArrays = arrays.filter(arr => arr !== null && arr !== undefined);
16
- const totalLength = validArrays.reduce((sum, arr) => sum + arr.length, 0);
14
+ // Single-pass: filter out undefined/null and calculate total length
15
+ const validArrays = [];
16
+ let totalLength = 0;
17
+ for (let i = 0; i < arrays.length; i++) {
18
+ const arr = arrays[i];
19
+ if (arr !== null && arr !== undefined) {
20
+ validArrays.push(arr);
21
+ totalLength += arr.length;
22
+ }
23
+ }
17
24
 
18
25
  const result = new Uint8Array(totalLength);
19
26
  let offset = 0;
@@ -119,10 +126,7 @@ function xor(a, b) {
119
126
  * @returns {Uint8Array} The filled array (same reference)
120
127
  */
121
128
  function fill(array, value) {
122
- const byte = value & 0xff;
123
- for (let i = 0; i < array.length; i++) {
124
- array[i] = byte;
125
- }
129
+ array.fill(value & 0xff);
126
130
  return array;
127
131
  }
128
132
 
@@ -132,9 +136,7 @@ function fill(array, value) {
132
136
  * @returns {Uint8Array} Copy of the array
133
137
  */
134
138
  function copy(array) {
135
- const result = new Uint8Array(array.length);
136
- result.set(array);
137
- return result;
139
+ return array.slice();
138
140
  }
139
141
 
140
142
  /**