@nomicfoundation/edr 0.12.0-alpha.0 → 0.12.0-next.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.
package/src/context.rs CHANGED
@@ -1,19 +1,37 @@
1
1
  use std::sync::Arc;
2
2
 
3
3
  use edr_eth::HashMap;
4
- use edr_napi_core::provider::{self, SyncProviderFactory};
4
+ use edr_napi_core::{
5
+ provider::{self, SyncProviderFactory},
6
+ solidity,
7
+ };
5
8
  use edr_solidity::contract_decoder::ContractDecoder;
9
+ use edr_solidity_tests::{
10
+ decode::RevertDecoder,
11
+ multi_runner::{SuiteResultAndArtifactId, TestContract, TestContracts},
12
+ TestFilterConfig,
13
+ };
6
14
  use napi::{
7
- Env, JsObject,
15
+ threadsafe_function::{
16
+ ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
17
+ },
8
18
  tokio::{runtime, sync::Mutex as AsyncMutex},
19
+ Env, JsFunction, JsObject,
9
20
  };
10
21
  use napi_derive::napi;
11
- use tracing_subscriber::{EnvFilter, Registry, prelude::*};
22
+ use tracing_subscriber::{prelude::*, EnvFilter, Registry};
12
23
 
13
24
  use crate::{
14
25
  config::{ProviderConfig, TracingConfigWithBuffers},
15
26
  logger::LoggerConfig,
16
27
  provider::{Provider, ProviderFactory},
28
+ solidity_tests::{
29
+ artifact::{Artifact, ArtifactId},
30
+ config::SolidityTestRunnerConfigArgs,
31
+ factory::SolidityTestRunnerFactory,
32
+ test_results::SuiteResult,
33
+ LinkingOutput,
34
+ },
17
35
  subscription::SubscriptionConfig,
18
36
  };
19
37
 
@@ -25,7 +43,7 @@ pub struct EdrContext {
25
43
  #[napi]
26
44
  impl EdrContext {
27
45
  #[doc = "Creates a new [`EdrContext`] instance. Should only be called once!"]
28
- #[napi(constructor)]
46
+ #[napi(catch_unwind, constructor)]
29
47
  pub fn new() -> napi::Result<Self> {
30
48
  let context = Context::new()?;
31
49
 
@@ -35,7 +53,7 @@ impl EdrContext {
35
53
  }
36
54
 
37
55
  #[doc = "Constructs a new provider with the provided configuration."]
38
- #[napi(ts_return_type = "Promise<Provider>")]
56
+ #[napi(catch_unwind, ts_return_type = "Promise<Provider>")]
39
57
  pub fn create_provider(
40
58
  &self,
41
59
  env: Env,
@@ -45,44 +63,64 @@ impl EdrContext {
45
63
  subscription_config: SubscriptionConfig,
46
64
  tracing_config: TracingConfigWithBuffers,
47
65
  ) -> napi::Result<JsObject> {
48
- let provider_config = edr_napi_core::provider::Config::try_from(provider_config)?;
49
- let logger_config = logger_config.resolve(&env)?;
66
+ let (deferred, promise) = env.create_deferred()?;
67
+
68
+ macro_rules! try_or_reject_promise {
69
+ ($expr:expr) => {
70
+ match $expr {
71
+ Ok(value) => value,
72
+ Err(error) => {
73
+ deferred.reject(error);
74
+ return Ok(promise);
75
+ }
76
+ }
77
+ };
78
+ }
79
+
80
+ let runtime = runtime::Handle::current();
81
+ let provider_config =
82
+ try_or_reject_promise!(provider_config.resolve(&env, runtime.clone()));
83
+
84
+ let logger_config = try_or_reject_promise!(logger_config.resolve(&env));
50
85
 
51
86
  // TODO: https://github.com/NomicFoundation/edr/issues/760
52
- let build_info_config =
53
- edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers((&tracing_config).into())
54
- .map_err(|err| napi::Error::from_reason(err.to_string()))?;
87
+ let build_info_config = try_or_reject_promise!(
88
+ edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers(
89
+ (&edr_napi_core::solidity::config::TracingConfigWithBuffers::from(tracing_config))
90
+ .into(),
91
+ )
92
+ .map_err(|error| napi::Error::from_reason(error.to_string()))
93
+ );
55
94
 
56
- let contract_decoder = ContractDecoder::new(&build_info_config).map_or_else(
57
- |error| Err(napi::Error::from_reason(error.to_string())),
58
- |contract_decoder| Ok(Arc::new(contract_decoder)),
59
- )?;
95
+ let contract_decoder = try_or_reject_promise!(ContractDecoder::new(&build_info_config)
96
+ .map_or_else(
97
+ |error| Err(napi::Error::from_reason(error.to_string())),
98
+ |contract_decoder| Ok(Arc::new(contract_decoder))
99
+ ));
60
100
 
61
101
  #[cfg(feature = "scenarios")]
62
102
  let scenario_file =
63
- runtime::Handle::current().block_on(crate::scenarios::scenario_file(
103
+ try_or_reject_promise!(runtime.clone().block_on(crate::scenarios::scenario_file(
64
104
  chain_type.clone(),
65
105
  provider_config.clone(),
66
106
  logger_config.enable,
67
- ))?;
107
+ )));
68
108
 
69
- let runtime = runtime::Handle::current();
70
109
  let builder = {
71
110
  // TODO: https://github.com/NomicFoundation/edr/issues/760
72
111
  // TODO: Don't block the JS event loop
73
112
  let context = runtime.block_on(async { self.inner.lock().await });
74
113
 
75
- context.create_provider_builder(
114
+ try_or_reject_promise!(context.create_provider_builder(
76
115
  &env,
77
116
  &chain_type,
78
117
  provider_config,
79
118
  logger_config,
80
119
  subscription_config.into(),
81
120
  &contract_decoder,
82
- )?
121
+ ))
83
122
  };
84
123
 
85
- let (deferred, promise) = env.create_deferred()?;
86
124
  runtime.clone().spawn_blocking(move || {
87
125
  let result = builder.build(runtime.clone()).map(|provider| {
88
126
  Provider::new(
@@ -101,7 +139,7 @@ impl EdrContext {
101
139
  }
102
140
 
103
141
  #[doc = "Registers a new provider factory for the provided chain type."]
104
- #[napi]
142
+ #[napi(catch_unwind)]
105
143
  pub async fn register_provider_factory(
106
144
  &self,
107
145
  chain_type: String,
@@ -111,10 +149,187 @@ impl EdrContext {
111
149
  context.register_provider_factory(chain_type, factory.as_inner().clone());
112
150
  Ok(())
113
151
  }
152
+
153
+ #[napi(catch_unwind)]
154
+ pub async fn register_solidity_test_runner_factory(
155
+ &self,
156
+ chain_type: String,
157
+ factory: &SolidityTestRunnerFactory,
158
+ ) -> napi::Result<()> {
159
+ let mut context = self.inner.lock().await;
160
+ context.register_solidity_test_runner(chain_type, factory.as_inner().clone());
161
+ Ok(())
162
+ }
163
+
164
+ #[doc = "Executes Solidity tests."]
165
+ #[doc = ""]
166
+ #[doc = "The function will return as soon as test execution is started."]
167
+ #[doc = "The progress callback will be called with the results of each test"]
168
+ #[doc = "suite. It is up to the caller to track how many times the callback"]
169
+ #[doc = "is called to know when all tests are done."]
170
+ #[allow(clippy::too_many_arguments)]
171
+ #[napi(catch_unwind, ts_return_type = "Promise<void>")]
172
+ pub fn run_solidity_tests(
173
+ &self,
174
+ env: Env,
175
+ chain_type: String,
176
+ artifacts: Vec<Artifact>,
177
+ test_suites: Vec<ArtifactId>,
178
+ config_args: SolidityTestRunnerConfigArgs,
179
+ tracing_config: TracingConfigWithBuffers,
180
+ #[napi(ts_arg_type = "(result: SuiteResult) => void")]
181
+ on_test_suite_completed_callback: JsFunction,
182
+ ) -> napi::Result<JsObject> {
183
+ let (deferred, promise) = env.create_deferred()?;
184
+
185
+ let on_test_suite_completed_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> =
186
+ match on_test_suite_completed_callback.create_threadsafe_function(
187
+ // Unbounded queue size
188
+ 0,
189
+ |ctx: ThreadSafeCallContext<SuiteResult>| Ok(vec![ctx.value]),
190
+ ) {
191
+ Ok(value) => value,
192
+ Err(error) => {
193
+ deferred.reject(error);
194
+ return Ok(promise);
195
+ }
196
+ };
197
+
198
+ let test_filter: Arc<TestFilterConfig> =
199
+ Arc::new(match config_args.try_get_test_filter() {
200
+ Ok(test_filter) => test_filter,
201
+ Err(error) => {
202
+ deferred.reject(error);
203
+ return Ok(promise);
204
+ }
205
+ });
206
+
207
+ let runtime = runtime::Handle::current();
208
+ let config = match config_args.resolve(&env, runtime.clone()) {
209
+ Ok(config) => config,
210
+ Err(error) => {
211
+ deferred.reject(error);
212
+ return Ok(promise);
213
+ }
214
+ };
215
+
216
+ let context = self.inner.clone();
217
+ runtime.clone().spawn(async move {
218
+ macro_rules! try_or_reject_deferred {
219
+ ($expr:expr) => {
220
+ match $expr {
221
+ Ok(value) => value,
222
+ Err(error) => {
223
+ deferred.reject(error);
224
+ return;
225
+ }
226
+ }
227
+ };
228
+ }
229
+ let factory = {
230
+ let context = context.lock().await;
231
+ try_or_reject_deferred!(context.solidity_test_runner_factory(&chain_type).await)
232
+ };
233
+
234
+ let linking_output =
235
+ try_or_reject_deferred!(LinkingOutput::link(&config.project_root, artifacts));
236
+
237
+ // Build revert decoder from ABIs of all artifacts.
238
+ let abis = linking_output
239
+ .known_contracts
240
+ .iter()
241
+ .map(|(_, contract)| &contract.abi);
242
+
243
+ let revert_decoder = RevertDecoder::new().with_abis(abis);
244
+
245
+ let test_suites = try_or_reject_deferred!(test_suites
246
+ .into_iter()
247
+ .map(edr_solidity::artifacts::ArtifactId::try_from)
248
+ .collect::<Result<Vec<_>, _>>());
249
+
250
+ let contracts = try_or_reject_deferred!(test_suites
251
+ .iter()
252
+ .map(|artifact_id| {
253
+ let contract_data = linking_output
254
+ .known_contracts
255
+ .get(artifact_id)
256
+ .ok_or_else(|| {
257
+ napi::Error::new(
258
+ napi::Status::GenericFailure,
259
+ format!("Unknown contract: {}", artifact_id.identifier()),
260
+ )
261
+ })?;
262
+
263
+ let bytecode = contract_data.bytecode.clone().ok_or_else(|| {
264
+ napi::Error::new(
265
+ napi::Status::GenericFailure,
266
+ format!(
267
+ "No bytecode for test suite contract: {}",
268
+ artifact_id.identifier()
269
+ ),
270
+ )
271
+ })?;
272
+
273
+ let test_contract = TestContract {
274
+ abi: contract_data.abi.clone(),
275
+ bytecode,
276
+ };
277
+
278
+ Ok((artifact_id.clone(), test_contract))
279
+ })
280
+ .collect::<napi::Result<TestContracts>>());
281
+
282
+ let include_traces = config.include_traces.into();
283
+
284
+ let test_runner = try_or_reject_deferred!(runtime
285
+ .clone()
286
+ .spawn_blocking(move || {
287
+ factory.create_test_runner(
288
+ runtime,
289
+ config,
290
+ contracts,
291
+ linking_output.known_contracts,
292
+ linking_output.libs_to_deploy,
293
+ revert_decoder,
294
+ tracing_config.into(),
295
+ )
296
+ })
297
+ .await
298
+ .expect("Failed to join test runner factory thread"));
299
+
300
+ let () = try_or_reject_deferred!(test_runner.run_tests(
301
+ test_filter,
302
+ Arc::new(
303
+ move |SuiteResultAndArtifactId {
304
+ artifact_id,
305
+ result,
306
+ }| {
307
+ let suite_result = SuiteResult::new(artifact_id, result, include_traces);
308
+
309
+ let status = on_test_suite_completed_callback
310
+ .call(suite_result, ThreadsafeFunctionCallMode::Blocking);
311
+
312
+ // This should always succeed since we're using an unbounded queue. We add
313
+ // an assertion for completeness.
314
+ assert_eq!(
315
+ status,
316
+ napi::Status::Ok,
317
+ "Failed to call on_test_suite_completed_callback with status: {status}"
318
+ );
319
+ }
320
+ ),
321
+ ));
322
+
323
+ deferred.resolve(move |_env| Ok(()));
324
+ });
325
+
326
+ Ok(promise)
327
+ }
114
328
  }
115
329
 
116
330
  pub struct Context {
117
331
  provider_factories: HashMap<String, Arc<dyn SyncProviderFactory>>,
332
+ solidity_test_runner_factories: HashMap<String, Arc<dyn solidity::SyncTestRunnerFactory>>,
118
333
  #[cfg(feature = "tracing")]
119
334
  _tracing_write_guard: tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>,
120
335
  }
@@ -158,6 +373,7 @@ impl Context {
158
373
 
159
374
  Ok(Self {
160
375
  provider_factories: HashMap::new(),
376
+ solidity_test_runner_factories: HashMap::new(),
161
377
  #[cfg(feature = "tracing")]
162
378
  _tracing_write_guard: guard,
163
379
  })
@@ -172,6 +388,15 @@ impl Context {
172
388
  self.provider_factories.insert(chain_type, factory);
173
389
  }
174
390
 
391
+ pub fn register_solidity_test_runner(
392
+ &mut self,
393
+ chain_type: String,
394
+ factory: Arc<dyn solidity::SyncTestRunnerFactory>,
395
+ ) {
396
+ self.solidity_test_runner_factories
397
+ .insert(chain_type, factory);
398
+ }
399
+
175
400
  /// Tries to create a new provider for the provided chain type and
176
401
  /// configuration.
177
402
  pub fn create_provider_builder(
@@ -198,4 +423,18 @@ impl Context {
198
423
  ))
199
424
  }
200
425
  }
426
+
427
+ pub async fn solidity_test_runner_factory(
428
+ &self,
429
+ chain_type: &str,
430
+ ) -> napi::Result<Arc<dyn solidity::SyncTestRunnerFactory>> {
431
+ if let Some(factory) = self.solidity_test_runner_factories.get(chain_type) {
432
+ Ok(Arc::clone(factory))
433
+ } else {
434
+ Err(napi::Error::new(
435
+ napi::Status::GenericFailure,
436
+ "Solidity test runner for provided chain type does not exist",
437
+ ))
438
+ }
439
+ }
201
440
  }
@@ -1,13 +1,13 @@
1
1
  use std::collections::HashMap;
2
2
 
3
- use napi::bindgen_prelude::{BigInt, Buffer};
3
+ use napi::bindgen_prelude::{BigInt, Uint8Array};
4
4
  use napi_derive::napi;
5
5
 
6
6
  #[napi(object)]
7
7
  pub struct DebugTraceResult {
8
8
  pub pass: bool,
9
9
  pub gas_used: BigInt,
10
- pub output: Option<Buffer>,
10
+ pub output: Option<Uint8Array>,
11
11
  pub struct_logs: Vec<DebugTraceLogItem>,
12
12
  }
13
13
 
@@ -0,0 +1,109 @@
1
+ use edr_instrument::coverage::{self, Version};
2
+ use napi::bindgen_prelude::Uint8Array;
3
+ use napi_derive::napi;
4
+
5
+ #[napi(object)]
6
+ pub struct InstrumentationResult {
7
+ /// The generated source code with coverage instrumentation.
8
+ #[napi(readonly)]
9
+ pub source: String,
10
+ /// The metadata for each instrumented code segment.
11
+ #[napi(readonly)]
12
+ pub metadata: Vec<InstrumentationMetadata>,
13
+ }
14
+
15
+ impl TryFrom<edr_instrument::coverage::InstrumentationResult> for InstrumentationResult {
16
+ type Error = usize;
17
+
18
+ fn try_from(
19
+ value: edr_instrument::coverage::InstrumentationResult,
20
+ ) -> Result<Self, Self::Error> {
21
+ let metadata = value
22
+ .metadata
23
+ .into_iter()
24
+ .map(InstrumentationMetadata::try_from)
25
+ .collect::<Result<Vec<_>, _>>()?;
26
+
27
+ Ok(InstrumentationResult {
28
+ source: value.source,
29
+ metadata,
30
+ })
31
+ }
32
+ }
33
+
34
+ #[napi(object)]
35
+ pub struct InstrumentationMetadata {
36
+ /// The tag that identifies the instrumented code. Tags are
37
+ /// deterministically generated from the source code, source id, and
38
+ /// Solidity version.
39
+ #[napi(readonly)]
40
+ pub tag: Uint8Array,
41
+ /// The kind of instrumented code. Currently, the only supported kind
42
+ /// is "statement".
43
+ #[napi(readonly)]
44
+ pub kind: String,
45
+ /// The starting position of the instrumented code - including trivia such
46
+ /// as whitespace - in the source code, in UTF-16 code units.
47
+ #[napi(readonly)]
48
+ pub start_utf16: i64,
49
+ /// The ending position of the instrumented code - including trivia such as
50
+ /// whitespace - in the source code, in UTF-16 code units.
51
+ #[napi(readonly)]
52
+ pub end_utf16: i64,
53
+ }
54
+
55
+ impl TryFrom<edr_instrument::coverage::InstrumentationMetadata> for InstrumentationMetadata {
56
+ type Error = usize;
57
+
58
+ fn try_from(
59
+ value: edr_instrument::coverage::InstrumentationMetadata,
60
+ ) -> Result<Self, Self::Error> {
61
+ let start_utf16 = value
62
+ .start_utf16
63
+ .try_into()
64
+ .map_err(|_error| value.start_utf16)?;
65
+ let end_utf16 = value
66
+ .end_utf16
67
+ .try_into()
68
+ .map_err(|_error| value.end_utf16)?;
69
+
70
+ Ok(InstrumentationMetadata {
71
+ tag: Uint8Array::with_data_copied(value.tag),
72
+ kind: value.kind.to_owned(),
73
+ start_utf16,
74
+ end_utf16,
75
+ })
76
+ }
77
+ }
78
+
79
+ /// Adds per-statement coverage instrumentation to the given Solidity source
80
+ /// code.
81
+ #[napi(catch_unwind)]
82
+ pub fn add_statement_coverage_instrumentation(
83
+ source_code: String,
84
+ source_id: String,
85
+ solidity_version: String,
86
+ coverage_library_path: String,
87
+ ) -> napi::Result<InstrumentationResult> {
88
+ let solidity_version = Version::parse(&solidity_version).map_err(|error| {
89
+ napi::Error::new(
90
+ napi::Status::InvalidArg,
91
+ format!("Invalid Solidity version: {error}"),
92
+ )
93
+ })?;
94
+
95
+ let instrumented = coverage::instrument_code(
96
+ &source_code,
97
+ &source_id,
98
+ solidity_version,
99
+ &coverage_library_path,
100
+ )
101
+ .map_err(|error| napi::Error::new(napi::Status::GenericFailure, error))?;
102
+
103
+ instrumented.try_into().map_err(|location| {
104
+ napi::Error::new(
105
+ napi::Status::GenericFailure,
106
+ format!("Cannot represent source locations in JavaScript: {location}."),
107
+ )
108
+ })
109
+ }
package/src/lib.rs CHANGED
@@ -18,10 +18,17 @@ pub mod config;
18
18
  /// Types related to an EDR N-API context.
19
19
  pub mod context;
20
20
  mod debug_trace;
21
+ /// Types and functions related to code coverage instrumentation.
22
+ pub mod instrument;
21
23
  /// Types for EVM execution logs.
22
24
  pub mod log;
23
25
  /// Types for an RPC request logger.
24
26
  pub mod logger;
27
+ /// Types for mocking provider behavior.
28
+ #[cfg(feature = "test-mock")]
29
+ mod mock;
30
+ /// Types for precompiles.
31
+ pub mod precompile;
25
32
  /// Types for Ethereum RPC providers.
26
33
  pub mod provider;
27
34
  /// Types for EVM execution results.
@@ -29,6 +36,9 @@ pub mod result;
29
36
  /// Types relating to benchmark scenarios.
30
37
  #[cfg(feature = "scenarios")]
31
38
  pub mod scenarios;
39
+ mod serde;
40
+ /// Solidity test runner.
41
+ pub mod solidity_tests;
32
42
  /// Types for subscribing to events.
33
43
  pub mod subscription;
34
44
  /// Types for EVM traces.
package/src/log.rs CHANGED
@@ -1,30 +1,28 @@
1
- use napi::{Env, JsBuffer, JsBufferValue, bindgen_prelude::Buffer};
1
+ use napi::bindgen_prelude::Uint8Array;
2
2
  use napi_derive::napi;
3
3
 
4
4
  /// Ethereum execution log.
5
5
  #[napi(object)]
6
6
  pub struct ExecutionLog {
7
- pub address: Buffer,
8
- pub topics: Vec<Buffer>,
9
- pub data: JsBuffer,
7
+ pub address: Uint8Array,
8
+ pub topics: Vec<Uint8Array>,
9
+ pub data: Uint8Array,
10
10
  }
11
11
 
12
- impl ExecutionLog {
13
- pub fn new(env: &Env, log: &edr_eth::log::ExecutionLog) -> napi::Result<Self> {
14
- let topics = log
12
+ impl From<&edr_eth::log::ExecutionLog> for ExecutionLog {
13
+ fn from(value: &edr_eth::log::ExecutionLog) -> Self {
14
+ let topics = value
15
15
  .topics()
16
16
  .iter()
17
- .map(|topic| Buffer::from(topic.as_slice()))
17
+ .map(Uint8Array::with_data_copied)
18
18
  .collect();
19
19
 
20
- let data = env
21
- .create_buffer_with_data(log.data.data.to_vec())
22
- .map(JsBufferValue::into_raw)?;
20
+ let data = Uint8Array::with_data_copied(&value.data.data);
23
21
 
24
- Ok(Self {
25
- address: Buffer::from(log.address.as_slice()),
22
+ Self {
23
+ address: Uint8Array::with_data_copied(value.address),
26
24
  topics,
27
25
  data,
28
- })
26
+ }
29
27
  }
30
28
  }
package/src/logger.rs CHANGED
@@ -1,38 +1,20 @@
1
- use std::sync::{Arc, mpsc::channel};
1
+ use std::sync::{mpsc::channel, Arc};
2
2
 
3
3
  use edr_eth::Bytes;
4
4
  use edr_napi_core::logger::LoggerError;
5
5
  use napi::{
6
- JsFunction, Status,
7
6
  threadsafe_function::{
8
7
  ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
9
8
  },
9
+ JsFunction, Status,
10
10
  };
11
11
  use napi_derive::napi;
12
12
 
13
- use crate::cast::TryCast;
14
-
15
- #[napi(object)]
16
- pub struct ContractAndFunctionName {
17
- /// The contract name.
18
- pub contract_name: String,
19
- /// The function name. Only present for calls.
20
- pub function_name: Option<String>,
21
- }
22
-
23
- impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
24
- type Error = napi::Error;
25
-
26
- fn try_cast(self) -> std::result::Result<(String, Option<String>), Self::Error> {
27
- Ok((self.contract_name, self.function_name))
28
- }
29
- }
30
-
31
13
  #[napi(object)]
32
14
  pub struct LoggerConfig {
33
15
  /// Whether to enable the logger.
34
16
  pub enable: bool,
35
- #[napi(ts_type = "(inputs: Buffer[]) => string[]")]
17
+ #[napi(ts_type = "(inputs: ArrayBuffer[]) => string[]")]
36
18
  pub decode_console_log_inputs_callback: JsFunction,
37
19
  #[napi(ts_type = "(message: string, replace: boolean) => void")]
38
20
  pub print_line_callback: JsFunction,
@@ -48,9 +30,11 @@ impl LoggerConfig {
48
30
  let inputs = ctx.env.create_array_with_length(ctx.value.len()).and_then(
49
31
  |mut inputs| {
50
32
  for (idx, input) in ctx.value.into_iter().enumerate() {
51
- ctx.env.create_buffer_with_data(input.to_vec()).and_then(
52
- |input| inputs.set_element(idx as u32, input.into_raw()),
53
- )?;
33
+ ctx.env
34
+ .create_arraybuffer_with_data(input.to_vec())
35
+ .and_then(|input| {
36
+ inputs.set_element(idx as u32, input.into_raw())
37
+ })?;
54
38
  }
55
39
 
56
40
  Ok(inputs)
@@ -60,8 +44,8 @@ impl LoggerConfig {
60
44
  Ok(vec![inputs])
61
45
  })?;
62
46
 
63
- // Maintain a weak reference to the function to avoid the event loop from
64
- // exiting.
47
+ // Maintain a weak reference to the function to avoid blocking the event loop
48
+ // from exiting.
65
49
  decode_console_log_inputs_callback.unref(env)?;
66
50
 
67
51
  let decode_console_log_inputs_fn = Arc::new(move |console_log_inputs| {
@@ -96,13 +80,27 @@ impl LoggerConfig {
96
80
  Ok(vec![message.into_unknown(), replace.into_unknown()])
97
81
  })?;
98
82
 
99
- // Maintain a weak reference to the function to avoid the event loop from
100
- // exiting.
83
+ // Maintain a weak reference to the function to avoid blocking the event loop
84
+ // from exiting.
101
85
  print_line_callback.unref(env)?;
102
86
 
103
87
  let print_line_fn = Arc::new(move |message, replace| {
104
- let status =
105
- print_line_callback.call((message, replace), ThreadsafeFunctionCallMode::Blocking);
88
+ let (sender, receiver) = channel();
89
+
90
+ let status = print_line_callback.call_with_return_value(
91
+ (message, replace),
92
+ ThreadsafeFunctionCallMode::Blocking,
93
+ move |()| {
94
+ sender.send(()).map_err(|_error| {
95
+ napi::Error::new(
96
+ Status::GenericFailure,
97
+ "Failed to send result from decode_console_log_inputs",
98
+ )
99
+ })
100
+ },
101
+ );
102
+
103
+ let () = receiver.recv().unwrap();
106
104
 
107
105
  if status == napi::Status::Ok {
108
106
  Ok(())