@reboot-dev/reboot 0.30.0 → 0.31.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 +7 -0
- package/index.js +58 -12
- package/package.json +2 -2
- package/reboot_native.cc +6 -2
- package/version.d.ts +1 -1
- package/version.js +1 -1
package/index.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare class ExternalContext {
|
|
|
44
44
|
export declare function getContext(): Context;
|
|
45
45
|
export declare function isWithinUntil(): boolean;
|
|
46
46
|
export declare function isWithinLoop(): boolean;
|
|
47
|
+
export declare function getLoopIteration(): number;
|
|
47
48
|
export declare function runWithContext<T>(context: Context, callback: () => T): Promise<T>;
|
|
48
49
|
export declare class Context {
|
|
49
50
|
#private;
|
|
@@ -323,6 +324,12 @@ export declare function untilPerWorkflow<T>(idempotencyAlias: string, context: W
|
|
|
323
324
|
parse?: undefined;
|
|
324
325
|
validate: (result: T) => boolean;
|
|
325
326
|
}): Promise<Exclude<T, boolean>>;
|
|
327
|
+
export declare function untilChanges<T>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<T>, options: {
|
|
328
|
+
equals: (previous: T, current: T) => boolean;
|
|
329
|
+
stringify?: (result: T) => string;
|
|
330
|
+
parse?: (value: string) => T;
|
|
331
|
+
validate?: (result: T) => boolean;
|
|
332
|
+
}): Promise<T>;
|
|
326
333
|
export declare const zod: {
|
|
327
334
|
tasks: {
|
|
328
335
|
TaskId: z.ZodCustom<protobuf_es.PartialMessage<tasks_pb.TaskId>, protobuf_es.PartialMessage<tasks_pb.TaskId>>;
|
package/index.js
CHANGED
|
@@ -9,7 +9,7 @@ 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, _ReaderContext_kind, _WriterContext_kind, _TransactionContext_kind, _WorkflowContext_kind, _Application_servicers, _Application_tokenVerifier, _Application_express, _Application_http, _Application_servers, _Application_createExternalContext, _Application_external;
|
|
12
|
+
var _Reboot_external, _ExternalContext_external, _a, _Context_external, _Context_isInternalConstructing, _Context_stateId, _ReaderContext_kind, _WriterContext_kind, _TransactionContext_kind, _WorkflowContext_kind, _Application_servicers, _Application_tokenVerifier, _Application_express, _Application_http, _Application_servers, _Application_createExternalContext, _Application_external;
|
|
13
13
|
import { auth_pb, errors_pb, protobuf_es, tasks_pb, } from "@reboot-dev/reboot-api";
|
|
14
14
|
import { strict as assert } from "assert";
|
|
15
15
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -148,6 +148,13 @@ export function isWithinLoop() {
|
|
|
148
148
|
}
|
|
149
149
|
return store.withinLoop;
|
|
150
150
|
}
|
|
151
|
+
export function getLoopIteration() {
|
|
152
|
+
const store = contextStorage.getStore();
|
|
153
|
+
if (!store) {
|
|
154
|
+
throw new Error("`getLoopIteration` may only be called within a `Servicer` method.");
|
|
155
|
+
}
|
|
156
|
+
return store.loopIteration;
|
|
157
|
+
}
|
|
151
158
|
export async function runWithContext(context, callback) {
|
|
152
159
|
return await contextStorage.run({
|
|
153
160
|
context,
|
|
@@ -158,6 +165,7 @@ export async function runWithContext(context, callback) {
|
|
|
158
165
|
export class Context {
|
|
159
166
|
constructor(external, cancelled) {
|
|
160
167
|
_Context_external.set(this, void 0);
|
|
168
|
+
_Context_stateId.set(this, void 0);
|
|
161
169
|
if (!__classPrivateFieldGet(_a, _a, "f", _Context_isInternalConstructing)) {
|
|
162
170
|
throw new TypeError("Context is not publicly constructable");
|
|
163
171
|
}
|
|
@@ -192,7 +200,10 @@ export class Context {
|
|
|
192
200
|
return null;
|
|
193
201
|
}
|
|
194
202
|
get stateId() {
|
|
195
|
-
|
|
203
|
+
if (__classPrivateFieldGet(this, _Context_stateId, "f") === undefined) {
|
|
204
|
+
__classPrivateFieldSet(this, _Context_stateId, reboot_native.Context_stateId(__classPrivateFieldGet(this, _Context_external, "f")), "f");
|
|
205
|
+
}
|
|
206
|
+
return __classPrivateFieldGet(this, _Context_stateId, "f");
|
|
196
207
|
}
|
|
197
208
|
get callerBearerToken() {
|
|
198
209
|
return reboot_native.Context_callerBearerToken(__classPrivateFieldGet(this, _Context_external, "f"));
|
|
@@ -207,7 +218,7 @@ export class Context {
|
|
|
207
218
|
return reboot_native.Context_generateIdempotentStateId(__classPrivateFieldGet(this, _Context_external, "f"), stateType, serviceName, method, idempotency);
|
|
208
219
|
}
|
|
209
220
|
}
|
|
210
|
-
_a = Context, _Context_external = new WeakMap();
|
|
221
|
+
_a = Context, _Context_external = new WeakMap(), _Context_stateId = new WeakMap();
|
|
211
222
|
_Context_isInternalConstructing = { value: false };
|
|
212
223
|
export class ReaderContext extends Context {
|
|
213
224
|
constructor(external, cancelled) {
|
|
@@ -278,6 +289,7 @@ export class WorkflowContext extends Context {
|
|
|
278
289
|
if (iteration === null) {
|
|
279
290
|
return;
|
|
280
291
|
}
|
|
292
|
+
store.loopIteration = iteration;
|
|
281
293
|
yield iteration;
|
|
282
294
|
if (ms > 0) {
|
|
283
295
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -289,6 +301,7 @@ export class WorkflowContext extends Context {
|
|
|
289
301
|
await iterate(false);
|
|
290
302
|
}
|
|
291
303
|
store.withinLoop = false;
|
|
304
|
+
delete store.loopIteration;
|
|
292
305
|
}
|
|
293
306
|
}
|
|
294
307
|
}
|
|
@@ -678,12 +691,9 @@ export async function retryReactivelyUntil(context, condition) {
|
|
|
678
691
|
}
|
|
679
692
|
}
|
|
680
693
|
catch (e) {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
else {
|
|
685
|
-
throw new Error(`${e}`);
|
|
686
|
-
}
|
|
694
|
+
const error = ensureError(e);
|
|
695
|
+
console.error(error);
|
|
696
|
+
throw error;
|
|
687
697
|
}
|
|
688
698
|
finally {
|
|
689
699
|
store.withinUntil = false;
|
|
@@ -737,10 +747,15 @@ async function memoize(idempotencyAliasOrTuple, context, callable, { stringify =
|
|
|
737
747
|
return "";
|
|
738
748
|
}
|
|
739
749
|
catch (e) {
|
|
740
|
-
|
|
741
|
-
|
|
750
|
+
const error = ensureError(e);
|
|
751
|
+
// We handle printing the exception for `until` in
|
|
752
|
+
// `retryReactivelyUntil`.
|
|
753
|
+
if (!until) {
|
|
754
|
+
console.error(error);
|
|
755
|
+
}
|
|
756
|
+
throw error;
|
|
742
757
|
}
|
|
743
|
-
}), atMostOnce);
|
|
758
|
+
}), atMostOnce, until);
|
|
744
759
|
// NOTE: we parse and validate `value` every time (even the first
|
|
745
760
|
// time, even though we validate above). These semantics are the
|
|
746
761
|
// same as Python (although Python uses the `type` keyword argument
|
|
@@ -806,6 +821,37 @@ export async function until(idempotencyAliasOrTuple, context, callable, options
|
|
|
806
821
|
export async function untilPerWorkflow(idempotencyAlias, context, callable, options = { validate: undefined }) {
|
|
807
822
|
return await until([idempotencyAlias, PER_WORKFLOW], context, callable, options);
|
|
808
823
|
}
|
|
824
|
+
export async function untilChanges(idempotencyAlias, context, callable, options) {
|
|
825
|
+
const iteration = getLoopIteration();
|
|
826
|
+
if (iteration === undefined) {
|
|
827
|
+
throw new Error("Waiting for changes must be done _within_ a control loop");
|
|
828
|
+
}
|
|
829
|
+
if (options.equals === undefined) {
|
|
830
|
+
// TODO: don't make `equals` required, instead use one of the
|
|
831
|
+
// various libraries that does deep equality.
|
|
832
|
+
throw new Error("Missing 'equals' option");
|
|
833
|
+
}
|
|
834
|
+
const { equals, ...optionsWithoutEquals } = options;
|
|
835
|
+
let previous = null;
|
|
836
|
+
if (iteration > 0) {
|
|
837
|
+
// Get the previous memoized result!
|
|
838
|
+
previous = (await untilPerWorkflow(`${idempotencyAlias} #${iteration - 1}`, context, (async () => {
|
|
839
|
+
throw new Error(`Missing memoized value for '${idempotencyAlias}'`);
|
|
840
|
+
}), optionsWithoutEquals));
|
|
841
|
+
}
|
|
842
|
+
// Wait until previous result does not equal current result.
|
|
843
|
+
return (await untilPerWorkflow(`${idempotencyAlias} #${iteration}`, context, (async () => {
|
|
844
|
+
const current = await callable();
|
|
845
|
+
if (iteration === 0) {
|
|
846
|
+
return current;
|
|
847
|
+
}
|
|
848
|
+
assert(previous !== null);
|
|
849
|
+
if (!equals(previous, current)) {
|
|
850
|
+
return current;
|
|
851
|
+
}
|
|
852
|
+
return false;
|
|
853
|
+
}), optionsWithoutEquals));
|
|
854
|
+
}
|
|
809
855
|
const launchSubprocessConsensus = (base64_args) => {
|
|
810
856
|
// Create a child process via `fork` (which does not mean `fork` as
|
|
811
857
|
// in POSIX fork/clone) that uses the exact same module that was
|
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.31.0",
|
|
7
7
|
"chalk": "^4.1.2",
|
|
8
8
|
"node-addon-api": "^7.0.0",
|
|
9
9
|
"node-gyp": ">=10.2.0",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"name": "@reboot-dev/reboot",
|
|
21
|
-
"version": "0.
|
|
21
|
+
"version": "0.31.0",
|
|
22
22
|
"description": "npm package for Reboot",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"preinstall": "node preinstall.cjs",
|
package/reboot_native.cc
CHANGED
|
@@ -2672,6 +2672,8 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
|
|
|
2672
2672
|
|
|
2673
2673
|
bool at_most_once = info[3].As<Napi::Boolean>();
|
|
2674
2674
|
|
|
2675
|
+
bool until = info[4].As<Napi::Boolean>();
|
|
2676
|
+
|
|
2675
2677
|
return NodePromiseFromPythonTaskWithContext(
|
|
2676
2678
|
info.Env(),
|
|
2677
2679
|
"memoize(...) in nodejs",
|
|
@@ -2681,7 +2683,8 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
|
|
|
2681
2683
|
alias = std::move(alias),
|
|
2682
2684
|
how = std::move(how),
|
|
2683
2685
|
js_callable = std::move(js_callable),
|
|
2684
|
-
at_most_once
|
|
2686
|
+
at_most_once,
|
|
2687
|
+
until]() {
|
|
2685
2688
|
py::object py_callable = py::cpp_function(
|
|
2686
2689
|
[js_callable = std::move(js_callable)]() mutable {
|
|
2687
2690
|
return PythonFutureFromNodePromise(
|
|
@@ -2704,7 +2707,8 @@ Napi::Value memoize(const Napi::CallbackInfo& info) {
|
|
|
2704
2707
|
py_context,
|
|
2705
2708
|
py_callable,
|
|
2706
2709
|
"type_t"_a = py::eval("str"),
|
|
2707
|
-
"at_most_once"_a = at_most_once
|
|
2710
|
+
"at_most_once"_a = at_most_once,
|
|
2711
|
+
"until"_a = until);
|
|
2708
2712
|
},
|
|
2709
2713
|
[](py::object py_json) {
|
|
2710
2714
|
return py_json.cast<std::string>();
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const REBOOT_VERSION = "0.
|
|
1
|
+
export declare const REBOOT_VERSION = "0.31.0";
|
package/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const REBOOT_VERSION = "0.
|
|
1
|
+
export const REBOOT_VERSION = "0.31.0";
|