@reboot-dev/reboot 0.33.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +58 -94
- package/index.js +105 -98
- package/package.json +5 -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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { errors_pb, IdempotencyOptions, protobuf_es, tasks_pb } from "@reboot-dev/reboot-api";
|
|
2
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
3
|
import { z } from "zod/v4";
|
|
3
4
|
import * as reboot_native from "./reboot_native.cjs";
|
|
4
5
|
import { Application as ExpressApplication, NextFunction as ExpressNextFunction, Request as ExpressRequest, Response as ExpressResponse } from "express";
|
|
@@ -52,30 +53,43 @@ export declare function runWithContext<T>(context: Context, callback: () => T):
|
|
|
52
53
|
export declare class Context {
|
|
53
54
|
#private;
|
|
54
55
|
readonly cancelled: Promise<void>;
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
readonly stateId: string;
|
|
57
|
+
readonly method: string;
|
|
58
|
+
readonly stateTypeName: string;
|
|
59
|
+
readonly callerBearerToken: string | null;
|
|
60
|
+
readonly cookie: string | null;
|
|
61
|
+
readonly appInternal: boolean;
|
|
62
|
+
readonly auth: Auth | null;
|
|
63
|
+
constructor({ external, stateId, method, stateTypeName, callerBearerToken, cookie, appInternal, auth, cancelled, }: {
|
|
64
|
+
external: any;
|
|
65
|
+
stateId: string;
|
|
66
|
+
method: string;
|
|
67
|
+
stateTypeName: string;
|
|
68
|
+
callerBearerToken: string | null;
|
|
69
|
+
cookie: string | null;
|
|
70
|
+
appInternal: boolean;
|
|
71
|
+
auth: Auth | null;
|
|
72
|
+
cancelled: Promise<void>;
|
|
73
|
+
});
|
|
74
|
+
static fromNativeExternal({ kind, ...options }: {
|
|
75
|
+
[x: string]: any;
|
|
76
|
+
kind: any;
|
|
77
|
+
}): ReaderContext | WriterContext | TransactionContext | WorkflowContext;
|
|
57
78
|
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
79
|
generateIdempotentStateId(stateType: string, serviceName: string, method: string, idempotency: IdempotencyOptions): Promise<any>;
|
|
66
80
|
}
|
|
67
81
|
export declare class ReaderContext extends Context {
|
|
68
82
|
#private;
|
|
69
|
-
constructor(
|
|
83
|
+
constructor(options: any);
|
|
70
84
|
}
|
|
71
85
|
export declare class WriterContext extends Context {
|
|
72
86
|
#private;
|
|
73
|
-
constructor(
|
|
87
|
+
constructor(options: any);
|
|
74
88
|
set sync(sync: boolean);
|
|
75
89
|
}
|
|
76
90
|
export declare class TransactionContext extends Context {
|
|
77
91
|
#private;
|
|
78
|
-
constructor(
|
|
92
|
+
constructor(options: any);
|
|
79
93
|
}
|
|
80
94
|
export type Interval = {
|
|
81
95
|
ms?: number;
|
|
@@ -86,7 +100,7 @@ export type Interval = {
|
|
|
86
100
|
};
|
|
87
101
|
export declare class WorkflowContext extends Context {
|
|
88
102
|
#private;
|
|
89
|
-
constructor(
|
|
103
|
+
constructor(options: any);
|
|
90
104
|
loop(alias: string, { interval }?: {
|
|
91
105
|
interval?: Interval;
|
|
92
106
|
}): AsyncGenerator<number, void, unknown>;
|
|
@@ -140,7 +154,7 @@ export declare abstract class TokenVerifier {
|
|
|
140
154
|
* `Auth` information if the token is valid, null otherwise.
|
|
141
155
|
*/
|
|
142
156
|
abstract verifyToken(context: ReaderContext, token?: string): Promise<Auth | null>;
|
|
143
|
-
_verifyToken(
|
|
157
|
+
_verifyToken(external: any, cancelled: Promise<void>, bytesCall: Uint8Array): Promise<Uint8Array | null>;
|
|
144
158
|
}
|
|
145
159
|
export type AuthorizerDecision = errors_pb.Unauthenticated | errors_pb.PermissionDenied | errors_pb.Ok;
|
|
146
160
|
/**
|
|
@@ -164,7 +178,7 @@ export declare abstract class Authorizer<StateType, RequestTypes> {
|
|
|
164
178
|
* `errors_pb.PermissionDenied()` otherwise.
|
|
165
179
|
*/
|
|
166
180
|
abstract authorize(methodName: string, context: ReaderContext, state?: StateType, request?: RequestTypes): Promise<AuthorizerDecision>;
|
|
167
|
-
_authorize?: (
|
|
181
|
+
_authorize?: (external: any, cancelled: Promise<void>, bytesCall: Uint8Array) => Promise<Uint8Array>;
|
|
168
182
|
}
|
|
169
183
|
export type AuthorizerCallable<StateType, RequestType> = (args: {
|
|
170
184
|
context: ReaderContext;
|
|
@@ -232,7 +246,7 @@ export declare namespace Application {
|
|
|
232
246
|
}
|
|
233
247
|
}
|
|
234
248
|
export declare function retryReactivelyUntil(context: WorkflowContext, condition: () => Promise<boolean>): Promise<void>;
|
|
235
|
-
export declare function retryReactivelyUntil<T>(context: WorkflowContext, condition: () => Promise<false |
|
|
249
|
+
export declare function retryReactivelyUntil<T>(context: WorkflowContext, condition: () => Promise<false | T>): Promise<T>;
|
|
236
250
|
export declare const ALWAYS: "ALWAYS";
|
|
237
251
|
export declare const PER_WORKFLOW: "PER_WORKFLOW";
|
|
238
252
|
export declare const PER_ITERATION: "PER_ITERATION";
|
|
@@ -242,99 +256,49 @@ export type AtMostLeastOnceTupleType = [
|
|
|
242
256
|
"PER_WORKFLOW" | "PER_ITERATION"
|
|
243
257
|
];
|
|
244
258
|
export declare function atMostOnce(idempotencyAliasOrTuple: string | AtMostLeastOnceTupleType, context: WorkflowContext, callable: () => Promise<void>, options?: {
|
|
245
|
-
|
|
246
|
-
parse?: undefined;
|
|
247
|
-
validate?: undefined;
|
|
259
|
+
schema?: undefined;
|
|
248
260
|
}): Promise<void>;
|
|
249
|
-
export declare function atMostOnce<
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
validate?: undefined;
|
|
253
|
-
} | {
|
|
254
|
-
stringify?: (result: T) => string;
|
|
255
|
-
parse?: undefined;
|
|
256
|
-
validate: (result: T) => boolean;
|
|
257
|
-
}): Promise<T>;
|
|
261
|
+
export declare function atMostOnce<Schema extends StandardSchemaV1>(idempotencyAliasOrTuple: string | AtMostLeastOnceTupleType, context: WorkflowContext, callable: () => Promise<StandardSchemaV1.InferInput<Schema>>, options: {
|
|
262
|
+
schema: Schema;
|
|
263
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
258
264
|
export declare function atMostOncePerWorkflow(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<void>, options?: {
|
|
259
|
-
|
|
260
|
-
parse?: undefined;
|
|
261
|
-
validate?: undefined;
|
|
265
|
+
schema?: undefined;
|
|
262
266
|
}): Promise<void>;
|
|
263
|
-
export declare function atMostOncePerWorkflow<
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
validate?: undefined;
|
|
267
|
-
} | {
|
|
268
|
-
stringify?: (result: T) => string;
|
|
269
|
-
parse?: undefined;
|
|
270
|
-
validate: (result: T) => boolean;
|
|
271
|
-
}): Promise<T>;
|
|
267
|
+
export declare function atMostOncePerWorkflow<Schema extends StandardSchemaV1>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<StandardSchemaV1.InferInput<Schema>>, options: {
|
|
268
|
+
schema: Schema;
|
|
269
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
272
270
|
export declare function atLeastOnce(idempotencyAliasOrTuple: string | AtMostLeastOnceTupleType, context: WorkflowContext, callable: () => Promise<void>, options?: {
|
|
273
|
-
|
|
274
|
-
parse?: undefined;
|
|
275
|
-
validate?: undefined;
|
|
271
|
+
schema?: undefined;
|
|
276
272
|
}): Promise<void>;
|
|
277
|
-
export declare function atLeastOnce<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
validate?: undefined;
|
|
281
|
-
} | {
|
|
282
|
-
stringify?: (result: T) => string;
|
|
283
|
-
parse?: undefined;
|
|
284
|
-
validate: (result: T) => boolean;
|
|
285
|
-
}): Promise<T>;
|
|
273
|
+
export declare function atLeastOnce<Schema extends StandardSchemaV1>(idempotencyAliasOrTuple: string | AtMostLeastOnceTupleType, context: WorkflowContext, callable: () => Promise<StandardSchemaV1.InferInput<Schema>>, options: {
|
|
274
|
+
schema: Schema;
|
|
275
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
286
276
|
export declare function atLeastOncePerWorkflow(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<void>, options?: {
|
|
287
|
-
|
|
288
|
-
parse?: undefined;
|
|
289
|
-
validate?: undefined;
|
|
277
|
+
schema?: undefined;
|
|
290
278
|
}): Promise<void>;
|
|
291
|
-
export declare function atLeastOncePerWorkflow<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
validate?: undefined;
|
|
295
|
-
} | {
|
|
296
|
-
stringify?: (result: T) => string;
|
|
297
|
-
parse?: undefined;
|
|
298
|
-
validate: (result: T) => boolean;
|
|
299
|
-
}): Promise<T>;
|
|
279
|
+
export declare function atLeastOncePerWorkflow<Schema extends StandardSchemaV1>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<StandardSchemaV1.InferInput<Schema>>, options: {
|
|
280
|
+
schema: Schema;
|
|
281
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
300
282
|
export type UntilTupleType = [
|
|
301
283
|
string,
|
|
302
284
|
"ALWAYS" | "PER_WORKFLOW" | "PER_ITERATION"
|
|
303
285
|
];
|
|
304
286
|
export declare function until(idempotencyAliasOrTuple: string | UntilTupleType, context: WorkflowContext, callable: () => Promise<boolean>, options?: {
|
|
305
|
-
|
|
306
|
-
parse?: undefined;
|
|
307
|
-
validate?: undefined;
|
|
287
|
+
schema?: undefined;
|
|
308
288
|
}): Promise<void>;
|
|
309
|
-
export declare function until<
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
validate?: undefined;
|
|
313
|
-
} | {
|
|
314
|
-
stringify?: (result: T) => string;
|
|
315
|
-
parse?: undefined;
|
|
316
|
-
validate: (result: T) => boolean;
|
|
317
|
-
}): Promise<Exclude<T, boolean>>;
|
|
289
|
+
export declare function until<Schema extends StandardSchemaV1>(idempotencyAliasOrTuple: string | UntilTupleType, context: WorkflowContext, callable: () => Promise<false | StandardSchemaV1.InferInput<Schema>>, options: {
|
|
290
|
+
schema: Schema;
|
|
291
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
318
292
|
export declare function untilPerWorkflow(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<boolean>, options?: {
|
|
319
|
-
|
|
320
|
-
parse?: undefined;
|
|
321
|
-
validate?: undefined;
|
|
293
|
+
schema?: undefined;
|
|
322
294
|
}): Promise<void>;
|
|
323
|
-
export declare function untilPerWorkflow<
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
validate: (result: T) => boolean;
|
|
331
|
-
}): Promise<Exclude<T, boolean>>;
|
|
332
|
-
export declare function untilChanges<T>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<T>, options: {
|
|
333
|
-
equals: (previous: T, current: T) => boolean;
|
|
334
|
-
stringify?: (result: T) => string;
|
|
335
|
-
parse?: (value: string) => T;
|
|
336
|
-
validate?: (result: T) => boolean;
|
|
337
|
-
}): Promise<T>;
|
|
295
|
+
export declare function untilPerWorkflow<Schema extends StandardSchemaV1>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<false | StandardSchemaV1.InferInput<Schema>>, options: {
|
|
296
|
+
schema: Schema;
|
|
297
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
298
|
+
export declare function untilChanges<Schema extends StandardSchemaV1>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<StandardSchemaV1.InferInput<Schema>>, { equals, schema, }: {
|
|
299
|
+
equals: (previous: StandardSchemaV1.InferOutput<Schema>, current: StandardSchemaV1.InferOutput<Schema>) => boolean;
|
|
300
|
+
schema: Schema;
|
|
301
|
+
}): Promise<StandardSchemaV1.InferOutput<Schema>>;
|
|
338
302
|
export declare const zod: {
|
|
339
303
|
tasks: {
|
|
340
304
|
TaskId: z.ZodCustom<protobuf_es.PartialMessage<tasks_pb.TaskId>, protobuf_es.PartialMessage<tasks_pb.TaskId>>;
|
package/index.js
CHANGED
|
@@ -9,12 +9,12 @@ 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, protobuf_es,
|
|
14
|
-
import { strict as assert } from "assert";
|
|
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 { assert, auth_pb, errors_pb, nodejs_pb, parse, protobuf_es, stringify, tasks_pb, toCamelCase, } from "@reboot-dev/reboot-api";
|
|
15
14
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
15
|
import { fork } from "node:child_process";
|
|
17
16
|
import { createRequire } from "node:module";
|
|
17
|
+
import toobusy from "toobusy-js";
|
|
18
18
|
import { z } from "zod/v4";
|
|
19
19
|
import * as reboot_native from "./reboot_native.cjs";
|
|
20
20
|
import { ensureError } from "./utils/errors.js";
|
|
@@ -168,80 +168,52 @@ export async function runWithContext(context, callback) {
|
|
|
168
168
|
}, callback);
|
|
169
169
|
}
|
|
170
170
|
export class Context {
|
|
171
|
-
constructor(external, cancelled) {
|
|
171
|
+
constructor({ external, stateId, method, stateTypeName, callerBearerToken, cookie, appInternal, auth, cancelled, }) {
|
|
172
172
|
_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
173
|
if (!__classPrivateFieldGet(_a, _a, "f", _Context_isInternalConstructing)) {
|
|
177
174
|
throw new TypeError("Context is not publicly constructable");
|
|
178
175
|
}
|
|
179
176
|
__classPrivateFieldSet(_a, _a, false, "f", _Context_isInternalConstructing);
|
|
180
177
|
__classPrivateFieldSet(this, _Context_external, external, "f");
|
|
178
|
+
this.stateId = stateId;
|
|
179
|
+
// Since we are passing a 'method' from Python, it will contain the
|
|
180
|
+
// Python-style method name, so we have to convert it there.
|
|
181
|
+
this.method = toCamelCase(method);
|
|
182
|
+
this.stateTypeName = stateTypeName;
|
|
183
|
+
this.callerBearerToken = callerBearerToken;
|
|
184
|
+
this.cookie = cookie;
|
|
185
|
+
this.appInternal = appInternal;
|
|
186
|
+
this.auth = auth;
|
|
181
187
|
this.cancelled = cancelled;
|
|
182
188
|
}
|
|
183
|
-
static fromNativeExternal(
|
|
189
|
+
static fromNativeExternal({ kind, ...options }) {
|
|
184
190
|
__classPrivateFieldSet(_a, _a, true, "f", _Context_isInternalConstructing);
|
|
185
191
|
if (kind === "reader") {
|
|
186
|
-
return new ReaderContext(
|
|
192
|
+
return new ReaderContext(options);
|
|
187
193
|
}
|
|
188
194
|
else if (kind === "writer") {
|
|
189
|
-
return new WriterContext(
|
|
195
|
+
return new WriterContext(options);
|
|
190
196
|
}
|
|
191
197
|
else if (kind === "transaction") {
|
|
192
|
-
return new TransactionContext(
|
|
198
|
+
return new TransactionContext(options);
|
|
193
199
|
}
|
|
194
200
|
else if (kind === "workflow") {
|
|
195
|
-
return new WorkflowContext(
|
|
201
|
+
return new WorkflowContext(options);
|
|
196
202
|
}
|
|
197
203
|
throw new Error("Unknown method kind");
|
|
198
204
|
}
|
|
199
205
|
get __external() {
|
|
200
206
|
return __classPrivateFieldGet(this, _Context_external, "f");
|
|
201
207
|
}
|
|
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
208
|
async generateIdempotentStateId(stateType, serviceName, method, idempotency) {
|
|
237
209
|
return reboot_native.Context_generateIdempotentStateId(__classPrivateFieldGet(this, _Context_external, "f"), stateType, serviceName, method, idempotency);
|
|
238
210
|
}
|
|
239
211
|
}
|
|
240
|
-
_a = Context, _Context_external = new WeakMap()
|
|
212
|
+
_a = Context, _Context_external = new WeakMap();
|
|
241
213
|
_Context_isInternalConstructing = { value: false };
|
|
242
214
|
export class ReaderContext extends Context {
|
|
243
|
-
constructor(
|
|
244
|
-
super(
|
|
215
|
+
constructor(options) {
|
|
216
|
+
super(options);
|
|
245
217
|
// Property helps `tsc` not treat a `ReaderContext` as any other
|
|
246
218
|
// context type that structurally looks equivalent.
|
|
247
219
|
_ReaderContext_kind.set(this, "reader");
|
|
@@ -249,8 +221,8 @@ export class ReaderContext extends Context {
|
|
|
249
221
|
}
|
|
250
222
|
_ReaderContext_kind = new WeakMap();
|
|
251
223
|
export class WriterContext extends Context {
|
|
252
|
-
constructor(
|
|
253
|
-
super(
|
|
224
|
+
constructor(options) {
|
|
225
|
+
super(options);
|
|
254
226
|
// Property helps `tsc` not treat a `WriterContext` as any other
|
|
255
227
|
// context type that structurally looks equivalent.
|
|
256
228
|
_WriterContext_kind.set(this, "writer");
|
|
@@ -268,13 +240,14 @@ export class WriterContext extends Context {
|
|
|
268
240
|
// 3. The writer is _not_ within a transaction.
|
|
269
241
|
// 4. The writer is _not_ running as a task.
|
|
270
242
|
set sync(sync) {
|
|
243
|
+
// TODO: optimize this so that we don't block the event loop.
|
|
271
244
|
reboot_native.WriterContext_set_sync(this.__external, sync);
|
|
272
245
|
}
|
|
273
246
|
}
|
|
274
247
|
_WriterContext_kind = new WeakMap();
|
|
275
248
|
export class TransactionContext extends Context {
|
|
276
|
-
constructor(
|
|
277
|
-
super(
|
|
249
|
+
constructor(options) {
|
|
250
|
+
super(options);
|
|
278
251
|
// Property helps `tsc` not treat a `TransactionContext` as any other
|
|
279
252
|
// context type that structurally looks equivalent.
|
|
280
253
|
_TransactionContext_kind.set(this, "transaction");
|
|
@@ -282,8 +255,8 @@ export class TransactionContext extends Context {
|
|
|
282
255
|
}
|
|
283
256
|
_TransactionContext_kind = new WeakMap();
|
|
284
257
|
export class WorkflowContext extends Context {
|
|
285
|
-
constructor(
|
|
286
|
-
super(
|
|
258
|
+
constructor(options) {
|
|
259
|
+
super(options);
|
|
287
260
|
// Property helps `tsc` not treat a `WorkflowContext` as any other
|
|
288
261
|
// context type that structurally looks equivalent.
|
|
289
262
|
_WorkflowContext_kind.set(this, "workflow");
|
|
@@ -398,8 +371,21 @@ export class Auth {
|
|
|
398
371
|
* Bearer` token when passed and optionally extract token metadata.
|
|
399
372
|
*/
|
|
400
373
|
export class TokenVerifier {
|
|
401
|
-
async _verifyToken(
|
|
402
|
-
const
|
|
374
|
+
async _verifyToken(external, cancelled, bytesCall) {
|
|
375
|
+
const call = nodejs_pb.VerifyTokenCall.fromBinary(bytesCall);
|
|
376
|
+
const context = Context.fromNativeExternal({
|
|
377
|
+
external,
|
|
378
|
+
kind: "reader",
|
|
379
|
+
stateId: call.context.stateId,
|
|
380
|
+
method: call.context.method,
|
|
381
|
+
stateTypeName: call.context.stateTypeName,
|
|
382
|
+
callerBearerToken: call.context.callerBearerToken,
|
|
383
|
+
cookie: call.context.cookie,
|
|
384
|
+
appInternal: call.context.appInternal,
|
|
385
|
+
auth: null,
|
|
386
|
+
cancelled,
|
|
387
|
+
});
|
|
388
|
+
const auth = await this.verifyToken(context, call.token);
|
|
403
389
|
if (!auth) {
|
|
404
390
|
return null;
|
|
405
391
|
}
|
|
@@ -617,6 +603,14 @@ export class Application {
|
|
|
617
603
|
return __classPrivateFieldGet(this, _Application_http, "f");
|
|
618
604
|
}
|
|
619
605
|
async run() {
|
|
606
|
+
if (process.env.REBOOT_ENABLE_EVENT_LOOP_LAG_MONITORING === "true") {
|
|
607
|
+
toobusy.onLag((lag) => {
|
|
608
|
+
console.log(`Node.js event loop lag detected! Latency: ${lag}ms.
|
|
609
|
+
This may indicate a blocking operation on the main thread
|
|
610
|
+
(e.g., CPU-intensive task). If you are not running such tasks,
|
|
611
|
+
please report this issue to the maintainers.`);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
620
614
|
return await reboot_native.Application_run(__classPrivateFieldGet(this, _Application_external, "f"));
|
|
621
615
|
}
|
|
622
616
|
get __external() {
|
|
@@ -731,9 +725,7 @@ export async function retryReactivelyUntil(context, condition) {
|
|
|
731
725
|
export const ALWAYS = "ALWAYS";
|
|
732
726
|
export const PER_WORKFLOW = "PER_WORKFLOW";
|
|
733
727
|
export const PER_ITERATION = "PER_ITERATION";
|
|
734
|
-
async function memoize(idempotencyAliasOrTuple, context, callable, {
|
|
735
|
-
assert(stringify !== undefined);
|
|
736
|
-
assert(parse !== undefined);
|
|
728
|
+
async function memoize(idempotencyAliasOrTuple, context, callable, { schema, atMostOnce, until = false, }) {
|
|
737
729
|
assert(atMostOnce !== undefined);
|
|
738
730
|
if (!(typeof idempotencyAliasOrTuple === "string" ||
|
|
739
731
|
(Array.isArray(idempotencyAliasOrTuple) &&
|
|
@@ -749,27 +741,38 @@ async function memoize(idempotencyAliasOrTuple, context, callable, { stringify =
|
|
|
749
741
|
try {
|
|
750
742
|
const t = await callable();
|
|
751
743
|
if (t !== undefined) {
|
|
752
|
-
if
|
|
753
|
-
|
|
754
|
-
throw new Error(`
|
|
744
|
+
// Fail early if the developer forgot to pass `schema`.
|
|
745
|
+
if (schema === undefined) {
|
|
746
|
+
throw new Error(`Expecting 'schema' as you are returning a value from the function passed to '${atMostOnce ? "atMostOnce" : until ? "until" : "atLeastOnce"}'`);
|
|
747
|
+
}
|
|
748
|
+
let validate = schema["~standard"].validate(t);
|
|
749
|
+
if (validate instanceof Promise) {
|
|
750
|
+
validate = await validate;
|
|
755
751
|
}
|
|
756
|
-
|
|
757
|
-
|
|
752
|
+
// If the `issues` field exists, the validation failed.
|
|
753
|
+
if (validate.issues) {
|
|
754
|
+
throw new Error(`Failed to validate result of function passed to '${atMostOnce ? "atMostOnce" : until ? "until" : "atLeastOnce"}': ${JSON.stringify(validate.issues, null, 2)}`);
|
|
758
755
|
}
|
|
756
|
+
// We use `stringify` from `@reboot-dev/reboot-api` because
|
|
757
|
+
// it can handle `BigInt` and `Uint8Array` which are common
|
|
758
|
+
// types from protobuf.
|
|
759
|
+
//
|
|
759
760
|
// NOTE: to differentiate `callable` returning `void` (or
|
|
760
|
-
// explicitly `undefined`) from `stringify` returning
|
|
761
|
-
// string we use `{ value:
|
|
762
|
-
const result = { value:
|
|
763
|
-
return
|
|
761
|
+
// explicitly `undefined`) from `stringify` returning
|
|
762
|
+
// an empty string we use `{ value: t }`.
|
|
763
|
+
const result = { value: t };
|
|
764
|
+
return stringify(result);
|
|
764
765
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
766
|
+
else {
|
|
767
|
+
// Fail early if the developer thinks that they have some
|
|
768
|
+
// value that they want to validate but we got `undefined`.
|
|
769
|
+
if (schema !== undefined) {
|
|
770
|
+
throw new Error(`Not expecting 'schema' as you are returning 'void' (or explicitly 'undefined') from the function passed to '${atMostOnce ? "atMostOnce" : until ? "until" : "atLeastOnce"}' ; did you mean to return a value (or if you want to explicitly return the absence of a value use 'null')`);
|
|
771
|
+
}
|
|
772
|
+
// NOTE: using the empty string to represent a `callable`
|
|
773
|
+
// returning `void` (or explicitly `undefined`).
|
|
774
|
+
return "";
|
|
769
775
|
}
|
|
770
|
-
// NOTE: using the empty string to represent a `callable`
|
|
771
|
-
// returning `void` (or explicitly `undefined`).
|
|
772
|
-
return "";
|
|
773
776
|
}
|
|
774
777
|
catch (e) {
|
|
775
778
|
const error = ensureError(e);
|
|
@@ -787,22 +790,27 @@ async function memoize(idempotencyAliasOrTuple, context, callable, { stringify =
|
|
|
787
790
|
// instead of the `parse` and `validate` properties we use here).
|
|
788
791
|
assert(result !== undefined);
|
|
789
792
|
if (result !== "") {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
793
|
+
// We use `parse` from `@reboot-dev/reboot-api` because it can
|
|
794
|
+
// handle `BigInt` and `Uint8Array` which are common types from
|
|
795
|
+
// protobuf.
|
|
796
|
+
const { value } = parse(result);
|
|
797
|
+
if (schema === undefined) {
|
|
798
|
+
throw new Error(`Expecting 'schema' as we have already memoized a result for the function passed to '${atMostOnce ? "atMostOnce" : until ? "until" : "atLeastOnce"}' has the code been updated to remove a previously existing 'schema'`);
|
|
799
|
+
}
|
|
800
|
+
let validate = schema["~standard"].validate(value);
|
|
801
|
+
if (validate instanceof Promise) {
|
|
802
|
+
validate = await validate;
|
|
803
|
+
}
|
|
804
|
+
// If the `issues` field exists, the validation failed.
|
|
805
|
+
if (validate.issues) {
|
|
806
|
+
throw new Error(`Failed to validate result of function passed to '${atMostOnce ? "atMostOnce" : until ? "until" : "atLeastOnce"}': ${JSON.stringify(validate.issues, null, 2)}`);
|
|
799
807
|
}
|
|
800
|
-
return
|
|
808
|
+
return validate.value;
|
|
801
809
|
}
|
|
802
810
|
// Otherwise `callable` must have returned void (or explicitly
|
|
803
811
|
// `undefined`), fall through.
|
|
804
812
|
}
|
|
805
|
-
export async function atMostOnce(idempotencyAliasOrTuple, context, callable, options = {
|
|
813
|
+
export async function atMostOnce(idempotencyAliasOrTuple, context, callable, options = {}) {
|
|
806
814
|
try {
|
|
807
815
|
return await memoize(idempotencyAliasOrTuple, context, callable, {
|
|
808
816
|
...options,
|
|
@@ -816,19 +824,19 @@ export async function atMostOnce(idempotencyAliasOrTuple, context, callable, opt
|
|
|
816
824
|
throw e;
|
|
817
825
|
}
|
|
818
826
|
}
|
|
819
|
-
export async function atMostOncePerWorkflow(idempotencyAlias, context, callable, options = {
|
|
827
|
+
export async function atMostOncePerWorkflow(idempotencyAlias, context, callable, options = {}) {
|
|
820
828
|
return atMostOnce([idempotencyAlias, PER_WORKFLOW], context, callable, options);
|
|
821
829
|
}
|
|
822
|
-
export async function atLeastOnce(idempotencyAliasOrTuple, context, callable, options = {
|
|
830
|
+
export async function atLeastOnce(idempotencyAliasOrTuple, context, callable, options = {}) {
|
|
823
831
|
return memoize(idempotencyAliasOrTuple, context, callable, {
|
|
824
832
|
...options,
|
|
825
833
|
atMostOnce: false,
|
|
826
834
|
});
|
|
827
835
|
}
|
|
828
|
-
export async function atLeastOncePerWorkflow(idempotencyAlias, context, callable, options = {
|
|
836
|
+
export async function atLeastOncePerWorkflow(idempotencyAlias, context, callable, options = {}) {
|
|
829
837
|
return await atLeastOnce([idempotencyAlias, PER_WORKFLOW], context, callable, options);
|
|
830
838
|
}
|
|
831
|
-
export async function until(idempotencyAliasOrTuple, context, callable, options = {
|
|
839
|
+
export async function until(idempotencyAliasOrTuple, context, callable, options = {}) {
|
|
832
840
|
// TODO(benh): figure out how to not use `as` type assertions here
|
|
833
841
|
// to appease the TypeScript compiler which otherwise isn't happy
|
|
834
842
|
// with passing on these types.
|
|
@@ -843,26 +851,25 @@ export async function until(idempotencyAliasOrTuple, context, callable, options
|
|
|
843
851
|
until: true,
|
|
844
852
|
});
|
|
845
853
|
}
|
|
846
|
-
export async function untilPerWorkflow(idempotencyAlias, context, callable, options = {
|
|
854
|
+
export async function untilPerWorkflow(idempotencyAlias, context, callable, options = {}) {
|
|
847
855
|
return await until([idempotencyAlias, PER_WORKFLOW], context, callable, options);
|
|
848
856
|
}
|
|
849
|
-
export async function untilChanges(idempotencyAlias, context, callable,
|
|
857
|
+
export async function untilChanges(idempotencyAlias, context, callable, { equals, schema, }) {
|
|
850
858
|
const iteration = getLoopIteration();
|
|
851
859
|
if (iteration === undefined) {
|
|
852
860
|
throw new Error("Waiting for changes must be done _within_ a control loop");
|
|
853
861
|
}
|
|
854
|
-
if (
|
|
862
|
+
if (equals === undefined) {
|
|
855
863
|
// TODO: don't make `equals` required, instead use one of the
|
|
856
864
|
// various libraries that does deep equality.
|
|
857
865
|
throw new Error("Missing 'equals' option");
|
|
858
866
|
}
|
|
859
|
-
const { equals, ...optionsWithoutEquals } = options;
|
|
860
867
|
let previous = null;
|
|
861
868
|
if (iteration > 0) {
|
|
862
869
|
// Get the previous memoized result!
|
|
863
870
|
previous = (await untilPerWorkflow(`${idempotencyAlias} #${iteration - 1}`, context, (async () => {
|
|
864
871
|
throw new Error(`Missing memoized value for '${idempotencyAlias}'`);
|
|
865
|
-
}),
|
|
872
|
+
}), { schema }));
|
|
866
873
|
}
|
|
867
874
|
// Wait until previous result does not equal current result.
|
|
868
875
|
return (await untilPerWorkflow(`${idempotencyAlias} #${iteration}`, context, (async () => {
|
|
@@ -875,7 +882,7 @@ export async function untilChanges(idempotencyAlias, context, callable, options)
|
|
|
875
882
|
return current;
|
|
876
883
|
}
|
|
877
884
|
return false;
|
|
878
|
-
}),
|
|
885
|
+
}), { schema }));
|
|
879
886
|
}
|
|
880
887
|
const launchSubprocessConsensus = (base64_args) => {
|
|
881
888
|
// Create a child process via `fork` (which does not mean `fork` as
|
|
@@ -897,7 +904,7 @@ const callback = (...args) => {
|
|
|
897
904
|
console.log(new Date(), ...args);
|
|
898
905
|
};
|
|
899
906
|
ensurePythonVenv();
|
|
900
|
-
reboot_native.initialize(callback,
|
|
907
|
+
reboot_native.initialize(callback, launchSubprocessConsensus);
|
|
901
908
|
// TODO: move these into @reboot-dev/reboot-api and also generate them
|
|
902
909
|
// via plugin, perhaps via a new plugin which emits the Zod schemas
|
|
903
910
|
// 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.35.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,13 @@
|
|
|
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",
|
|
19
|
+
"@standard-schema/spec": "1.0.0"
|
|
18
20
|
},
|
|
19
21
|
"type": "module",
|
|
20
22
|
"name": "@reboot-dev/reboot",
|
|
21
|
-
"version": "0.
|
|
23
|
+
"version": "0.35.0",
|
|
22
24
|
"description": "npm package for Reboot",
|
|
23
25
|
"scripts": {
|
|
24
26
|
"preinstall": "node preinstall.cjs",
|