feat(worker): add perActivityOptions to declareWorkflow#125
feat(worker): add perActivityOptions to declareWorkflow#125
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support in @temporal-contract/worker for specifying ActivityOptions per activity when declaring workflows, with per-activity values overriding the workflow defaults.
Changes:
- Extend
declareWorkflowwith optionalperActivityOptionsand makeactivityOptionsoptional. - Build activity proxies with either default options or per-activity merged options.
- Add a changeset and a new test workflow intended to demonstrate the feature.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/worker/src/workflow.ts | Adds perActivityOptions support and makes activityOptions optional when creating activity proxies. |
| packages/worker/src/tests/test.workflows.ts | Adds a new workflow export intended to use per-activity overrides. |
| .changeset/per-activity-options.md | Documents the new option and bumps @temporal-contract/worker minor version. |
| const rawActivities: Record<string, (...args: unknown[]) => Promise<unknown>> = {}; | ||
| for (const activityName of allActivityNames) { | ||
| const specificOptions = perActivityOptionsMap?.[activityName]; | ||
| if (specificOptions !== undefined) { | ||
| const proxy = proxyActivities<Record<string, (...args: unknown[]) => Promise<unknown>>>({ | ||
| ...activityOptions, | ||
| ...specificOptions, | ||
| }); | ||
| rawActivities[activityName] = proxy[activityName] as ( | ||
| ...args: unknown[] | ||
| ) => Promise<unknown>; | ||
| } else if (defaultProxy !== null) { | ||
| rawActivities[activityName] = defaultProxy[activityName] as ( | ||
| ...args: unknown[] | ||
| ) => Promise<unknown>; | ||
| } | ||
| } |
There was a problem hiding this comment.
When activityOptions is undefined and perActivityOptions doesn’t include an entry for every activity in the contract/definition, this loop simply omits those activities from rawActivities. That later triggers createValidatedActivities to throw “Activity implementation not found…”, which is misleading (the implementation exists; the proxy wasn’t created due to missing options). Consider explicitly detecting this case here and throwing a clearer error listing which activity names are missing options (or otherwise ensure a proxy is created for every activity).
| if (definition.activities || contract.activities) { | ||
| const rawActivities = | ||
| proxyActivities<Record<string, (...args: unknown[]) => Promise<unknown>>>(activityOptions); | ||
| const allActivityNames = [ | ||
| ...Object.keys(definition.activities ?? {}), | ||
| ...Object.keys(contract.activities ?? {}), | ||
| ]; | ||
|
|
||
| const defaultProxy = | ||
| activityOptions !== undefined | ||
| ? proxyActivities<Record<string, (...args: unknown[]) => Promise<unknown>>>( | ||
| activityOptions, | ||
| ) | ||
| : null; | ||
|
|
||
| const perActivityOptionsMap = perActivityOptions as | ||
| | Record<string, ActivityOptions | undefined> | ||
| | undefined; | ||
| const rawActivities: Record<string, (...args: unknown[]) => Promise<unknown>> = {}; | ||
| for (const activityName of allActivityNames) { | ||
| const specificOptions = perActivityOptionsMap?.[activityName]; | ||
| if (specificOptions !== undefined) { | ||
| const proxy = proxyActivities<Record<string, (...args: unknown[]) => Promise<unknown>>>({ | ||
| ...activityOptions, | ||
| ...specificOptions, | ||
| }); | ||
| rawActivities[activityName] = proxy[activityName] as ( | ||
| ...args: unknown[] | ||
| ) => Promise<unknown>; | ||
| } else if (defaultProxy !== null) { | ||
| rawActivities[activityName] = defaultProxy[activityName] as ( | ||
| ...args: unknown[] | ||
| ) => Promise<unknown>; | ||
| } | ||
| } |
There was a problem hiding this comment.
This PR adds perActivityOptions, but there’s no test that actually executes a workflow using per-activity overrides and asserts the behavior. The new test workflow in test.workflows.ts isn’t referenced by the integration tests, so the feature is currently unexercised. Please add/adjust an integration test to run a workflow that uses perActivityOptions (e.g., with activityOptions omitted and per-activity options supplied for all activities) to ensure it works end-to-end.
|
|
||
| // Workflow that uses per-activity options | ||
| export const workflowWithPerActivityOptions = declareWorkflow({ | ||
| workflowName: "workflowWithActivities", |
There was a problem hiding this comment.
workflowWithPerActivityOptions sets workflowName: "workflowWithActivities", which duplicates the existing workflow and means starting workflowWithPerActivityOptions by name won’t run this implementation (Temporal resolves workflow type by exported symbol name). This makes the new workflow a confusing duplicate and it doesn’t actually demonstrate per-activity options in tests. Consider either updating the existing workflowWithActivities export to include perActivityOptions, or add a new workflow definition to testContract and use a matching workflowName + integration test.
| workflowName: "workflowWithActivities", | |
| workflowName: "workflowWithPerActivityOptions", |
Allow specifying ActivityOptions per activity in declareWorkflow via the new optional perActivityOptions field. Options are merged with the default activityOptions (per-activity values take precedence), enabling fine-grained control over timeouts and retry policies per activity. Closes #122 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73c0a4f to
edea609
Compare
Summary
perActivityOptionsfield todeclareWorkflowthat allows specifyingActivityOptionsper activityactivityOptions(per-activity values take precedence)activityOptionsis now optional — you can rely solely onperActivityOptionsif you preferUsage
Test plan
pnpm typecheck)pnpm test)workflowWithPerActivityOptionstest workflow demonstrating the featureCloses #122
🤖 Generated with Claude Code