@hatchet-dev/typescript-sdk 1.19.2-alpha.1 → 1.19.2-alpha.3
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.js +4 -5
- 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/parent-run-context-vars.js +10 -4
- 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
|
}
|
|
@@ -796,12 +806,13 @@ class DurableContext extends Context {
|
|
|
796
806
|
workflowName = workflow.name;
|
|
797
807
|
}
|
|
798
808
|
workflowName = (0, apply_namespace_1.applyNamespace)(workflowName, this.v1.config.namespace).toLowerCase();
|
|
809
|
+
const childIndex = this.nextChildIndex();
|
|
799
810
|
const triggerOpts = {
|
|
800
811
|
name: workflowName,
|
|
801
812
|
input: JSON.stringify(input || {}),
|
|
802
813
|
parentId: this.action.workflowRunId,
|
|
803
814
|
parentTaskRunExternalId: this.action.taskRunExternalId,
|
|
804
|
-
childIndex
|
|
815
|
+
childIndex,
|
|
805
816
|
childKey: options === null || options === void 0 ? void 0 : options.key,
|
|
806
817
|
additionalMetadata: (options === null || options === void 0 ? void 0 : options.additionalMetadata)
|
|
807
818
|
? JSON.stringify(options.additionalMetadata)
|
|
@@ -810,7 +821,6 @@ class DurableContext extends Context {
|
|
|
810
821
|
priority: options === null || options === void 0 ? void 0 : options.priority,
|
|
811
822
|
desiredWorkerLabels: {},
|
|
812
823
|
};
|
|
813
|
-
this.spawnIndex += 1;
|
|
814
824
|
return { workflowName, triggerOpts };
|
|
815
825
|
}
|
|
816
826
|
/**
|
package/v1/declaration.js
CHANGED
|
@@ -77,13 +77,12 @@ class BaseWorkflowDeclaration {
|
|
|
77
77
|
: undefined,
|
|
78
78
|
warn: (message) => this.client.admin.logger.warn(message),
|
|
79
79
|
});
|
|
80
|
-
// Snapshot
|
|
81
|
-
//
|
|
80
|
+
// Snapshot childIndex before incrementing — shared with Context.spawnIndex
|
|
81
|
+
// via parentRunContextManager so both spawning APIs produce unique indices.
|
|
82
82
|
const baseChildIndex = parentRunContext === null || parentRunContext === void 0 ? void 0 : parentRunContext.childIndex;
|
|
83
83
|
const inputCount = Array.isArray(input) ? input.length : 1;
|
|
84
84
|
parent_run_context_vars_1.parentRunContextManager.incrementChildIndex(inputCount);
|
|
85
|
-
const
|
|
86
|
-
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: childIndexForRun, 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 });
|
|
85
|
+
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 });
|
|
87
86
|
if (Array.isArray(input)) {
|
|
88
87
|
let resp = [];
|
|
89
88
|
for (let i = 0; i < input.length; i += 500) {
|
|
@@ -91,7 +90,7 @@ class BaseWorkflowDeclaration {
|
|
|
91
90
|
const batchResp = yield this.client.admin.runWorkflows(batch.map((inp, batchIdx) => ({
|
|
92
91
|
workflowName: this.definition.name,
|
|
93
92
|
input: inp,
|
|
94
|
-
options: Object.assign(Object.assign({}, runOpts), { childIndex: (
|
|
93
|
+
options: Object.assign(Object.assign({}, runOpts), { childIndex: (baseChildIndex !== null && baseChildIndex !== void 0 ? baseChildIndex : 0) + i + batchIdx }),
|
|
95
94
|
})));
|
|
96
95
|
resp = resp.concat(batchResp);
|
|
97
96
|
}
|
|
@@ -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,
|
|
@@ -86,6 +87,10 @@ const workflows = [
|
|
|
86
87
|
workflow_19.timeoutTask,
|
|
87
88
|
workflow_19.refreshTimeoutTask,
|
|
88
89
|
workflow_20.webhookWorkflow,
|
|
90
|
+
workflow_21.childIndexChild,
|
|
91
|
+
workflow_21.childIndexParent,
|
|
92
|
+
workflow_21.scenarioTask,
|
|
93
|
+
workflow_21.orchestratorTask,
|
|
89
94
|
];
|
|
90
95
|
function main() {
|
|
91
96
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -16,16 +16,22 @@ class ParentRunContextManager {
|
|
|
16
16
|
var _a;
|
|
17
17
|
const parentRunContext = this.getContext();
|
|
18
18
|
if (parentRunContext) {
|
|
19
|
-
|
|
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);
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
incrementChildIndex(n) {
|
|
23
26
|
var _a;
|
|
24
27
|
const parentRunContext = this.getContext();
|
|
25
28
|
if (parentRunContext) {
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
+
// Mutate in place — do NOT use enterWith/setContext here.
|
|
30
|
+
// storage.run() gives every async descendant the same object reference,
|
|
31
|
+
// so direct mutation is visible across all await boundaries within the
|
|
32
|
+
// same task execution. enterWith would create a new object whose
|
|
33
|
+
// updates are invisible to parent async contexts after an await.
|
|
34
|
+
parentRunContext.childIndex = ((_a = parentRunContext.childIndex) !== null && _a !== void 0 ? _a : 0) + n;
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
37
|
getContext() {
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const HATCHET_VERSION = "1.19.2-alpha.
|
|
1
|
+
export declare const HATCHET_VERSION = "1.19.2-alpha.3";
|
package/version.js
CHANGED