Vihren v0.2.0: typed async workflow runs
Vihren v0.2.0 tightens the generated codegen client surface with typed async workflow run handles.
Vihren’s first codegen path made the simple case pleasant: start a workflow, wait for it to finish, and get a typed Go value back. That is still the right shape when the caller wants the workflow result immediately.
Temporal also supports a different mode: start the workflow now, keep a handle
to the in-flight execution, and wait for the result later. In the Go SDK, that
async start path returns a client.WorkflowRun, which is Temporal’s control
handle for the workflow execution.
Vihren v0.2.0 generates the typed version of that handle. Generated async
workflow clients now return values such as HelloWorkflowRun instead of raw
Temporal client.WorkflowRun values, so the handle still exposes workflow
identity while completion stays typed.
The synchronous generated client path is unchanged:
out, err := codegenhello.NewClient(c).HelloWorkflow(ctx,
client.StartWorkflowOptions{
ID: "hello-ada",
TaskQueue: codegenhello.DefaultTaskQueue,
},
codegenhello.GreetingInput{Name: "Ada"},
)
The async path now keeps the result type on the handle:
run, err := codegenhello.NewClient(c).HelloWorkflowAsync(ctx,
client.StartWorkflowOptions{
ID: "hello-ada",
TaskQueue: codegenhello.DefaultTaskQueue,
},
codegenhello.GreetingInput{Name: "Ada"},
)
if err != nil {
return err
}
out, err := run.Get(ctx)
That replaces the old raw Temporal shape:
var out codegenhello.GreetingOutput
err := run.Get(ctx, &out)
The generated handle still exposes Temporal run identity:
workflowID := run.GetID()
runID := run.GetRunID()
Generated shape
For examples/codegenhello, the relevant generated code now looks like this:
type activityRegistry interface {
RegisterActivityWithOptions(activityFunc any, options activity.RegisterOptions)
}
type workflowRegistry interface {
RegisterWorkflowWithOptions(workflowFunc any, options workflow.RegisterOptions)
}
func Register(r worker.Registry, greetingActivities *GreetingActivities) {
RegisterActivities(r, greetingActivities)
RegisterWorkflows(r)
}
type HelloWorkflowRun struct {
run client.WorkflowRun
}
func (run HelloWorkflowRun) GetID() string {
return run.run.GetID()
}
func (run HelloWorkflowRun) GetRunID() string {
return run.run.GetRunID()
}
func (run HelloWorkflowRun) Get(ctx context.Context) (GreetingOutput, error) {
var out GreetingOutput
if err := run.run.Get(ctx, &out); err != nil {
return GreetingOutput{}, err
}
return out, nil
}
func (cl Client) HelloWorkflow(
ctx context.Context,
opts client.StartWorkflowOptions,
in GreetingInput,
) (GreetingOutput, error) {
run, err := cl.HelloWorkflowAsync(ctx, opts, in)
if err != nil {
return GreetingOutput{}, err
}
return run.Get(ctx)
}
There are two compatibility notes:
Client.<Workflow>remains the simple start-and-await API.- Combined generated
Registerstill accepts Temporal’sworker.Registry.
The breaking change is limited to code that calls <Workflow>Async and expects
client.WorkflowRun directly. Change those call sites to use the generated run
handle and call out, err := run.Get(ctx).
Install
go get github.com/vihren-dev/[email protected]
go install github.com/vihren-dev/vihren/cmd/[email protected]
Then regenerate:
go generate ./...
The compact example remains the fastest way to try the release:
go run ./examples/codegenhello/cmd/codegenhello-embedded