feat!: replace @swan-io/boxed with neverthrow#206
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates temporal-contract’s public API from the @swan-io/boxed / Future<Result<...>> pattern to neverthrow’s Result / ResultAsync, and removes the now-obsolete @temporal-contract/boxed package and related documentation/tooling references.
Changes:
- Replaced
Future<Result<T, E>>return types withResultAsync<T, E>across worker workflow context helpers, cancellation scopes, typed client APIs, and examples/tests. - Removed the
packages/boxed/package and scrubbed references from workspace config, knip, docs build scripts, and docs navigation. - Updated docs site and READMEs, including a new migration guide for consumers upgrading from boxed.
Reviewed changes
Copilot reviewed 61 out of 62 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates high-level docs and installation guidance for neverthrow-based API. |
| pnpm-workspace.yaml | Removes @swan-io/boxed from catalog; adds neverthrow. |
| pnpm-lock.yaml | Reflects dependency graph updates: removes boxed packages, adds neverthrow. |
| packages/worker/src/workflow.ts | Migrates workflow context child-workflow APIs to ResultAsync and neverthrow constructors. |
| packages/worker/src/errors.ts | Updates cancellation-scope error docs to match neverthrow terminology. |
| packages/worker/src/cancellation.ts | Reimplements cancellation scope helpers to return ResultAsync. |
| packages/worker/src/cancellation.spec.ts | Updates cancellation tests for .isErr() semantics. |
| packages/worker/src/activity.ts | Changes activity implementation types from Future/Result to ResultAsync and adjusts wrapper logic/docs. |
| packages/worker/src/activity.spec.ts | Updates unit tests to use neverthrow helpers (okAsync/errAsync/ResultAsync). |
| packages/worker/src/tests/worker.spec.ts | Updates integration tests to assert neverthrow Result behavior. |
| packages/worker/README.md | Updates worker package docs/examples to use ResultAsync. |
| packages/worker/package.json | Replaces boxed deps with neverthrow dependency. |
| packages/client/src/types.ts | Migrates client-facing inferred signatures to ResultAsync. |
| packages/client/src/schedule.ts | Ports schedule client wrapper from Future/Result to ResultAsync. |
| packages/client/src/schedule.spec.ts | Updates schedule tests for neverthrow results. |
| packages/client/src/internal.ts | Replaces makeFuture helper with makeResultAsync using neverthrow. |
| packages/client/src/tests/client.spec.ts | Updates client integration tests for neverthrow results/match signature. |
| packages/client/package.json | Replaces boxed dep with neverthrow dependency. |
| packages/boxed/vitest.config.ts | Deletes boxed package test config (package removal). |
| packages/boxed/typedoc.json | Deletes boxed package typedoc config (package removal). |
| packages/boxed/tsconfig.json | Deletes boxed package tsconfig (package removal). |
| packages/boxed/src/result.ts | Deletes boxed Result implementation (package removal). |
| packages/boxed/src/result.spec.ts | Deletes boxed Result tests (package removal). |
| packages/boxed/src/interop.ts | Deletes boxed interop helpers (package removal). |
| packages/boxed/src/interop.spec.ts | Deletes boxed interop tests (package removal). |
| packages/boxed/src/index.ts | Deletes boxed package entrypoint (package removal). |
| packages/boxed/src/future.ts | Deletes boxed Future implementation (package removal). |
| packages/boxed/src/future.spec.ts | Deletes boxed Future tests (package removal). |
| packages/boxed/README.md | Deletes boxed package README (package removal). |
| packages/boxed/package.json | Deletes boxed package manifest (package removal). |
| packages/boxed/CHANGELOG.md | Deletes boxed package changelog (package removal). |
| knip.json | Removes boxed package from knip workspace config. |
| examples/order-processing-worker/src/application/workflows.ts | Updates example workflow docs/comments for neverthrow terminology. |
| examples/order-processing-worker/src/application/activities.ts | Ports example activities to ResultAsync.fromPromise / okAsync. |
| examples/order-processing-worker/package.json | Swaps boxed dependency for neverthrow. |
| examples/order-processing-client/src/client.ts | Updates example client to .isErr() and neverthrow naming. |
| docs/scripts/copy-docs.ts | Removes boxed package from docs copy/build dependency list. |
| docs/package.json | Removes docs dependency on removed @temporal-contract/boxed package. |
| docs/index.md | Updates landing page examples to neverthrow usage and .match(okFn, errFn). |
| docs/guide/worker-usage.md | Migrates guide from Future/Result to neverthrow and updates snippets. |
| docs/guide/worker-implementation.md | Migrates deep-dive guide to neverthrow patterns and examples. |
| docs/guide/troubleshooting.md | Updates troubleshooting guidance and snippets for neverthrow. |
| docs/guide/result-pattern.md | Rewrites Result pattern guide around Result / ResultAsync. |
| docs/guide/migrating-to-neverthrow.md | Adds a dedicated migration mapping guide from boxed to neverthrow. |
| docs/guide/installation.md | Updates install instructions to include neverthrow and remove boxed. |
| docs/guide/getting-started.md | Updates getting-started snippets and terminology for neverthrow. |
| docs/guide/entry-points.md | Updates architecture guide snippets to ResultAsync-based APIs. |
| docs/guide/client-usage.md | Updates client guide to ResultAsync returns and positional .match. |
| docs/guide/boxed-vs-swan.md | Removes boxed-vs-swan page (no longer relevant after migration). |
| docs/guide/activity-handlers.md | Updates activity handler typing guide for neverthrow. |
| docs/examples/index.md | Updates examples index wording to neverthrow. |
| docs/examples/basic-order-processing.md | Updates example narrative to Result / ResultAsync terminology. |
| docs/api/index.md | Removes boxed API link and explains neverthrow as the source of Result types. |
| docs/.vitepress/config.ts | Updates sidebar navigation to include migration guide; removes boxed API entry. |
| .changeset/swap-boxed-for-neverthrow.md | Adds major-version changeset describing the breaking migration. |
| .changeset/config.json | Removes @temporal-contract/boxed from the fixed release group. |
| .agents/rules/project-overview.md | Updates agent docs to reflect neverthrow-based error handling. |
| .agents/rules/handlers.md | Updates handler guidance/snippets to ResultAsync. |
| .agents/rules/dependencies.md | Updates canonical dependency list (neverthrow replaces boxed). |
| .agents/rules/code-style.md | Updates code-style rules to reference neverthrow Result/ResultAsync. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
The Result/Future surface returned by every typed-client method, activity
implementation, and workflow context helper now uses neverthrow's
ResultAsync instead of @swan-io/boxed's Future<Result>. The
@temporal-contract/boxed package has been removed entirely.
This is a breaking change for every downstream consumer.
Notable mappings:
Result.Ok / Result.Error -> ok / err (or okAsync / errAsync)
Future<Result<T, E>> -> ResultAsync<T, E>
Future.fromPromise(p, mapErr) -> ResultAsync.fromPromise(p, mapErr)
.isError() -> .isErr()
.flatMap / .flatMapOk -> .andThen
.mapError -> .mapErr
.getOr -> .unwrapOr
.match({ Ok, Error }) -> .match(okFn, errFn) // positional
.tap / .tapOk / .tapError -> inline via .map / .mapErr
A migration guide lives at docs/guide/migrating-to-neverthrow.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Worker and client both expose `ResultAsync<T, E>` types directly in their public API surface. With neverthrow as a regular dependency, a consumer who installs their own copy at a different version ends up with two disjoint nominal `ResultAsync` types in their type-checker — the one emitted in our `.d.ts` and the one their code imports — even though the runtime classes are compatible. Move it to peerDependencies (^8) and keep it in devDependencies so the local build still resolves it. Matches the pattern already used for `@temporalio/*`. Also resolves the inconsistency the Copilot reviewer flagged on the README (which already described neverthrow as a peer dep). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The order-processing example's integration spec still asserted on the
boxed `{ tag: "Ok" | "Error", value | error }` shape. neverthrow's
Result has no `tag` field, so every `expect.objectContaining({ tag })`
matcher silently fell through and the suite failed in CI.
Translate the remaining six tests to the `.isOk()` / `.isErr()` +
narrow-then-assert pattern used elsewhere in the migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces
@swan-io/boxedwithneverthrowacross every public surface — typed client, worker, activities, workflow context, cancellation scopes — and removes the@temporal-contract/boxedpackage entirely. Major version bump forcontract,worker,client, andtesting.Why
@swan-io/boxedaffecting our usage prompted the move.neverthrowis the most widely-used Result library on npm, with a more familiar API for new contributors.Result<T, E>) and async (ResultAsync<T, E>) cases — the customFutureclass and the@temporal-contract/boxedre-export package both go away.What changed
Future<Result<T, E>>→ResultAsync<T, E>everywhere a typed-client method, activity, or workflow helper used to return one.Result<T, E>is now imported fromneverthrow.@temporal-contract/boxed.packages/boxed/deleted, dropped from changeset config, knip config, docs scripts, and the docs sidebar.packages/client,packages/worker, and the order-processing examples.pnpm test). Full workspacepnpm typecheckis clean.docs/guide/migrating-to-neverthrow.mdand linked from the docs sidebar, README, installation guide, and API index..agents/rules/*+ the root README.API mapping highlights
Result.Ok(v)/Result.Error(e)ok(v)/err(e)Future.value(Result.Ok(v))okAsync(v)Future.fromPromise(p, mapErr)ResultAsync.fromPromise(p, mapErr).isError().isErr().flatMap/.flatMapOk.andThen.mapError.mapErr.getOr.unwrapOr.match({ Ok, Error }).match(okFn, errFn)(positional).tap/.tapOk/.tapError.map(v => { sideEffect(v); return v })Full mapping in
docs/guide/migrating-to-neverthrow.md.Test plan
pnpm install --frozen-lockfilesucceeds with the new lockfile.Independence from #205
Branched off
main, not from #205 (the CI publish fix). The two are orthogonal — landing order doesn't matter, and either rebase will be trivial.🤖 Generated with Claude Code