@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 +7 -7
- package/index.js +16 -15
- package/package.json +2 -2
- package/reboot_native.cc +187 -111
- package/version.d.ts +1 -1
- package/version.js +1 -1
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
|
|
47
|
-
constructor(external: any,
|
|
48
|
-
static fromNativeExternal(external: any, kind: string,
|
|
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,
|
|
60
|
+
constructor(external: any, cancelled: any);
|
|
61
61
|
}
|
|
62
62
|
export declare class WriterContext extends Context {
|
|
63
63
|
#private;
|
|
64
|
-
constructor(external: 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,
|
|
69
|
+
constructor(external: any, cancelled: any);
|
|
70
70
|
}
|
|
71
71
|
export declare class WorkflowContext extends Context {
|
|
72
72
|
#private;
|
|
73
|
-
constructor(external: 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,
|
|
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.
|
|
148
|
+
this.cancelled = cancelled;
|
|
149
149
|
}
|
|
150
|
-
static fromNativeExternal(external, kind,
|
|
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,
|
|
153
|
+
return new ReaderContext(external, cancelled);
|
|
154
154
|
}
|
|
155
155
|
else if (kind === "writer") {
|
|
156
|
-
return new WriterContext(external,
|
|
156
|
+
return new WriterContext(external, cancelled);
|
|
157
157
|
}
|
|
158
158
|
else if (kind === "transaction") {
|
|
159
|
-
return new TransactionContext(external,
|
|
159
|
+
return new TransactionContext(external, cancelled);
|
|
160
160
|
}
|
|
161
161
|
else if (kind === "workflow") {
|
|
162
|
-
return new WorkflowContext(external,
|
|
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,
|
|
199
|
-
super(external,
|
|
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,
|
|
208
|
-
super(external,
|
|
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,
|
|
232
|
-
super(external,
|
|
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,
|
|
241
|
-
super(external,
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
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("
|
|
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
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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
|
-
|
|
533
|
+
js_deferred = std::move(js_deferred),
|
|
491
534
|
result = std::move(result)](Napi::Env env) mutable {
|
|
492
535
|
try {
|
|
493
|
-
|
|
536
|
+
js_deferred->Resolve(node_callback(env, std::move(result)));
|
|
494
537
|
} catch (const Napi::Error& e) {
|
|
495
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
554
|
+
[js_deferred = std::move(js_deferred),
|
|
512
555
|
exception = std::move(exception)](Napi::Env env) mutable {
|
|
513
|
-
|
|
556
|
+
js_deferred->Reject(Napi::Error::New(env, exception).Value());
|
|
514
557
|
});
|
|
515
558
|
}
|
|
516
559
|
});
|
|
517
560
|
|
|
518
|
-
|
|
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
|
-
|
|
532
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
[
|
|
560
|
-
|
|
561
|
-
|
|
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
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
[
|
|
662
|
+
[js_deferred = std::move(js_deferred),
|
|
586
663
|
exception = std::move(exception)](Napi::Env env) mutable {
|
|
587
|
-
|
|
664
|
+
js_deferred->Reject(Napi::Error::New(env, exception).Value());
|
|
588
665
|
});
|
|
589
666
|
}
|
|
590
667
|
});
|
|
591
668
|
|
|
592
|
-
|
|
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("
|
|
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("
|
|
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
|
-
"
|
|
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*
|
|
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
|
|
1144
|
+
Napi::Promise js_cancelled = NodePromiseFromPythonFuture(
|
|
1056
1145
|
env,
|
|
1057
|
-
[
|
|
1058
|
-
// After returning we won't need `
|
|
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
|
|
1062
|
-
delete
|
|
1063
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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("
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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("
|
|
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
|
-
//
|
|
1638
|
-
//
|
|
1639
|
-
|
|
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
|
-
//
|
|
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->
|
|
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
|
-
"
|
|
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("
|
|
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
|
-
"
|
|
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
|
-
[
|
|
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
|
-
"
|
|
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
|
-
[
|
|
2070
|
-
|
|
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
|
-
//
|
|
2191
|
-
//
|
|
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
|
-
{"
|
|
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
|
-
//
|
|
2225
|
-
|
|
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
|
-
//
|
|
2240
|
-
|
|
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
|
-
"
|
|
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("
|
|
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.
|
|
1
|
+
export declare const REBOOT_VERSION = "0.25.6";
|
package/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const REBOOT_VERSION = "0.25.
|
|
1
|
+
export const REBOOT_VERSION = "0.25.6";
|