@reboot-dev/reboot 0.33.1 → 0.35.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
@@ -2,6 +2,10 @@
2
2
  #include <pybind11/pybind11.h>
3
3
  #include <pybind11/stl.h>
4
4
 
5
+ #if __linux__
6
+ #include <sys/eventfd.h>
7
+ #endif
8
+
5
9
  #include <atomic>
6
10
  #include <future>
7
11
  #include <iostream>
@@ -21,8 +25,53 @@ using namespace pybind11::literals;
21
25
  namespace py = pybind11;
22
26
 
23
27
 
28
+ // Helper when debugging, left for future optimization work.
29
+ struct Timer {
30
+ Timer(const char* name)
31
+ : name_(name) {
32
+ start_ = std::chrono::high_resolution_clock::now();
33
+ }
34
+
35
+ void elapsed(const char* step = nullptr) const {
36
+ auto end = std::chrono::high_resolution_clock::now();
37
+ auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
38
+ end - start_);
39
+ std::stringstream ss;
40
+ ss << name_
41
+ << (step != nullptr ? " at " + std::string(step) : std::string(""))
42
+ << " took " << duration.count() << "ms"
43
+ << std::endl;
44
+ std::cout << ss.str();
45
+ }
46
+
47
+ std::string name_;
48
+ std::chrono::time_point<std::chrono::high_resolution_clock> start_;
49
+ };
50
+
51
+
52
+ void _RunNodeFunctions(
53
+ Napi::Env env,
54
+ Napi::Function /* callback */,
55
+ void* /* context */,
56
+ void* /* data */);
57
+
58
+
24
59
  struct PythonNodeAdaptor {
25
- PythonNodeAdaptor() {}
60
+ PythonNodeAdaptor() {
61
+ #if __linux__
62
+ read_fd_ = write_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
63
+ #else
64
+ int pipe_fds[2];
65
+ if (pipe(pipe_fds) == -1) {
66
+ perror("pipe(...)");
67
+ abort();
68
+ }
69
+
70
+ // TODO: make nonblocking and close on exec.
71
+ read_fd_ = pipe_fds[0];
72
+ write_fd_ = pipe_fds[1];
73
+ #endif
74
+ }
26
75
 
27
76
  ~PythonNodeAdaptor() {
28
77
  // We must join because thread captures `this`.
@@ -68,20 +117,41 @@ struct PythonNodeAdaptor {
68
117
  }
69
118
 
70
119
  template <typename F>
71
- void ScheduleCallbackOnPythonEventLoop(F&& f) {
72
- auto function = [f = std::forward<F>(f)]() mutable {
73
- f();
74
- };
120
+ void ScheduleCallbackOnPythonEventLoop(F&& f, bool high_priority = true) {
121
+ bool signal_fd = false;
75
122
 
76
123
  {
77
124
  std::lock_guard<std::mutex> lock(mutex);
78
- python_functions.emplace_back(std::move(function));
125
+ python_functions.emplace_back(std::move(f));
126
+ if (high_priority && should_signal_fd_) {
127
+ signal_fd = true;
128
+ should_signal_fd_ = false;
129
+ }
130
+ }
131
+
132
+ if (signal_fd) {
133
+ uint64_t one = 1;
134
+ // Writing to this file descriptor communicates to Python
135
+ // (see `public/rebootdev/nodejs/python.py`) that
136
+ // `run_functions()` should be called (while holding the Python
137
+ // GIL). In `Initialize()` we've set up `run_functions()` to run
138
+ // the `python_functions` we've just added our callback to.
139
+ if (write(write_fd_, &one, sizeof(one)) < 0) {
140
+ perror("write(...)");
141
+ abort();
142
+ }
79
143
  }
80
- python_functions_not_empty.notify_one();
81
144
  }
82
145
 
83
146
  template <typename F>
84
- void ScheduleCallbackOnNodeEventLoop(F&& f) {
147
+ void ScheduleCallbackOnPythonEventLoopLowPriority(F&& f) {
148
+ return ScheduleCallbackOnPythonEventLoop(
149
+ std::move(f),
150
+ /* high_priority = */ false);
151
+ }
152
+
153
+ template <typename F>
154
+ void ScheduleCallbackOnNodeEventLoop(F&& f, bool high_priority = true) {
85
155
  // It is possible that Node.js is shutting down and has already
86
156
  // called the `thread_safe_function` finalizer and trying to use
87
157
  // `thread_safe_function` will raise SIGABRT or SIGSEGV.
@@ -99,36 +169,67 @@ struct PythonNodeAdaptor {
99
169
  return;
100
170
  }
101
171
 
102
- napi_status status = thread_safe_function.BlockingCall(
103
- [f = std::forward<F>(f)](
104
- Napi::Env env,
105
- Napi::Function /* js_callback */) mutable {
106
- try {
107
- f(env);
108
- } catch (const std::exception& e) {
109
- PythonNodeAdaptor::HandleException(e);
110
- }
111
- });
172
+ bool call_thread_safe_function = false;
112
173
 
113
- // TODO: handle each of the possible values for `status`:
114
- //
115
- // napi_ok: the call was successfully added to the queue.
116
- //
117
- // napi_queue_full: the queue was full when trying to call in a
118
- // non-blocking method.
119
- //
120
- // napi_closing: the thread-safe function is aborted and cannot
121
- // accept more calls.
122
- //
123
- // napi_invalid_arg: the thread-safe function is closed.
124
- //
125
- // napi_generic_failure: a generic error occurred when attempting
126
- //
174
+ {
175
+ std::lock_guard<std::mutex> lock(node_functions_mutex_);
176
+ node_functions_.emplace_back(std::move(f));
177
+ if (high_priority && should_call_thread_safe_function_) {
178
+ call_thread_safe_function = true;
179
+ should_call_thread_safe_function_ = false;
180
+ }
181
+ }
182
+
183
+ if (call_thread_safe_function) {
184
+ napi_status status = thread_safe_function.BlockingCall();
185
+
186
+ // TODO: handle each of the possible values for `status`:
187
+ //
188
+ // napi_ok: the call was successfully added to the queue.
189
+ //
190
+ // napi_queue_full: the queue was full when trying to call in a
191
+ // non-blocking method.
192
+ //
193
+ // napi_closing: the thread-safe function is aborted and cannot
194
+ // accept more calls.
195
+ //
196
+ // napi_invalid_arg: the thread-safe function is closed.
197
+ //
198
+ // napi_generic_failure: a generic error occurred when attempting
199
+ //
200
+
201
+ if (status != napi_ok) {
202
+ Napi::Error::Fatal(
203
+ "ThreadEntry",
204
+ "Napi::ThreadSafeNapi::Function.BlockingCall() failed");
205
+ }
206
+ }
207
+ }
208
+
209
+ template <typename F>
210
+ void ScheduleCallbackOnNodeEventLoopLowPriority(F&& f) {
211
+ return ScheduleCallbackOnNodeEventLoop(
212
+ std::move(f),
213
+ /* high_priority = */ false);
214
+ }
127
215
 
128
- if (status != napi_ok) {
129
- Napi::Error::Fatal(
130
- "ThreadEntry",
131
- "Napi::ThreadSafeNapi::Function.BlockingCall() failed");
216
+ void RunNodeFunctions(Napi::Env& env) {
217
+ std::list<std::function<void(Napi::Env&)>> functions;
218
+ {
219
+ std::lock_guard<std::mutex> lock(node_functions_mutex_);
220
+ functions = std::move(node_functions_);
221
+ should_call_thread_safe_function_ = true;
222
+ }
223
+ for (auto&& function : functions) {
224
+ try {
225
+ function(env);
226
+ } 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;
232
+ }
132
233
  }
133
234
  }
134
235
 
@@ -162,23 +263,29 @@ struct PythonNodeAdaptor {
162
263
  // into Python or expecting calls from Python.
163
264
  std::atomic<int> references{0};
164
265
 
165
- Napi::ThreadSafeFunction thread_safe_function;
266
+ Napi::TypedThreadSafeFunction<
267
+ void,
268
+ void,
269
+ _RunNodeFunctions>
270
+ thread_safe_function;
271
+ std::mutex node_functions_mutex_;
272
+ std::list<std::function<void(Napi::Env&)>> node_functions_;
273
+ bool should_call_thread_safe_function_ = true;
166
274
 
167
275
  bool thread_safe_function_finalized = false;
168
276
 
169
277
  std::thread thread;
170
278
  std::mutex mutex;
171
- std::condition_variable python_functions_not_empty;
172
279
  std::list<std::function<void()>> python_functions;
280
+ int read_fd_ = -1;
281
+ int write_fd_ = -1;
282
+ bool should_signal_fd_ = true;
173
283
  };
174
284
 
175
285
 
176
286
  static PythonNodeAdaptor* adaptor = new PythonNodeAdaptor();
177
287
 
178
288
  // References to nodejs functions so we don't have to look them up.
179
- static Napi::FunctionReference* js_Context_fromNativeExternal =
180
- new Napi::FunctionReference();
181
-
182
289
  static Napi::FunctionReference* js_launchSubprocessConsensus =
183
290
  new Napi::FunctionReference();
184
291
 
@@ -186,7 +293,7 @@ static Napi::FunctionReference* js_launchSubprocessConsensus =
186
293
  struct NapiReferenceDeleter {
187
294
  template <typename T>
188
295
  void operator()(Napi::Reference<T>* reference) {
189
- adaptor->ScheduleCallbackOnNodeEventLoop(
296
+ adaptor->ScheduleCallbackOnNodeEventLoopLowPriority(
190
297
  [reference](Napi::Env) {
191
298
  delete reference;
192
299
  });
@@ -246,7 +353,7 @@ Napi::External<py::object> make_napi_external(
246
353
  env,
247
354
  py_object,
248
355
  [](Napi::Env, py::object* py_object) {
249
- adaptor->ScheduleCallbackOnPythonEventLoop(
356
+ adaptor->ScheduleCallbackOnPythonEventLoopLowPriority(
250
357
  [py_object]() {
251
358
  delete py_object;
252
359
  });
@@ -260,18 +367,30 @@ Napi::External<py::object> make_napi_external(
260
367
  }
261
368
 
262
369
 
370
+ void _RunNodeFunctions(
371
+ Napi::Env env,
372
+ Napi::Function /* callback */,
373
+ void* /* context */,
374
+ void* /* data */) {
375
+ adaptor->RunNodeFunctions(env);
376
+ }
377
+
378
+
263
379
  void PythonNodeAdaptor::Initialize(
264
380
  Napi::Env& env,
265
381
  const Napi::Function& js_callback) {
266
- thread_safe_function = Napi::ThreadSafeFunction::New(
382
+ thread_safe_function = Napi::TypedThreadSafeFunction<
383
+ void,
384
+ void,
385
+ _RunNodeFunctions>::New(
267
386
  /* Environment: */ env,
268
387
  /* JS callback: */ js_callback,
269
388
  /* Resource name: */ "reboot_native",
270
389
  /* Max queue size (0 = unlimited): */ 0,
271
390
  /* Initial thread count: */ 1,
272
- /* Context: */ this,
391
+ /* Context: */ (void*) nullptr,
273
392
  // Finalizer:
274
- [this](Napi::Env env, void*, PythonNodeAdaptor*) {
393
+ [this](Napi::Env env, void*, void*) {
275
394
  // Set that we've been finalized so that we don't use
276
395
  // `thread_safe_function` again, see comment in
277
396
  // `ScheduleCallbackOnNodeEventLoop`.
@@ -345,35 +464,35 @@ void PythonNodeAdaptor::Initialize(
345
464
  return py_future;
346
465
  });
347
466
 
348
- py::object event_loop_thread = module.attr("EventLoopThread")();
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;
471
+
472
+ {
473
+ std::unique_lock<std::mutex> lock(mutex);
474
+ functions = std::move(python_functions);
475
+ should_signal_fd_ = true;
476
+ }
477
+
478
+ for (auto&& function : functions) {
479
+ try {
480
+ function();
481
+ } catch (std::exception& e) {
482
+ PythonNodeAdaptor::HandleException(e);
483
+ }
484
+ }
485
+ });
486
+
487
+ py::object event_loop_thread = module.attr("EventLoopThread")(
488
+ read_fd_);
349
489
 
350
490
  py::gil_scoped_release release;
351
491
 
352
492
  while (true) {
353
- std::function<void()> function;
354
-
355
- {
356
- std::unique_lock<std::mutex> lock(mutex);
357
- python_functions_not_empty.wait(
358
- lock,
359
- [this] { return !python_functions.empty(); });
360
-
361
- function = std::move(python_functions.front());
362
- python_functions.pop_front();
363
- }
364
-
365
- {
366
- py::gil_scoped_acquire acquire;
367
-
368
- event_loop_thread.attr("run_callback_on_event_loop")(
369
- py::cpp_function([function = std::move(function)]() {
370
- try {
371
- function();
372
- } catch (const std::exception& e) {
373
- PythonNodeAdaptor::HandleException(e);
374
- }
375
- }));
376
- }
493
+ std::this_thread::sleep_until(
494
+ std::chrono::time_point<std::chrono::system_clock>::max());
495
+ continue;
377
496
  }
378
497
  } catch (const std::exception& e) {
379
498
  std::cout << e.what() << std::endl;
@@ -968,12 +1087,8 @@ void Initialize(const Napi::CallbackInfo& info) {
968
1087
  std::call_once(initialize_once, [&]() {
969
1088
  Napi::Env env = info.Env();
970
1089
 
971
- js_Context_fromNativeExternal->Reset(
972
- info[1].As<Napi::Function>(),
973
- /* refcount = */ 1);
974
-
975
1090
  js_launchSubprocessConsensus->Reset(
976
- info[2].As<Napi::Function>(),
1091
+ info[1].As<Napi::Function>(),
977
1092
  /* refcount = */ 1);
978
1093
 
979
1094
  // NOTE: must initialize after storing above nodejs functions.
@@ -1133,15 +1248,10 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
1133
1248
 
1134
1249
 
1135
1250
  // NOTE: must be called within _Node_.
1136
- Napi::Object make_js_context(
1251
+ Napi::Promise make_js_cancelled(
1137
1252
  Napi::Env& env,
1138
- py::object* py_context,
1139
- py::object* py_cancelled,
1140
- const std::string& kind) {
1141
- Napi::External<py::object> js_external_context =
1142
- make_napi_external(env, py_context);
1143
-
1144
- Napi::Promise js_cancelled = NodePromiseFromPythonFuture(
1253
+ py::object* py_cancelled) {
1254
+ return NodePromiseFromPythonFuture(
1145
1255
  env,
1146
1256
  [py_cancelled]() {
1147
1257
  // After returning we won't need `py_cancelled` anymore, so we
@@ -1161,13 +1271,6 @@ Napi::Object make_js_context(
1161
1271
  // this lambda or the one above.
1162
1272
  return env.Undefined();
1163
1273
  });
1164
-
1165
- return js_Context_fromNativeExternal
1166
- ->Call(
1167
- {js_external_context,
1168
- Napi::String::New(env, kind),
1169
- js_cancelled})
1170
- .As<Napi::Object>();
1171
1274
  }
1172
1275
 
1173
1276
 
@@ -1191,11 +1294,9 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1191
1294
  // Trampolines us from Python through C++ into Node.
1192
1295
  attributes["_authorize"] = py::cpp_function(
1193
1296
  [](py::object self,
1194
- const std::string& method_name,
1195
1297
  py::object py_reader_context,
1196
1298
  py::object py_cancelled,
1197
- std::optional<std::string> bytes_state,
1198
- std::optional<std::string> bytes_request) {
1299
+ std::string bytes_call) {
1199
1300
  NapiSafeObjectReference* js_authorizer_reference =
1200
1301
  self.attr("_js_authorizer")
1201
1302
  .cast<NapiSafeObjectReference*>();
@@ -1204,30 +1305,21 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1204
1305
  [js_authorizer_reference,
1205
1306
  py_reader_context = new py::object(py_reader_context),
1206
1307
  py_cancelled = new py::object(py_cancelled),
1207
- method_name,
1208
- bytes_state = std::move(bytes_state),
1209
- bytes_request = std::move(bytes_request)](Napi::Env env) {
1308
+ bytes_call = std::move(bytes_call)](Napi::Env env) {
1210
1309
  std::vector<Napi::Value> js_args;
1211
1310
 
1212
- Napi::Object js_context = make_js_context(
1213
- env,
1214
- py_reader_context,
1215
- py_cancelled,
1216
- "reader");
1311
+ Napi::External<py::object> js_external_context =
1312
+ make_napi_external(env, py_reader_context);
1217
1313
 
1218
- Napi::Object js_authorizer = js_authorizer_reference->Value(env);
1314
+ js_args.push_back(js_external_context);
1315
+
1316
+ Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
1219
1317
 
1220
- Napi::Value js_bytes_request = bytes_request.has_value()
1221
- ? str_to_uint8array(env, *bytes_request)
1222
- : env.Undefined();
1223
- Napi::Value js_bytes_state = bytes_state.has_value()
1224
- ? str_to_uint8array(env, *bytes_state)
1225
- : env.Undefined();
1318
+ js_args.push_back(js_cancelled);
1226
1319
 
1227
- js_args.push_back(Napi::String::New(env, method_name));
1228
- js_args.push_back(js_context);
1229
- js_args.push_back(js_bytes_state);
1230
- js_args.push_back(js_bytes_request);
1320
+ js_args.push_back(str_to_uint8array(env, bytes_call));
1321
+
1322
+ Napi::Object js_authorizer = js_authorizer_reference->Value(env);
1231
1323
 
1232
1324
  return js_authorizer.Get("_authorize")
1233
1325
  .As<Napi::Function>()
@@ -1243,11 +1335,9 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1243
1335
  });
1244
1336
  },
1245
1337
  py::name("_authorize"),
1246
- py::arg("method_name"),
1247
1338
  py::arg("context"),
1248
1339
  py::arg("cancelled"),
1249
- py::arg("state"),
1250
- py::arg("request"),
1340
+ py::arg("bytes_call"),
1251
1341
  py::is_method(py::none()));
1252
1342
 
1253
1343
  // Now define our subclass.
@@ -1412,41 +1502,41 @@ py::object make_py_user_servicer(
1412
1502
  // calls.
1413
1503
  attributes["_trampoline"] = py::cpp_function(
1414
1504
  [](NapiSafeObjectReference& js_servicer_reference,
1415
- const std::string& kind,
1416
- const std::string& method,
1417
1505
  py::object py_context,
1418
1506
  py::object py_cancelled,
1419
- const std::string& json_state,
1420
- const std::string& json_request) {
1507
+ std::string bytes_call) {
1421
1508
  return PythonFutureFromNodePromise(
1422
1509
  [&js_servicer_reference,
1423
- kind,
1424
- method,
1425
1510
  py_context = new py::object(py_context),
1426
1511
  py_cancelled = new py::object(py_cancelled),
1427
- json_state,
1428
- json_request](Napi::Env env) {
1512
+ bytes_call = std::move(bytes_call)](Napi::Env env) {
1429
1513
  std::vector<Napi::Value> js_args;
1430
1514
 
1431
- Napi::Object js_context =
1432
- make_js_context(env, py_context, py_cancelled, kind);
1515
+ Napi::External<py::object> js_external_context =
1516
+ make_napi_external(env, py_context);
1517
+
1518
+ js_args.push_back(js_external_context);
1433
1519
 
1434
- js_args.push_back(js_context);
1520
+ Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
1435
1521
 
1436
- js_args.push_back(Napi::String::New(env, json_state));
1437
- js_args.push_back(Napi::String::New(env, json_request));
1522
+ js_args.push_back(js_cancelled);
1523
+
1524
+ js_args.push_back(str_to_uint8array(env, bytes_call));
1438
1525
 
1439
1526
  Napi::Object js_servicer =
1440
1527
  js_servicer_reference.Value(env);
1441
1528
 
1442
1529
  return js_servicer
1443
- .Get("_" + method)
1530
+ .Get("__dispatch")
1444
1531
  .As<Napi::Function>()
1445
1532
  .Call(js_servicer, js_args)
1446
1533
  .As<Napi::Object>();
1447
1534
  },
1448
1535
  [](Napi::Env env, Napi::Value value) {
1449
- return std::string(value.As<Napi::String>().Utf8Value());
1536
+ return uint8array_to_str(value.As<Napi::Uint8Array>());
1537
+ },
1538
+ [](std::string&& bytes_result) -> py::object {
1539
+ return py::bytes(bytes_result);
1450
1540
  });
1451
1541
  });
1452
1542
 
@@ -1562,19 +1652,11 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1562
1652
  [](py::object self,
1563
1653
  py::object py_reader_context,
1564
1654
  py::object py_cancelled,
1565
- py::object py_token) {
1655
+ std::string bytes_call) {
1566
1656
  NapiSafeObjectReference* js_token_verifier_reference =
1567
1657
  self.attr("_js_token_verifier")
1568
1658
  .cast<NapiSafeObjectReference*>();
1569
1659
 
1570
- // Converting to 'py::str' involves Python runtime access.
1571
- // Perform this conversion here, on the Python thread, before entering
1572
- // Node.
1573
- std::optional<std::string> token;
1574
- if (!py_token.is_none()) {
1575
- token = py::str(py_token);
1576
- }
1577
-
1578
1660
  return PythonFutureFromNodePromise(
1579
1661
  [js_token_verifier_reference,
1580
1662
  // We allocate 'py_cancelled' and 'py_reader_context' with 'new
@@ -1583,21 +1665,19 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1583
1665
  // thread, see 'make_js_context'.
1584
1666
  py_reader_context = new py::object(py_reader_context),
1585
1667
  py_cancelled = new py::object(py_cancelled),
1586
- token = std::move(token)](Napi::Env env) {
1668
+ bytes_call = std::move(bytes_call)](Napi::Env env) {
1587
1669
  std::vector<Napi::Value> js_args;
1588
1670
 
1589
- js_args.push_back(
1590
- make_js_context(
1591
- env,
1592
- py_reader_context,
1593
- py_cancelled,
1594
- "reader"));
1671
+ Napi::External<py::object> js_external_context =
1672
+ make_napi_external(env, py_reader_context);
1595
1673
 
1596
- if (token.has_value()) {
1597
- js_args.push_back(Napi::String::New(env, *token));
1598
- } else {
1599
- js_args.push_back(env.Null());
1600
- }
1674
+ js_args.push_back(js_external_context);
1675
+
1676
+ Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
1677
+
1678
+ js_args.push_back(js_cancelled);
1679
+
1680
+ js_args.push_back(str_to_uint8array(env, bytes_call));
1601
1681
 
1602
1682
  Napi::Object js_token_verifier =
1603
1683
  js_token_verifier_reference->Value(env);
@@ -1626,7 +1706,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1626
1706
  py::name("_verify_token"),
1627
1707
  py::arg("context"),
1628
1708
  py::arg("cancelled"),
1629
- py::arg("py_token"),
1709
+ py::arg("bytes_call"),
1630
1710
  py::is_method(py::none()));
1631
1711
 
1632
1712
  py::object py_parent_class =
@@ -2331,151 +2411,6 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
2331
2411
  }
2332
2412
 
2333
2413
 
2334
- Napi::Value Context_auth(const Napi::CallbackInfo& info) {
2335
- Napi::External<py::object> js_external_context =
2336
- info[0].As<Napi::External<py::object>>();
2337
-
2338
- // CHECK(...CheckTypeTag(...));
2339
-
2340
- py::object* py_context = js_external_context.Data();
2341
-
2342
- std::optional<std::string> auth_bytes = RunCallbackOnPythonEventLoop(
2343
- [py_context]() -> std::optional<std::string> {
2344
- py::object py_auth = py_context->attr("auth");
2345
-
2346
- if (py_auth.is_none()) {
2347
- return std::nullopt;
2348
- } else {
2349
- std::string auth_bytes = py::bytes(py_auth.attr("to_proto_bytes")());
2350
- return auth_bytes;
2351
- }
2352
- });
2353
-
2354
- if (auth_bytes.has_value()) {
2355
- Napi::Env env = info.Env();
2356
- return str_to_uint8array(env, *auth_bytes);
2357
- } else {
2358
- return info.Env().Null();
2359
- }
2360
- }
2361
-
2362
-
2363
- Napi::Value Context_stateId(const Napi::CallbackInfo& info) {
2364
- Napi::External<py::object> js_external_context =
2365
- info[0].As<Napi::External<py::object>>();
2366
-
2367
- // CHECK(...CheckTypeTag(...));
2368
-
2369
- py::object* py_context = js_external_context.Data();
2370
-
2371
- std::string state_id = RunCallbackOnPythonEventLoop(
2372
- [py_context]() {
2373
- py::str state_id = py_context->attr("state_id");
2374
- return std::string(state_id);
2375
- });
2376
-
2377
- return Napi::String::New(info.Env(), state_id);
2378
- }
2379
-
2380
-
2381
- Napi::Value Context_stateTypeName(const Napi::CallbackInfo& info) {
2382
- Napi::External<py::object> js_external_context =
2383
- info[0].As<Napi::External<py::object>>();
2384
-
2385
- // CHECK(...CheckTypeTag(...));
2386
-
2387
- py::object* py_context = js_external_context.Data();
2388
-
2389
- std::string state_type_name = RunCallbackOnPythonEventLoop(
2390
- [py_context]() {
2391
- py::str state_type_name = py_context->attr("state_type_name");
2392
- return std::string(state_type_name);
2393
- });
2394
-
2395
- return Napi::String::New(info.Env(), state_type_name);
2396
- }
2397
-
2398
-
2399
- Napi::Value Context_method(const Napi::CallbackInfo& info) {
2400
- Napi::External<py::object> js_external_context =
2401
- info[0].As<Napi::External<py::object>>();
2402
-
2403
- // CHECK(...CheckTypeTag(...));
2404
-
2405
- py::object* py_context = js_external_context.Data();
2406
-
2407
- std::string method = RunCallbackOnPythonEventLoop(
2408
- [py_context]() {
2409
- py::str method = py_context->attr("method");
2410
- return std::string(method);
2411
- });
2412
-
2413
- return Napi::String::New(info.Env(), method);
2414
- }
2415
-
2416
-
2417
- Napi::Value Context_callerBearerToken(
2418
- const Napi::CallbackInfo& info) {
2419
- Napi::External<py::object> js_external_context =
2420
- info[0].As<Napi::External<py::object>>();
2421
-
2422
- // CHECK(...CheckTypeTag(...));
2423
-
2424
- py::object* py_context = js_external_context.Data();
2425
-
2426
- std::optional<std::string> caller_bearer_token = RunCallbackOnPythonEventLoop(
2427
- [py_context]() -> std::optional<std::string> {
2428
- py::object caller_bearer_token = py_context->attr(
2429
- "caller_bearer_token");
2430
- if (caller_bearer_token.is_none()) {
2431
- return std::nullopt;
2432
- } else {
2433
- return std::string(py::str(caller_bearer_token));
2434
- }
2435
- });
2436
- if (caller_bearer_token.has_value()) {
2437
- return Napi::String::New(info.Env(), *caller_bearer_token);
2438
- } else {
2439
- return info.Env().Null();
2440
- }
2441
- }
2442
-
2443
-
2444
- Napi::Value Context_cookie(const Napi::CallbackInfo& info) {
2445
- Napi::External<py::object> js_external_context =
2446
- info[0].As<Napi::External<py::object>>();
2447
-
2448
- // CHECK(...CheckTypeTag(...));
2449
-
2450
- py::object* py_context = js_external_context.Data();
2451
-
2452
- std::string cookie = RunCallbackOnPythonEventLoop(
2453
- [py_context]() {
2454
- py::str cookie = py_context->attr("cookie");
2455
- return std::string(cookie);
2456
- });
2457
-
2458
- return Napi::String::New(info.Env(), cookie);
2459
- }
2460
-
2461
-
2462
- Napi::Value Context_appInternal(const Napi::CallbackInfo& info) {
2463
- Napi::External<py::object> js_external_context =
2464
- info[0].As<Napi::External<py::object>>();
2465
-
2466
- // CHECK(...CheckTypeTag(...));
2467
-
2468
- py::object* py_context = js_external_context.Data();
2469
-
2470
- bool app_internal = RunCallbackOnPythonEventLoop(
2471
- [py_context]() {
2472
- return py_context->attr("app_internal").cast<bool>();
2473
- });
2474
-
2475
- return Napi::Boolean::New(info.Env(), app_internal);
2476
- }
2477
-
2478
-
2479
2414
  Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
2480
2415
  // NOTE: we immediately get a safe reference to the `Napi::External`
2481
2416
  // so that Node will not garbage collect it and the `py::object*` we
@@ -2911,34 +2846,6 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
2911
2846
  Napi::String::New(env, "Service_call"),
2912
2847
  Napi::Function::New<Service_call>(env));
2913
2848
 
2914
- exports.Set(
2915
- Napi::String::New(env, "Context_auth"),
2916
- Napi::Function::New<Context_auth>(env));
2917
-
2918
- exports.Set(
2919
- Napi::String::New(env, "Context_stateId"),
2920
- Napi::Function::New<Context_stateId>(env));
2921
-
2922
- exports.Set(
2923
- Napi::String::New(env, "Context_stateTypeName"),
2924
- Napi::Function::New<Context_stateTypeName>(env));
2925
-
2926
- exports.Set(
2927
- Napi::String::New(env, "Context_method"),
2928
- Napi::Function::New<Context_method>(env));
2929
-
2930
- exports.Set(
2931
- Napi::String::New(env, "Context_callerBearerToken"),
2932
- Napi::Function::New<Context_callerBearerToken>(env));
2933
-
2934
- exports.Set(
2935
- Napi::String::New(env, "Context_cookie"),
2936
- Napi::Function::New<Context_cookie>(env));
2937
-
2938
- exports.Set(
2939
- Napi::String::New(env, "Context_appInternal"),
2940
- Napi::Function::New<Context_appInternal>(env));
2941
-
2942
2849
  exports.Set(
2943
2850
  Napi::String::New(env, "Context_generateIdempotentStateId"),
2944
2851
  Napi::Function::New<Context_generateIdempotentStateId>(env));