@reboot-dev/reboot 0.33.1 → 0.34.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/index.d.ts +28 -15
- package/index.js +52 -57
- package/package.json +4 -3
- package/reboot_native.cc +233 -326
- package/reboot_native.cjs +0 -8
- package/version.d.ts +1 -1
- package/version.js +1 -1
package/index.d.ts
CHANGED
|
@@ -52,30 +52,43 @@ export declare function runWithContext<T>(context: Context, callback: () => T):
|
|
|
52
52
|
export declare class Context {
|
|
53
53
|
#private;
|
|
54
54
|
readonly cancelled: Promise<void>;
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
readonly stateId: string;
|
|
56
|
+
readonly method: string;
|
|
57
|
+
readonly stateTypeName: string;
|
|
58
|
+
readonly callerBearerToken: string;
|
|
59
|
+
readonly cookie: string;
|
|
60
|
+
readonly appInternal: boolean;
|
|
61
|
+
readonly auth: Auth | null;
|
|
62
|
+
constructor({ external, stateId, method, stateTypeName, callerBearerToken, cookie, appInternal, auth, cancelled, }: {
|
|
63
|
+
external: any;
|
|
64
|
+
stateId: string;
|
|
65
|
+
method: string;
|
|
66
|
+
stateTypeName: string;
|
|
67
|
+
callerBearerToken: string;
|
|
68
|
+
cookie: string;
|
|
69
|
+
appInternal: boolean;
|
|
70
|
+
auth: Auth | null;
|
|
71
|
+
cancelled: Promise<void>;
|
|
72
|
+
});
|
|
73
|
+
static fromNativeExternal({ kind, ...options }: {
|
|
74
|
+
[x: string]: any;
|
|
75
|
+
kind: any;
|
|
76
|
+
}): ReaderContext | WriterContext | TransactionContext | WorkflowContext;
|
|
57
77
|
get __external(): any;
|
|
58
|
-
get auth(): Auth | null;
|
|
59
|
-
get stateId(): string;
|
|
60
|
-
get stateTypeName(): string;
|
|
61
|
-
get method(): string;
|
|
62
|
-
get callerBearerToken(): string | null;
|
|
63
|
-
get cookie(): string;
|
|
64
|
-
get appInternal(): boolean;
|
|
65
78
|
generateIdempotentStateId(stateType: string, serviceName: string, method: string, idempotency: IdempotencyOptions): Promise<any>;
|
|
66
79
|
}
|
|
67
80
|
export declare class ReaderContext extends Context {
|
|
68
81
|
#private;
|
|
69
|
-
constructor(
|
|
82
|
+
constructor(options: any);
|
|
70
83
|
}
|
|
71
84
|
export declare class WriterContext extends Context {
|
|
72
85
|
#private;
|
|
73
|
-
constructor(
|
|
86
|
+
constructor(options: any);
|
|
74
87
|
set sync(sync: boolean);
|
|
75
88
|
}
|
|
76
89
|
export declare class TransactionContext extends Context {
|
|
77
90
|
#private;
|
|
78
|
-
constructor(
|
|
91
|
+
constructor(options: any);
|
|
79
92
|
}
|
|
80
93
|
export type Interval = {
|
|
81
94
|
ms?: number;
|
|
@@ -86,7 +99,7 @@ export type Interval = {
|
|
|
86
99
|
};
|
|
87
100
|
export declare class WorkflowContext extends Context {
|
|
88
101
|
#private;
|
|
89
|
-
constructor(
|
|
102
|
+
constructor(options: any);
|
|
90
103
|
loop(alias: string, { interval }?: {
|
|
91
104
|
interval?: Interval;
|
|
92
105
|
}): AsyncGenerator<number, void, unknown>;
|
|
@@ -140,7 +153,7 @@ export declare abstract class TokenVerifier {
|
|
|
140
153
|
* `Auth` information if the token is valid, null otherwise.
|
|
141
154
|
*/
|
|
142
155
|
abstract verifyToken(context: ReaderContext, token?: string): Promise<Auth | null>;
|
|
143
|
-
_verifyToken(
|
|
156
|
+
_verifyToken(external: any, cancelled: Promise<void>, bytesCall: Uint8Array): Promise<Uint8Array | null>;
|
|
144
157
|
}
|
|
145
158
|
export type AuthorizerDecision = errors_pb.Unauthenticated | errors_pb.PermissionDenied | errors_pb.Ok;
|
|
146
159
|
/**
|
|
@@ -164,7 +177,7 @@ export declare abstract class Authorizer<StateType, RequestTypes> {
|
|
|
164
177
|
* `errors_pb.PermissionDenied()` otherwise.
|
|
165
178
|
*/
|
|
166
179
|
abstract authorize(methodName: string, context: ReaderContext, state?: StateType, request?: RequestTypes): Promise<AuthorizerDecision>;
|
|
167
|
-
_authorize?: (
|
|
180
|
+
_authorize?: (external: any, cancelled: Promise<void>, bytesCall: Uint8Array) => Promise<Uint8Array>;
|
|
168
181
|
}
|
|
169
182
|
export type AuthorizerCallable<StateType, RequestType> = (args: {
|
|
170
183
|
context: ReaderContext;
|
package/index.js
CHANGED
|
@@ -9,12 +9,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _Reboot_external, _ExternalContext_external, _a, _Context_external, _Context_isInternalConstructing,
|
|
13
|
-
import { auth_pb, errors_pb,
|
|
12
|
+
var _Reboot_external, _ExternalContext_external, _a, _Context_external, _Context_isInternalConstructing, _ReaderContext_kind, _WriterContext_kind, _TransactionContext_kind, _WorkflowContext_kind, _Application_servicers, _Application_tokenVerifier, _Application_express, _Application_http, _Application_servers, _Application_createExternalContext, _Application_external;
|
|
13
|
+
import { auth_pb, errors_pb, nodejs_pb, protobuf_es, tasks_pb, toCamelCase, } from "@reboot-dev/reboot-api";
|
|
14
14
|
import { strict as assert } from "assert";
|
|
15
15
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
16
|
import { fork } from "node:child_process";
|
|
17
17
|
import { createRequire } from "node:module";
|
|
18
|
+
import toobusy from "toobusy-js";
|
|
18
19
|
import { z } from "zod/v4";
|
|
19
20
|
import * as reboot_native from "./reboot_native.cjs";
|
|
20
21
|
import { ensureError } from "./utils/errors.js";
|
|
@@ -168,80 +169,52 @@ export async function runWithContext(context, callback) {
|
|
|
168
169
|
}, callback);
|
|
169
170
|
}
|
|
170
171
|
export class Context {
|
|
171
|
-
constructor(external, cancelled) {
|
|
172
|
+
constructor({ external, stateId, method, stateTypeName, callerBearerToken, cookie, appInternal, auth, cancelled, }) {
|
|
172
173
|
_Context_external.set(this, void 0);
|
|
173
|
-
_Context_stateId.set(this, void 0);
|
|
174
|
-
_Context_method.set(this, void 0);
|
|
175
|
-
_Context_stateTypeName.set(this, void 0);
|
|
176
174
|
if (!__classPrivateFieldGet(_a, _a, "f", _Context_isInternalConstructing)) {
|
|
177
175
|
throw new TypeError("Context is not publicly constructable");
|
|
178
176
|
}
|
|
179
177
|
__classPrivateFieldSet(_a, _a, false, "f", _Context_isInternalConstructing);
|
|
180
178
|
__classPrivateFieldSet(this, _Context_external, external, "f");
|
|
179
|
+
this.stateId = stateId;
|
|
180
|
+
// Since we are passing a 'method' from Python, it will contain the
|
|
181
|
+
// Python-style method name, so we have to convert it there.
|
|
182
|
+
this.method = toCamelCase(method);
|
|
183
|
+
this.stateTypeName = stateTypeName;
|
|
184
|
+
this.callerBearerToken = callerBearerToken;
|
|
185
|
+
this.cookie = cookie;
|
|
186
|
+
this.appInternal = appInternal;
|
|
187
|
+
this.auth = auth;
|
|
181
188
|
this.cancelled = cancelled;
|
|
182
189
|
}
|
|
183
|
-
static fromNativeExternal(
|
|
190
|
+
static fromNativeExternal({ kind, ...options }) {
|
|
184
191
|
__classPrivateFieldSet(_a, _a, true, "f", _Context_isInternalConstructing);
|
|
185
192
|
if (kind === "reader") {
|
|
186
|
-
return new ReaderContext(
|
|
193
|
+
return new ReaderContext(options);
|
|
187
194
|
}
|
|
188
195
|
else if (kind === "writer") {
|
|
189
|
-
return new WriterContext(
|
|
196
|
+
return new WriterContext(options);
|
|
190
197
|
}
|
|
191
198
|
else if (kind === "transaction") {
|
|
192
|
-
return new TransactionContext(
|
|
199
|
+
return new TransactionContext(options);
|
|
193
200
|
}
|
|
194
201
|
else if (kind === "workflow") {
|
|
195
|
-
return new WorkflowContext(
|
|
202
|
+
return new WorkflowContext(options);
|
|
196
203
|
}
|
|
197
204
|
throw new Error("Unknown method kind");
|
|
198
205
|
}
|
|
199
206
|
get __external() {
|
|
200
207
|
return __classPrivateFieldGet(this, _Context_external, "f");
|
|
201
208
|
}
|
|
202
|
-
get auth() {
|
|
203
|
-
const authBytes = reboot_native.Context_auth(__classPrivateFieldGet(this, _Context_external, "f"));
|
|
204
|
-
if (authBytes) {
|
|
205
|
-
return Auth.fromProtoBytes(authBytes);
|
|
206
|
-
}
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
get stateId() {
|
|
210
|
-
if (__classPrivateFieldGet(this, _Context_stateId, "f") === undefined) {
|
|
211
|
-
__classPrivateFieldSet(this, _Context_stateId, reboot_native.Context_stateId(__classPrivateFieldGet(this, _Context_external, "f")), "f");
|
|
212
|
-
}
|
|
213
|
-
return __classPrivateFieldGet(this, _Context_stateId, "f");
|
|
214
|
-
}
|
|
215
|
-
get stateTypeName() {
|
|
216
|
-
if (__classPrivateFieldGet(this, _Context_stateTypeName, "f") === undefined) {
|
|
217
|
-
__classPrivateFieldSet(this, _Context_stateTypeName, reboot_native.Context_stateTypeName(__classPrivateFieldGet(this, _Context_external, "f")), "f");
|
|
218
|
-
}
|
|
219
|
-
return __classPrivateFieldGet(this, _Context_stateTypeName, "f");
|
|
220
|
-
}
|
|
221
|
-
get method() {
|
|
222
|
-
if (__classPrivateFieldGet(this, _Context_method, "f") === undefined) {
|
|
223
|
-
__classPrivateFieldSet(this, _Context_method, toCamelCase(reboot_native.Context_method(__classPrivateFieldGet(this, _Context_external, "f"))), "f");
|
|
224
|
-
}
|
|
225
|
-
return __classPrivateFieldGet(this, _Context_method, "f");
|
|
226
|
-
}
|
|
227
|
-
get callerBearerToken() {
|
|
228
|
-
return reboot_native.Context_callerBearerToken(__classPrivateFieldGet(this, _Context_external, "f"));
|
|
229
|
-
}
|
|
230
|
-
get cookie() {
|
|
231
|
-
return reboot_native.Context_cookie(__classPrivateFieldGet(this, _Context_external, "f"));
|
|
232
|
-
}
|
|
233
|
-
get appInternal() {
|
|
234
|
-
return reboot_native.Context_appInternal(__classPrivateFieldGet(this, _Context_external, "f"));
|
|
235
|
-
}
|
|
236
209
|
async generateIdempotentStateId(stateType, serviceName, method, idempotency) {
|
|
237
210
|
return reboot_native.Context_generateIdempotentStateId(__classPrivateFieldGet(this, _Context_external, "f"), stateType, serviceName, method, idempotency);
|
|
238
211
|
}
|
|
239
212
|
}
|
|
240
|
-
_a = Context, _Context_external = new WeakMap()
|
|
213
|
+
_a = Context, _Context_external = new WeakMap();
|
|
241
214
|
_Context_isInternalConstructing = { value: false };
|
|
242
215
|
export class ReaderContext extends Context {
|
|
243
|
-
constructor(
|
|
244
|
-
super(
|
|
216
|
+
constructor(options) {
|
|
217
|
+
super(options);
|
|
245
218
|
// Property helps `tsc` not treat a `ReaderContext` as any other
|
|
246
219
|
// context type that structurally looks equivalent.
|
|
247
220
|
_ReaderContext_kind.set(this, "reader");
|
|
@@ -249,8 +222,8 @@ export class ReaderContext extends Context {
|
|
|
249
222
|
}
|
|
250
223
|
_ReaderContext_kind = new WeakMap();
|
|
251
224
|
export class WriterContext extends Context {
|
|
252
|
-
constructor(
|
|
253
|
-
super(
|
|
225
|
+
constructor(options) {
|
|
226
|
+
super(options);
|
|
254
227
|
// Property helps `tsc` not treat a `WriterContext` as any other
|
|
255
228
|
// context type that structurally looks equivalent.
|
|
256
229
|
_WriterContext_kind.set(this, "writer");
|
|
@@ -268,13 +241,14 @@ export class WriterContext extends Context {
|
|
|
268
241
|
// 3. The writer is _not_ within a transaction.
|
|
269
242
|
// 4. The writer is _not_ running as a task.
|
|
270
243
|
set sync(sync) {
|
|
244
|
+
// TODO: optimize this so that we don't block the event loop.
|
|
271
245
|
reboot_native.WriterContext_set_sync(this.__external, sync);
|
|
272
246
|
}
|
|
273
247
|
}
|
|
274
248
|
_WriterContext_kind = new WeakMap();
|
|
275
249
|
export class TransactionContext extends Context {
|
|
276
|
-
constructor(
|
|
277
|
-
super(
|
|
250
|
+
constructor(options) {
|
|
251
|
+
super(options);
|
|
278
252
|
// Property helps `tsc` not treat a `TransactionContext` as any other
|
|
279
253
|
// context type that structurally looks equivalent.
|
|
280
254
|
_TransactionContext_kind.set(this, "transaction");
|
|
@@ -282,8 +256,8 @@ export class TransactionContext extends Context {
|
|
|
282
256
|
}
|
|
283
257
|
_TransactionContext_kind = new WeakMap();
|
|
284
258
|
export class WorkflowContext extends Context {
|
|
285
|
-
constructor(
|
|
286
|
-
super(
|
|
259
|
+
constructor(options) {
|
|
260
|
+
super(options);
|
|
287
261
|
// Property helps `tsc` not treat a `WorkflowContext` as any other
|
|
288
262
|
// context type that structurally looks equivalent.
|
|
289
263
|
_WorkflowContext_kind.set(this, "workflow");
|
|
@@ -398,8 +372,21 @@ export class Auth {
|
|
|
398
372
|
* Bearer` token when passed and optionally extract token metadata.
|
|
399
373
|
*/
|
|
400
374
|
export class TokenVerifier {
|
|
401
|
-
async _verifyToken(
|
|
402
|
-
const
|
|
375
|
+
async _verifyToken(external, cancelled, bytesCall) {
|
|
376
|
+
const call = nodejs_pb.VerifyTokenCall.fromBinary(bytesCall);
|
|
377
|
+
const context = Context.fromNativeExternal({
|
|
378
|
+
external,
|
|
379
|
+
kind: "reader",
|
|
380
|
+
stateId: call.context.stateId,
|
|
381
|
+
method: call.context.method,
|
|
382
|
+
stateTypeName: call.context.stateTypeName,
|
|
383
|
+
callerBearerToken: call.context.callerBearerToken,
|
|
384
|
+
cookie: call.context.cookie,
|
|
385
|
+
appInternal: call.context.appInternal,
|
|
386
|
+
auth: null,
|
|
387
|
+
cancelled,
|
|
388
|
+
});
|
|
389
|
+
const auth = await this.verifyToken(context, call.token);
|
|
403
390
|
if (!auth) {
|
|
404
391
|
return null;
|
|
405
392
|
}
|
|
@@ -617,6 +604,14 @@ export class Application {
|
|
|
617
604
|
return __classPrivateFieldGet(this, _Application_http, "f");
|
|
618
605
|
}
|
|
619
606
|
async run() {
|
|
607
|
+
if (process.env.REBOOT_ENABLE_EVENT_LOOP_LAG_MONITORING === "true") {
|
|
608
|
+
toobusy.onLag((lag) => {
|
|
609
|
+
console.log(`Node.js event loop lag detected! Latency: ${lag}ms.
|
|
610
|
+
This may indicate a blocking operation on the main thread
|
|
611
|
+
(e.g., CPU-intensive task). If you are not running such tasks,
|
|
612
|
+
please report this issue to the maintainers.`);
|
|
613
|
+
});
|
|
614
|
+
}
|
|
620
615
|
return await reboot_native.Application_run(__classPrivateFieldGet(this, _Application_external, "f"));
|
|
621
616
|
}
|
|
622
617
|
get __external() {
|
|
@@ -897,7 +892,7 @@ const callback = (...args) => {
|
|
|
897
892
|
console.log(new Date(), ...args);
|
|
898
893
|
};
|
|
899
894
|
ensurePythonVenv();
|
|
900
|
-
reboot_native.initialize(callback,
|
|
895
|
+
reboot_native.initialize(callback, launchSubprocessConsensus);
|
|
901
896
|
// TODO: move these into @reboot-dev/reboot-api and also generate them
|
|
902
897
|
// via plugin, perhaps via a new plugin which emits the Zod schemas
|
|
903
898
|
// for all protobuf messages, which might be easier after we've moved
|
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.
|
|
6
|
+
"@reboot-dev/reboot-api": "0.34.0",
|
|
7
7
|
"chalk": "^4.1.2",
|
|
8
8
|
"node-addon-api": "^7.0.0",
|
|
9
9
|
"node-gyp": ">=10.2.0",
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
"express": "^5.1.0",
|
|
15
15
|
"@scarf/scarf": "1.4.0",
|
|
16
16
|
"tsx": "^4.19.2",
|
|
17
|
-
"zod": "^3.25.51"
|
|
17
|
+
"zod": "^3.25.51",
|
|
18
|
+
"toobusy-js": "^0.5.1"
|
|
18
19
|
},
|
|
19
20
|
"type": "module",
|
|
20
21
|
"name": "@reboot-dev/reboot",
|
|
21
|
-
"version": "0.
|
|
22
|
+
"version": "0.34.0",
|
|
22
23
|
"description": "npm package for Reboot",
|
|
23
24
|
"scripts": {
|
|
24
25
|
"preinstall": "node preinstall.cjs",
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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::
|
|
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->
|
|
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->
|
|
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::
|
|
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: */
|
|
391
|
+
/* Context: */ (void*) nullptr,
|
|
273
392
|
// Finalizer:
|
|
274
|
-
[this](Napi::Env env, void*,
|
|
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
|
-
|
|
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::
|
|
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[
|
|
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::
|
|
1251
|
+
Napi::Promise make_js_cancelled(
|
|
1137
1252
|
Napi::Env& env,
|
|
1138
|
-
py::object*
|
|
1139
|
-
|
|
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::
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
1314
|
+
js_args.push_back(js_external_context);
|
|
1315
|
+
|
|
1316
|
+
Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
|
|
1219
1317
|
|
|
1220
|
-
|
|
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(
|
|
1228
|
-
|
|
1229
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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::
|
|
1432
|
-
|
|
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
|
-
|
|
1520
|
+
Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
|
|
1435
1521
|
|
|
1436
|
-
js_args.push_back(
|
|
1437
|
-
|
|
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("
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1668
|
+
bytes_call = std::move(bytes_call)](Napi::Env env) {
|
|
1587
1669
|
std::vector<Napi::Value> js_args;
|
|
1588
1670
|
|
|
1589
|
-
|
|
1590
|
-
|
|
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
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
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("
|
|
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));
|
package/reboot_native.cjs
CHANGED
|
@@ -74,14 +74,6 @@ exports.Reboot_stop = reboot_native.exports.Reboot_stop;
|
|
|
74
74
|
exports.Reboot_up = reboot_native.exports.Reboot_up;
|
|
75
75
|
exports.Reboot_down = reboot_native.exports.Reboot_down;
|
|
76
76
|
exports.Reboot_url = reboot_native.exports.Reboot_url;
|
|
77
|
-
exports.Context_auth = reboot_native.exports.Context_auth;
|
|
78
|
-
exports.Context_stateId = reboot_native.exports.Context_stateId;
|
|
79
|
-
exports.Context_stateTypeName = reboot_native.exports.Context_stateTypeName;
|
|
80
|
-
exports.Context_method = reboot_native.exports.Context_method;
|
|
81
|
-
exports.Context_callerBearerToken =
|
|
82
|
-
reboot_native.exports.Context_callerBearerToken;
|
|
83
|
-
exports.Context_cookie = reboot_native.exports.Context_cookie;
|
|
84
|
-
exports.Context_appInternal = reboot_native.exports.Context_appInternal;
|
|
85
77
|
exports.Context_generateIdempotentStateId =
|
|
86
78
|
reboot_native.exports.Context_generateIdempotentStateId;
|
|
87
79
|
exports.WriterContext_set_sync = reboot_native.exports.WriterContext_set_sync;
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const REBOOT_VERSION = "0.
|
|
1
|
+
export declare const REBOOT_VERSION = "0.34.0";
|
package/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const REBOOT_VERSION = "0.
|
|
1
|
+
export const REBOOT_VERSION = "0.34.0";
|