@reboot-dev/reboot 0.42.0 → 0.44.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/reboot_native.cc CHANGED
@@ -27,20 +27,18 @@ namespace py = pybind11;
27
27
 
28
28
  // Helper when debugging, left for future optimization work.
29
29
  struct Timer {
30
- Timer(const char* name)
31
- : name_(name) {
30
+ Timer(const char* name) : name_(name) {
32
31
  start_ = std::chrono::high_resolution_clock::now();
33
32
  }
34
33
 
35
34
  void elapsed(const char* step = nullptr) const {
36
35
  auto end = std::chrono::high_resolution_clock::now();
37
- auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
38
- end - start_);
36
+ auto duration =
37
+ std::chrono::duration_cast<std::chrono::milliseconds>(end - start_);
39
38
  std::stringstream ss;
40
39
  ss << name_
41
40
  << (step != nullptr ? " at " + std::string(step) : std::string(""))
42
- << " took " << duration.count() << "ms"
43
- << std::endl;
41
+ << " took " << duration.count() << "ms" << std::endl;
44
42
  std::cout << ss.str();
45
43
  }
46
44
 
@@ -87,9 +85,9 @@ struct PythonNodeAdaptor {
87
85
  if (const py::error_already_set* e_py =
88
86
  dynamic_cast<const py::error_already_set*>(&e)) {
89
87
  // This is a Python exception. Is it an `InputError`?
90
- if (e_py->matches(py::module::import(
91
- "rebootdev.aio.exceptions")
92
- .attr("InputError"))) {
88
+ if (e_py->matches(
89
+ py::module::import("rebootdev.aio.exceptions")
90
+ .attr("InputError"))) {
93
91
  // This is an InputError, which means it reports a mistake in the input
94
92
  // provided by the developer. We want to print _only_ the user-friendly
95
93
  // error message in the exception, without intimidating stack traces.
@@ -104,15 +102,13 @@ struct PythonNodeAdaptor {
104
102
  // help diagnose the problem.
105
103
  std::cerr << "Unexpected library exception: " << e_py->what()
106
104
  << std::endl
107
- << "Please report this bug to the maintainers!"
108
- << std::endl;
105
+ << "Please report this bug to the maintainers!" << std::endl;
109
106
  }
110
107
  } else {
111
108
  // This is a C++ exception; something went wrong in the C++ code. Request
112
109
  // that the developer reports the issue.
113
- std::cerr
114
- << "Unexpected adapter exception: " << e.what() << std::endl
115
- << "Please report this bug to the maintainers!" << std::endl;
110
+ std::cerr << "Unexpected adapter exception: " << e.what() << std::endl
111
+ << "Please report this bug to the maintainers!" << std::endl;
116
112
  }
117
113
  }
118
114
 
@@ -224,11 +220,8 @@ struct PythonNodeAdaptor {
224
220
  try {
225
221
  function(env);
226
222
  } catch (const std::exception& e) {
227
- std::cerr
228
- << "Unexpected exception: " << e.what()
229
- << "\n"
230
- << "Please report this bug to the maintainers!"
231
- << std::endl;
223
+ std::cerr << "Unexpected exception: " << e.what() << "\n"
224
+ << "Please report this bug to the maintainers!" << std::endl;
232
225
  }
233
226
  }
234
227
  }
@@ -263,10 +256,7 @@ struct PythonNodeAdaptor {
263
256
  // into Python or expecting calls from Python.
264
257
  std::atomic<int> references{0};
265
258
 
266
- Napi::TypedThreadSafeFunction<
267
- void,
268
- void,
269
- _RunNodeFunctions>
259
+ Napi::TypedThreadSafeFunction<void, void, _RunNodeFunctions>
270
260
  thread_safe_function;
271
261
  std::mutex node_functions_mutex_;
272
262
  std::list<std::function<void(Napi::Env&)>> node_functions_;
@@ -294,9 +284,7 @@ struct NapiReferenceDeleter {
294
284
  template <typename T>
295
285
  void operator()(Napi::Reference<T>* reference) {
296
286
  adaptor->ScheduleCallbackOnNodeEventLoopLowPriority(
297
- [reference](Napi::Env) {
298
- delete reference;
299
- });
287
+ [reference](Napi::Env) { delete reference; });
300
288
  }
301
289
  };
302
290
 
@@ -335,13 +323,9 @@ using NapiSafeObjectReference = NapiSafeReference<Napi::Object>;
335
323
  // also includes our C++ <-> Python class wrappers for storing NAPI
336
324
  // objects in Python to properly handle memory management.
337
325
  PYBIND11_EMBEDDED_MODULE(reboot_native, m) {
338
- py::class_<NapiSafeFunctionReference>(
339
- m,
340
- "NapiSafeFunctionReference");
326
+ py::class_<NapiSafeFunctionReference>(m, "NapiSafeFunctionReference");
341
327
 
342
- py::class_<NapiSafeObjectReference>(
343
- m,
344
- "NapiSafeObjectReference");
328
+ py::class_<NapiSafeObjectReference>(m, "NapiSafeObjectReference");
345
329
  }
346
330
 
347
331
 
@@ -354,9 +338,7 @@ Napi::External<py::object> make_napi_external(
354
338
  py_object,
355
339
  [](Napi::Env, py::object* py_object) {
356
340
  adaptor->ScheduleCallbackOnPythonEventLoopLowPriority(
357
- [py_object]() {
358
- delete py_object;
359
- });
341
+ [py_object]() { delete py_object; });
360
342
  });
361
343
 
362
344
  if (type_tag != nullptr) {
@@ -379,126 +361,119 @@ void _RunNodeFunctions(
379
361
  void PythonNodeAdaptor::Initialize(
380
362
  Napi::Env& env,
381
363
  const Napi::Function& js_callback) {
382
- thread_safe_function = Napi::TypedThreadSafeFunction<
383
- void,
384
- void,
385
- _RunNodeFunctions>::New(
386
- /* Environment: */ env,
387
- /* JS callback: */ js_callback,
388
- /* Resource name: */ "reboot_native",
389
- /* Max queue size (0 = unlimited): */ 0,
390
- /* Initial thread count: */ 1,
391
- /* Context: */ (void*) nullptr,
392
- // Finalizer:
393
- [this](Napi::Env env, void*, void*) {
394
- // Set that we've been finalized so that we don't use
395
- // `thread_safe_function` again, see comment in
396
- // `ScheduleCallbackOnNodeEventLoop`.
397
- thread_safe_function_finalized = true; },
398
- /* Finalizer data: */ (void*) nullptr);
364
+ thread_safe_function =
365
+ Napi::TypedThreadSafeFunction<void, void, _RunNodeFunctions>::New(
366
+ /* Environment: */
367
+ env,
368
+ /* JS callback: */ js_callback,
369
+ /* Resource name: */ "reboot_native",
370
+ /* Max queue size (0 = unlimited): */ 0,
371
+ /* Initial thread count: */ 1,
372
+ /* Context: */ (void*) nullptr,
373
+ // Finalizer:
374
+ [this](Napi::Env env, void*, void*) {
375
+ // Set that we've been finalized so that we don't use
376
+ // `thread_safe_function` again, see comment in
377
+ // `ScheduleCallbackOnNodeEventLoop`.
378
+ thread_safe_function_finalized = true;
379
+ },
380
+ /* Finalizer data: */ (void*) nullptr);
399
381
 
400
382
  // We start with our thread safe function unreferenced so that
401
383
  // if the user's code never calls into us we won't keep Node
402
384
  // from exiting.
403
385
  thread_safe_function.Unref(env);
404
386
 
405
- thread = std::thread(
406
- [this]() {
407
- // Set environment variables _before_ initializing the
408
- // interpreter because it may cache the variables. Also, some
409
- // of the variables need to be set before we start importing
410
- // anything.
411
- //
412
- // Don't try and manipulate signals when running within Node!
413
- setenv("REBOOT_SIGNALS_AVAILABLE", "false", 1);
414
- // Specify that we are running from within `node`.
415
- setenv("REBOOT_NODEJS", "true", 1);
416
-
417
- py::initialize_interpreter();
418
-
419
- try {
420
- py::object module = py::module::import("rebootdev.nodejs.python");
421
-
422
- module.attr("launch_subprocess_server") = py::cpp_function(
423
- [](py::str py_base64_args) {
424
- py::object py_future =
425
- py::module::import("asyncio").attr("Future")();
426
-
427
- adaptor->ScheduleCallbackOnNodeEventLoop(
428
- [base64_args = std::string(py_base64_args),
429
- py_future = new py::object(py_future)](Napi::Env env) {
430
- try {
431
- Napi::Number js_pid =
432
- js_launchSubprocessServer
433
- ->Call(
434
- {Napi::String::New(env, base64_args)})
435
- .As<Napi::Number>();
436
-
437
- adaptor->ScheduleCallbackOnPythonEventLoop(
438
- [py_future, pid = js_pid.Int64Value()]() {
439
- bool cancelled =
440
- py_future->attr("cancelled")()
441
- .cast<bool>();
442
- if (!cancelled) {
443
- py_future->attr("set_result")(pid);
444
- }
445
- delete py_future;
446
- });
447
- } catch (const Napi::Error& e) {
448
- adaptor->ScheduleCallbackOnPythonEventLoop(
449
- [py_future, what = e.what()]() {
450
- bool cancelled =
451
- py_future->attr("cancelled")()
452
- .cast<bool>();
453
- if (!cancelled) {
454
- py_future->attr("set_exception")(
455
- py::module::import("builtins")
456
- .attr("Exception")(
457
- what));
458
- }
459
- delete py_future;
460
- });
461
- }
462
- });
387
+ thread = std::thread([this]() {
388
+ // Set environment variables _before_ initializing the
389
+ // interpreter because it may cache the variables. Also, some
390
+ // of the variables need to be set before we start importing
391
+ // anything.
392
+ //
393
+ // Don't try and manipulate signals when running within Node!
394
+ setenv("REBOOT_SIGNALS_AVAILABLE", "false", 1);
395
+ // Specify that we are running from within `node`.
396
+ setenv("REBOOT_NODEJS", "true", 1);
463
397
 
464
- return py_future;
465
- });
398
+ py::initialize_interpreter();
466
399
 
467
- module.attr("run_functions") = py::cpp_function(
468
- [this]() {
469
- std::list<std::function<void()>> functions;
470
- std::vector<std::pair<py::object*, std::string>> futures;
400
+ try {
401
+ py::object module = py::module::import("rebootdev.nodejs.python");
471
402
 
472
- {
473
- std::unique_lock<std::mutex> lock(mutex);
474
- functions = std::move(python_functions);
475
- should_signal_fd_ = true;
476
- }
403
+ module.attr("launch_subprocess_server") =
404
+ py::cpp_function([](py::str py_base64_args) {
405
+ py::object py_future =
406
+ py::module::import("asyncio").attr("Future")();
477
407
 
478
- for (auto&& function : functions) {
408
+ adaptor->ScheduleCallbackOnNodeEventLoop(
409
+ [base64_args = std::string(py_base64_args),
410
+ py_future = new py::object(py_future)](Napi::Env env) {
479
411
  try {
480
- function();
481
- } catch (std::exception& e) {
482
- PythonNodeAdaptor::HandleException(e);
412
+ Napi::Number js_pid =
413
+ js_launchSubprocessServer
414
+ ->Call({Napi::String::New(env, base64_args)})
415
+ .As<Napi::Number>();
416
+
417
+ adaptor->ScheduleCallbackOnPythonEventLoop(
418
+ [py_future, pid = js_pid.Int64Value()]() {
419
+ bool cancelled =
420
+ py_future->attr("cancelled")().cast<bool>();
421
+ if (!cancelled) {
422
+ py_future->attr("set_result")(pid);
423
+ }
424
+ delete py_future;
425
+ });
426
+ } catch (const Napi::Error& e) {
427
+ adaptor->ScheduleCallbackOnPythonEventLoop(
428
+ [py_future, what = e.what()]() {
429
+ bool cancelled =
430
+ py_future->attr("cancelled")().cast<bool>();
431
+ if (!cancelled) {
432
+ py_future->attr("set_exception")(
433
+ py::module::import("builtins")
434
+ .attr("Exception")(what));
435
+ }
436
+ delete py_future;
437
+ });
483
438
  }
484
- }
485
- });
439
+ });
440
+
441
+ return py_future;
442
+ });
486
443
 
487
- py::object event_loop_thread = module.attr("EventLoopThread")(
488
- read_fd_);
444
+ module.attr("run_functions") = py::cpp_function([this]() {
445
+ std::list<std::function<void()>> functions;
446
+ std::vector<std::pair<py::object*, std::string>> futures;
489
447
 
490
- py::gil_scoped_release release;
448
+ {
449
+ std::unique_lock<std::mutex> lock(mutex);
450
+ functions = std::move(python_functions);
451
+ should_signal_fd_ = true;
452
+ }
491
453
 
492
- while (true) {
493
- std::this_thread::sleep_until(
494
- std::chrono::time_point<std::chrono::system_clock>::max());
495
- continue;
454
+ for (auto&& function : functions) {
455
+ try {
456
+ function();
457
+ } catch (std::exception& e) {
458
+ PythonNodeAdaptor::HandleException(e);
496
459
  }
497
- } catch (const std::exception& e) {
498
- std::cout << e.what() << std::endl;
499
- throw;
500
460
  }
501
461
  });
462
+
463
+ py::object event_loop_thread = module.attr("EventLoopThread")(read_fd_);
464
+
465
+ py::gil_scoped_release release;
466
+
467
+ while (true) {
468
+ std::this_thread::sleep_until(
469
+ std::chrono::time_point<std::chrono::system_clock>::max());
470
+ continue;
471
+ }
472
+ } catch (const std::exception& e) {
473
+ std::cout << e.what() << std::endl;
474
+ throw;
475
+ }
476
+ });
502
477
  }
503
478
 
504
479
 
@@ -555,21 +530,19 @@ template <typename F, typename T = std::invoke_result_t<F>>
555
530
  T RunCallbackOnPythonEventLoop(F&& f) {
556
531
  std::promise<T> promise;
557
532
 
558
- adaptor->ScheduleCallbackOnPythonEventLoop(
559
- [&f, &promise]() {
560
- try {
561
- if constexpr (std::is_void<T>::value) {
562
- f();
563
- promise.set_value();
564
- } else {
565
- promise.set_value(f());
566
- }
567
- } catch (const std::exception& e) {
568
- promise.set_exception(
569
- std::make_exception_ptr(
570
- std::runtime_error(e.what())));
571
- }
572
- });
533
+ adaptor->ScheduleCallbackOnPythonEventLoop([&f, &promise]() {
534
+ try {
535
+ if constexpr (std::is_void<T>::value) {
536
+ f();
537
+ promise.set_value();
538
+ } else {
539
+ promise.set_value(f());
540
+ }
541
+ } catch (const std::exception& e) {
542
+ promise.set_exception(
543
+ std::make_exception_ptr(std::runtime_error(e.what())));
544
+ }
545
+ });
573
546
 
574
547
  return promise.get_future().get();
575
548
  }
@@ -598,8 +571,7 @@ T RunCallbackOnNodeEventLoop(F&& f, bool warn = false) {
598
571
  .Call({e.Value(), Napi::String::New(env, "\n")});
599
572
  }
600
573
  promise.set_exception(
601
- std::make_exception_ptr(
602
- std::runtime_error(e.what())));
574
+ std::make_exception_ptr(std::runtime_error(e.what())));
603
575
  } catch (const std::exception& e) {
604
576
  if (warn) {
605
577
  env.Global()
@@ -612,8 +584,7 @@ T RunCallbackOnNodeEventLoop(F&& f, bool warn = false) {
612
584
  Napi::String::New(env, "\n")});
613
585
  }
614
586
  promise.set_exception(
615
- std::make_exception_ptr(
616
- std::runtime_error(e.what())));
587
+ std::make_exception_ptr(std::runtime_error(e.what())));
617
588
  }
618
589
  });
619
590
 
@@ -658,8 +629,7 @@ Napi::Promise NodePromiseFromPythonCallback(
658
629
  } catch (const std::exception& e) {
659
630
  // TODO: is this code unreachable because Node.js
660
631
  // will properly always only pass us a `Napi::Error`?
661
- js_deferred->Reject(
662
- Napi::Error::New(env, e.what()).Value());
632
+ js_deferred->Reject(Napi::Error::New(env, e.what()).Value());
663
633
  } catch (...) {
664
634
  // TODO: is this code unreachable because Node.js
665
635
  // will properly always only pass us a `Napi::Error`?
@@ -678,16 +648,14 @@ Napi::Promise NodePromiseFromPythonCallback(
678
648
  });
679
649
 
680
650
  // Allow Node to exit after we've resolved or rejected the promise.
681
- auto js_resolve_reject = Napi::Function::New(
682
- env,
683
- [](const Napi::CallbackInfo& info) {
651
+ auto js_resolve_reject =
652
+ Napi::Function::New(env, [](const Napi::CallbackInfo& info) {
684
653
  adaptor->AllowNodeToExit(info.Env());
685
654
  });
686
655
 
687
- js_promise
688
- .Get("then")
689
- .As<Napi::Function>()
690
- .Call(js_promise, {js_resolve_reject, js_resolve_reject});
656
+ js_promise.Get("then").As<Napi::Function>().Call(
657
+ js_promise,
658
+ {js_resolve_reject, js_resolve_reject});
691
659
 
692
660
  return js_promise;
693
661
  }
@@ -719,10 +687,10 @@ Napi::Promise NodePromiseFromPythonFuture(
719
687
  auto js_promise = js_deferred->Promise();
720
688
 
721
689
  adaptor->ScheduleCallbackOnPythonEventLoop(
722
- [python_future_callback = std::forward<PythonFutureCallback>(
723
- python_future_callback),
724
- python_done_callback = std::forward<PythonDoneCallback>(
725
- python_done_callback),
690
+ [python_future_callback =
691
+ std::forward<PythonFutureCallback>(python_future_callback),
692
+ python_done_callback =
693
+ std::forward<PythonDoneCallback>(python_done_callback),
726
694
  node_callback = std::forward<NodeCallback>(node_callback),
727
695
  js_deferred = std::move(js_deferred)]() mutable {
728
696
  try {
@@ -730,8 +698,8 @@ Napi::Promise NodePromiseFromPythonFuture(
730
698
  py::object py_future = python_future_callback();
731
699
 
732
700
  py_future.attr("add_done_callback")(py::cpp_function(
733
- [python_done_callback = std::forward<PythonDoneCallback>(
734
- python_done_callback),
701
+ [python_done_callback =
702
+ std::forward<PythonDoneCallback>(python_done_callback),
735
703
  node_callback = std::forward<NodeCallback>(node_callback),
736
704
  js_deferred = std::move(js_deferred),
737
705
  // NOTE: need to keep a reference to `py_future` so that it
@@ -766,9 +734,8 @@ Napi::Promise NodePromiseFromPythonFuture(
766
734
  js_deferred = std::move(js_deferred),
767
735
  result = std::move(result)](Napi::Env env) mutable {
768
736
  // TODO: try / catch around `node_callback`.
769
- auto js_result = node_callback(
770
- env,
771
- std::move(result));
737
+ auto js_result =
738
+ node_callback(env, std::move(result));
772
739
  js_deferred->Resolve(js_result);
773
740
  });
774
741
  }
@@ -787,16 +754,14 @@ Napi::Promise NodePromiseFromPythonFuture(
787
754
  });
788
755
 
789
756
  // Allow Node to exit after we've resolved or rejected the promise.
790
- auto js_resolve_reject = Napi::Function::New(
791
- env,
792
- [](const Napi::CallbackInfo& info) {
757
+ auto js_resolve_reject =
758
+ Napi::Function::New(env, [](const Napi::CallbackInfo& info) {
793
759
  adaptor->AllowNodeToExit(info.Env());
794
760
  });
795
761
 
796
- js_promise
797
- .Get("then")
798
- .As<Napi::Function>()
799
- .Call(js_promise, {js_resolve_reject, js_resolve_reject});
762
+ js_promise.Get("then").As<Napi::Function>().Call(
763
+ js_promise,
764
+ {js_resolve_reject, js_resolve_reject});
800
765
 
801
766
  return js_promise;
802
767
  }
@@ -815,9 +780,7 @@ Napi::Promise NodePromiseFromPythonFuture(
815
780
  // TODO: check that `py_result` is `None`.
816
781
  return std::nullopt;
817
782
  },
818
- [](Napi::Env env, std::nullopt_t) {
819
- return env.Null();
820
- });
783
+ [](Napi::Env env, std::nullopt_t) { return env.Null(); });
821
784
  }
822
785
 
823
786
 
@@ -838,8 +801,8 @@ Napi::Promise NodePromiseFromPythonTask(
838
801
  env,
839
802
  [name,
840
803
  create_task = std::move(create_task),
841
- python_task_callback = std::forward<PythonTaskCallback>(
842
- python_task_callback)]() mutable {
804
+ python_task_callback =
805
+ std::forward<PythonTaskCallback>(python_task_callback)]() mutable {
843
806
  py::object py_task = std::apply(
844
807
  [&name, &python_task_callback](
845
808
  const std::string& import,
@@ -875,9 +838,7 @@ Napi::Promise NodePromiseFromPythonTask(
875
838
  // TODO: check that `py_result` is `None`.
876
839
  return std::nullopt;
877
840
  },
878
- [](Napi::Env env, std::nullopt_t) {
879
- return env.Null();
880
- });
841
+ [](Napi::Env env, std::nullopt_t) { return env.Null(); });
881
842
  }
882
843
 
883
844
 
@@ -900,11 +861,9 @@ Napi::Promise NodePromiseFromPythonTaskWithContext(
900
861
  std::string("rebootdev.nodejs.python"),
901
862
  std::string("create_task_with_context"),
902
863
  py_context),
903
- [js_context, // Ensures `py_context` remains valid.
904
- python_task_callback =
905
- std::forward<PythonTaskCallback>(python_task_callback)]() mutable {
906
- return python_task_callback();
907
- },
864
+ [js_context, // Ensures `py_context` remains valid.
865
+ python_task_callback = std::forward<PythonTaskCallback>(
866
+ python_task_callback)]() mutable { return python_task_callback(); },
908
867
  std::forward<PythonDoneCallback>(python_done_callback),
909
868
  std::forward<NodeCallback>(node_callback));
910
869
  }
@@ -925,9 +884,7 @@ Napi::Promise NodePromiseFromPythonTaskWithContext(
925
884
  // TODO: check that `py_result` is `None`.
926
885
  return std::nullopt;
927
886
  },
928
- [](Napi::Env env, std::nullopt_t) {
929
- return env.Null();
930
- });
887
+ [](Napi::Env env, std::nullopt_t) { return env.Null(); });
931
888
  }
932
889
 
933
890
 
@@ -952,64 +909,58 @@ py::object PythonFutureFromNodePromise(
952
909
  try {
953
910
  Napi::Object js_promise = node_promise_callback(env);
954
911
 
955
- js_promise
956
- .Get("then")
957
- .As<Napi::Function>()
958
- .Call(
959
- js_promise,
960
- {Napi::Function::New(
961
- env,
962
- [node_promise_resolved_callback =
963
- std::forward<NodePromiseResolvedCallback>(
964
- node_promise_resolved_callback),
965
- python_callback = std::forward<PythonCallback>(
966
- python_callback),
967
- py_future](const Napi::CallbackInfo& info) mutable {
968
- // TODO: put a try/catch around
969
- // `node_promise_resolved_callback` to handle
970
- // any errors.
971
-
972
- auto result = node_promise_resolved_callback(
973
- info.Env(),
974
- info[0]);
975
-
976
- adaptor->ScheduleCallbackOnPythonEventLoop(
977
- [python_callback = std::forward<PythonCallback>(
978
- python_callback),
979
- py_future,
980
- result = std::move(result)]() mutable {
981
- bool cancelled =
982
- py_future->attr("cancelled")().cast<bool>();
983
- if (!cancelled) {
984
- // TODO: put a try/catch around
985
- // `python_callback` to handle any
986
- // errors.
987
- py_future->attr("set_result")(
988
- python_callback(std::move(result)));
989
- }
990
- delete py_future;
991
- });
992
- }),
993
- Napi::Function::New(
994
- env,
995
- [py_future](const Napi::CallbackInfo& info) {
996
- std::string message = message_from_js_error(
997
- info[0].As<Napi::Object>());
998
-
999
- adaptor->ScheduleCallbackOnPythonEventLoop(
1000
- [py_future, message = std::move(message)]() {
1001
- bool cancelled =
1002
- py_future->attr("cancelled")()
1003
- .cast<bool>();
1004
- if (!cancelled) {
1005
- py_future->attr("set_exception")(
1006
- py::module::import("builtins")
1007
- .attr("Exception")(
1008
- message));
1009
- }
1010
- delete py_future;
1011
- });
1012
- })});
912
+ js_promise.Get("then").As<Napi::Function>().Call(
913
+ js_promise,
914
+ {Napi::Function::New(
915
+ env,
916
+ [node_promise_resolved_callback =
917
+ std::forward<NodePromiseResolvedCallback>(
918
+ node_promise_resolved_callback),
919
+ python_callback =
920
+ std::forward<PythonCallback>(python_callback),
921
+ py_future](const Napi::CallbackInfo& info) mutable {
922
+ // TODO: put a try/catch around
923
+ // `node_promise_resolved_callback` to handle
924
+ // any errors.
925
+
926
+ auto result =
927
+ node_promise_resolved_callback(info.Env(), info[0]);
928
+
929
+ adaptor->ScheduleCallbackOnPythonEventLoop(
930
+ [python_callback =
931
+ std::forward<PythonCallback>(python_callback),
932
+ py_future,
933
+ result = std::move(result)]() mutable {
934
+ bool cancelled =
935
+ py_future->attr("cancelled")().cast<bool>();
936
+ if (!cancelled) {
937
+ // TODO: put a try/catch around
938
+ // `python_callback` to handle any
939
+ // errors.
940
+ py_future->attr("set_result")(
941
+ python_callback(std::move(result)));
942
+ }
943
+ delete py_future;
944
+ });
945
+ }),
946
+ Napi::Function::New(
947
+ env,
948
+ [py_future](const Napi::CallbackInfo& info) {
949
+ std::string message =
950
+ message_from_js_error(info[0].As<Napi::Object>());
951
+
952
+ adaptor->ScheduleCallbackOnPythonEventLoop(
953
+ [py_future, message = std::move(message)]() {
954
+ bool cancelled =
955
+ py_future->attr("cancelled")().cast<bool>();
956
+ if (!cancelled) {
957
+ py_future->attr("set_exception")(
958
+ py::module::import("builtins")
959
+ .attr("Exception")(message));
960
+ }
961
+ delete py_future;
962
+ });
963
+ })});
1013
964
  } catch (const std::exception& e) {
1014
965
  // NOTE: we're just catching exception here vs `Napi::Error`
1015
966
  // since all we care about is `what()` but we're making sure
@@ -1021,31 +972,28 @@ py::object PythonFutureFromNodePromise(
1021
972
 
1022
973
  std::string message = e.what();
1023
974
 
1024
- adaptor->ScheduleCallbackOnPythonEventLoop(
1025
- [py_future, message = std::move(message)]() {
1026
- bool cancelled =
1027
- py_future->attr("cancelled")().cast<bool>();
1028
- if (!cancelled) {
1029
- py_future->attr("set_exception")(
1030
- py::module::import("builtins")
1031
- .attr("Exception")(message));
1032
- }
1033
- delete py_future;
1034
- });
975
+ adaptor->ScheduleCallbackOnPythonEventLoop([py_future,
976
+ message = std::move(
977
+ message)]() {
978
+ bool cancelled = py_future->attr("cancelled")().cast<bool>();
979
+ if (!cancelled) {
980
+ py_future->attr("set_exception")(
981
+ py::module::import("builtins").attr("Exception")(message));
982
+ }
983
+ delete py_future;
984
+ });
1035
985
  } catch (...) {
1036
986
  // TODO: is this code unreachable because Node.js
1037
987
  // will properly always only pass us a `Napi::Error`?
1038
- adaptor->ScheduleCallbackOnPythonEventLoop(
1039
- [py_future]() {
1040
- bool cancelled =
1041
- py_future->attr("cancelled")().cast<bool>();
1042
- if (!cancelled) {
1043
- py_future->attr("set_exception")(
1044
- py::module::import("builtins")
1045
- .attr("Exception")("Unknown error"));
1046
- }
1047
- delete py_future;
1048
- });
988
+ adaptor->ScheduleCallbackOnPythonEventLoop([py_future]() {
989
+ bool cancelled = py_future->attr("cancelled")().cast<bool>();
990
+ if (!cancelled) {
991
+ py_future->attr("set_exception")(
992
+ py::module::import("builtins")
993
+ .attr("Exception")("Unknown error"));
994
+ }
995
+ delete py_future;
996
+ });
1049
997
  }
1050
998
  });
1051
999
 
@@ -1053,19 +1001,14 @@ py::object PythonFutureFromNodePromise(
1053
1001
  }
1054
1002
 
1055
1003
 
1056
- template <
1057
- typename NodePromiseCallback,
1058
- typename NodePromiseResolvedCallback>
1004
+ template <typename NodePromiseCallback, typename NodePromiseResolvedCallback>
1059
1005
  py::object PythonFutureFromNodePromise(
1060
1006
  NodePromiseCallback&& node_promise_callback,
1061
1007
  NodePromiseResolvedCallback&& node_promise_resolved_callback) {
1062
1008
  return PythonFutureFromNodePromise(
1063
1009
  std::forward<NodePromiseCallback>(node_promise_callback),
1064
- std::forward<NodePromiseResolvedCallback>(
1065
- node_promise_resolved_callback),
1066
- [](auto&& result) {
1067
- return std::move(result);
1068
- });
1010
+ std::forward<NodePromiseResolvedCallback>(node_promise_resolved_callback),
1011
+ [](auto&& result) { return std::move(result); });
1069
1012
  }
1070
1013
 
1071
1014
 
@@ -1074,12 +1017,8 @@ py::object PythonFutureFromNodePromise(
1074
1017
  NodePromiseCallback&& node_promise_callback) {
1075
1018
  return PythonFutureFromNodePromise(
1076
1019
  std::forward<NodePromiseCallback>(node_promise_callback),
1077
- [](Napi::Env, Napi::Value) {
1078
- return std::nullopt;
1079
- },
1080
- [](auto&&) {
1081
- return py::none();
1082
- });
1020
+ [](Napi::Env, Napi::Value) { return std::nullopt; },
1021
+ [](auto&&) { return py::none(); });
1083
1022
  }
1084
1023
 
1085
1024
 
@@ -1105,11 +1044,10 @@ void ImportPy(const Napi::CallbackInfo& info) {
1105
1044
  std::string module = info[0].As<Napi::String>().Utf8Value();
1106
1045
  std::string base64_encoded_rbt_py = info[1].As<Napi::String>().Utf8Value();
1107
1046
 
1108
- RunCallbackOnPythonEventLoop(
1109
- [&module, &base64_encoded_rbt_py]() {
1110
- py::module::import("rebootdev.nodejs.python")
1111
- .attr("import_py")(module, base64_encoded_rbt_py);
1112
- });
1047
+ RunCallbackOnPythonEventLoop([&module, &base64_encoded_rbt_py]() {
1048
+ py::module::import("rebootdev.nodejs.python")
1049
+ .attr("import_py")(module, base64_encoded_rbt_py);
1050
+ });
1113
1051
  }
1114
1052
 
1115
1053
 
@@ -1134,19 +1072,16 @@ napi_type_tag MakeTypeTag(const std::string& module, const std::string& type) {
1134
1072
  }
1135
1073
 
1136
1074
 
1137
- static const napi_type_tag reboot_aio_tests_Reboot = MakeTypeTag(
1138
- "reboot.aio.tests",
1139
- "Reboot");
1075
+ static const napi_type_tag reboot_aio_tests_Reboot =
1076
+ MakeTypeTag("reboot.aio.tests", "Reboot");
1140
1077
 
1141
1078
 
1142
- static const napi_type_tag reboot_aio_applications_Application = MakeTypeTag(
1143
- "reboot.aio.applications",
1144
- "Application");
1079
+ static const napi_type_tag reboot_aio_applications_Application =
1080
+ MakeTypeTag("reboot.aio.applications", "Application");
1145
1081
 
1146
1082
 
1147
- static const napi_type_tag reboot_aio_external_ExternalContext = MakeTypeTag(
1148
- "rebootdev.aio.external",
1149
- "ExternalContext");
1083
+ static const napi_type_tag reboot_aio_external_ExternalContext =
1084
+ MakeTypeTag("rebootdev.aio.external", "ExternalContext");
1150
1085
 
1151
1086
 
1152
1087
  Napi::Value python3Path(const Napi::CallbackInfo& info) {
@@ -1173,16 +1108,13 @@ std::string uint8array_to_str(const Napi::Uint8Array& arr) {
1173
1108
 
1174
1109
 
1175
1110
  Napi::Value Reboot_constructor(const Napi::CallbackInfo& info) {
1176
- py::object* py_reboot = RunCallbackOnPythonEventLoop(
1177
- []() {
1178
- py::object tests = py::module::import("reboot.aio.tests");
1179
- return new py::object(tests.attr("Reboot")());
1180
- });
1111
+ py::object* py_reboot = RunCallbackOnPythonEventLoop([]() {
1112
+ py::object tests = py::module::import("reboot.aio.tests");
1113
+ return new py::object(tests.attr("Reboot")());
1114
+ });
1181
1115
 
1182
- Napi::External<py::object> js_external_reboot = make_napi_external(
1183
- info.Env(),
1184
- py_reboot,
1185
- &reboot_aio_tests_Reboot);
1116
+ Napi::External<py::object> js_external_reboot =
1117
+ make_napi_external(info.Env(), py_reboot, &reboot_aio_tests_Reboot);
1186
1118
 
1187
1119
  return js_external_reboot;
1188
1120
  }
@@ -1216,27 +1148,21 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
1216
1148
  }
1217
1149
 
1218
1150
  py::object* py_context = RunCallbackOnPythonEventLoop(
1219
- [py_reboot,
1220
- &name,
1221
- &idempotency_seed,
1222
- &bearer_token,
1223
- app_internal]() {
1151
+ [py_reboot, &name, &idempotency_seed, &bearer_token, app_internal]() {
1224
1152
  py::object py_idempotency_seed = py::none();
1225
1153
  if (idempotency_seed.has_value()) {
1226
- py_idempotency_seed =
1227
- py::module::import("uuid")
1228
- .attr("UUID")(py::str(*idempotency_seed));
1154
+ py_idempotency_seed = py::module::import("uuid").attr("UUID")(
1155
+ py::str(*idempotency_seed));
1229
1156
  }
1230
1157
  py::object py_bearer_token = py::none();
1231
1158
  if (bearer_token.has_value()) {
1232
1159
  py_bearer_token = py::str(*bearer_token);
1233
1160
  }
1234
- return new py::object(
1235
- py_reboot->attr("create_external_context")(
1236
- "name"_a = name,
1237
- "idempotency_seed"_a = py_idempotency_seed,
1238
- "bearer_token"_a = py_bearer_token,
1239
- "app_internal"_a = app_internal));
1161
+ return new py::object(py_reboot->attr("create_external_context")(
1162
+ "name"_a = name,
1163
+ "idempotency_seed"_a = py_idempotency_seed,
1164
+ "bearer_token"_a = py_bearer_token,
1165
+ "app_internal"_a = app_internal));
1240
1166
  });
1241
1167
 
1242
1168
  Napi::External<py::object> js_external_context = make_napi_external(
@@ -1244,17 +1170,14 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
1244
1170
  py_context,
1245
1171
  &reboot_aio_external_ExternalContext);
1246
1172
 
1247
- return js_from_native_external
1248
- .Call(
1249
- info.Env().Global(),
1250
- {js_external_context});
1173
+ return js_from_native_external.Call(
1174
+ info.Env().Global(),
1175
+ {js_external_context});
1251
1176
  }
1252
1177
 
1253
1178
 
1254
1179
  // NOTE: must be called within _Node_.
1255
- Napi::Promise make_js_cancelled(
1256
- Napi::Env& env,
1257
- py::object* py_cancelled) {
1180
+ Napi::Promise make_js_cancelled(Napi::Env& env, py::object* py_cancelled) {
1258
1181
  return NodePromiseFromPythonFuture(
1259
1182
  "make_js_cancelled",
1260
1183
  env,
@@ -1303,8 +1226,7 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1303
1226
  py::object py_cancelled,
1304
1227
  std::string bytes_call) {
1305
1228
  NapiSafeObjectReference* js_authorizer_reference =
1306
- self.attr("_js_authorizer")
1307
- .cast<NapiSafeObjectReference*>();
1229
+ self.attr("_js_authorizer").cast<NapiSafeObjectReference*>();
1308
1230
 
1309
1231
  return PythonFutureFromNodePromise(
1310
1232
  [js_authorizer_reference,
@@ -1346,12 +1268,11 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1346
1268
  py::is_method(py::none()));
1347
1269
 
1348
1270
  // Now define our subclass.
1349
- py::object py_parent_class =
1350
- py::module::import("rebootdev.nodejs.python")
1351
- .attr("NodeAdaptorAuthorizer");
1271
+ py::object py_parent_class = py::module::import("rebootdev.nodejs.python")
1272
+ .attr("NodeAdaptorAuthorizer");
1352
1273
 
1353
- py::object py_parent_metaclass = py::reinterpret_borrow<py::object>(
1354
- (PyObject*) &PyType_Type);
1274
+ py::object py_parent_metaclass =
1275
+ py::reinterpret_borrow<py::object>((PyObject*) &PyType_Type);
1355
1276
 
1356
1277
  py::object py_class = py_parent_metaclass(
1357
1278
  "_NodeAdaptorAuthorizer",
@@ -1389,20 +1310,14 @@ std::vector<std::shared_ptr<ServicerDetails>> make_servicer_details(
1389
1310
  Napi::Function js_servicer_constructor = value.As<Napi::Function>();
1390
1311
 
1391
1312
  std::string name =
1392
- js_servicer_constructor
1393
- .Get("name")
1394
- .As<Napi::String>()
1395
- .Utf8Value();
1313
+ js_servicer_constructor.Get("name").As<Napi::String>().Utf8Value();
1396
1314
 
1397
- std::string rbt_module =
1398
- js_servicer_constructor
1399
- .Get("__rbtModule__")
1400
- .As<Napi::String>()
1401
- .Utf8Value();
1315
+ std::string rbt_module = js_servicer_constructor.Get("__rbtModule__")
1316
+ .As<Napi::String>()
1317
+ .Utf8Value();
1402
1318
 
1403
1319
  std::string servicer_node_adaptor =
1404
- js_servicer_constructor
1405
- .Get("__servicerNodeAdaptor__")
1320
+ js_servicer_constructor.Get("__servicerNodeAdaptor__")
1406
1321
  .As<Napi::String>()
1407
1322
  .Utf8Value();
1408
1323
 
@@ -1418,23 +1333,17 @@ std::vector<std::shared_ptr<ServicerDetails>> make_servicer_details(
1418
1333
  Napi::Persistent(js_servicer_constructor))}));
1419
1334
  } else if (
1420
1335
  value.IsObject()
1421
- && !value
1422
- .As<Napi::Object>()
1336
+ && !value.As<Napi::Object>()
1423
1337
  .Get("nativeServicerModule")
1424
1338
  .IsUndefined()) {
1425
- std::string module =
1426
- value
1427
- .As<Napi::Object>()
1428
- .Get("nativeServicerModule")
1429
- .As<Napi::String>()
1430
- .Utf8Value();
1339
+ std::string module = value.As<Napi::Object>()
1340
+ .Get("nativeServicerModule")
1341
+ .As<Napi::String>()
1342
+ .Utf8Value();
1431
1343
  servicer_details.push_back(
1432
- std::make_shared<ServicerDetails>(NativeServicerDetails{
1433
- module}));
1344
+ std::make_shared<ServicerDetails>(NativeServicerDetails{module}));
1434
1345
  } else {
1435
- Napi::Error::New(
1436
- env,
1437
- "Unexpected `servicer` type.")
1346
+ Napi::Error::New(env, "Unexpected `servicer` type.")
1438
1347
  .ThrowAsJavaScriptException();
1439
1348
  }
1440
1349
  }
@@ -1484,8 +1393,7 @@ py::object make_py_user_servicer(
1484
1393
  Napi::External<py::object> js_external_self =
1485
1394
  make_napi_external(env, py_self);
1486
1395
 
1487
- js_servicer
1488
- .Get("__storeExternal")
1396
+ js_servicer.Get("__storeExternal")
1489
1397
  .As<Napi::Function>()
1490
1398
  .Call(js_servicer, {js_external_self});
1491
1399
 
@@ -1505,11 +1413,11 @@ py::object make_py_user_servicer(
1505
1413
 
1506
1414
  // Trampolines us from Python through C++ into Node for Servicer method
1507
1415
  // calls.
1508
- attributes["_trampoline"] = py::cpp_function(
1509
- [](NapiSafeObjectReference& js_servicer_reference,
1510
- py::object py_context,
1511
- py::object py_cancelled,
1512
- std::string bytes_call) {
1416
+ attributes["_trampoline"] =
1417
+ py::cpp_function([](NapiSafeObjectReference& js_servicer_reference,
1418
+ py::object py_context,
1419
+ py::object py_cancelled,
1420
+ std::string bytes_call) {
1513
1421
  return PythonFutureFromNodePromise(
1514
1422
  [&js_servicer_reference,
1515
1423
  py_context = new py::object(py_context),
@@ -1528,11 +1436,9 @@ py::object make_py_user_servicer(
1528
1436
 
1529
1437
  js_args.push_back(str_to_uint8array(env, bytes_call));
1530
1438
 
1531
- Napi::Object js_servicer =
1532
- js_servicer_reference.Value(env);
1439
+ Napi::Object js_servicer = js_servicer_reference.Value(env);
1533
1440
 
1534
- return js_servicer
1535
- .Get("__dispatch")
1441
+ return js_servicer.Get("__dispatch")
1536
1442
  .As<Napi::Function>()
1537
1443
  .Call(js_servicer, js_args)
1538
1444
  .As<Napi::Object>();
@@ -1551,13 +1457,12 @@ py::object make_py_user_servicer(
1551
1457
  std::optional<NapiSafeObjectReference> js_authorizer;
1552
1458
  try {
1553
1459
  js_authorizer = RunCallbackOnNodeEventLoop(
1554
- [&js_servicer_reference](Napi::Env env)
1555
- -> std::optional<NapiSafeObjectReference> {
1460
+ [&js_servicer_reference](
1461
+ Napi::Env env) -> std::optional<NapiSafeObjectReference> {
1556
1462
  Napi::Object js_servicer = js_servicer_reference.Value(env);
1557
- Napi::Value js_authorizer =
1558
- js_servicer.Get("_authorizer")
1559
- .As<Napi::Function>()
1560
- .Call(js_servicer, {});
1463
+ Napi::Value js_authorizer = js_servicer.Get("_authorizer")
1464
+ .As<Napi::Function>()
1465
+ .Call(js_servicer, {});
1561
1466
  if (!js_authorizer.IsNull()) {
1562
1467
  return NapiSafeObjectReference(
1563
1468
  js_authorizer.As<Napi::Object>());
@@ -1587,8 +1492,8 @@ py::object make_py_user_servicer(
1587
1492
  py::module::import(details.rbt_module.c_str())
1588
1493
  .attr(details.servicer_node_adaptor.c_str());
1589
1494
 
1590
- py::object py_metaclass = py::reinterpret_borrow<py::object>(
1591
- (PyObject*) &PyType_Type);
1495
+ py::object py_metaclass =
1496
+ py::reinterpret_borrow<py::object>((PyObject*) &PyType_Type);
1592
1497
 
1593
1498
  // Our subclass will be in the `reboot_native` module with
1594
1499
  // the same name as what is used in JS/TS, e.g.,
@@ -1606,8 +1511,7 @@ py::object make_py_user_servicer(
1606
1511
 
1607
1512
  // NOTE: must be called from within _Python_.
1608
1513
  py::list make_py_servicers(
1609
- const std::vector<
1610
- std::shared_ptr<ServicerDetails>>& servicer_details) {
1514
+ const std::vector<std::shared_ptr<ServicerDetails>>& servicer_details) {
1611
1515
  py::module_ module = py::module::import("reboot_native");
1612
1516
 
1613
1517
  py::list py_servicers;
@@ -1619,8 +1523,7 @@ py::list make_py_servicers(
1619
1523
  auto native_details =
1620
1524
  std::get_if<NativeServicerDetails>(details.get())) {
1621
1525
  py_servicers.attr("extend")(
1622
- py::module::import(
1623
- native_details->py_servicers_module.c_str())
1526
+ py::module::import(native_details->py_servicers_module.c_str())
1624
1527
  .attr("servicers")());
1625
1528
  }
1626
1529
  // TODO: Can I get exhaustiveness checking for the std::variant?
@@ -1659,8 +1562,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1659
1562
  py::object py_cancelled,
1660
1563
  std::string bytes_call) {
1661
1564
  NapiSafeObjectReference* js_token_verifier_reference =
1662
- self.attr("_js_token_verifier")
1663
- .cast<NapiSafeObjectReference*>();
1565
+ self.attr("_js_token_verifier").cast<NapiSafeObjectReference*>();
1664
1566
 
1665
1567
  return PythonFutureFromNodePromise(
1666
1568
  [js_token_verifier_reference,
@@ -1687,8 +1589,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1687
1589
  Napi::Object js_token_verifier =
1688
1590
  js_token_verifier_reference->Value(env);
1689
1591
 
1690
- return js_token_verifier
1691
- .Get("_verifyToken")
1592
+ return js_token_verifier.Get("_verifyToken")
1692
1593
  .As<Napi::Function>()
1693
1594
  .Call(js_token_verifier, {js_args})
1694
1595
  .As<Napi::Object>();
@@ -1714,12 +1615,11 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1714
1615
  py::arg("bytes_call"),
1715
1616
  py::is_method(py::none()));
1716
1617
 
1717
- py::object py_parent_class =
1718
- py::module::import("rebootdev.nodejs.python")
1719
- .attr("NodeAdaptorTokenVerifier");
1618
+ py::object py_parent_class = py::module::import("rebootdev.nodejs.python")
1619
+ .attr("NodeAdaptorTokenVerifier");
1720
1620
 
1721
- py::object py_parent_metaclass = py::reinterpret_borrow<py::object>(
1722
- (PyObject*) &PyType_Type);
1621
+ py::object py_parent_metaclass =
1622
+ py::reinterpret_borrow<py::object>((PyObject*) &PyType_Type);
1723
1623
 
1724
1624
  py::object py_token_verifier = py_parent_metaclass(
1725
1625
  "_NodeAdaptorTokenVerifier",
@@ -1734,19 +1634,19 @@ Napi::Value Reboot_up(const Napi::CallbackInfo& info) {
1734
1634
  // NOTE: we immediately get a safe reference to the `Napi::External`
1735
1635
  // so that Node will not garbage collect it and the `py::object*` we
1736
1636
  // get out of it will remain valid.
1737
- auto js_external_reboot = NapiSafeReference(
1738
- info[0].As<Napi::External<py::object>>());
1637
+ auto js_external_reboot =
1638
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
1739
1639
 
1740
1640
  // CHECK(js_external_reboot.CheckTypeTag(&reboot_aio_tests_Reboot));
1741
1641
 
1742
1642
  py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
1743
1643
 
1744
- auto js_external_application = NapiSafeReference(
1745
- info[1].As<Napi::External<py::object>>());
1644
+ auto js_external_application =
1645
+ NapiSafeReference(info[1].As<Napi::External<py::object>>());
1746
1646
 
1747
1647
  py::object* py_application = js_external_application.Value(info.Env()).Data();
1748
1648
 
1749
- bool local_envoy = false;
1649
+ std::optional<bool> local_envoy;
1750
1650
  if (!info[2].IsUndefined()) {
1751
1651
  local_envoy = info[2].As<Napi::Boolean>();
1752
1652
  }
@@ -1760,12 +1660,16 @@ Napi::Value Reboot_up(const Napi::CallbackInfo& info) {
1760
1660
  info.Env(),
1761
1661
  "Reboot.up(...) in nodejs",
1762
1662
  {"asyncio", "create_task"},
1763
- [js_external_reboot, // Ensures `py_reboot` remains valid.
1663
+ [js_external_reboot, // Ensures `py_reboot` remains valid.
1764
1664
  py_reboot,
1765
- js_external_application, // Ensures `py_application` remains valid.
1665
+ js_external_application, // Ensures `py_application` remains valid.
1766
1666
  py_application,
1767
1667
  local_envoy,
1768
1668
  local_envoy_port]() {
1669
+ py::object py_local_envoy = py::none();
1670
+ if (local_envoy.has_value()) {
1671
+ py_local_envoy = py::bool_(*local_envoy);
1672
+ }
1769
1673
  return py_reboot->attr("up")(
1770
1674
  py_application,
1771
1675
  // NOTE: while we support subprocess servers
@@ -1774,7 +1678,7 @@ Napi::Value Reboot_up(const Napi::CallbackInfo& info) {
1774
1678
  // clone a process like we do with multiprocessing
1775
1679
  // in Python.
1776
1680
  "in_process"_a = true,
1777
- "local_envoy"_a = local_envoy,
1681
+ "local_envoy"_a = py_local_envoy,
1778
1682
  "local_envoy_port"_a = local_envoy_port);
1779
1683
  },
1780
1684
  [](py::object py_revision) {
@@ -1790,8 +1694,8 @@ Napi::Value Reboot_up(const Napi::CallbackInfo& info) {
1790
1694
  }
1791
1695
 
1792
1696
  Napi::Value Reboot_down(const Napi::CallbackInfo& info) {
1793
- auto js_external_reboot = NapiSafeReference(
1794
- info[0].As<Napi::External<py::object>>());
1697
+ auto js_external_reboot =
1698
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
1795
1699
 
1796
1700
  // CHECK(js_external_reboot.CheckTypeTag(&reboot_aio_tests_Reboot));
1797
1701
 
@@ -1801,18 +1705,16 @@ Napi::Value Reboot_down(const Napi::CallbackInfo& info) {
1801
1705
  info.Env(),
1802
1706
  "Reboot.down() in nodejs",
1803
1707
  {"asyncio", "create_task"},
1804
- [js_external_reboot, // Ensures `py_reboot` remains valid.
1805
- py_reboot]() {
1806
- return py_reboot->attr("down")();
1807
- });
1708
+ [js_external_reboot, // Ensures `py_reboot` remains valid.
1709
+ py_reboot]() { return py_reboot->attr("down")(); });
1808
1710
  }
1809
1711
 
1810
1712
  Napi::Value Reboot_start(const Napi::CallbackInfo& info) {
1811
1713
  // NOTE: we immediately get a safe reference to the `Napi::External`
1812
1714
  // so that Node will not garbage collect it and the `py::object*` we
1813
1715
  // get out of it will remain valid.
1814
- auto js_external_reboot = NapiSafeReference(
1815
- info[0].As<Napi::External<py::object>>());
1716
+ auto js_external_reboot =
1717
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
1816
1718
 
1817
1719
  // CHECK(js_external_reboot.CheckTypeTag(&reboot_aio_tests_Reboot));
1818
1720
 
@@ -1826,18 +1728,16 @@ Napi::Value Reboot_start(const Napi::CallbackInfo& info) {
1826
1728
  info.Env(),
1827
1729
  "Reboot.start() in nodejs",
1828
1730
  {"asyncio", "create_task"},
1829
- [js_external_reboot, // Ensures `py_reboot` remains valid.
1830
- py_reboot]() {
1831
- return py_reboot->attr("start")();
1832
- });
1731
+ [js_external_reboot, // Ensures `py_reboot` remains valid.
1732
+ py_reboot]() { return py_reboot->attr("start")(); });
1833
1733
  }
1834
1734
 
1835
1735
  Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
1836
1736
  // NOTE: we immediately get a safe reference to the `Napi::External`
1837
1737
  // so that Node will not garbage collect it and the `py::object*` we
1838
1738
  // get out of it will remain valid.
1839
- auto js_external_reboot = NapiSafeReference(
1840
- info[0].As<Napi::External<py::object>>());
1739
+ auto js_external_reboot =
1740
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
1841
1741
 
1842
1742
  // CHECK(js_external_reboot.CheckTypeTag(&reboot_aio_tests_Reboot));
1843
1743
 
@@ -1847,22 +1747,18 @@ Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
1847
1747
  info.Env(),
1848
1748
  "Reboot.stop() in nodejs",
1849
1749
  {"asyncio", "create_task"},
1850
- [js_external_reboot, // Ensures `py_reboot` remains valid.
1851
- py_reboot]() {
1852
- return py_reboot->attr("stop")();
1853
- });
1750
+ [js_external_reboot, // Ensures `py_reboot` remains valid.
1751
+ py_reboot]() { return py_reboot->attr("stop")(); });
1854
1752
 
1855
1753
  // Allow Node to exit after we've resolved or rejected the promise.
1856
- auto js_resolve_reject = Napi::Function::New(
1857
- info.Env(),
1858
- [](const Napi::CallbackInfo& info) {
1754
+ auto js_resolve_reject =
1755
+ Napi::Function::New(info.Env(), [](const Napi::CallbackInfo& info) {
1859
1756
  adaptor->AllowNodeToExit(info.Env());
1860
1757
  });
1861
1758
 
1862
- js_promise
1863
- .Get("then")
1864
- .As<Napi::Function>()
1865
- .Call(js_promise, {js_resolve_reject, js_resolve_reject});
1759
+ js_promise.Get("then").As<Napi::Function>().Call(
1760
+ js_promise,
1761
+ {js_resolve_reject, js_resolve_reject});
1866
1762
 
1867
1763
  return js_promise;
1868
1764
  }
@@ -1873,18 +1769,17 @@ Napi::Value Reboot_url(const Napi::CallbackInfo& info) {
1873
1769
  // NOTE: we immediately get a safe reference to the `Napi::External`
1874
1770
  // so that Node will not garbage collect it and the `py::object*` we
1875
1771
  // get out of it will remain valid.
1876
- auto js_external_reboot = NapiSafeReference(
1877
- info[0].As<Napi::External<py::object>>());
1772
+ auto js_external_reboot =
1773
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
1878
1774
 
1879
1775
  // CHECK(...CheckTypeTag(...));
1880
1776
 
1881
1777
  py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
1882
1778
 
1883
- std::string url = RunCallbackOnPythonEventLoop(
1884
- [py_reboot]() {
1885
- py::str url = py_reboot->attr("url")();
1886
- return std::string(url);
1887
- });
1779
+ std::string url = RunCallbackOnPythonEventLoop([py_reboot]() {
1780
+ py::str url = py_reboot->attr("url")();
1781
+ return std::string(url);
1782
+ });
1888
1783
 
1889
1784
  return Napi::String::New(info.Env(), url);
1890
1785
  }
@@ -1900,13 +1795,11 @@ Napi::Value Service_constructor(const Napi::CallbackInfo& info) {
1900
1795
 
1901
1796
  std::string id = js_args.Get("id").As<Napi::String>().Utf8Value();
1902
1797
 
1903
- py::object* py_service = RunCallbackOnPythonEventLoop(
1904
- [&rbt_module, &node_adaptor, &id]() {
1798
+ py::object* py_service =
1799
+ RunCallbackOnPythonEventLoop([&rbt_module, &node_adaptor, &id]() {
1905
1800
  py::object py_module = py::module::import(rbt_module.c_str());
1906
1801
  py::object py_schedule_type =
1907
- py_module
1908
- .attr(node_adaptor.c_str())
1909
- .attr("_Schedule");
1802
+ py_module.attr(node_adaptor.c_str()).attr("_Schedule");
1910
1803
  return new py::object(py_module.attr(node_adaptor.c_str())(
1911
1804
  // The call will stay within the same application.
1912
1805
  "application_id"_a = py::none(),
@@ -1950,8 +1843,7 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
1950
1843
 
1951
1844
  // CHECK(...CheckTypeTag(...));
1952
1845
 
1953
- py::object* py_context =
1954
- js_external_context.Value(info.Env()).Data();
1846
+ py::object* py_context = js_external_context.Value(info.Env()).Data();
1955
1847
 
1956
1848
  std::string json_request = js_args.Get("jsonRequest").As<Napi::String>();
1957
1849
 
@@ -1961,13 +1853,13 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
1961
1853
  info.Env(),
1962
1854
  "servicer._" + kind + "(\"" + method + "\", ...) in nodejs",
1963
1855
  js_external_context,
1964
- [js_external_service, // Ensures `py_service` remains valid.
1856
+ [js_external_service, // Ensures `py_service` remains valid.
1965
1857
  py_service,
1966
1858
  kind,
1967
1859
  method,
1968
1860
  request_module,
1969
1861
  request_type,
1970
- js_external_context, // Ensures `py_context` remains valid.
1862
+ js_external_context, // Ensures `py_context` remains valid.
1971
1863
  py_context,
1972
1864
  json_request,
1973
1865
  json_options]() {
@@ -1994,9 +1886,7 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
1994
1886
  json_request,
1995
1887
  json_options);
1996
1888
  },
1997
- [](py::object py_json) {
1998
- return py_json.cast<std::string>();
1999
- },
1889
+ [](py::object py_json) { return py_json.cast<std::string>(); },
2000
1890
  [](Napi::Env env, std::string&& json) {
2001
1891
  return Napi::String::New(env, json);
2002
1892
  });
@@ -2014,8 +1904,7 @@ Napi::Value Task_await(const Napi::CallbackInfo& info) {
2014
1904
 
2015
1905
  // CHECK(...CheckTypeTag(...));
2016
1906
 
2017
- py::object* py_context =
2018
- js_external_context.Value(info.Env()).Data();
1907
+ py::object* py_context = js_external_context.Value(info.Env()).Data();
2019
1908
 
2020
1909
  std::string rbt_module = js_args.Get("rbtModule").As<Napi::String>();
2021
1910
 
@@ -2027,27 +1916,23 @@ Napi::Value Task_await(const Napi::CallbackInfo& info) {
2027
1916
 
2028
1917
  return NodePromiseFromPythonTaskWithContext(
2029
1918
  info.Env(),
2030
- "rebootdev.nodejs.python.task_await(\""
2031
- + state_name + "\", \""
2032
- + method + "\", ...) in nodejs",
1919
+ "rebootdev.nodejs.python.task_await(\"" + state_name + "\", \"" + method
1920
+ + "\", ...) in nodejs",
2033
1921
  js_external_context,
2034
1922
  [rbt_module = std::move(rbt_module),
2035
1923
  state_name = std::move(state_name),
2036
1924
  method = std::move(method),
2037
- js_external_context, // Ensures `py_context` remains valid.
1925
+ js_external_context, // Ensures `py_context` remains valid.
2038
1926
  py_context,
2039
1927
  json_task_id]() {
2040
1928
  return py::module::import("rebootdev.nodejs.python")
2041
1929
  .attr("task_await")(
2042
1930
  py_context,
2043
- py::module::import(rbt_module.c_str())
2044
- .attr(state_name.c_str()),
1931
+ py::module::import(rbt_module.c_str()).attr(state_name.c_str()),
2045
1932
  method,
2046
1933
  json_task_id);
2047
1934
  },
2048
- [](py::object py_json) {
2049
- return py_json.cast<std::string>();
2050
- },
1935
+ [](py::object py_json) { return py_json.cast<std::string>(); },
2051
1936
  [](Napi::Env env, std::string&& json) {
2052
1937
  return Napi::String::New(env, json);
2053
1938
  });
@@ -2078,21 +1963,17 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
2078
1963
 
2079
1964
  std::optional<std::string> idempotency_required_reason;
2080
1965
  if (!info[6].IsUndefined()) {
2081
- idempotency_required_reason =
2082
- info[6]
2083
- .As<Napi::String>()
2084
- .Utf8Value();
1966
+ idempotency_required_reason = info[6].As<Napi::String>().Utf8Value();
2085
1967
  }
2086
1968
 
2087
- py::object* py_external_context = RunCallbackOnPythonEventLoop(
2088
- [&name,
2089
- &url,
2090
- &bearer_token,
2091
- &idempotency_seed,
2092
- &idempotency_required,
2093
- &idempotency_required_reason]() {
2094
- py::object py_external = py::module::import(
2095
- "rebootdev.aio.external");
1969
+ py::object* py_external_context =
1970
+ RunCallbackOnPythonEventLoop([&name,
1971
+ &url,
1972
+ &bearer_token,
1973
+ &idempotency_seed,
1974
+ &idempotency_required,
1975
+ &idempotency_required_reason]() {
1976
+ py::object py_external = py::module::import("rebootdev.aio.external");
2096
1977
 
2097
1978
  auto convert_str =
2098
1979
  [](const std::optional<std::string>& optional) -> py::object {
@@ -2109,8 +1990,8 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
2109
1990
  "bearer_token"_a = convert_str(bearer_token),
2110
1991
  "idempotency_seed"_a = convert_str(idempotency_seed),
2111
1992
  "idempotency_required"_a = py::bool_(idempotency_required),
2112
- "idempotency_required_reason"_a = convert_str(
2113
- idempotency_required_reason)));
1993
+ "idempotency_required_reason"_a =
1994
+ convert_str(idempotency_required_reason)));
2114
1995
  });
2115
1996
 
2116
1997
  Napi::External<py::object> js_external_context = make_napi_external(
@@ -2122,8 +2003,8 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
2122
2003
  }
2123
2004
 
2124
2005
  Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2125
- auto js_from_native_external = NapiSafeFunctionReference(
2126
- info[0].As<Napi::Function>());
2006
+ auto js_from_native_external =
2007
+ NapiSafeFunctionReference(info[0].As<Napi::Function>());
2127
2008
 
2128
2009
  Napi::Array js_servicers = info[1].As<Napi::Array>();
2129
2010
 
@@ -2137,8 +2018,7 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2137
2018
 
2138
2019
  auto servicer_details = make_servicer_details(info.Env(), js_servicers);
2139
2020
 
2140
- auto js_initialize = NapiSafeFunctionReference(
2141
- info[3].As<Napi::Function>());
2021
+ auto js_initialize = NapiSafeFunctionReference(info[3].As<Napi::Function>());
2142
2022
 
2143
2023
  std::optional<std::string> initialize_bearer_token;
2144
2024
  if (!info[4].IsUndefined()) {
@@ -2184,31 +2064,27 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2184
2064
  Napi::Function js_create_external_context =
2185
2065
  Napi::Function::New(
2186
2066
  env,
2187
- [js_from_native_external = std::move(
2188
- js_from_native_external),
2067
+ [js_from_native_external =
2068
+ std::move(js_from_native_external),
2189
2069
  // Ensures `py_channel_manager` remains valid.
2190
2070
  js_external_channel_manager = NapiSafeReference(
2191
2071
  js_external_channel_manager)](
2192
2072
  const Napi::CallbackInfo& info) mutable {
2193
2073
  Napi::Object js_args = info[0].As<Napi::Object>();
2194
2074
 
2195
- std::string name =
2196
- js_args
2197
- .Get("name")
2198
- .As<Napi::String>()
2199
- .Utf8Value();
2075
+ std::string name = js_args.Get("name")
2076
+ .As<Napi::String>()
2077
+ .Utf8Value();
2200
2078
 
2201
2079
  std::optional<std::string> bearer_token;
2202
2080
  if (!js_args.Get("bearerToken").IsUndefined()) {
2203
- bearer_token =
2204
- js_args.Get("bearerToken")
2205
- .As<Napi::String>()
2206
- .Utf8Value();
2081
+ bearer_token = js_args.Get("bearerToken")
2082
+ .As<Napi::String>()
2083
+ .Utf8Value();
2207
2084
  }
2208
2085
 
2209
2086
  py::object* py_channel_manager =
2210
- js_external_channel_manager
2211
- .Value(info.Env())
2087
+ js_external_channel_manager.Value(info.Env())
2212
2088
  .Data();
2213
2089
 
2214
2090
  return NodePromiseFromPythonCallback(
@@ -2246,32 +2122,30 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2246
2122
  js_from_native_external](
2247
2123
  Napi::Env env,
2248
2124
  py::object* py_context) {
2249
- // (check_line_length skip)
2250
- Napi::External<py::object> js_external_context =
2251
- make_napi_external(
2125
+ Napi::External<py::object>
2126
+ // (check_line_length skip)
2127
+ js_external_context = make_napi_external(
2252
2128
  env,
2253
2129
  py_context,
2254
2130
  // (check_line_length skip)
2255
2131
  &reboot_aio_external_ExternalContext);
2256
2132
 
2257
- return js_from_native_external
2258
- .Value(env)
2133
+ return js_from_native_external.Value(env)
2259
2134
  .Call(
2260
2135
  env.Global(),
2261
2136
  {js_external_context,
2262
- // (check_line_length skip)
2263
- Napi::String::New(env, "external")});
2137
+ Napi::String::New(
2138
+ env,
2139
+ "external")});
2264
2140
  });
2265
2141
  });
2266
2142
 
2267
- return js_web_framework_start
2268
- .Value(env)
2143
+ return js_web_framework_start.Value(env)
2269
2144
  .Call(
2270
2145
  env.Global(),
2271
2146
  {Napi::String::New(env, server_id),
2272
- port.has_value()
2273
- ? Napi::Number::New(env, *port)
2274
- : env.Null(),
2147
+ port.has_value() ? Napi::Number::New(env, *port)
2148
+ : env.Null(),
2275
2149
  js_create_external_context})
2276
2150
  .As<Napi::Object>();
2277
2151
  },
@@ -2282,17 +2156,13 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2282
2156
  });
2283
2157
 
2284
2158
  py::object py_web_framework_stop = py::cpp_function(
2285
- [js_web_framework_stop = std::move(js_web_framework_stop)](
2286
- std::string server_id) {
2159
+ [js_web_framework_stop =
2160
+ std::move(js_web_framework_stop)](std::string server_id) {
2287
2161
  return PythonFutureFromNodePromise(
2288
2162
  [js_web_framework_stop = std::move(js_web_framework_stop),
2289
- server_id = std::move(server_id)](
2290
- Napi::Env env) mutable {
2291
- return js_web_framework_stop
2292
- .Value(env)
2293
- .Call(
2294
- env.Global(),
2295
- {Napi::String::New(env, server_id)})
2163
+ server_id = std::move(server_id)](Napi::Env env) mutable {
2164
+ return js_web_framework_stop.Value(env)
2165
+ .Call(env.Global(), {Napi::String::New(env, server_id)})
2296
2166
  .As<Napi::Object>();
2297
2167
  });
2298
2168
  });
@@ -2303,15 +2173,15 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2303
2173
  py::object py_context) mutable {
2304
2174
  return PythonFutureFromNodePromise(
2305
2175
  [js_initialize,
2306
- js_from_native_external, // NOTE: need a _copy_ of
2307
- // both `js_initialize`
2308
- // and
2309
- // `js_from_native_external`
2310
- // here since
2311
- // `py_initialize` may be
2312
- // called more than once!
2313
- py_context = new py::object(py_context)](
2314
- Napi::Env env) mutable {
2176
+ js_from_native_external, // NOTE: need a _copy_ of
2177
+ // both `js_initialize`
2178
+ // and
2179
+ // `js_from_native_external`
2180
+ // here since
2181
+ // `py_initialize` may be
2182
+ // called more than once!
2183
+ py_context =
2184
+ new py::object(py_context)](Napi::Env env) mutable {
2315
2185
  Napi::External<py::object> js_external_context =
2316
2186
  make_napi_external(
2317
2187
  env,
@@ -2319,16 +2189,14 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2319
2189
  &reboot_aio_external_ExternalContext);
2320
2190
 
2321
2191
  Napi::Object js_context =
2322
- js_from_native_external
2323
- .Value(env)
2192
+ js_from_native_external.Value(env)
2324
2193
  .Call(
2325
2194
  env.Global(),
2326
2195
  {js_external_context,
2327
2196
  Napi::String::New(env, "initialize")})
2328
2197
  .As<Napi::Object>();
2329
2198
 
2330
- return js_initialize
2331
- .Value(env)
2199
+ return js_initialize.Value(env)
2332
2200
  .Call(env.Global(), {js_context})
2333
2201
  .As<Napi::Object>();
2334
2202
  });
@@ -2351,8 +2219,7 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2351
2219
  "web_framework_start"_a = py_web_framework_start,
2352
2220
  "web_framework_stop"_a = py_web_framework_stop,
2353
2221
  "initialize"_a = py_initialize,
2354
- "initialize_bearer_token"_a =
2355
- py_initialize_bearer_token,
2222
+ "initialize_bearer_token"_a = py_initialize_bearer_token,
2356
2223
  "token_verifier"_a = py_token_verifier));
2357
2224
  });
2358
2225
 
@@ -2370,47 +2237,37 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
2370
2237
  // that we can get calls from Python.
2371
2238
  adaptor->KeepNodeFromExiting(info.Env());
2372
2239
 
2373
- auto js_external_application = NapiSafeReference(
2374
- info[0].As<Napi::External<py::object>>());
2240
+ auto js_external_application =
2241
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2375
2242
 
2376
- py::object* py_application =
2377
- js_external_application.Value(info.Env()).Data();
2243
+ py::object* py_application = js_external_application.Value(info.Env()).Data();
2378
2244
 
2379
2245
  Napi::Promise js_promise = NodePromiseFromPythonTask(
2380
2246
  info.Env(),
2381
2247
  "Application.run() in nodejs",
2382
2248
  {"rebootdev.nodejs.python", "create_task"},
2383
- [js_external_application, // Ensures `py_application` remains valid.
2384
- py_application]() {
2385
- return py_application->attr("run")();
2386
- });
2249
+ [js_external_application, // Ensures `py_application` remains valid.
2250
+ py_application]() { return py_application->attr("run")(); });
2387
2251
 
2388
2252
  // More to do if `Application.run` returns ...
2389
- js_promise
2390
- .Get("then")
2391
- .As<Napi::Function>()
2392
- .Call(
2393
- js_promise,
2394
- {Napi::Function::New(
2395
- info.Env(),
2396
- [](const Napi::CallbackInfo& info) {
2397
- // Allow Node to exit after `Application.run` returns.
2398
- adaptor->AllowNodeToExit(info.Env());
2399
- }),
2400
- Napi::Function::New(
2401
- info.Env(),
2402
- [](const Napi::CallbackInfo& info) {
2403
- // There was an error, let's also set the
2404
- // `process.exitCode` to reflect this.
2405
- info.Env()
2406
- .Global()
2407
- .Get("process")
2408
- .As<Napi::Object>()
2409
- .Set("exitCode", 1);
2410
-
2411
- // Allow Node to exit after `Application.run` returns.
2412
- adaptor->AllowNodeToExit(info.Env());
2413
- })});
2253
+ js_promise.Get("then").As<Napi::Function>().Call(
2254
+ js_promise,
2255
+ {Napi::Function::New(
2256
+ info.Env(),
2257
+ [](const Napi::CallbackInfo& info) {
2258
+ // Allow Node to exit after `Application.run` returns.
2259
+ adaptor->AllowNodeToExit(info.Env());
2260
+ }),
2261
+ Napi::Function::New(info.Env(), [](const Napi::CallbackInfo& info) {
2262
+ // There was an error, let's also set the
2263
+ // `process.exitCode` to reflect this.
2264
+ info.Env().Global().Get("process").As<Napi::Object>().Set(
2265
+ "exitCode",
2266
+ 1);
2267
+
2268
+ // Allow Node to exit after `Application.run` returns.
2269
+ adaptor->AllowNodeToExit(info.Env());
2270
+ })});
2414
2271
 
2415
2272
  return js_promise;
2416
2273
  }
@@ -2420,22 +2277,18 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
2420
2277
  // NOTE: we immediately get a safe reference to the `Napi::External`
2421
2278
  // so that Node will not garbage collect it and the `py::object*` we
2422
2279
  // get out of it will remain valid.
2423
- auto js_external_context = NapiSafeReference(
2424
- info[0].As<Napi::External<py::object>>());
2280
+ auto js_external_context =
2281
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2425
2282
 
2426
2283
  py::object* py_context = js_external_context.Value(info.Env()).Data();
2427
2284
 
2428
- std::string state_type =
2429
- info[1].As<Napi::String>().Utf8Value();
2285
+ std::string state_type = info[1].As<Napi::String>().Utf8Value();
2430
2286
 
2431
- std::string service_name =
2432
- info[2].As<Napi::String>().Utf8Value();
2287
+ std::string service_name = info[2].As<Napi::String>().Utf8Value();
2433
2288
 
2434
- std::string method =
2435
- info[3].As<Napi::String>().Utf8Value();
2289
+ std::string method = info[3].As<Napi::String>().Utf8Value();
2436
2290
 
2437
- Napi::Object idempotency_options =
2438
- info[4].As<Napi::Object>();
2291
+ Napi::Object idempotency_options = info[4].As<Napi::Object>();
2439
2292
  auto js_key = idempotency_options.Get("key");
2440
2293
  std::optional<std::string> key;
2441
2294
  if (js_key.IsString()) {
@@ -2454,7 +2307,7 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
2454
2307
 
2455
2308
  return NodePromiseFromPythonCallback(
2456
2309
  info.Env(),
2457
- [js_external_context, // Ensures `py_context` remains valid.
2310
+ [js_external_context, // Ensures `py_context` remains valid.
2458
2311
  py_context,
2459
2312
  state_type = std::move(state_type),
2460
2313
  service_name = std::move(service_name),
@@ -2510,25 +2363,23 @@ Napi::Value WriterContext_set_sync(const Napi::CallbackInfo& info) {
2510
2363
  // NOTE: we immediately get a safe reference to the `Napi::External`
2511
2364
  // so that Node will not garbage collect it and the `py::object*` we
2512
2365
  // get out of it will remain valid.
2513
- auto js_external_context = NapiSafeReference(
2514
- info[0].As<Napi::External<py::object>>());
2366
+ auto js_external_context =
2367
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2515
2368
 
2516
2369
  py::object* py_context = js_external_context.Value(info.Env()).Data();
2517
2370
 
2518
2371
  bool sync = info[1].As<Napi::Boolean>();
2519
2372
 
2520
2373
  RunCallbackOnPythonEventLoop(
2521
- [&py_context, sync]() {
2522
- py_context->attr("sync") = sync;
2523
- });
2374
+ [&py_context, sync]() { py_context->attr("sync") = sync; });
2524
2375
 
2525
2376
  return info.Env().Undefined();
2526
2377
  }
2527
2378
 
2528
2379
 
2529
2380
  Napi::Value WorkflowContext_loop(const Napi::CallbackInfo& info) {
2530
- auto js_external_context = NapiSafeReference(
2531
- info[0].As<Napi::External<py::object>>());
2381
+ auto js_external_context =
2382
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2532
2383
 
2533
2384
  // CHECK(...CheckTypeTag(...));
2534
2385
 
@@ -2540,51 +2391,47 @@ Napi::Value WorkflowContext_loop(const Napi::CallbackInfo& info) {
2540
2391
  info.Env(),
2541
2392
  "context.loop(...) in nodejs",
2542
2393
  js_external_context,
2543
- [js_external_context, // Ensures `py_context` remains valid.
2394
+ [js_external_context, // Ensures `py_context` remains valid.
2544
2395
  py_context,
2545
2396
  alias = std::move(alias)]() {
2546
2397
  return py::module::import("rebootdev.nodejs.python")
2547
2398
  .attr("loop")(py_context, alias);
2548
2399
  },
2549
- [](py::object py_iterate) {
2550
- return new py::object(py_iterate);
2551
- },
2400
+ [](py::object py_iterate) { return new py::object(py_iterate); },
2552
2401
  [js_external_context](Napi::Env env, py::object* py_iterate) mutable {
2553
2402
  Napi::External<py::object> js_iterate_external =
2554
2403
  make_napi_external(env, py_iterate);
2555
2404
 
2556
- Napi::Function js_iterate =
2557
- Napi::Function::New(
2558
- env,
2559
- [js_external_context,
2560
- js_iterate_external = // Ensures `py_iterate` remains valid.
2561
- NapiSafeReference(js_iterate_external),
2562
- py_iterate](
2563
- const Napi::CallbackInfo& info) mutable {
2564
- bool more = info[0].As<Napi::Boolean>();
2565
- return NodePromiseFromPythonTaskWithContext(
2566
- info.Env(),
2567
- "iterate(...) in nodejs",
2568
- js_external_context,
2569
- [js_iterate_external, py_iterate, more]() {
2570
- return (*py_iterate)(more);
2571
- },
2572
- [](py::object py_iteration) -> std::optional<int> {
2573
- if (!py_iteration.is_none()) {
2574
- return py_iteration.cast<int>();
2575
- } else {
2576
- return std::nullopt;
2577
- }
2578
- },
2579
- [](Napi::Env env,
2580
- std::optional<int>&& iteration) -> Napi::Value {
2581
- if (iteration.has_value()) {
2582
- return Napi::Number::New(env, *iteration);
2583
- } else {
2584
- return env.Null();
2585
- }
2586
- });
2587
- });
2405
+ Napi::Function js_iterate = Napi::Function::New(
2406
+ env,
2407
+ [js_external_context,
2408
+ js_iterate_external = // Ensures `py_iterate` remains valid.
2409
+ NapiSafeReference(js_iterate_external),
2410
+ py_iterate](const Napi::CallbackInfo& info) mutable {
2411
+ bool more = info[0].As<Napi::Boolean>();
2412
+ return NodePromiseFromPythonTaskWithContext(
2413
+ info.Env(),
2414
+ "iterate(...) in nodejs",
2415
+ js_external_context,
2416
+ [js_iterate_external, py_iterate, more]() {
2417
+ return (*py_iterate)(more);
2418
+ },
2419
+ [](py::object py_iteration) -> std::optional<int> {
2420
+ if (!py_iteration.is_none()) {
2421
+ return py_iteration.cast<int>();
2422
+ } else {
2423
+ return std::nullopt;
2424
+ }
2425
+ },
2426
+ [](Napi::Env env,
2427
+ std::optional<int>&& iteration) -> Napi::Value {
2428
+ if (iteration.has_value()) {
2429
+ return Napi::Number::New(env, *iteration);
2430
+ } else {
2431
+ return env.Null();
2432
+ }
2433
+ });
2434
+ });
2588
2435
 
2589
2436
  return js_iterate;
2590
2437
  });
@@ -2592,21 +2439,20 @@ Napi::Value WorkflowContext_loop(const Napi::CallbackInfo& info) {
2592
2439
 
2593
2440
 
2594
2441
  Napi::Value retry_reactively_until(const Napi::CallbackInfo& info) {
2595
- auto js_external_context = NapiSafeReference(
2596
- info[0].As<Napi::External<py::object>>());
2442
+ auto js_external_context =
2443
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2597
2444
 
2598
2445
  // CHECK(...CheckTypeTag(...));
2599
2446
 
2600
2447
  py::object* py_context = js_external_context.Value(info.Env()).Data();
2601
2448
 
2602
- auto js_condition = NapiSafeFunctionReference(
2603
- info[1].As<Napi::Function>());
2449
+ auto js_condition = NapiSafeFunctionReference(info[1].As<Napi::Function>());
2604
2450
 
2605
2451
  return NodePromiseFromPythonTaskWithContext(
2606
2452
  info.Env(),
2607
2453
  "retry_reactively_until(...) in nodejs",
2608
2454
  js_external_context,
2609
- [js_external_context, // Ensures `py_context` remains valid.
2455
+ [js_external_context, // Ensures `py_context` remains valid.
2610
2456
  py_context,
2611
2457
  js_condition = std::move(js_condition)]() {
2612
2458
  py::object py_condition = py::cpp_function(
@@ -2617,8 +2463,7 @@ Napi::Value retry_reactively_until(const Napi::CallbackInfo& info) {
2617
2463
  // `py_condition` may be called more
2618
2464
  // than once!
2619
2465
  [js_condition](Napi::Env env) mutable {
2620
- return js_condition
2621
- .Value(env)
2466
+ return js_condition.Value(env)
2622
2467
  .Call(env.Global(), {})
2623
2468
  .As<Napi::Object>();
2624
2469
  },
@@ -2634,8 +2479,8 @@ Napi::Value retry_reactively_until(const Napi::CallbackInfo& info) {
2634
2479
 
2635
2480
 
2636
2481
  Napi::Value memoize(const Napi::CallbackInfo& info) {
2637
- auto js_external_context = NapiSafeReference(
2638
- info[0].As<Napi::External<py::object>>());
2482
+ auto js_external_context =
2483
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2639
2484
 
2640
2485
  // CHECK(...CheckTypeTag(...));
2641
2486
 
@@ -2647,8 +2492,7 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
2647
2492
  auto js_how = idempotency_tuple.Get((uint32_t) 1);
2648
2493
  std::string how = js_how.As<Napi::String>().Utf8Value();
2649
2494
 
2650
- auto js_callable = NapiSafeFunctionReference(
2651
- info[2].As<Napi::Function>());
2495
+ auto js_callable = NapiSafeFunctionReference(info[2].As<Napi::Function>());
2652
2496
 
2653
2497
  bool at_most_once = info[3].As<Napi::Boolean>();
2654
2498
 
@@ -2658,21 +2502,20 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
2658
2502
  info.Env(),
2659
2503
  "memoize(...) in nodejs",
2660
2504
  js_external_context,
2661
- [js_external_context, // Ensures `py_context` remains valid.
2505
+ [js_external_context, // Ensures `py_context` remains valid.
2662
2506
  py_context,
2663
2507
  alias = std::move(alias),
2664
2508
  how = std::move(how),
2665
2509
  js_callable = std::move(js_callable),
2666
2510
  at_most_once,
2667
2511
  until]() {
2668
- py::object py_callable = py::cpp_function(
2669
- [js_callable = std::move(js_callable)]() mutable {
2512
+ py::object py_callable =
2513
+ py::cpp_function([js_callable = std::move(js_callable)]() mutable {
2670
2514
  return PythonFutureFromNodePromise(
2671
2515
  // NOTE: need a _copy_ of `js_callable` here since
2672
2516
  // `py_callable` may be called more than once!
2673
2517
  [js_callable](Napi::Env env) mutable {
2674
- return js_callable
2675
- .Value(env)
2518
+ return js_callable.Value(env)
2676
2519
  .Call(env.Global(), {})
2677
2520
  .As<Napi::Object>();
2678
2521
  },
@@ -2690,9 +2533,7 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
2690
2533
  "at_most_once"_a = at_most_once,
2691
2534
  "until"_a = until);
2692
2535
  },
2693
- [](py::object py_json) {
2694
- return py_json.cast<std::string>();
2695
- },
2536
+ [](py::object py_json) { return py_json.cast<std::string>(); },
2696
2537
  [](Napi::Env env, std::string&& json) {
2697
2538
  return Napi::String::New(env, json);
2698
2539
  });
@@ -2700,15 +2541,15 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
2700
2541
 
2701
2542
 
2702
2543
  Napi::Value Servicer_read(const Napi::CallbackInfo& info) {
2703
- auto js_external_servicer = NapiSafeReference(
2704
- info[0].As<Napi::External<py::object>>());
2544
+ auto js_external_servicer =
2545
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2705
2546
 
2706
2547
  // CHECK(...CheckTypeTag(...));
2707
2548
 
2708
2549
  py::object* py_servicer = js_external_servicer.Value(info.Env()).Data();
2709
2550
 
2710
- auto js_external_context = NapiSafeReference(
2711
- info[1].As<Napi::External<py::object>>());
2551
+ auto js_external_context =
2552
+ NapiSafeReference(info[1].As<Napi::External<py::object>>());
2712
2553
 
2713
2554
  // CHECK(...CheckTypeTag(...));
2714
2555
 
@@ -2720,16 +2561,14 @@ Napi::Value Servicer_read(const Napi::CallbackInfo& info) {
2720
2561
  info.Env(),
2721
2562
  "servicer._read(...) in nodejs",
2722
2563
  js_external_context,
2723
- [js_external_servicer, // Ensures `py_servicer` remains valid.
2564
+ [js_external_servicer, // Ensures `py_servicer` remains valid.
2724
2565
  py_servicer,
2725
- js_external_context, // Ensures `py_context` remains valid.
2566
+ js_external_context, // Ensures `py_context` remains valid.
2726
2567
  py_context,
2727
2568
  json_options = std::move(json_options)]() {
2728
2569
  return py_servicer->attr("_read")(py_context, json_options);
2729
2570
  },
2730
- [](py::object py_json) {
2731
- return py_json.cast<std::string>();
2732
- },
2571
+ [](py::object py_json) { return py_json.cast<std::string>(); },
2733
2572
  [](Napi::Env env, std::string&& json) {
2734
2573
  return Napi::String::New(env, json);
2735
2574
  });
@@ -2737,22 +2576,21 @@ Napi::Value Servicer_read(const Napi::CallbackInfo& info) {
2737
2576
 
2738
2577
 
2739
2578
  Napi::Value Servicer_write(const Napi::CallbackInfo& info) {
2740
- auto js_external_servicer = NapiSafeReference(
2741
- info[0].As<Napi::External<py::object>>());
2579
+ auto js_external_servicer =
2580
+ NapiSafeReference(info[0].As<Napi::External<py::object>>());
2742
2581
 
2743
2582
  // CHECK(...CheckTypeTag(...));
2744
2583
 
2745
2584
  py::object* py_servicer = js_external_servicer.Value(info.Env()).Data();
2746
2585
 
2747
- auto js_external_context = NapiSafeReference(
2748
- info[1].As<Napi::External<py::object>>());
2586
+ auto js_external_context =
2587
+ NapiSafeReference(info[1].As<Napi::External<py::object>>());
2749
2588
 
2750
2589
  // CHECK(...CheckTypeTag(...));
2751
2590
 
2752
2591
  py::object* py_context = js_external_context.Value(info.Env()).Data();
2753
2592
 
2754
- auto js_writer = NapiSafeFunctionReference(
2755
- info[2].As<Napi::Function>());
2593
+ auto js_writer = NapiSafeFunctionReference(info[2].As<Napi::Function>());
2756
2594
 
2757
2595
  std::string json_options = info[3].As<Napi::String>().Utf8Value();
2758
2596
 
@@ -2760,23 +2598,21 @@ Napi::Value Servicer_write(const Napi::CallbackInfo& info) {
2760
2598
  info.Env(),
2761
2599
  "servicer._write(...) in nodejs",
2762
2600
  js_external_context,
2763
- [js_external_servicer, // Ensures `py_servicer` remains valid.
2601
+ [js_external_servicer, // Ensures `py_servicer` remains valid.
2764
2602
  py_servicer,
2765
- js_external_context, // Ensures `py_context` remains valid.
2603
+ js_external_context, // Ensures `py_context` remains valid.
2766
2604
  py_context,
2767
2605
  js_writer = std::move(js_writer),
2768
2606
  json_options = std::move(json_options)]() {
2769
2607
  py::object py_writer = py::cpp_function(
2770
- [js_writer = std::move(js_writer)](
2771
- std::string state_json) mutable {
2608
+ [js_writer = std::move(js_writer)](std::string state_json) mutable {
2772
2609
  return PythonFutureFromNodePromise(
2773
- [js_writer, // NOTE: need a _copy_ of
2774
- // `js_writer` here since
2775
- // `py_writer` may be called more
2776
- // than once!
2610
+ [js_writer, // NOTE: need a _copy_ of
2611
+ // `js_writer` here since
2612
+ // `py_writer` may be called more
2613
+ // than once!
2777
2614
  state_json](Napi::Env env) mutable {
2778
- return js_writer
2779
- .Value(env)
2615
+ return js_writer.Value(env)
2780
2616
  .Call(
2781
2617
  env.Global(),
2782
2618
  {Napi::String::New(env, state_json)})
@@ -2787,14 +2623,9 @@ Napi::Value Servicer_write(const Napi::CallbackInfo& info) {
2787
2623
  });
2788
2624
  });
2789
2625
 
2790
- return py_servicer->attr("_write")(
2791
- py_context,
2792
- py_writer,
2793
- json_options);
2794
- },
2795
- [](py::object py_result) {
2796
- return py_result.cast<std::string>();
2626
+ return py_servicer->attr("_write")(py_context, py_writer, json_options);
2797
2627
  },
2628
+ [](py::object py_result) { return py_result.cast<std::string>(); },
2798
2629
  [](Napi::Env env, std::string&& result) {
2799
2630
  return Napi::String::New(env, result);
2800
2631
  });