@stoprocent/noble 1.19.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +393 -650
  2. package/examples/advertisement-discovery.js +57 -48
  3. package/examples/connect-address.js +59 -34
  4. package/examples/echo.js +59 -69
  5. package/examples/enter-exit.js +55 -49
  6. package/examples/multiple-bindings.js +53 -0
  7. package/examples/peripheral-explorer-async.js +39 -21
  8. package/examples/peripheral-explorer.ts +52 -0
  9. package/index.d.ts +249 -209
  10. package/index.js +4 -1
  11. package/jest.config.js +4 -0
  12. package/lib/characteristic.js +153 -127
  13. package/lib/{win/src/callbacks.h → common/include/Emit.h} +17 -14
  14. package/lib/common/include/Peripheral.h +31 -0
  15. package/lib/common/include/ThreadSafeCallback.h +95 -0
  16. package/lib/{win/src/callbacks.cc → common/src/Emit.cc} +111 -68
  17. package/lib/descriptor.js +57 -54
  18. package/lib/hci-socket/acl-stream.js +2 -4
  19. package/lib/hci-socket/bindings.js +96 -73
  20. package/lib/hci-socket/gap.js +2 -3
  21. package/lib/hci-socket/gatt.js +2 -5
  22. package/lib/hci-socket/hci.js +19 -7
  23. package/lib/hci-socket/signaling.js +2 -3
  24. package/lib/hci-socket/smp.js +2 -3
  25. package/lib/hci-socket/vs.js +1 -0
  26. package/lib/mac/binding.gyp +5 -7
  27. package/lib/mac/bindings.js +1 -3
  28. package/lib/mac/src/ble_manager.h +1 -8
  29. package/lib/mac/src/ble_manager.mm +87 -44
  30. package/lib/mac/src/napi_objc.h +1 -0
  31. package/lib/mac/src/napi_objc.mm +0 -6
  32. package/lib/mac/src/noble_mac.h +5 -3
  33. package/lib/mac/src/noble_mac.mm +99 -57
  34. package/lib/mac/src/objc_cpp.h +3 -2
  35. package/lib/mac/src/objc_cpp.mm +0 -6
  36. package/lib/noble.js +579 -488
  37. package/lib/peripheral.js +171 -174
  38. package/lib/resolve-bindings.js +37 -30
  39. package/lib/service.js +58 -55
  40. package/lib/win/binding.gyp +4 -11
  41. package/lib/win/bindings.js +1 -3
  42. package/lib/win/src/ble_manager.cc +291 -166
  43. package/lib/win/src/ble_manager.h +11 -13
  44. package/lib/win/src/napi_winrt.cc +1 -7
  45. package/lib/win/src/napi_winrt.h +1 -1
  46. package/lib/win/src/noble_winrt.cc +88 -61
  47. package/lib/win/src/noble_winrt.h +5 -3
  48. package/lib/win/src/notify_map.cc +0 -7
  49. package/lib/win/src/notify_map.h +1 -8
  50. package/lib/win/src/peripheral_winrt.cc +29 -11
  51. package/lib/win/src/peripheral_winrt.h +1 -1
  52. package/lib/win/src/radio_watcher.cc +79 -69
  53. package/lib/win/src/radio_watcher.h +30 -11
  54. package/lib/win/src/winrt_cpp.cc +1 -1
  55. package/lib/win/src/winrt_cpp.h +3 -0
  56. package/package.json +14 -17
  57. package/prebuilds/darwin-x64+arm64/@stoprocent+noble.node +0 -0
  58. package/prebuilds/win32-ia32/@stoprocent+noble.node +0 -0
  59. package/prebuilds/win32-x64/@stoprocent+noble.node +0 -0
  60. package/test/lib/characteristic.test.js +202 -322
  61. package/test/lib/descriptor.test.js +62 -95
  62. package/test/lib/hci-socket/acl-stream.test.js +112 -108
  63. package/test/lib/hci-socket/bindings.test.js +576 -365
  64. package/test/lib/hci-socket/hci.test.js +442 -473
  65. package/test/lib/hci-socket/signaling.test.js +45 -48
  66. package/test/lib/hci-socket/smp.test.js +144 -142
  67. package/test/lib/hci-socket/vs.test.js +193 -18
  68. package/test/lib/peripheral.test.js +492 -322
  69. package/test/lib/resolve-bindings.test.js +207 -82
  70. package/test/lib/service.test.js +79 -88
  71. package/test/noble.test.js +381 -1085
  72. package/.editorconfig +0 -11
  73. package/.nycrc.json +0 -4
  74. package/codecov.yml +0 -5
  75. package/examples/cache-gatt-discovery.js +0 -198
  76. package/examples/cache-gatt-reconnect.js +0 -164
  77. package/examples/ext-advertisement-discovery.js +0 -65
  78. package/examples/peripheral-explorer.js +0 -225
  79. package/examples/pizza/central.js +0 -194
  80. package/examples/pizza/pizza.js +0 -60
  81. package/examples/test/test.custom.js +0 -131
  82. package/examples/uart-bind-params.js +0 -28
  83. package/lib/distributed/bindings.js +0 -326
  84. package/lib/mac/src/callbacks.cc +0 -222
  85. package/lib/mac/src/callbacks.h +0 -84
  86. package/lib/mac/src/peripheral.h +0 -23
  87. package/lib/resolve-bindings-web.js +0 -9
  88. package/lib/webbluetooth/bindings.js +0 -368
  89. package/lib/websocket/bindings.js +0 -321
  90. package/lib/win/src/peripheral.h +0 -23
  91. package/test/lib/distributed/bindings.test.js +0 -918
  92. package/test/lib/webbluetooth/bindings.test.js +0 -190
  93. package/test/lib/websocket/bindings.test.js +0 -456
  94. package/test/mocha.setup.js +0 -0
  95. package/with-bindings.js +0 -5
  96. package/with-custom-binding.js +0 -6
  97. package/ws-slave.js +0 -404
@@ -1,161 +1,187 @@
1
- const events = require('events');
2
- const util = require('util');
1
+ const { EventEmitter } = require('events');
3
2
 
4
3
  const characteristics = require('./characteristics.json');
5
4
 
6
- function Characteristic (noble, peripheralId, serviceUuid, uuid, properties) {
7
- this._noble = noble;
8
- this._peripheralId = peripheralId;
9
- this._serviceUuid = serviceUuid;
10
-
11
- this.uuid = uuid;
12
- this.name = null;
13
- this.type = null;
14
- this.properties = properties;
15
- this.descriptors = null;
5
+ class Characteristic extends EventEmitter {
6
+
7
+ constructor (noble, peripheralId, serviceUuid, uuid, properties) {
8
+ super();
9
+ this._noble = noble;
10
+ this._peripheralId = peripheralId;
11
+ this._serviceUuid = serviceUuid;
12
+
13
+ this.uuid = uuid;
14
+ this.name = null;
15
+ this.type = null;
16
+ this.properties = properties;
17
+ this.descriptors = null;
18
+
19
+ const characteristic = characteristics[uuid];
20
+ if (characteristic) {
21
+ this.name = characteristic.name;
22
+ this.type = characteristic.type;
23
+ }
24
+ }
16
25
 
17
- const characteristic = characteristics[uuid];
18
- if (characteristic) {
19
- this.name = characteristic.name;
20
- this.type = characteristic.type;
26
+ toString () {
27
+ return JSON.stringify({
28
+ uuid: this.uuid,
29
+ name: this.name,
30
+ type: this.type,
31
+ properties: this.properties
32
+ });
21
33
  }
22
- }
23
34
 
24
- util.inherits(Characteristic, events.EventEmitter);
25
-
26
- Characteristic.prototype.toString = function () {
27
- return JSON.stringify({
28
- uuid: this.uuid,
29
- name: this.name,
30
- type: this.type,
31
- properties: this.properties
32
- });
33
- };
34
-
35
- const read = function (callback) {
36
- if (callback) {
37
- const onRead = (data, isNotification) => {
38
- // only call the callback if 'read' event and non-notification
39
- // 'read' for non-notifications is only present for backwards compatbility
40
- if (!isNotification) {
41
- // remove the listener
42
- this.removeListener('read', onRead);
43
-
44
- // call the callback
45
- callback(null, data);
46
- }
47
- };
48
-
49
- this.on('read', onRead);
50
- }
51
-
52
- this._noble.read(
53
- this._peripheralId,
54
- this._serviceUuid,
55
- this.uuid
56
- );
57
- };
58
-
59
- Characteristic.prototype.read = read;
60
- Characteristic.prototype.readAsync = util.promisify(read);
61
-
62
- const write = function (data, withoutResponse, callback) {
63
- if (process.title !== 'browser') {
64
- const allowedTypes = [
65
- Buffer,
66
- Uint8Array,
67
- Uint16Array,
68
- Uint32Array
69
- ];
70
- if (!allowedTypes.some((allowedType) => data instanceof allowedType)) {
71
- throw new Error(`data must be a ${allowedTypes.map((allowedType) => allowedType.name).join(' or ')}`);
35
+ read (callback) {
36
+ if (callback) {
37
+ const onRead = (data, isNotification, error) => {
38
+ // only call the callback if 'read' event and non-notification
39
+ // 'read' for non-notifications is only present for backwards compatbility
40
+ if (!isNotification) {
41
+ // remove the listener
42
+ this.removeListener('read', onRead);
43
+ // call the callback
44
+ callback(error, data);
45
+ }
46
+ };
47
+
48
+ this.on('read', onRead);
72
49
  }
50
+
51
+ this._noble.read(
52
+ this._peripheralId,
53
+ this._serviceUuid,
54
+ this.uuid
55
+ );
73
56
  }
74
57
 
75
- if (callback) {
76
- this.once('write', () => {
77
- callback(null);
58
+ async readAsync () {
59
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
60
+ return new Promise((resolve, reject) => {
61
+ this.read((error, data) => error ? reject(error) : resolve(data));
62
+ });
78
63
  });
79
64
  }
80
65
 
81
- this._noble.write(
82
- this._peripheralId,
83
- this._serviceUuid,
84
- this.uuid,
85
- data,
86
- withoutResponse
87
- );
88
- };
66
+ write (data, withoutResponse, callback) {
67
+ if (process.title !== 'browser') {
68
+ const allowedTypes = [
69
+ Buffer,
70
+ Uint8Array,
71
+ Uint16Array,
72
+ Uint32Array
73
+ ];
74
+ if (!allowedTypes.some((allowedType) => data instanceof allowedType)) {
75
+ throw new Error(`data must be a ${allowedTypes.map((allowedType) => allowedType.name).join(' or ')}`);
76
+ }
77
+ }
89
78
 
90
- Characteristic.prototype.write = write;
91
- Characteristic.prototype.writeAsync = util.promisify(write);
79
+ if (callback) {
80
+ this.once('write', error => callback(error));
81
+ }
92
82
 
93
- const broadcast = function (broadcast, callback) {
94
- if (callback) {
95
- this.once('broadcast', () => {
96
- callback(null);
83
+ this._noble.write(
84
+ this._peripheralId,
85
+ this._serviceUuid,
86
+ this.uuid,
87
+ data,
88
+ withoutResponse
89
+ );
90
+ }
91
+
92
+ async writeAsync (data, withoutResponse) {
93
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
94
+ return new Promise((resolve, reject) => {
95
+ this.write(data, withoutResponse, error => error ? reject(error) : resolve());
96
+ });
97
97
  });
98
98
  }
99
99
 
100
- this._noble.broadcast(
101
- this._peripheralId,
102
- this._serviceUuid,
103
- this.uuid,
104
- broadcast
105
- );
106
- };
100
+ broadcast (broadcast, callback) {
101
+ if (callback) {
102
+ this.once('broadcast', error => callback(error));
103
+ }
107
104
 
108
- Characteristic.prototype.broadcast = broadcast;
109
- Characteristic.prototype.broadcastAsync = util.promisify(broadcast);
105
+ this._noble.broadcast(
106
+ this._peripheralId,
107
+ this._serviceUuid,
108
+ this.uuid,
109
+ broadcast
110
+ );
111
+ }
110
112
 
111
- // deprecated in favour of subscribe/unsubscribe
112
- const notify = function (notify, callback) {
113
- if (callback) {
114
- this.once('notify', () => {
115
- callback(null);
113
+ async broadcastAsync (broadcast) {
114
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
115
+ return new Promise((resolve, reject) => {
116
+ this.broadcast(broadcast, (state, error) => error ? reject(error) : resolve(state));
117
+ });
116
118
  });
117
119
  }
118
120
 
119
- this._noble.notify(
120
- this._peripheralId,
121
- this._serviceUuid,
122
- this.uuid,
123
- notify
124
- );
125
- };
121
+ notify (notify, callback) {
122
+ if (callback) {
123
+ this.once('notify', (state, error) => callback(error, state));
124
+ }
126
125
 
127
- Characteristic.prototype.notify = notify;
128
- Characteristic.prototype.notifyAsync = util.promisify(notify);
126
+ this._noble.notify(
127
+ this._peripheralId,
128
+ this._serviceUuid,
129
+ this.uuid,
130
+ notify
131
+ );
132
+ }
129
133
 
130
- const subscribe = function (callback) {
131
- this.notify(true, callback);
132
- };
134
+ async notifyAsync (notify) {
135
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
136
+ return new Promise((resolve, reject) => {
137
+ this.notify(notify, (error, state) => error ? reject(error) : resolve(state));
138
+ });
139
+ });
140
+ }
133
141
 
134
- Characteristic.prototype.subscribe = subscribe;
135
- Characteristic.prototype.subscribeAsync = util.promisify(subscribe);
142
+ subscribe (callback) {
143
+ this.notify(true, callback);
144
+ }
136
145
 
137
- const unsubscribe = function (callback) {
138
- this.notify(false, callback);
139
- };
146
+ async subscribeAsync () {
147
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
148
+ return new Promise((resolve, reject) => {
149
+ this.subscribe((error, state) => error ? reject(error) : resolve(state));
150
+ });
151
+ });
152
+ }
140
153
 
141
- Characteristic.prototype.unsubscribe = unsubscribe;
142
- Characteristic.prototype.unsubscribeAsync = util.promisify(unsubscribe);
154
+ unsubscribe (callback) {
155
+ this.notify(false, callback);
156
+ }
143
157
 
144
- const discoverDescriptors = function (callback) {
145
- if (callback) {
146
- this.once('descriptorsDiscover', descriptors => {
147
- callback(null, descriptors);
158
+ async unsubscribeAsync () {
159
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
160
+ return new Promise((resolve, reject) => {
161
+ this.unsubscribe(error => error ? reject(error) : resolve());
162
+ });
148
163
  });
149
164
  }
150
165
 
151
- this._noble.discoverDescriptors(
152
- this._peripheralId,
153
- this._serviceUuid,
154
- this.uuid
155
- );
156
- };
166
+ discoverDescriptors (callback) {
167
+ if (callback) {
168
+ this.once('descriptorsDiscover', (descriptors, error) => callback(error, descriptors));
169
+ }
170
+
171
+ this._noble.discoverDescriptors(
172
+ this._peripheralId,
173
+ this._serviceUuid,
174
+ this.uuid
175
+ );
176
+ }
157
177
 
158
- Characteristic.prototype.discoverDescriptors = discoverDescriptors;
159
- Characteristic.prototype.discoverDescriptorsAsync = util.promisify(discoverDescriptors);
178
+ async discoverDescriptorsAsync () {
179
+ return this._noble._withDisconnectHandler(this._peripheralId, () => {
180
+ return new Promise((resolve, reject) => {
181
+ this.discoverDescriptors((error, descriptors) => error ? reject(error) : resolve(descriptors));
182
+ });
183
+ });
184
+ }
185
+ }
160
186
 
161
187
  module.exports = Characteristic;
@@ -1,9 +1,11 @@
1
1
  #pragma once
2
2
 
3
3
  #include <napi.h>
4
- #include "peripheral.h"
4
+ #include <vector>
5
+ #include <string>
5
6
 
6
- class ThreadSafeCallback;
7
+ #include "Peripheral.h"
8
+ #include "ThreadSafeCallback.h"
7
9
 
8
10
  class Emit
9
11
  {
@@ -11,22 +13,23 @@ public:
11
13
  // clang-format off
12
14
  void Wrap(const Napi::Value& receiver, const Napi::Function& callback);
13
15
  void RadioState(const std::string& status);
16
+ void Address(const std::string& address);
14
17
  void ScanState(bool start);
15
18
  void Scan(const std::string& uuid, int rssi, const Peripheral& peripheral);
16
19
  void Connected(const std::string& uuid, const std::string& error = "");
17
20
  void Disconnected(const std::string& uuid);
18
- void RSSI(const std::string& uuid, int rssi);
19
- void ServicesDiscovered(const std::string& uuid, const std::vector<std::string>& serviceUuids);
20
- void IncludedServicesDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::string>& serviceUuids);
21
- void CharacteristicsDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::pair<std::string, std::vector<std::string>>>& characteristics);
22
- void Read(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const Data& data, bool isNotification);
23
- void Write(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid);
24
- void Notify(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, bool state);
25
- void DescriptorsDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::vector<std::string>& descriptorUuids);
26
- void ReadValue(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& descriptorUuid, const Data& data);
27
- void WriteValue(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& descriptorUuid);
28
- void ReadHandle(const std::string& uuid, int descriptorHandle, const Data& data);
29
- void WriteHandle(const std::string& uuid, int descriptorHandle);
21
+ void RSSI(const std::string& uuid, int rssi, const std::string& error = "");
22
+ void ServicesDiscovered(const std::string& uuid, const std::vector<std::string>& serviceUuids, const std::string& error = "");
23
+ void IncludedServicesDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::string>& serviceUuids, const std::string& error = "");
24
+ void CharacteristicsDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::pair<std::string, std::vector<std::string>>>& characteristics, const std::string& error = "");
25
+ void Read(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const Data& data, bool isNotification, const std::string& error = "");
26
+ void Write(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& error = "");
27
+ void Notify(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, bool state, const std::string& error = "");
28
+ void DescriptorsDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::vector<std::string>& descriptorUuids, const std::string& error = "");
29
+ void ReadValue(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& descriptorUuid, const Data& data, const std::string& error = "");
30
+ void WriteValue(const std::string& uuid, const std::string& serviceUuid, const std::string& characteristicUuid, const std::string& descriptorUuid, const std::string& error = "");
31
+ void ReadHandle(const std::string& uuid, int descriptorHandle, const Data& data, const std::string& error = "");
32
+ void WriteHandle(const std::string& uuid, int descriptorHandle, const std::string& error = "");
30
33
  // clang-format on
31
34
  protected:
32
35
  std::shared_ptr<ThreadSafeCallback> mCallback;
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #include <vector>
4
+ #include <string>
5
+ #include <optional>
6
+
7
+ using Data = std::vector<uint8_t>;
8
+
9
+ enum AddressType {
10
+ PUBLIC,
11
+ RANDOM,
12
+ UNKNOWN,
13
+ };
14
+
15
+ class Peripheral {
16
+ public:
17
+ Peripheral():
18
+ address("unknown"),
19
+ addressType(UNKNOWN),
20
+ connectable(false) {}
21
+
22
+ std::string address;
23
+ AddressType addressType;
24
+ bool connectable;
25
+
26
+ std::optional<std::string> name;
27
+ std::optional<int> txPowerLevel;
28
+ std::optional<Data> manufacturerData;
29
+ std::optional<std::vector<std::pair<std::string, Data>>> serviceData;
30
+ std::optional<std::vector<std::string>> serviceUuids;
31
+ };
@@ -0,0 +1,95 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <vector>
5
+ #include <functional>
6
+
7
+ class ThreadSafeCallback {
8
+ public:
9
+ // More descriptive type aliases
10
+ using ArgumentVector = std::vector<napi_value>;
11
+ using ArgumentFunction = std::function<void(napi_env, ArgumentVector&)>;
12
+
13
+ // Constructor with validation
14
+ ThreadSafeCallback(const Napi::Value& receiver, const Napi::Function& jsCallback);
15
+
16
+ // Destructor
17
+ ~ThreadSafeCallback();
18
+
19
+ // Delete copy and move operations explicitly
20
+ ThreadSafeCallback(const ThreadSafeCallback&) = delete;
21
+ ThreadSafeCallback& operator=(const ThreadSafeCallback&) = delete;
22
+ ThreadSafeCallback& operator=(ThreadSafeCallback&&) = delete;
23
+
24
+ // Public interface
25
+ void call(ArgumentFunction argFunction);
26
+
27
+ private:
28
+ // Static callback handler
29
+ static void callJsCallback(Napi::Env env,
30
+ Napi::Function jsCallback,
31
+ Napi::Reference<Napi::Value>* context,
32
+ ArgumentFunction* argFn);
33
+
34
+ // Type alias for the thread-safe function
35
+ using ThreadSafeFunc = Napi::TypedThreadSafeFunction<
36
+ Napi::Reference<Napi::Value>,
37
+ ArgumentFunction,
38
+ callJsCallback>;
39
+
40
+ // Member variables
41
+ Napi::Reference<Napi::Value> receiver_;
42
+ ThreadSafeFunc threadSafeFunction_;
43
+ };
44
+
45
+ // Implementation
46
+
47
+ inline ThreadSafeCallback::ThreadSafeCallback(
48
+ const Napi::Value& receiver,
49
+ const Napi::Function& jsCallback) {
50
+
51
+ if (!(receiver.IsObject() || receiver.IsFunction())) {
52
+ throw Napi::Error::New(jsCallback.Env(),
53
+ "Callback receiver must be an object or function");
54
+ }
55
+ if (!jsCallback.IsFunction()) {
56
+ throw Napi::Error::New(jsCallback.Env(),
57
+ "Callback must be a function");
58
+ }
59
+
60
+ receiver_ = Napi::Persistent(receiver);
61
+ threadSafeFunction_ = ThreadSafeFunc::New(
62
+ jsCallback.Env(),
63
+ jsCallback,
64
+ "ThreadSafeCallback callback",
65
+ 0, 1,
66
+ &receiver_);
67
+ }
68
+
69
+ inline ThreadSafeCallback::~ThreadSafeCallback() {
70
+ threadSafeFunction_.Abort();
71
+ }
72
+
73
+ inline void ThreadSafeCallback::call(ArgumentFunction argFunction) {
74
+ auto argFn = new ArgumentFunction(argFunction);
75
+ if (threadSafeFunction_.BlockingCall(argFn) != napi_ok) {
76
+ delete argFn;
77
+ }
78
+ }
79
+
80
+ inline void ThreadSafeCallback::callJsCallback(
81
+ Napi::Env env,
82
+ Napi::Function jsCallback,
83
+ Napi::Reference<Napi::Value>* context,
84
+ ArgumentFunction* argFn) {
85
+
86
+ if (argFn != nullptr) {
87
+ ArgumentVector args;
88
+ (*argFn)(env, args);
89
+ delete argFn;
90
+
91
+ if (env != nullptr && jsCallback != nullptr) {
92
+ jsCallback.Call(context->Value(), args);
93
+ }
94
+ }
95
+ }