@workflow/web: Decompose workflow-api-client.ts into focused modules and add test infrastructure#1070
Conversation
🦋 Changeset detectedLatest commit: 02a0cae The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@alizenhom is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
e904ffd to
50f626a
Compare
|
@alizenhom Thanks for contributing! We'll try to review this soon
I think all the things you listed are great and helpful. We'll happily take any observability and testing improvements. For creating a Convex world, note that we don't take any community worlds into the main package. Instead, we recommend publishing your own NPM package, and then adding the package and description to |
VaguelySerious
left a comment
There was a problem hiding this comment.
Still reviewing, but a comment ahead of time: would you be able to move all the new client-related files into a sub-folder app/lib/client?
|
@VaguelySerious Thanks for the response!
sure! Meanwhile will work on more o11y and testing improvements, on the side will work also on the convex world and make a PR to update |
| * Unwraps a server action result, throwing WorkflowWebAPIError on failure. | ||
| * Use for simple action wrappers where you just want the result or an exception. | ||
| */ | ||
| export async function unwrapOrThrow<T>( |
There was a problem hiding this comment.
Functionally of this seems equivalent to the previous happy path, but in error paths the error wrapping is slightly different: the new generic usePaginatedList catch block wraps non-Error
exceptions into WorkflowWebAPIError with layer: 'client' instead of preserving the layer from the server action error. Is this intentional?
There was a problem hiding this comment.
yes!, the behavior is equivalent on all reachable code paths because fetchFn is always wrapped in unwrapOrThrow, which only ever throws WorkflowWebAPIError (an Error subclass). The layer: 'client' fallback only applies to non-Error exceptions, which can't arise here.
| import { resolve } from 'node:path'; | ||
| import { defineConfig } from 'vitest/config'; | ||
|
|
||
| export default defineConfig({ |
There was a problem hiding this comment.
package.json needs a test script to be picked up in CI. Add "test": "vitest run" to packages/web/package.json to fix
There was a problem hiding this comment.
packages/web/package.json already has this script "test": "vitest run".
check it here
VaguelySerious
left a comment
There was a problem hiding this comment.
Looks like there's a lockfile issue - could you resole that? Then it'd be ready to merge
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
@VaguelySerious Thanks! |
Summary
workflow-api-client.ts(~1,330 lines) into 8 focused, single-responsibility modules while preserving a barrel re-export for backward compatibilityvitest+jsdom+@testing-library/reacttest infrastructure for packages/web and adds comprehensive unit tests across all extracted modulesMotivation
workflow-api-client.tshad grown to 1,329 lines with extensive duplication across 5 distinct patterns — paginated list hooks, exhaustive fetch helpers, merge-by-ID functions, poll functions, and server action wrappers — each repeated 2-3 times with only the fetch call or ID key varying. This made it difficult to test, navigate, and reason about individual concerns, and the package had no unit tests or test runner configured.What Changed?
The monolith was split along natural boundaries into standalone modules:
workflow-errors.ts—>WorkflowWebAPIErrorclass,getErrorMessage,unwrapServerActionResult, andunwrapOrThrowhelpers for consistent error handling across the packageworkflow-primitives.ts—> Pure utility functions (mergeById,fetchAllPaginated,pollResource) that provide the generic pagination and polling building blocks used by the hooksworkflow-actions.ts—> Thin wrappers around server actions (cancelRun,recreateRun,reenqueueRun,wakeUpRun,resumeHook) with uniform error unwrappingworkflow-streams.ts—>readStreamandlistStreamsfor fetching workflow stream data via the Fetch APIhooks/:use-paginated-list.ts—> GenericusePaginatedListhook with page caching and navigation, plus concreteuseWorkflowRunsanduseWorkflowHookshooks built on top of ituse-resource-data.ts—>useWorkflowResourceDatafor fetching individual run/step/hook/sleep detail views with auto-refreshuse-trace-viewer.ts—>useWorkflowTraceViewerDatafor the trace viewer, combining run, steps, hooks, and events with live polling and exhaustive paginationuse-workflow-streams.ts—>useWorkflowStreamsfor polling the list of stream IDs on a runworkflow-api-client.tsis now a ~39-line barrel that re-exports everything, so existing consumers are unaffected.Test infrastructure
vitest,jsdom, and@testing-library/reactas dev dependenciesvitest.config.tswithjsdomenvironment and ~ path aliaspnpm test)Test coverage
Added 8 test files organized to mirror the module structure:
Utilities & core logic
workflow-errors.test.ts—> error class construction, 403 message handling, unwrapOrThrow success/failure/rejection pathsworkflow-primitives.test.ts—> mergeById deduplication and custom ID keys, fetchAllPaginated multi-page traversal and error bail-out, pollResource with both always and onHasMore cursor strategiesworkflow-actions.test.ts—> success and failure cases for all 5 action wrappersworkflow-streams.test.ts—> readStream with startIndex/abort signal/non-ok responses/null body, listStreams success and structured error parsingReact hooks (using
@testing-library/react+jsdom)use-paginated-list.test.ts—> initial fetch, error propagation, parameter forwarding, cursor-based nextPage, previousPage cache hit, and reload (covers both useWorkflowRuns and useWorkflowHooks)use-resource-data.test.ts—> run/step/hook/sleep resource types, enabled: false short-circuit, error statesuse-trace-viewer.test.ts—> combined initial load of run + steps + hooks + events, paginated step population, error handlinguse-workflow-streams.test.ts—> initial fetch and error handlingNotes
Apologies for the size of this PR, the extraction touched a lot of files but the actual logic is unchanged. Happy to break it up differently or address any feedback.
As a new contributor still getting familiar with the codebase, there may be rough edges I've missed. I'd appreciate any guidance on the following directions I'm interested in exploring next:
Would love to know if either of these aligns with the project's priorities!