@hatchet-dev/typescript-sdk 1.20.0 → 1.21.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/package.json +1 -1
- package/v1/client/worker/context.d.ts +2 -0
- package/v1/client/worker/context.js +18 -8
- package/v1/declaration.d.ts +30 -0
- package/v1/declaration.js +139 -12
- package/v1/examples/child_index/workflow.d.ts +21 -0
- package/v1/examples/child_index/workflow.js +155 -0
- package/v1/examples/e2e-worker.js +5 -0
- package/v1/examples/simple/bulk.js +19 -0
- package/v1/parent-run-context-vars.d.ts +1 -3
- package/v1/parent-run-context-vars.js +5 -14
- package/version.d.ts +1 -1
- package/version.js +1 -1
package/package.json
CHANGED
|
@@ -81,8 +81,10 @@ export declare class Context<T, K = {}> {
|
|
|
81
81
|
worker: ContextWorker;
|
|
82
82
|
overridesData: Record<string, any>;
|
|
83
83
|
_logger: Logger;
|
|
84
|
+
/** @deprecated — kept for backward compat; prefer {@link nextChildIndex}. */
|
|
84
85
|
spawnIndex: number;
|
|
85
86
|
streamIndex: number;
|
|
87
|
+
protected nextChildIndex(n?: number): number;
|
|
86
88
|
constructor(action: Action, v1: HatchetClient, worker: InternalWorker);
|
|
87
89
|
get abortController(): AbortController;
|
|
88
90
|
get cancelled(): boolean;
|
|
@@ -30,6 +30,7 @@ const transformer_1 = require("../../conditions/transformer");
|
|
|
30
30
|
const condition_1 = require("../../../protoc/v1/shared/condition");
|
|
31
31
|
const apply_namespace_1 = require("../../../util/apply-namespace");
|
|
32
32
|
const abort_error_1 = require("../../../util/abort-error");
|
|
33
|
+
const parent_run_context_vars_1 = require("../../parent-run-context-vars");
|
|
33
34
|
const crypto_1 = require("crypto");
|
|
34
35
|
const duration_1 = require("../duration");
|
|
35
36
|
const engine_version_1 = require("./engine-version");
|
|
@@ -74,10 +75,19 @@ class ContextWorker {
|
|
|
74
75
|
}
|
|
75
76
|
exports.ContextWorker = ContextWorker;
|
|
76
77
|
class Context {
|
|
78
|
+
nextChildIndex(n = 1) {
|
|
79
|
+
var _a;
|
|
80
|
+
const ctx = parent_run_context_vars_1.parentRunContextManager.getContext();
|
|
81
|
+
const idx = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.childIndex) !== null && _a !== void 0 ? _a : this.spawnIndex;
|
|
82
|
+
parent_run_context_vars_1.parentRunContextManager.incrementChildIndex(n);
|
|
83
|
+
this.spawnIndex = idx + n;
|
|
84
|
+
return idx;
|
|
85
|
+
}
|
|
77
86
|
constructor(action, v1, worker) {
|
|
78
87
|
// @deprecated use ctx.abortController instead
|
|
79
88
|
this.controller = new AbortController();
|
|
80
89
|
this.overridesData = {};
|
|
90
|
+
/** @deprecated — kept for backward compat; prefer {@link nextChildIndex}. */
|
|
81
91
|
this.spawnIndex = 0;
|
|
82
92
|
this.streamIndex = 0;
|
|
83
93
|
try {
|
|
@@ -379,8 +389,8 @@ class Context {
|
|
|
379
389
|
throw new hatchet_error_1.default(`Cannot run with sticky: workflow ${workflowName} is not registered on the worker`);
|
|
380
390
|
}
|
|
381
391
|
const { workflowRunId, taskRunExternalId } = this.action;
|
|
382
|
-
const
|
|
383
|
-
this.
|
|
392
|
+
const childIndex = this.nextChildIndex();
|
|
393
|
+
const finalOpts = Object.assign(Object.assign({}, opts), { parentId: workflowRunId, parentTaskRunExternalId: taskRunExternalId, childIndex, childKey: options === null || options === void 0 ? void 0 : options.key, desiredWorkerId: sticky ? this.worker.id() : undefined, _standaloneTaskName: workflow instanceof declaration_1.TaskWorkflowDeclaration ? workflow._standalone_task_name : undefined });
|
|
384
394
|
return { workflowName, opts: finalOpts };
|
|
385
395
|
}
|
|
386
396
|
spawn(workflow, input, options) {
|
|
@@ -561,12 +571,12 @@ class Context {
|
|
|
561
571
|
// `signal` must never be sent over the wire.
|
|
562
572
|
const optsWithoutSignal = Object.assign({}, opts);
|
|
563
573
|
delete optsWithoutSignal.signal;
|
|
574
|
+
const childIndex = this.nextChildIndex();
|
|
564
575
|
const resp = {
|
|
565
576
|
workflowName: name,
|
|
566
577
|
input,
|
|
567
|
-
options: Object.assign(Object.assign({}, optsWithoutSignal), { parentId: workflowRunId, parentTaskRunExternalId: taskRunExternalId, childIndex
|
|
578
|
+
options: Object.assign(Object.assign({}, optsWithoutSignal), { parentId: workflowRunId, parentTaskRunExternalId: taskRunExternalId, childIndex, desiredWorkerId: sticky ? this.worker.id() : undefined }),
|
|
568
579
|
};
|
|
569
|
-
this.spawnIndex += 1;
|
|
570
580
|
return resp;
|
|
571
581
|
});
|
|
572
582
|
try {
|
|
@@ -614,8 +624,8 @@ class Context {
|
|
|
614
624
|
throw new hatchet_error_1.default(`cannot run with sticky: workflow ${name} is not registered on the worker`);
|
|
615
625
|
}
|
|
616
626
|
try {
|
|
617
|
-
const
|
|
618
|
-
this.
|
|
627
|
+
const childIndex = this.nextChildIndex();
|
|
628
|
+
const resp = yield this.v1.admin.runWorkflow(name, input, Object.assign({ parentId: workflowRunId, parentTaskRunExternalId: taskRunExternalId, childIndex, desiredWorkerId: sticky ? this.worker.id() : undefined }, opts));
|
|
619
629
|
if (workflow instanceof declaration_1.TaskWorkflowDeclaration) {
|
|
620
630
|
resp._standaloneTaskName = workflow._standalone_task_name;
|
|
621
631
|
}
|
|
@@ -805,12 +815,13 @@ class DurableContext extends Context {
|
|
|
805
815
|
workflowName = workflow.name;
|
|
806
816
|
}
|
|
807
817
|
workflowName = (0, apply_namespace_1.applyNamespace)(workflowName, this.v1.config.namespace).toLowerCase();
|
|
818
|
+
const childIndex = this.nextChildIndex();
|
|
808
819
|
const triggerOpts = {
|
|
809
820
|
name: workflowName,
|
|
810
821
|
input: JSON.stringify(input || {}),
|
|
811
822
|
parentId: this.action.workflowRunId,
|
|
812
823
|
parentTaskRunExternalId: this.action.taskRunExternalId,
|
|
813
|
-
childIndex
|
|
824
|
+
childIndex,
|
|
814
825
|
childKey: options === null || options === void 0 ? void 0 : options.key,
|
|
815
826
|
additionalMetadata: (options === null || options === void 0 ? void 0 : options.additionalMetadata)
|
|
816
827
|
? JSON.stringify(options.additionalMetadata)
|
|
@@ -819,7 +830,6 @@ class DurableContext extends Context {
|
|
|
819
830
|
priority: options === null || options === void 0 ? void 0 : options.priority,
|
|
820
831
|
desiredWorkerLabels: {},
|
|
821
832
|
};
|
|
822
|
-
this.spawnIndex += 1;
|
|
823
833
|
return { workflowName, triggerOpts };
|
|
824
834
|
}
|
|
825
835
|
/**
|
package/v1/declaration.d.ts
CHANGED
|
@@ -65,6 +65,10 @@ export type RunOpts = {
|
|
|
65
65
|
comparator?: WorkerLabelComparator;
|
|
66
66
|
}>;
|
|
67
67
|
};
|
|
68
|
+
export type RunManyOpt<I extends InputType = UnknownInputType> = {
|
|
69
|
+
input: I;
|
|
70
|
+
opts?: RunOpts;
|
|
71
|
+
};
|
|
68
72
|
/**
|
|
69
73
|
* Helper type to safely extract output types from task results
|
|
70
74
|
* @hidden
|
|
@@ -263,6 +267,13 @@ export declare class BaseWorkflowDeclaration<I extends InputType = UnknownInputT
|
|
|
263
267
|
*/
|
|
264
268
|
runNoWait(input: I, options?: RunOpts, _standaloneTaskName?: string): Promise<WorkflowRunRef<O>>;
|
|
265
269
|
runNoWait(input: I[], options?: RunOpts, _standaloneTaskName?: string): Promise<WorkflowRunRef<O>[]>;
|
|
270
|
+
/**
|
|
271
|
+
* Synchronously trigger multiple workflow runs without waiting for completion.
|
|
272
|
+
* @param runs A list of run requests containing input and per-run options.
|
|
273
|
+
* @returns WorkflowRunRef values in the same order as input runs.
|
|
274
|
+
* @throws Error if the workflow is not bound to a Hatchet client.
|
|
275
|
+
*/
|
|
276
|
+
runManyNoWait(runs: RunManyOpt<I>[], _standaloneTaskName?: string): Promise<WorkflowRunRef<O>[]>;
|
|
266
277
|
/**
|
|
267
278
|
* @alias run
|
|
268
279
|
* Triggers a workflow run and waits for the result.
|
|
@@ -283,6 +294,12 @@ export declare class BaseWorkflowDeclaration<I extends InputType = UnknownInputT
|
|
|
283
294
|
*/
|
|
284
295
|
run(input: I, options?: RunOpts, _standaloneTaskName?: string): Promise<O>;
|
|
285
296
|
run(input: I[], options?: RunOpts, _standaloneTaskName?: string): Promise<O[]>;
|
|
297
|
+
/**
|
|
298
|
+
* Executes many workflow runs and waits for all results.
|
|
299
|
+
* @param runs A list of run requests containing input and per-run options.
|
|
300
|
+
* @returns Results in the same order as input runs.
|
|
301
|
+
*/
|
|
302
|
+
runMany(runs: RunManyOpt<I>[], _standaloneTaskName?: string): Promise<O[]>;
|
|
286
303
|
/**
|
|
287
304
|
* Schedules a workflow to run at a specific date and time in the future.
|
|
288
305
|
* @param enqueueAt The date when the workflow should be triggered.
|
|
@@ -376,6 +393,7 @@ export declare class BaseWorkflowDeclaration<I extends InputType = UnknownInputT
|
|
|
376
393
|
* - Scheduled execution with `schedule()`
|
|
377
394
|
* - Cron-based recurring execution with `cron()`
|
|
378
395
|
* - Bulk execution by passing an array input to `run()` and `runNoWait()`
|
|
396
|
+
* - Per-run bulk execution options with `runMany()` and `runManyNoWait()`
|
|
379
397
|
*
|
|
380
398
|
* Tasks within workflows can be defined with `workflow.task()` or
|
|
381
399
|
* `workflow.durableTask()` and arranged into complex dependency patterns.
|
|
@@ -484,6 +502,18 @@ export declare class TaskWorkflowDeclaration<I extends InputType = UnknownInputT
|
|
|
484
502
|
*/
|
|
485
503
|
runNoWait(input: I & GlobalInput, options?: RunOpts): Promise<WorkflowRunRef<O & Resolved<GlobalOutput, MiddlewareAfter>>>;
|
|
486
504
|
runNoWait(input: (I & GlobalInput)[], options?: RunOpts): Promise<WorkflowRunRef<O & Resolved<GlobalOutput, MiddlewareAfter>>[]>;
|
|
505
|
+
/**
|
|
506
|
+
* Triggers many task runs and waits for all results.
|
|
507
|
+
* @param runs - The list of inputs and optional per-run options.
|
|
508
|
+
* @returns A promise that resolves with ordered task outputs.
|
|
509
|
+
*/
|
|
510
|
+
runMany(runs: RunManyOpt<I & GlobalInput>[]): Promise<(O & Resolved<GlobalOutput, MiddlewareAfter>)[]>;
|
|
511
|
+
/**
|
|
512
|
+
* Triggers many task runs without waiting for completion.
|
|
513
|
+
* @param runs - The list of inputs and optional per-run options.
|
|
514
|
+
* @returns WorkflowRunRef values for each scheduled run.
|
|
515
|
+
*/
|
|
516
|
+
runManyNoWait(runs: RunManyOpt<I & GlobalInput>[]): Promise<WorkflowRunRef<O & Resolved<GlobalOutput, MiddlewareAfter>>[]>;
|
|
487
517
|
/**
|
|
488
518
|
* Schedules the task to run at a specific date and time.
|
|
489
519
|
* @param enqueueAt - The date when the task should be triggered.
|
package/v1/declaration.js
CHANGED
|
@@ -15,6 +15,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
15
15
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
16
16
|
});
|
|
17
17
|
};
|
|
18
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
19
|
+
var t = {};
|
|
20
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
21
|
+
t[p] = s[p];
|
|
22
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
23
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
24
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
25
|
+
t[p[i]] = s[p[i]];
|
|
26
|
+
}
|
|
27
|
+
return t;
|
|
28
|
+
};
|
|
18
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
30
|
exports.TaskWorkflowDeclaration = exports.WorkflowDeclaration = exports.BaseWorkflowDeclaration = exports.StickyStrategy = exports.Priority = void 0;
|
|
20
31
|
exports.CreateTaskWorkflow = CreateTaskWorkflow;
|
|
@@ -63,9 +74,7 @@ class BaseWorkflowDeclaration {
|
|
|
63
74
|
if (!this.client) {
|
|
64
75
|
throw UNBOUND_ERR;
|
|
65
76
|
}
|
|
66
|
-
// set the parent run context
|
|
67
77
|
const parentRunContext = parent_run_context_vars_1.parentRunContextManager.getContext();
|
|
68
|
-
parent_run_context_vars_1.parentRunContextManager.incrementChildIndex(Array.isArray(input) ? input.length : 1);
|
|
69
78
|
if (!parentRunContext && ((options === null || options === void 0 ? void 0 : options.childKey) || (options === null || options === void 0 ? void 0 : options.sticky))) {
|
|
70
79
|
this.client.admin.logger.warn('ignoring childKey or sticky because run is not being spawned from a parent task');
|
|
71
80
|
}
|
|
@@ -79,20 +88,21 @@ class BaseWorkflowDeclaration {
|
|
|
79
88
|
: undefined,
|
|
80
89
|
warn: (message) => this.client.admin.logger.warn(message),
|
|
81
90
|
});
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
// Snapshot childIndex before incrementing — shared with Context.spawnIndex
|
|
92
|
+
// via parentRunContextManager so both spawning APIs produce unique indices.
|
|
93
|
+
const baseChildIndex = parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.childIndex;
|
|
94
|
+
const inputCount = Array.isArray(input) ? input.length : 1;
|
|
95
|
+
parent_run_context_vars_1.parentRunContextManager.incrementChildIndex(inputCount);
|
|
96
|
+
const runOpts = Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), { parentId: parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.parentId, parentTaskRunExternalId: parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.parentTaskRunExternalId, childIndex: baseChildIndex, sticky: (options === null || options === void 0 ? void 0 : options.sticky) ? parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.desiredWorkerId : undefined, childKey: options === null || options === void 0 ? void 0 : options.childKey });
|
|
84
97
|
if (Array.isArray(input)) {
|
|
85
98
|
let resp = [];
|
|
86
99
|
for (let i = 0; i < input.length; i += 500) {
|
|
87
100
|
const batch = input.slice(i, i + 500);
|
|
88
|
-
const batchResp = yield this.client.admin.runWorkflows(batch.map((inp) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
options: Object.assign(Object.assign({}, runOpts), { childIndex: ((_a = runOpts.childIndex) !== null && _a !== void 0 ? _a : 0) + i }),
|
|
94
|
-
});
|
|
95
|
-
}));
|
|
101
|
+
const batchResp = yield this.client.admin.runWorkflows(batch.map((inp, batchIdx) => ({
|
|
102
|
+
workflowName: this.definition.name,
|
|
103
|
+
input: inp,
|
|
104
|
+
options: Object.assign(Object.assign({}, runOpts), { childIndex: (baseChildIndex !== null && baseChildIndex !== void 0 ? baseChildIndex : 0) + i + batchIdx }),
|
|
105
|
+
})));
|
|
96
106
|
resp = resp.concat(batchResp);
|
|
97
107
|
}
|
|
98
108
|
const res = [];
|
|
@@ -118,6 +128,59 @@ class BaseWorkflowDeclaration {
|
|
|
118
128
|
return res;
|
|
119
129
|
});
|
|
120
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Synchronously trigger multiple workflow runs without waiting for completion.
|
|
133
|
+
* @param runs A list of run requests containing input and per-run options.
|
|
134
|
+
* @returns WorkflowRunRef values in the same order as input runs.
|
|
135
|
+
* @throws Error if the workflow is not bound to a Hatchet client.
|
|
136
|
+
*/
|
|
137
|
+
runManyNoWait(runs, _standaloneTaskName) {
|
|
138
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
if (!this.client) {
|
|
140
|
+
throw UNBOUND_ERR;
|
|
141
|
+
}
|
|
142
|
+
const parentRunContext = parent_run_context_vars_1.parentRunContextManager.getContext();
|
|
143
|
+
const hasChildKeyOrSticky = runs.some((run) => { var _a, _b; return ((_a = run.opts) === null || _a === void 0 ? void 0 : _a.childKey) || ((_b = run.opts) === null || _b === void 0 ? void 0 : _b.sticky); });
|
|
144
|
+
if (!parentRunContext && hasChildKeyOrSticky) {
|
|
145
|
+
this.client.admin.logger.warn('ignoring childKey or sticky because run is not being spawned from a parent task');
|
|
146
|
+
}
|
|
147
|
+
const inheritedSignal = parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.signal;
|
|
148
|
+
(0, abort_error_1.throwIfAborted)(inheritedSignal, {
|
|
149
|
+
isTrigger: true,
|
|
150
|
+
context: (parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.parentTaskRunExternalId)
|
|
151
|
+
? `task run ${parentRunContext.parentTaskRunExternalId}`
|
|
152
|
+
: undefined,
|
|
153
|
+
warn: (message) => this.client.admin.logger.warn(message),
|
|
154
|
+
});
|
|
155
|
+
parent_run_context_vars_1.parentRunContextManager.incrementChildIndex(runs.length);
|
|
156
|
+
const baseOpts = {
|
|
157
|
+
parentId: parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.parentId,
|
|
158
|
+
parentTaskRunExternalId: parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.parentTaskRunExternalId,
|
|
159
|
+
childIndex: parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.childIndex,
|
|
160
|
+
};
|
|
161
|
+
let resp = [];
|
|
162
|
+
for (let i = 0; i < runs.length; i += 500) {
|
|
163
|
+
const batch = runs.slice(i, i + 500);
|
|
164
|
+
const batchResp = yield this.client.admin.runWorkflows(batch.map((run, batchIndex) => {
|
|
165
|
+
var _a, _b, _c;
|
|
166
|
+
const _d = (_a = run.opts) !== null && _a !== void 0 ? _a : {}, { sticky } = _d, restOpts = __rest(_d, ["sticky"]);
|
|
167
|
+
return {
|
|
168
|
+
workflowName: this.definition.name,
|
|
169
|
+
input: run.input,
|
|
170
|
+
options: Object.assign(Object.assign(Object.assign({}, baseOpts), restOpts), { childIndex: ((_b = baseOpts.childIndex) !== null && _b !== void 0 ? _b : 0) + i + batchIndex, desiredWorkerId: sticky ? parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.desiredWorkerId : undefined, childKey: (_c = run.opts) === null || _c === void 0 ? void 0 : _c.childKey }),
|
|
171
|
+
};
|
|
172
|
+
}));
|
|
173
|
+
resp = resp.concat(batchResp);
|
|
174
|
+
}
|
|
175
|
+
resp.forEach((ref) => {
|
|
176
|
+
if (_standaloneTaskName) {
|
|
177
|
+
ref._standaloneTaskName = _standaloneTaskName;
|
|
178
|
+
}
|
|
179
|
+
ref.defaultSignal = inheritedSignal;
|
|
180
|
+
});
|
|
181
|
+
return resp;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
121
184
|
runAndWait(input, options, _standaloneTaskName) {
|
|
122
185
|
return __awaiter(this, void 0, void 0, function* () {
|
|
123
186
|
if (!this.client) {
|
|
@@ -163,6 +226,43 @@ class BaseWorkflowDeclaration {
|
|
|
163
226
|
return res.result();
|
|
164
227
|
});
|
|
165
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Executes many workflow runs and waits for all results.
|
|
231
|
+
* @param runs A list of run requests containing input and per-run options.
|
|
232
|
+
* @returns Results in the same order as input runs.
|
|
233
|
+
*/
|
|
234
|
+
runMany(runs, _standaloneTaskName) {
|
|
235
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
236
|
+
var _a;
|
|
237
|
+
if (!this.client) {
|
|
238
|
+
throw UNBOUND_ERR;
|
|
239
|
+
}
|
|
240
|
+
const durableCtx = (_a = parent_run_context_vars_1.parentRunContextManager.getContext()) === null || _a === void 0 ? void 0 : _a.durableContext;
|
|
241
|
+
if (durableCtx) {
|
|
242
|
+
return durableCtx.spawnChildren(runs.map((run) => ({
|
|
243
|
+
workflow: this,
|
|
244
|
+
input: run.input,
|
|
245
|
+
options: run.opts,
|
|
246
|
+
})));
|
|
247
|
+
}
|
|
248
|
+
const refs = yield this.runManyNoWait(runs, _standaloneTaskName);
|
|
249
|
+
const returnExceptions = runs.some((run) => { var _a; return (_a = run.opts) === null || _a === void 0 ? void 0 : _a.returnExceptions; });
|
|
250
|
+
if (returnExceptions) {
|
|
251
|
+
const settled = yield Promise.allSettled(refs.map((ref) => ref.result()));
|
|
252
|
+
return settled.map((s) => {
|
|
253
|
+
if (s.status === 'fulfilled') {
|
|
254
|
+
return s.value;
|
|
255
|
+
}
|
|
256
|
+
const { reason } = s;
|
|
257
|
+
if (reason instanceof Error) {
|
|
258
|
+
return reason;
|
|
259
|
+
}
|
|
260
|
+
return new Error(Array.isArray(reason) ? reason.join('; ') : String(reason));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return Promise.all(refs.map((ref) => ref.result()));
|
|
264
|
+
});
|
|
265
|
+
}
|
|
166
266
|
/**
|
|
167
267
|
* Schedules a workflow to run at a specific date and time in the future.
|
|
168
268
|
* @param enqueueAt The date when the workflow should be triggered.
|
|
@@ -345,6 +445,7 @@ exports.BaseWorkflowDeclaration = BaseWorkflowDeclaration;
|
|
|
345
445
|
* - Scheduled execution with `schedule()`
|
|
346
446
|
* - Cron-based recurring execution with `cron()`
|
|
347
447
|
* - Bulk execution by passing an array input to `run()` and `runNoWait()`
|
|
448
|
+
* - Per-run bulk execution options with `runMany()` and `runManyNoWait()`
|
|
348
449
|
*
|
|
349
450
|
* Tasks within workflows can be defined with `workflow.task()` or
|
|
350
451
|
* `workflow.durableTask()` and arranged into complex dependency patterns.
|
|
@@ -493,6 +594,32 @@ class TaskWorkflowDeclaration extends BaseWorkflowDeclaration {
|
|
|
493
594
|
: _super.runNoWait.call(this, input, options, this._standalone_task_name);
|
|
494
595
|
});
|
|
495
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Triggers many task runs and waits for all results.
|
|
599
|
+
* @param runs - The list of inputs and optional per-run options.
|
|
600
|
+
* @returns A promise that resolves with ordered task outputs.
|
|
601
|
+
*/
|
|
602
|
+
runMany(runs) {
|
|
603
|
+
const _super = Object.create(null, {
|
|
604
|
+
runMany: { get: () => super.runMany }
|
|
605
|
+
});
|
|
606
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
607
|
+
return _super.runMany.call(this, runs, this._standalone_task_name);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Triggers many task runs without waiting for completion.
|
|
612
|
+
* @param runs - The list of inputs and optional per-run options.
|
|
613
|
+
* @returns WorkflowRunRef values for each scheduled run.
|
|
614
|
+
*/
|
|
615
|
+
runManyNoWait(runs) {
|
|
616
|
+
const _super = Object.create(null, {
|
|
617
|
+
runManyNoWait: { get: () => super.runManyNoWait }
|
|
618
|
+
});
|
|
619
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
620
|
+
return _super.runManyNoWait.call(this, runs, this._standalone_task_name);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
496
623
|
/**
|
|
497
624
|
* Schedules the task to run at a specific date and time.
|
|
498
625
|
* @param enqueueAt - The date when the task should be triggered.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const childIndexChild: import("../..").TaskWorkflowDeclaration<{
|
|
2
|
+
tag: string;
|
|
3
|
+
}, {
|
|
4
|
+
tag: string;
|
|
5
|
+
}, {}, {}, {}, {}>;
|
|
6
|
+
export declare const childIndexParent: import("../..").WorkflowDeclaration<{
|
|
7
|
+
n: number;
|
|
8
|
+
}, {}, {}>;
|
|
9
|
+
export declare const scenarioTask: import("../..").TaskWorkflowDeclaration<{
|
|
10
|
+
scenarioId: string;
|
|
11
|
+
}, {
|
|
12
|
+
scenarioId: string;
|
|
13
|
+
status: string;
|
|
14
|
+
}, {}, {}, {}, {}>;
|
|
15
|
+
export declare const orchestratorTask: import("../..").TaskWorkflowDeclaration<import("../..").UnknownInputType, {
|
|
16
|
+
total: number;
|
|
17
|
+
expected: number;
|
|
18
|
+
uniqueRunIds: number;
|
|
19
|
+
hasDuplicates: boolean;
|
|
20
|
+
scenarios: string[];
|
|
21
|
+
}, {}, {}, {}, {}>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.orchestratorTask = exports.scenarioTask = exports.childIndexParent = exports.childIndexChild = void 0;
|
|
16
|
+
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
17
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
18
|
+
exports.childIndexChild = hatchet_client_1.hatchet.task({
|
|
19
|
+
name: 'child-index-child',
|
|
20
|
+
fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
+
return { tag: input.tag };
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
exports.childIndexParent = hatchet_client_1.hatchet.workflow({
|
|
25
|
+
name: 'child-index-parent',
|
|
26
|
+
});
|
|
27
|
+
exports.childIndexParent.task({
|
|
28
|
+
name: 'mixed-spawn',
|
|
29
|
+
executionTimeout: '3m',
|
|
30
|
+
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
+
const { n } = input;
|
|
32
|
+
const ctxResults = [];
|
|
33
|
+
for (let i = 0; i < n; i++) {
|
|
34
|
+
const result = yield ctx.runChild(exports.childIndexChild, { tag: `ctx-${i}` });
|
|
35
|
+
ctxResults.push(result.tag);
|
|
36
|
+
}
|
|
37
|
+
const refs = yield Promise.all(Array.from({ length: n }, (_, i) => exports.childIndexChild.runNoWait({ tag: `rnw-${i}` })));
|
|
38
|
+
const runIds = yield Promise.all(refs.map((r) => r.runId));
|
|
39
|
+
const outputs = yield Promise.all(refs.map((r) => r.output));
|
|
40
|
+
const rnwResults = outputs.map((o) => o.tag);
|
|
41
|
+
return {
|
|
42
|
+
ctxResults,
|
|
43
|
+
rnwResults,
|
|
44
|
+
runIds,
|
|
45
|
+
};
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
function buildTree() {
|
|
49
|
+
// Root → Fund → Portfolio → Portfolio → Project structure
|
|
50
|
+
// ~30 total nodes to keep the test manageable
|
|
51
|
+
return {
|
|
52
|
+
id: 'fund-root',
|
|
53
|
+
children: [
|
|
54
|
+
{
|
|
55
|
+
id: 'fund-a',
|
|
56
|
+
children: [
|
|
57
|
+
{
|
|
58
|
+
id: 'portfolio-a1',
|
|
59
|
+
children: [
|
|
60
|
+
{ id: 'project-a1a', children: [] },
|
|
61
|
+
{ id: 'project-a1b', children: [] },
|
|
62
|
+
{ id: 'project-a1c', children: [] },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'portfolio-a2',
|
|
67
|
+
children: [
|
|
68
|
+
{ id: 'project-a2a', children: [] },
|
|
69
|
+
{ id: 'project-a2b', children: [] },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'fund-b',
|
|
76
|
+
children: [
|
|
77
|
+
{
|
|
78
|
+
id: 'portfolio-b1',
|
|
79
|
+
children: [
|
|
80
|
+
{ id: 'project-b1a', children: [] },
|
|
81
|
+
{ id: 'project-b1b', children: [] },
|
|
82
|
+
{ id: 'project-b1c', children: [] },
|
|
83
|
+
{ id: 'project-b1d', children: [] },
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'portfolio-b2',
|
|
88
|
+
children: [
|
|
89
|
+
{ id: 'project-b2a', children: [] },
|
|
90
|
+
{ id: 'project-b2b', children: [] },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'fund-c',
|
|
97
|
+
children: [
|
|
98
|
+
{
|
|
99
|
+
id: 'portfolio-c1',
|
|
100
|
+
children: [
|
|
101
|
+
{ id: 'project-c1a', children: [] },
|
|
102
|
+
{ id: 'project-c1b', children: [] },
|
|
103
|
+
{ id: 'project-c1c', children: [] },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function countNodes(node) {
|
|
112
|
+
return 1 + node.children.reduce((sum, c) => sum + countNodes(c), 0);
|
|
113
|
+
}
|
|
114
|
+
exports.scenarioTask = hatchet_client_1.hatchet.task({
|
|
115
|
+
name: 'child-index-scenario',
|
|
116
|
+
fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
|
+
yield (0, sleep_1.default)(100);
|
|
118
|
+
return { scenarioId: input.scenarioId, status: 'completed' };
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
exports.orchestratorTask = hatchet_client_1.hatchet.task({
|
|
122
|
+
name: 'child-index-orchestrator',
|
|
123
|
+
executionTimeout: '5m',
|
|
124
|
+
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
+
const tree = buildTree();
|
|
126
|
+
const totalExpected = countNodes(tree);
|
|
127
|
+
const collectedRunIds = [];
|
|
128
|
+
const collectedScenarios = [];
|
|
129
|
+
// Recursive bottom-up traversal — mirrors Evan's runChildScenarioFn pattern.
|
|
130
|
+
// Process children first, wait for completion, then process this node.
|
|
131
|
+
function processNode(node) {
|
|
132
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
+
for (const child of node.children) {
|
|
134
|
+
yield processNode(child);
|
|
135
|
+
}
|
|
136
|
+
const runRef = yield exports.scenarioTask.runNoWait({
|
|
137
|
+
scenarioId: node.id,
|
|
138
|
+
});
|
|
139
|
+
const runId = yield runRef.runId;
|
|
140
|
+
collectedRunIds.push(runId);
|
|
141
|
+
collectedScenarios.push(node.id);
|
|
142
|
+
yield runRef.output;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
yield processNode(tree);
|
|
146
|
+
const uniqueRunIds = new Set(collectedRunIds);
|
|
147
|
+
return {
|
|
148
|
+
total: collectedRunIds.length,
|
|
149
|
+
expected: totalExpected,
|
|
150
|
+
uniqueRunIds: uniqueRunIds.size,
|
|
151
|
+
hasDuplicates: uniqueRunIds.size !== collectedRunIds.length,
|
|
152
|
+
scenarios: collectedScenarios,
|
|
153
|
+
};
|
|
154
|
+
}),
|
|
155
|
+
});
|
|
@@ -39,6 +39,7 @@ const e2e_workflows_1 = require("./simple/e2e-workflows");
|
|
|
39
39
|
const workflow_18 = require("./streaming/workflow");
|
|
40
40
|
const workflow_19 = require("./timeout/workflow");
|
|
41
41
|
const workflow_20 = require("./webhooks/workflow");
|
|
42
|
+
const workflow_21 = require("./child_index/workflow");
|
|
42
43
|
const workflows = [
|
|
43
44
|
workflow_1.bulkChild,
|
|
44
45
|
workflow_1.bulkParentWorkflow,
|
|
@@ -89,6 +90,10 @@ const workflows = [
|
|
|
89
90
|
workflow_19.timeoutTask,
|
|
90
91
|
workflow_19.refreshTimeoutTask,
|
|
91
92
|
workflow_20.webhookWorkflow,
|
|
93
|
+
workflow_21.childIndexChild,
|
|
94
|
+
workflow_21.childIndexParent,
|
|
95
|
+
workflow_21.scenarioTask,
|
|
96
|
+
workflow_21.orchestratorTask,
|
|
92
97
|
];
|
|
93
98
|
function main() {
|
|
94
99
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -26,6 +26,25 @@ function main() {
|
|
|
26
26
|
console.log(res[0].TransformedMessage);
|
|
27
27
|
console.log(res[1].TransformedMessage);
|
|
28
28
|
// !!
|
|
29
|
+
// > Bulk Run a Task with runMany
|
|
30
|
+
const runManyRes = yield workflow_1.simple.runMany([
|
|
31
|
+
{
|
|
32
|
+
input: {
|
|
33
|
+
Message: 'HeLlO WoRlD',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
input: {
|
|
38
|
+
Message: 'Hello MoOn',
|
|
39
|
+
},
|
|
40
|
+
opts: {
|
|
41
|
+
priority: 3,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
console.log(runManyRes[0].TransformedMessage);
|
|
46
|
+
console.log(runManyRes[1].TransformedMessage);
|
|
47
|
+
// !!
|
|
29
48
|
// > Bulk Run Tasks from within a Task
|
|
30
49
|
const parent = hatchet_client_1.hatchet.task({
|
|
31
50
|
name: 'simple',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DurableContext } from './client/worker/context';
|
|
1
|
+
import type { DurableContext } from './client/worker/context';
|
|
2
2
|
export interface ParentRunContext {
|
|
3
3
|
parentId: string;
|
|
4
4
|
/**
|
|
@@ -22,8 +22,6 @@ export declare class ParentRunContextManager {
|
|
|
22
22
|
private storage;
|
|
23
23
|
constructor();
|
|
24
24
|
runWithContext<T>(opts: ParentRunContext, fn: () => T): T;
|
|
25
|
-
setContext(opts: ParentRunContext): void;
|
|
26
|
-
setParentRunIdAndIncrementChildIndex(opts: ParentRunContext): void;
|
|
27
25
|
incrementChildIndex(n: number): void;
|
|
28
26
|
getContext(): ParentRunContext | undefined;
|
|
29
27
|
}
|
|
@@ -9,25 +9,16 @@ class ParentRunContextManager {
|
|
|
9
9
|
runWithContext(opts, fn) {
|
|
10
10
|
return this.storage.run(Object.assign({}, opts), fn);
|
|
11
11
|
}
|
|
12
|
-
setContext(opts) {
|
|
13
|
-
this.storage.enterWith(Object.assign({}, opts));
|
|
14
|
-
}
|
|
15
|
-
setParentRunIdAndIncrementChildIndex(opts) {
|
|
16
|
-
var _a;
|
|
17
|
-
const parentRunContext = this.getContext();
|
|
18
|
-
if (parentRunContext) {
|
|
19
|
-
parentRunContext.parentId = opts.parentId;
|
|
20
|
-
parentRunContext.parentTaskRunExternalId = opts.parentTaskRunExternalId;
|
|
21
|
-
parentRunContext.childIndex = ((_a = parentRunContext.childIndex) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
22
|
-
this.setContext(parentRunContext);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
12
|
incrementChildIndex(n) {
|
|
26
13
|
var _a;
|
|
27
14
|
const parentRunContext = this.getContext();
|
|
28
15
|
if (parentRunContext) {
|
|
16
|
+
// Mutate in place — do NOT use enterWith here.
|
|
17
|
+
// storage.run() gives every async descendant the same object reference,
|
|
18
|
+
// so direct mutation is visible across all await boundaries within the
|
|
19
|
+
// same task execution. enterWith would replace the object, and the new
|
|
20
|
+
// object's updates would be invisible to parent async contexts after an await.
|
|
29
21
|
parentRunContext.childIndex = ((_a = parentRunContext.childIndex) !== null && _a !== void 0 ? _a : 0) + n;
|
|
30
|
-
this.setContext(parentRunContext);
|
|
31
22
|
}
|
|
32
23
|
}
|
|
33
24
|
getContext() {
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const HATCHET_VERSION = "1.
|
|
1
|
+
export declare const HATCHET_VERSION = "1.21.0";
|
package/version.js
CHANGED