@reboot-dev/reboot 0.25.5 → 0.25.6

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/index.d.ts CHANGED
@@ -43,9 +43,9 @@ export declare function getContext(): Context;
43
43
  export declare function runWithContext<T>(context: Context, callback: () => T): Promise<T>;
44
44
  export declare class Context {
45
45
  #private;
46
- readonly aborted: Promise<void>;
47
- constructor(external: any, aborted: Promise<void>);
48
- static fromNativeExternal(external: any, kind: string, aborted: Promise<void>): ReaderContext | WriterContext | TransactionContext | WorkflowContext;
46
+ readonly cancelled: Promise<void>;
47
+ constructor(external: any, cancelled: Promise<void>);
48
+ static fromNativeExternal(external: any, kind: string, cancelled: Promise<void>): ReaderContext | WriterContext | TransactionContext | WorkflowContext;
49
49
  get __external(): any;
50
50
  get auth(): Auth | null;
51
51
  get stateId(): string;
@@ -57,20 +57,20 @@ export declare class Context {
57
57
  }
58
58
  export declare class ReaderContext extends Context {
59
59
  #private;
60
- constructor(external: any, aborted: any);
60
+ constructor(external: any, cancelled: any);
61
61
  }
62
62
  export declare class WriterContext extends Context {
63
63
  #private;
64
- constructor(external: any, aborted: any);
64
+ constructor(external: any, cancelled: any);
65
65
  set sync(sync: boolean);
66
66
  }
67
67
  export declare class TransactionContext extends Context {
68
68
  #private;
69
- constructor(external: any, aborted: any);
69
+ constructor(external: any, cancelled: any);
70
70
  }
71
71
  export declare class WorkflowContext extends Context {
72
72
  #private;
73
- constructor(external: any, aborted: any);
73
+ constructor(external: any, cancelled: any);
74
74
  }
75
75
  export declare function clearField(field: any, target: any): void;
76
76
  export declare function clearFields(target: any): void;
package/index.js CHANGED
@@ -138,28 +138,28 @@ export async function runWithContext(context, callback) {
138
138
  return await contextStorage.run(context, callback);
139
139
  }
140
140
  export class Context {
141
- constructor(external, aborted) {
141
+ constructor(external, cancelled) {
142
142
  _Context_external.set(this, void 0);
143
143
  if (!__classPrivateFieldGet(_a, _a, "f", _Context_isInternalConstructing)) {
144
144
  throw new TypeError("Context is not publicly constructable");
145
145
  }
146
146
  __classPrivateFieldSet(_a, _a, false, "f", _Context_isInternalConstructing);
147
147
  __classPrivateFieldSet(this, _Context_external, external, "f");
148
- this.aborted = aborted;
148
+ this.cancelled = cancelled;
149
149
  }
150
- static fromNativeExternal(external, kind, aborted) {
150
+ static fromNativeExternal(external, kind, cancelled) {
151
151
  __classPrivateFieldSet(_a, _a, true, "f", _Context_isInternalConstructing);
152
152
  if (kind === "reader") {
153
- return new ReaderContext(external, aborted);
153
+ return new ReaderContext(external, cancelled);
154
154
  }
155
155
  else if (kind === "writer") {
156
- return new WriterContext(external, aborted);
156
+ return new WriterContext(external, cancelled);
157
157
  }
158
158
  else if (kind === "transaction") {
159
- return new TransactionContext(external, aborted);
159
+ return new TransactionContext(external, cancelled);
160
160
  }
161
161
  else if (kind === "workflow") {
162
- return new WorkflowContext(external, aborted);
162
+ return new WorkflowContext(external, cancelled);
163
163
  }
164
164
  throw new Error("Unknown method kind");
165
165
  }
@@ -195,8 +195,8 @@ export class Context {
195
195
  _a = Context, _Context_external = new WeakMap();
196
196
  _Context_isInternalConstructing = { value: false };
197
197
  export class ReaderContext extends Context {
198
- constructor(external, aborted) {
199
- super(external, aborted);
198
+ constructor(external, cancelled) {
199
+ super(external, cancelled);
200
200
  // Property helps `tsc` not treat a `ReaderContext` as any other
201
201
  // context type that structurally looks equivalent.
202
202
  _ReaderContext_kind.set(this, "reader");
@@ -204,8 +204,8 @@ export class ReaderContext extends Context {
204
204
  }
205
205
  _ReaderContext_kind = new WeakMap();
206
206
  export class WriterContext extends Context {
207
- constructor(external, aborted) {
208
- super(external, aborted);
207
+ constructor(external, cancelled) {
208
+ super(external, cancelled);
209
209
  // Property helps `tsc` not treat a `WriterContext` as any other
210
210
  // context type that structurally looks equivalent.
211
211
  _WriterContext_kind.set(this, "writer");
@@ -228,8 +228,8 @@ export class WriterContext extends Context {
228
228
  }
229
229
  _WriterContext_kind = new WeakMap();
230
230
  export class TransactionContext extends Context {
231
- constructor(external, aborted) {
232
- super(external, aborted);
231
+ constructor(external, cancelled) {
232
+ super(external, cancelled);
233
233
  // Property helps `tsc` not treat a `TransactionContext` as any other
234
234
  // context type that structurally looks equivalent.
235
235
  _TransactionContext_kind.set(this, "transaction");
@@ -237,8 +237,8 @@ export class TransactionContext extends Context {
237
237
  }
238
238
  _TransactionContext_kind = new WeakMap();
239
239
  export class WorkflowContext extends Context {
240
- constructor(external, aborted) {
241
- super(external, aborted);
240
+ constructor(external, cancelled) {
241
+ super(external, cancelled);
242
242
  // Property helps `tsc` not treat a `WorkflowContext` as any other
243
243
  // context type that structurally looks equivalent.
244
244
  _WorkflowContext_kind.set(this, "workflow");
@@ -455,6 +455,7 @@ export class Application {
455
455
  // TODO: expose `.use()` to allow users to add their own middleware.
456
456
  __classPrivateFieldGet(this, _Application_express, "f").use(express.json());
457
457
  __classPrivateFieldGet(this, _Application_express, "f").use(express.urlencoded({ extended: true }));
458
+ __classPrivateFieldGet(this, _Application_express, "f").disable("x-powered-by");
458
459
  __classPrivateFieldSet(this, _Application_http, new Application.Http(__classPrivateFieldGet(this, _Application_express, "f"), async (args) => {
459
460
  return await __classPrivateFieldGet(this, _Application_createExternalContext, "f").call(this, args);
460
461
  }), "f");
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "@bufbuild/protobuf": "1.3.2",
4
4
  "@bufbuild/protoplugin": "1.3.2",
5
5
  "@bufbuild/protoc-gen-es": "1.3.2",
6
- "@reboot-dev/reboot-api": "0.25.5",
6
+ "@reboot-dev/reboot-api": "0.25.6",
7
7
  "chalk": "^4.1.2",
8
8
  "node-addon-api": "^7.0.0",
9
9
  "node-gyp": ">=10.2.0",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "type": "module",
18
18
  "name": "@reboot-dev/reboot",
19
- "version": "0.25.5",
19
+ "version": "0.25.6",
20
20
  "description": "npm package for Reboot",
21
21
  "scripts": {
22
22
  "postinstall": "rbt || exit 0",
package/reboot_native.cc CHANGED
@@ -2,6 +2,7 @@
2
2
  #include <pybind11/pybind11.h>
3
3
  #include <pybind11/stl.h>
4
4
 
5
+ #include <atomic>
5
6
  #include <future>
6
7
  #include <iostream>
7
8
  #include <list>
@@ -38,7 +39,7 @@ struct PythonNodeAdaptor {
38
39
  dynamic_cast<const py::error_already_set*>(&e)) {
39
40
  // This is a Python exception. Is it an `InputError`?
40
41
  if (e_py->matches(py::module::import(
41
- "reboot.controller.exceptions")
42
+ "rebootdev.aio.exceptions")
42
43
  .attr("InputError"))) {
43
44
  // This is an InputError, which means it reports a mistake in the input
44
45
  // provided by the developer. We want to print _only_ the user-friendly
@@ -131,6 +132,36 @@ struct PythonNodeAdaptor {
131
132
  }
132
133
  }
133
134
 
135
+ void KeepNodeFromExiting(Napi::Env env) {
136
+ if (references.fetch_add(1, std::memory_order_acq_rel) == 0) {
137
+ // Call `Ref()` on our NAPI thread safe function so that it
138
+ // can't be cleaned up and thus we won't exit.
139
+ //
140
+ // NOTE: `Ref()` and `Unref()` will just set and unset an
141
+ // "is_referenced" boolean flag, rather than incrementing and
142
+ // decrementing a reference count, hence we do the atomic
143
+ // reference counting ourselves.
144
+ thread_safe_function.Ref(env);
145
+ }
146
+ }
147
+
148
+ void AllowNodeToExit(Napi::Env env) {
149
+ if (references.fetch_sub(1, std::memory_order_acq_rel) == 1) {
150
+ // Call `Unref()` on our NAPI thread safe function so that it
151
+ // can be cleaned up and we can exit.
152
+ //
153
+ // NOTE: `Ref()` and `Unref()` will just set and unset an
154
+ // "is_referenced" boolean flag, rather than incrementing and
155
+ // decrementing a reference count, hence we do the atomic
156
+ // reference counting ourselves.
157
+ thread_safe_function.Unref(env);
158
+ }
159
+ }
160
+
161
+ // Reference counting to keep Node from exiting when we're calling
162
+ // into Python or expecting calls from Python.
163
+ std::atomic<int> references{0};
164
+
134
165
  Napi::ThreadSafeFunction thread_safe_function;
135
166
 
136
167
  bool thread_safe_function_finalized = false;
@@ -267,7 +298,7 @@ void PythonNodeAdaptor::Initialize(
267
298
  py::initialize_interpreter();
268
299
 
269
300
  try {
270
- py::object module = py::module::import("reboot.nodejs.python");
301
+ py::object module = py::module::import("rebootdev.nodejs.python");
271
302
 
272
303
  module.attr("launch_subprocess_consensus") = py::cpp_function(
273
304
  [](py::str py_base64_args) {
@@ -476,46 +507,70 @@ Napi::Promise NodePromiseFromPythonCallback(
476
507
  Napi::Env env,
477
508
  PythonCallback&& python_callback,
478
509
  NodeCallback&& node_callback) {
479
- auto deferred = std::make_shared<Napi::Promise::Deferred>(env);
480
- auto promise = deferred->Promise();
510
+ // Keep Node from exiting since we are calling into Python and Node
511
+ // otherwise might not know that outstanding promises that may be
512
+ // being `await`'ed are still being worked on.
513
+ //
514
+ // TODO: if `Application.run()` was called then Node will already
515
+ // not exit so unfortunately this is a noop and worse, the extra
516
+ // callback we attach to the promise via `.then()` below hurts
517
+ // performance. Consider a mechanism in that case and possibly
518
+ // others where we can not do this for better performance but still
519
+ // be correct.
520
+ adaptor->KeepNodeFromExiting(env);
521
+
522
+ auto js_deferred = std::make_shared<Napi::Promise::Deferred>(env);
523
+ auto js_promise = js_deferred->Promise();
481
524
 
482
525
  adaptor->ScheduleCallbackOnPythonEventLoop(
483
526
  [python_callback = std::forward<PythonCallback>(python_callback),
484
527
  node_callback = std::forward<NodeCallback>(node_callback),
485
- deferred = std::move(deferred)]() mutable {
528
+ js_deferred = std::move(js_deferred)]() mutable {
486
529
  try {
487
530
  auto result = python_callback();
488
531
  adaptor->ScheduleCallbackOnNodeEventLoop(
489
532
  [node_callback = std::forward<NodeCallback>(node_callback),
490
- deferred = std::move(deferred),
533
+ js_deferred = std::move(js_deferred),
491
534
  result = std::move(result)](Napi::Env env) mutable {
492
535
  try {
493
- deferred->Resolve(node_callback(env, std::move(result)));
536
+ js_deferred->Resolve(node_callback(env, std::move(result)));
494
537
  } catch (const Napi::Error& e) {
495
- deferred->Reject(e.Value());
538
+ js_deferred->Reject(e.Value());
496
539
  } catch (const std::exception& e) {
497
540
  // TODO: is this code unreachable because Node.js
498
541
  // will properly always only pass us a `Napi::Error`?
499
- deferred->Reject(
542
+ js_deferred->Reject(
500
543
  Napi::Error::New(env, e.what()).Value());
501
544
  } catch (...) {
502
545
  // TODO: is this code unreachable because Node.js
503
546
  // will properly always only pass us a `Napi::Error`?
504
- deferred->Reject(
547
+ js_deferred->Reject(
505
548
  Napi::Error::New(env, "Unknown error").Value());
506
549
  }
507
550
  });
508
551
  } catch (pybind11::error_already_set& e) {
509
552
  std::string exception = py_exception_str(e.value());
510
553
  adaptor->ScheduleCallbackOnNodeEventLoop(
511
- [deferred = std::move(deferred),
554
+ [js_deferred = std::move(js_deferred),
512
555
  exception = std::move(exception)](Napi::Env env) mutable {
513
- deferred->Reject(Napi::Error::New(env, exception).Value());
556
+ js_deferred->Reject(Napi::Error::New(env, exception).Value());
514
557
  });
515
558
  }
516
559
  });
517
560
 
518
- return promise;
561
+ // Allow Node to exit after we've resolved or rejected the promise.
562
+ auto js_resolve_reject = Napi::Function::New(
563
+ env,
564
+ [](const Napi::CallbackInfo& info) {
565
+ adaptor->AllowNodeToExit(info.Env());
566
+ });
567
+
568
+ js_promise
569
+ .Get("then")
570
+ .As<Napi::Function>()
571
+ .Call(js_promise, {js_resolve_reject, js_resolve_reject});
572
+
573
+ return js_promise;
519
574
  }
520
575
 
521
576
 
@@ -528,8 +583,20 @@ Napi::Promise NodePromiseFromPythonFuture(
528
583
  PythonFutureCallback&& python_future_callback,
529
584
  PythonDoneCallback&& python_done_callback,
530
585
  NodeCallback&& node_callback) {
531
- auto deferred = std::make_shared<Napi::Promise::Deferred>(env);
532
- auto promise = deferred->Promise();
586
+ // Keep Node from exiting since we are calling into Python and Node
587
+ // otherwise might not know that outstanding promises that may be
588
+ // being `await`'ed are still being worked on.
589
+ //
590
+ // TODO: if `Application.run()` was called then Node will already
591
+ // not exit so unfortunately this is a noop and worse, the extra
592
+ // callback we attach to the promise via `.then()` below hurts
593
+ // performance. Consider a mechanism in that case and possibly
594
+ // others where we can not do this for better performance but still
595
+ // be correct.
596
+ adaptor->KeepNodeFromExiting(env);
597
+
598
+ auto js_deferred = std::make_shared<Napi::Promise::Deferred>(env);
599
+ auto js_promise = js_deferred->Promise();
533
600
 
534
601
  adaptor->ScheduleCallbackOnPythonEventLoop(
535
602
  [python_future_callback = std::forward<PythonFutureCallback>(
@@ -537,7 +604,7 @@ Napi::Promise NodePromiseFromPythonFuture(
537
604
  python_done_callback = std::forward<PythonDoneCallback>(
538
605
  python_done_callback),
539
606
  node_callback = std::forward<NodeCallback>(node_callback),
540
- deferred = std::move(deferred)]() mutable {
607
+ js_deferred = std::move(js_deferred)]() mutable {
541
608
  try {
542
609
  // TODO: check that `py_future` is a future!
543
610
  py::object py_future = python_future_callback();
@@ -546,35 +613,45 @@ Napi::Promise NodePromiseFromPythonFuture(
546
613
  [python_done_callback = std::forward<PythonDoneCallback>(
547
614
  python_done_callback),
548
615
  node_callback = std::forward<NodeCallback>(node_callback),
549
- deferred = std::move(deferred),
616
+ js_deferred = std::move(js_deferred),
550
617
  // NOTE: need to keep a reference to `py_future` so that it
551
618
  // doesn't get destroyed before completing!
552
619
  py_future = new py::object(py_future)](py::object) mutable {
553
- py::object py_exception = py_future->attr("exception")();
554
- if (!py_exception.is_none()) {
555
- py::str py_exception_string = py_exception_str(py_exception);
556
- std::string exception = py_exception_string;
557
-
620
+ // Need to check `cancelled()` _first_ because
621
+ // `exception()` will raise if it has been cancelled.
622
+ if (py_future->attr("cancelled")().cast<bool>()) {
558
623
  adaptor->ScheduleCallbackOnNodeEventLoop(
559
- [deferred = std::move(deferred),
560
- exception = std::move(exception)](Napi::Env env) {
561
- deferred->Reject(
562
- Napi::Error::New(env, exception).Value());
624
+ [js_deferred = std::move(js_deferred)](Napi::Env env) {
625
+ js_deferred->Reject(
626
+ Napi::Error::New(env, "Cancelled").Value());
563
627
  });
564
628
  } else {
565
- // TODO: try / catch around `python_done_callback`.
566
- py::object py_result = py_future->attr("result")();
567
- auto result = python_done_callback(std::move(py_result));
568
-
569
- adaptor->ScheduleCallbackOnNodeEventLoop(
570
- [node_callback =
571
- std::forward<NodeCallback>(node_callback),
572
- deferred = std::move(deferred),
573
- result = std::move(result)](Napi::Env env) mutable {
574
- // TODO: try / catch around `node_callback`.
575
- auto js_result = node_callback(env, std::move(result));
576
- deferred->Resolve(js_result);
577
- });
629
+ py::object py_exception = py_future->attr("exception")();
630
+ if (!py_exception.is_none()) {
631
+ std::string exception = py_exception_str(py_exception);
632
+ adaptor->ScheduleCallbackOnNodeEventLoop(
633
+ [js_deferred = std::move(js_deferred),
634
+ exception = std::move(exception)](Napi::Env env) {
635
+ js_deferred->Reject(
636
+ Napi::Error::New(env, exception).Value());
637
+ });
638
+ } else {
639
+ // TODO: try / catch around `python_done_callback`.
640
+ py::object py_result = py_future->attr("result")();
641
+ auto result = python_done_callback(std::move(py_result));
642
+
643
+ adaptor->ScheduleCallbackOnNodeEventLoop(
644
+ [node_callback =
645
+ std::forward<NodeCallback>(node_callback),
646
+ js_deferred = std::move(js_deferred),
647
+ result = std::move(result)](Napi::Env env) mutable {
648
+ // TODO: try / catch around `node_callback`.
649
+ auto js_result = node_callback(
650
+ env,
651
+ std::move(result));
652
+ js_deferred->Resolve(js_result);
653
+ });
654
+ }
578
655
  }
579
656
  delete py_future;
580
657
  },
@@ -582,14 +659,26 @@ Napi::Promise NodePromiseFromPythonFuture(
582
659
  } catch (pybind11::error_already_set& e) {
583
660
  std::string exception = py_exception_str(e.value());
584
661
  adaptor->ScheduleCallbackOnNodeEventLoop(
585
- [deferred = std::move(deferred),
662
+ [js_deferred = std::move(js_deferred),
586
663
  exception = std::move(exception)](Napi::Env env) mutable {
587
- deferred->Reject(Napi::Error::New(env, exception).Value());
664
+ js_deferred->Reject(Napi::Error::New(env, exception).Value());
588
665
  });
589
666
  }
590
667
  });
591
668
 
592
- return promise;
669
+ // Allow Node to exit after we've resolved or rejected the promise.
670
+ auto js_resolve_reject = Napi::Function::New(
671
+ env,
672
+ [](const Napi::CallbackInfo& info) {
673
+ adaptor->AllowNodeToExit(info.Env());
674
+ });
675
+
676
+ js_promise
677
+ .Get("then")
678
+ .As<Napi::Function>()
679
+ .Call(js_promise, {js_resolve_reject, js_resolve_reject});
680
+
681
+ return js_promise;
593
682
  }
594
683
 
595
684
 
@@ -685,7 +774,7 @@ Napi::Promise NodePromiseFromPythonTaskWithContext(
685
774
  env,
686
775
  std::move(name),
687
776
  std::make_tuple(
688
- std::string("reboot.nodejs.python"),
777
+ std::string("rebootdev.nodejs.python"),
689
778
  std::string("create_task_with_context"),
690
779
  py_context),
691
780
  [js_context, // Ensures `py_context` remains valid.
@@ -899,7 +988,7 @@ void ImportPy(const Napi::CallbackInfo& info) {
899
988
 
900
989
  RunCallbackOnPythonEventLoop(
901
990
  [&module, &base64_encoded_rbt_py]() {
902
- py::module::import("reboot.nodejs.python")
991
+ py::module::import("rebootdev.nodejs.python")
903
992
  .attr("import_py")(module, base64_encoded_rbt_py);
904
993
  });
905
994
  }
@@ -937,7 +1026,7 @@ static const napi_type_tag reboot_aio_applications_Application = MakeTypeTag(
937
1026
 
938
1027
 
939
1028
  static const napi_type_tag reboot_aio_external_ExternalContext = MakeTypeTag(
940
- "reboot.aio.external",
1029
+ "rebootdev.aio.external",
941
1030
  "ExternalContext");
942
1031
 
943
1032
 
@@ -1047,20 +1136,20 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
1047
1136
  Napi::Object make_js_context(
1048
1137
  Napi::Env& env,
1049
1138
  py::object* py_context,
1050
- py::object* py_aborted,
1139
+ py::object* py_cancelled,
1051
1140
  const std::string& kind) {
1052
1141
  Napi::External<py::object> js_external_context =
1053
1142
  make_napi_external(env, py_context);
1054
1143
 
1055
- Napi::Promise js_aborted = NodePromiseFromPythonFuture(
1144
+ Napi::Promise js_cancelled = NodePromiseFromPythonFuture(
1056
1145
  env,
1057
- [py_aborted]() {
1058
- // After returning we won't need `py_aborted` anymore, so we
1146
+ [py_cancelled]() {
1147
+ // After returning we won't need `py_cancelled` anymore, so we
1059
1148
  // make a copy on the stack so we can delete our copy on the
1060
1149
  // heap.
1061
- py::object py_aborted_on_stack = *py_aborted;
1062
- delete py_aborted;
1063
- return py_aborted_on_stack;
1150
+ py::object py_cancelled_on_stack = *py_cancelled;
1151
+ delete py_cancelled;
1152
+ return py_cancelled_on_stack;
1064
1153
  },
1065
1154
  [](py::object py_result) {
1066
1155
  // TODO: check that `py_result` is `None`.
@@ -1077,7 +1166,7 @@ Napi::Object make_js_context(
1077
1166
  ->Call(
1078
1167
  {js_external_context,
1079
1168
  Napi::String::New(env, kind),
1080
- js_aborted})
1169
+ js_cancelled})
1081
1170
  .As<Napi::Object>();
1082
1171
  }
1083
1172
 
@@ -1104,7 +1193,7 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1104
1193
  [](py::object self,
1105
1194
  const std::string& method_name,
1106
1195
  py::object py_reader_context,
1107
- py::object py_aborted,
1196
+ py::object py_cancelled,
1108
1197
  std::optional<std::string> bytes_state,
1109
1198
  std::optional<std::string> bytes_request) {
1110
1199
  NapiSafeObjectReference* js_authorizer_reference =
@@ -1114,14 +1203,17 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1114
1203
  return PythonFutureFromNodePromise(
1115
1204
  [js_authorizer_reference,
1116
1205
  py_reader_context = new py::object(py_reader_context),
1117
- py_aborted = new py::object(py_aborted),
1206
+ py_cancelled = new py::object(py_cancelled),
1118
1207
  method_name,
1119
1208
  bytes_state = std::move(bytes_state),
1120
1209
  bytes_request = std::move(bytes_request)](Napi::Env env) {
1121
1210
  std::vector<Napi::Value> js_args;
1122
1211
 
1123
- Napi::Object js_context =
1124
- make_js_context(env, py_reader_context, py_aborted, "reader");
1212
+ Napi::Object js_context = make_js_context(
1213
+ env,
1214
+ py_reader_context,
1215
+ py_cancelled,
1216
+ "reader");
1125
1217
 
1126
1218
  Napi::Object js_authorizer = js_authorizer_reference->Value(env);
1127
1219
 
@@ -1153,14 +1245,14 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
1153
1245
  py::name("_authorize"),
1154
1246
  py::arg("method_name"),
1155
1247
  py::arg("context"),
1156
- py::arg("aborted"),
1248
+ py::arg("cancelled"),
1157
1249
  py::arg("state"),
1158
1250
  py::arg("request"),
1159
1251
  py::is_method(py::none()));
1160
1252
 
1161
1253
  // Now define our subclass.
1162
1254
  py::object py_parent_class =
1163
- py::module::import("reboot.nodejs.python")
1255
+ py::module::import("rebootdev.nodejs.python")
1164
1256
  .attr("NodeAdaptorAuthorizer");
1165
1257
 
1166
1258
  py::object py_parent_metaclass = py::reinterpret_borrow<py::object>(
@@ -1323,7 +1415,7 @@ py::object make_py_user_servicer(
1323
1415
  const std::string& kind,
1324
1416
  const std::string& method,
1325
1417
  py::object py_context,
1326
- py::object py_aborted,
1418
+ py::object py_cancelled,
1327
1419
  const std::string& json_state,
1328
1420
  const std::string& json_request) {
1329
1421
  return PythonFutureFromNodePromise(
@@ -1331,13 +1423,13 @@ py::object make_py_user_servicer(
1331
1423
  kind,
1332
1424
  method,
1333
1425
  py_context = new py::object(py_context),
1334
- py_aborted = new py::object(py_aborted),
1426
+ py_cancelled = new py::object(py_cancelled),
1335
1427
  json_state,
1336
1428
  json_request](Napi::Env env) {
1337
1429
  std::vector<Napi::Value> js_args;
1338
1430
 
1339
1431
  Napi::Object js_context =
1340
- make_js_context(env, py_context, py_aborted, kind);
1432
+ make_js_context(env, py_context, py_cancelled, kind);
1341
1433
 
1342
1434
  js_args.push_back(js_context);
1343
1435
 
@@ -1469,7 +1561,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1469
1561
  attributes["_verify_token"] = py::cpp_function(
1470
1562
  [](py::object self,
1471
1563
  py::object py_reader_context,
1472
- py::object py_aborted,
1564
+ py::object py_cancelled,
1473
1565
  py::object py_token) {
1474
1566
  NapiSafeObjectReference* js_token_verifier_reference =
1475
1567
  self.attr("_js_token_verifier")
@@ -1485,12 +1577,12 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1485
1577
 
1486
1578
  return PythonFutureFromNodePromise(
1487
1579
  [js_token_verifier_reference,
1488
- // We allocate 'py_aborted' and 'py_reader_context' with 'new
1580
+ // We allocate 'py_cancelled' and 'py_reader_context' with 'new
1489
1581
  // py::object' on the Python thread, ensuring they remain valid
1490
1582
  // when passed into Node. Cleanup is handled later on the Python
1491
1583
  // thread, see 'make_js_context'.
1492
1584
  py_reader_context = new py::object(py_reader_context),
1493
- py_aborted = new py::object(py_aborted),
1585
+ py_cancelled = new py::object(py_cancelled),
1494
1586
  token = std::move(token)](Napi::Env env) {
1495
1587
  std::vector<Napi::Value> js_args;
1496
1588
 
@@ -1498,7 +1590,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1498
1590
  make_js_context(
1499
1591
  env,
1500
1592
  py_reader_context,
1501
- py_aborted,
1593
+ py_cancelled,
1502
1594
  "reader"));
1503
1595
 
1504
1596
  if (token.has_value()) {
@@ -1533,12 +1625,12 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1533
1625
  },
1534
1626
  py::name("_verify_token"),
1535
1627
  py::arg("context"),
1536
- py::arg("aborted"),
1628
+ py::arg("cancelled"),
1537
1629
  py::arg("py_token"),
1538
1630
  py::is_method(py::none()));
1539
1631
 
1540
1632
  py::object py_parent_class =
1541
- py::module::import("reboot.nodejs.python")
1633
+ py::module::import("rebootdev.nodejs.python")
1542
1634
  .attr("NodeAdaptorTokenVerifier");
1543
1635
 
1544
1636
  py::object py_parent_metaclass = py::reinterpret_borrow<py::object>(
@@ -1634,14 +1726,9 @@ Napi::Value Reboot_start(const Napi::CallbackInfo& info) {
1634
1726
 
1635
1727
  py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
1636
1728
 
1637
- // Call Ref() on our NAPI thread safe function so that it can't be cleaned up
1638
- // until Unref() is called in `Reboot_stop()`.
1639
- // See:
1640
- // (check_line_length skip)
1641
- // https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
1642
- // Note that Ref() and Unref() will set and unset an "is_referenced" boolean
1643
- // flag, rather than incrementing and decrementing a reference count.
1644
- adaptor->thread_safe_function.Ref(info.Env());
1729
+ // Want to keep Node from exiting until we've called
1730
+ // `Reboot.stop()`.
1731
+ adaptor->KeepNodeFromExiting(info.Env());
1645
1732
 
1646
1733
  return NodePromiseFromPythonTask(
1647
1734
  info.Env(),
@@ -1673,18 +1760,11 @@ Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
1673
1760
  return py_reboot->attr("stop")();
1674
1761
  });
1675
1762
 
1676
- // Call Unref() on our NAPI thread safe function so that the
1677
- // object can be destroyed and the event loop can exit.
1678
- // See:
1679
- // (check_line_length skip)
1680
- // https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
1681
- // Note that Ref() and Unref() will set and unset an
1682
- // "is_referenced" boolean flag, rather than incrementing
1683
- // and decrementing a reference count.
1763
+ // Allow Node to exit after we've resolved or rejected the promise.
1684
1764
  auto js_resolve_reject = Napi::Function::New(
1685
1765
  info.Env(),
1686
1766
  [](const Napi::CallbackInfo& info) {
1687
- adaptor->thread_safe_function.Unref(info.Env());
1767
+ adaptor->AllowNodeToExit(info.Env());
1688
1768
  });
1689
1769
 
1690
1770
  js_promise
@@ -1855,7 +1935,7 @@ Napi::Value Task_await(const Napi::CallbackInfo& info) {
1855
1935
 
1856
1936
  return NodePromiseFromPythonTaskWithContext(
1857
1937
  info.Env(),
1858
- "reboot.nodejs.python.task_await(\""
1938
+ "rebootdev.nodejs.python.task_await(\""
1859
1939
  + state_name + "\", \""
1860
1940
  + method + "\", ...) in nodejs",
1861
1941
  js_external_context,
@@ -1865,7 +1945,7 @@ Napi::Value Task_await(const Napi::CallbackInfo& info) {
1865
1945
  js_external_context, // Ensures `py_context` remains valid.
1866
1946
  py_context,
1867
1947
  json_task_id]() {
1868
- return py::module::import("reboot.nodejs.python")
1948
+ return py::module::import("rebootdev.nodejs.python")
1869
1949
  .attr("task_await")(
1870
1950
  py_context,
1871
1951
  py::module::import(rbt_module.c_str())
@@ -1920,7 +2000,7 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
1920
2000
  &idempotency_required,
1921
2001
  &idempotency_required_reason]() {
1922
2002
  py::object py_external = py::module::import(
1923
- "reboot.aio.external");
2003
+ "rebootdev.aio.external");
1924
2004
 
1925
2005
  auto convert_str =
1926
2006
  [](const std::optional<std::string>& optional) -> py::object {
@@ -2041,8 +2121,7 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2041
2121
 
2042
2122
  return NodePromiseFromPythonCallback(
2043
2123
  info.Env(),
2044
- [js_from_native_external,
2045
- name = std::move(name),
2124
+ [name = std::move(name),
2046
2125
  bearer_token = std::move(bearer_token),
2047
2126
  // Ensures `py_channel_manager`
2048
2127
  // remains valid. Need a copy
@@ -2058,7 +2137,7 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2058
2137
 
2059
2138
  return new py::object(
2060
2139
  py::module::import(
2061
- "reboot.aio.external")
2140
+ "rebootdev.aio.external")
2062
2141
  .attr("ExternalContext")(
2063
2142
  "name"_a = py::str(name),
2064
2143
  "channel_manager"_a =
@@ -2066,8 +2145,13 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2066
2145
  "bearer_token"_a =
2067
2146
  py_bearer_token));
2068
2147
  },
2069
- [js_from_native_external = std::move(
2070
- js_from_native_external)](
2148
+ [
2149
+ // Need a copy, because we will
2150
+ // create the 'ExternalContext' more
2151
+ // than once, which means we wiil
2152
+ // call this function more than once
2153
+ // as well.
2154
+ js_from_native_external](
2071
2155
  Napi::Env env,
2072
2156
  py::object* py_context) {
2073
2157
  // (check_line_length skip)
@@ -2187,15 +2271,9 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
2187
2271
 
2188
2272
 
2189
2273
  Napi::Value Application_run(const Napi::CallbackInfo& info) {
2190
- // Call Ref() on our NAPI thread safe function so that it can't be
2191
- // cleaned up and thus we won't exit.
2192
- //
2193
- // See:
2194
- // (check_line_length skip)
2195
- // https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
2196
- // Note that Ref() and Unref() will set and unset an "is_referenced" boolean
2197
- // flag, rather than incrementing and decrementing a reference count.
2198
- adaptor->thread_safe_function.Ref(info.Env());
2274
+ // Keep Node from exiting while we're running `Application.run` so
2275
+ // that we can get calls from Python.
2276
+ adaptor->KeepNodeFromExiting(info.Env());
2199
2277
 
2200
2278
  auto js_external_application = NapiSafeReference(
2201
2279
  info[0].As<Napi::External<py::object>>());
@@ -2206,7 +2284,7 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
2206
2284
  Napi::Promise js_promise = NodePromiseFromPythonTask(
2207
2285
  info.Env(),
2208
2286
  "Application.run() in nodejs",
2209
- {"reboot.nodejs.python", "create_task"},
2287
+ {"rebootdev.nodejs.python", "create_task"},
2210
2288
  [js_external_application, // Ensures `py_application` remains valid.
2211
2289
  py_application]() {
2212
2290
  return py_application->attr("run")();
@@ -2221,9 +2299,8 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
2221
2299
  {Napi::Function::New(
2222
2300
  info.Env(),
2223
2301
  [](const Napi::CallbackInfo& info) {
2224
- // When `Application.run` exits, we unref our
2225
- // thread_safe_function to cause the runtime to exit.
2226
- adaptor->thread_safe_function.Unref(info.Env());
2302
+ // Allow Node to exit after `Application.run` returns.
2303
+ adaptor->AllowNodeToExit(info.Env());
2227
2304
  }),
2228
2305
  Napi::Function::New(
2229
2306
  info.Env(),
@@ -2236,9 +2313,8 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
2236
2313
  .As<Napi::Object>()
2237
2314
  .Set("exitCode", 1);
2238
2315
 
2239
- // When `Application.run` exits, we unref our
2240
- // thread_safe_function to cause the runtime to exit.
2241
- adaptor->thread_safe_function.Unref(info.Env());
2316
+ // Allow Node to exit after `Application.run` returns.
2317
+ adaptor->AllowNodeToExit(info.Env());
2242
2318
  })});
2243
2319
 
2244
2320
  return js_promise;
@@ -2429,7 +2505,7 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
2429
2505
  py_alias = py::cast(*alias);
2430
2506
  }
2431
2507
  py::object py_idempotency_module = py::module::import(
2432
- "reboot.aio.idempotency");
2508
+ "rebootdev.aio.idempotency");
2433
2509
  py::object py_idempotency = py::object(
2434
2510
  py_idempotency_module.attr("Idempotency")(
2435
2511
  "key"_a = py_key,
@@ -2508,7 +2584,7 @@ Napi::Value retry_reactively_until(const Napi::CallbackInfo& info) {
2508
2584
  });
2509
2585
  });
2510
2586
 
2511
- return py::module::import("reboot.aio.contexts")
2587
+ return py::module::import("rebootdev.aio.contexts")
2512
2588
  .attr("retry_reactively_until")(py_context, py_condition);
2513
2589
  });
2514
2590
  }
package/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const REBOOT_VERSION = "0.25.5";
1
+ export declare const REBOOT_VERSION = "0.25.6";
package/version.js CHANGED
@@ -1 +1 @@
1
- export const REBOOT_VERSION = "0.25.5";
1
+ export const REBOOT_VERSION = "0.25.6";