From 7a300b5c480b855eb750ba8e31d12b561bc334f3 Mon Sep 17 00:00:00 2001 From: Benoit Travers Date: Wed, 29 Jul 2026 18:31:28 +0200 Subject: [PATCH 1/2] chore(deps): bump unthrown to 5.0.0 and migrate tag to P.tag unthrown 5.0.0 is out. The only API change since 5.0.0-beta.7 is that the standalone `tag` export moved onto the pattern namespace as `P.tag(t)`, so every call site, TSDoc example, README and doc page is updated and the peer range is raised to `^5.0.0`. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/bump-unthrown-v5-stable.md | 31 ++++++++++++++ README.md | 4 +- docs/explanation/the-result-model.md | 8 ++-- docs/how-to/add-activity-middleware.md | 4 +- docs/how-to/install.md | 18 ++++---- docs/how-to/intercept-client-calls.md | 6 +-- docs/how-to/migrate-from-neverthrow.md | 14 +++---- docs/how-to/model-domain-errors.md | 22 +++++----- docs/how-to/run-child-workflows.md | 8 ++-- docs/how-to/troubleshoot.md | 2 +- docs/how-to/upgrade-to-v8.md | 40 +++++++++++++----- docs/index.md | 12 +++--- docs/reference/errors.md | 4 +- docs/tutorial/adding-signals-and-queries.md | 8 ++-- docs/tutorial/your-first-workflow.md | 12 +++--- .../order-processing-client/src/client.ts | 22 +++++----- packages/client/package.json | 2 +- packages/client/src/__tests__/client.spec.ts | 12 +++--- packages/client/src/client.spec.ts | 12 +++--- packages/client/src/client.ts | 32 +++++++------- packages/contract/package.json | 2 +- packages/contract/src/errors.ts | 2 +- packages/worker/README.md | 13 +++--- packages/worker/package.json | 2 +- .../src/activity-contract-errors.spec.ts | 4 +- packages/worker/src/activity.ts | 6 +-- packages/worker/src/cancellation.ts | 2 +- packages/worker/src/workflow.ts | 12 +++--- pnpm-lock.yaml | 42 +++++++++---------- pnpm-workspace.yaml | 4 +- 30 files changed, 206 insertions(+), 156 deletions(-) create mode 100644 .changeset/bump-unthrown-v5-stable.md diff --git a/.changeset/bump-unthrown-v5-stable.md b/.changeset/bump-unthrown-v5-stable.md new file mode 100644 index 00000000..c1dcbb58 --- /dev/null +++ b/.changeset/bump-unthrown-v5-stable.md @@ -0,0 +1,31 @@ +--- +"@temporal-contract/contract": patch +"@temporal-contract/client": patch +"@temporal-contract/worker": patch +--- + +Bump `unthrown` to the stable `5.0.0` and raise the peer range to `^5.0.0`. + +The only API change since `5.0.0-beta.7` is that the standalone `tag` export is +gone — it now lives on the pattern namespace as `P.tag(t)`, alongside every +other pattern constructor (`P._` / `P.any` / `P.instanceOf` / `P.when` / +`P.union`). The type and runtime behaviour are unchanged: it still produces the +`{ _tag: t }` pattern, still narrows to the matching variant with its payload, +and still works in grouped patterns. + +Migration is mechanical — drop `tag` from the import (keeping or adding `P`) and +prefix the call sites: + +```diff +- import { tag } from "unthrown"; ++ import { P } from "unthrown"; + + result.mapErrCases((matcher) => +- matcher.with(tag("@temporal-contract/WorkflowFailedError"), (error) => handle(error)), ++ matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (error) => handle(error)), + ); +``` + +Every example in the docs, READMEs and TSDoc has been updated to the `P.tag` +spelling, and the upgrade guide gained a note for anyone already tracking an +8.0 beta. diff --git a/README.md b/README.md index 382c3f16..907aa61b 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,8 @@ result.match({ ok: (output) => console.log(output.transactionId), errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowFailedError"), // ...exhaustive — a missing tag is a compile error (error) => console.error(error.message), ), diff --git a/docs/explanation/the-result-model.md b/docs/explanation/the-result-model.md index 0deef7d1..a58d9091 100644 --- a/docs/explanation/the-result-model.md +++ b/docs/explanation/the-result-model.md @@ -121,16 +121,16 @@ The `errCases` handler and the `*ErrCases` combinators receive a matcher rather than the bare error: ```typescript -import { tag } from "unthrown"; +import { P } from "unthrown"; result.match({ ok: (output) => output.transactionId, errCases: (matcher) => matcher - .with(tag("@temporal-contract/ContractError"), (e) => handleDomain(e)) + .with(P.tag("@temporal-contract/ContractError"), (e) => handleDomain(e)) .with( - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (e) => handleInfra(e), ), defect: (cause) => report(cause), diff --git a/docs/how-to/add-activity-middleware.md b/docs/how-to/add-activity-middleware.md index 2abf353e..9b7c3679 100644 --- a/docs/how-to/add-activity-middleware.md +++ b/docs/how-to/add-activity-middleware.md @@ -38,13 +38,13 @@ import { declareActivitiesHandler, type ActivityMiddleware, } from "@temporal-contract/worker/activity"; -import { P, tag } from "unthrown"; +import { P } from "unthrown"; const logging: ActivityMiddleware = ({ activityName, workflowName }, next) => next().tapErrCases((matcher) => matcher.with( P.instanceOf(ApplicationFailure), - tag("@temporal-contract/ContractError"), + P.tag("@temporal-contract/ContractError"), (error) => { logger.warn({ activityName, workflowName, error }, "activity failed"); }, diff --git a/docs/how-to/install.md b/docs/how-to/install.md index 21cc04b4..632f0c03 100644 --- a/docs/how-to/install.md +++ b/docs/how-to/install.md @@ -54,15 +54,15 @@ describes a compatible set. Do not mix versions. These are peers, not dependencies, because they appear in the packages' public types. Your code and the library must resolve to the _same_ copy. -| Peer | Required by | Range | -| ---------------------- | ------------------------ | --------------- | -| `unthrown` | contract, worker, client | `^5.0.0-beta.7` | -| `@temporalio/common` | worker, client | `^1` | -| `@temporalio/worker` | worker, testing | `^1` | -| `@temporalio/workflow` | worker | `^1` | -| `@temporalio/client` | client, testing | `^1` | -| `@temporalio/testing` | testing | `^1` | -| `vitest` | testing | `^4` | +| Peer | Required by | Range | +| ---------------------- | ------------------------ | -------- | +| `unthrown` | contract, worker, client | `^5.0.0` | +| `@temporalio/common` | worker, client | `^1` | +| `@temporalio/worker` | worker, testing | `^1` | +| `@temporalio/workflow` | worker | `^1` | +| `@temporalio/client` | client, testing | `^1` | +| `@temporalio/testing` | testing | `^1` | +| `vitest` | testing | `^4` | Plus a [Standard Schema](https://standardschema.dev/) library to write your schemas with — Zod, Valibot, or ArkType. diff --git a/docs/how-to/intercept-client-calls.md b/docs/how-to/intercept-client-calls.md index fcb0ec26..fe9d8985 100644 --- a/docs/how-to/intercept-client-calls.md +++ b/docs/how-to/intercept-client-calls.md @@ -95,13 +95,13 @@ Modeled domain errors (`WorkflowNotFoundError`, a `ContractError`, a validation failure) stay on the `err` channel. Branch on those with `flatMapErrCases`: ```typescript -import { Err, Ok, P, tag } from "unthrown"; +import { Err, Ok, P } from "unthrown"; const fallback: ClientInterceptor = (args, next) => next().flatMapErrCases((matcher) => matcher // Idempotent start: treat "already running" as success. - .with(tag("@temporal-contract/WorkflowAlreadyStartedError"), () => Ok(undefined).toAsync()) + .with(P.tag("@temporal-contract/WorkflowAlreadyStartedError"), () => Ok(undefined).toAsync()) // The matcher must cover the whole union — `P._` passes the rest through. .with(P._, (error) => Err(error).toAsync()), ); @@ -110,7 +110,7 @@ const fallback: ClientInterceptor = (args, next) => ::: warning The matcher is exhaustive `flatMapErrCases`, `mapErrCases`, `tapErrCases`, and `recoverErrCases` all require a match that covers every member of the error union — here the nine -members of `ClientCallError`. A single `.with(tag(...))` arm will not compile. +members of `ClientCallError`. A single `.with(P.tag(...))` arm will not compile. Handle the cases you care about, then close with `P._`. ::: diff --git a/docs/how-to/migrate-from-neverthrow.md b/docs/how-to/migrate-from-neverthrow.md index 3fdf74d0..5c857c53 100644 --- a/docs/how-to/migrate-from-neverthrow.md +++ b/docs/how-to/migrate-from-neverthrow.md @@ -104,10 +104,10 @@ exhaustive matcher instead of the bare error: result.mapErr((error) => new WrappedError(error)); // after — one arm per tag in the union (abbreviated here; see the note below) -import { tag } from "unthrown"; +import { P } from "unthrown"; result.mapErrCases((matcher) => - matcher.with(tag("@temporal-contract/WorkflowFailedError"), (error) => new WrappedError(error)), + matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (error) => new WrappedError(error)), ); ``` @@ -125,9 +125,9 @@ into one branch is compact: ```typescript matcher.with( - tag("@temporal-contract/WorkflowNotFoundError"), - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowFailedError"), (error) => report(error), ); ``` @@ -146,8 +146,8 @@ const message = result.match({ ok: (value) => `charged ${value.transactionId}`, errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowValidationError"), (error) => `failed: ${error.message}`, ), defect: (cause) => `unexpected: ${String(cause)}`, diff --git a/docs/how-to/model-domain-errors.md b/docs/how-to/model-domain-errors.md index 36af9915..f05b2708 100644 --- a/docs/how-to/model-domain-errors.md +++ b/docs/how-to/model-domain-errors.md @@ -129,7 +129,7 @@ Declaring errors on an activity **changes its workflow-side call signature.** So an errors-declaring activity is awaited as a result, not a plain value: ```typescript -import { tag } from "unthrown"; +import { P } from "unthrown"; implementation: async (context, order) => { const charged = await context.activities.chargeCard({ @@ -141,7 +141,7 @@ implementation: async (context, order) => { ok: (payment) => ({ status: "completed" as const, transactionId: payment.transactionId }), errCases: (matcher) => matcher - .with(tag("@temporal-contract/ContractError"), (error) => { + .with(P.tag("@temporal-contract/ContractError"), (error) => { // error.errorName narrows; error.data is typed from the schema if (error.errorName === "CardDeclined") { return { status: "failed" as const, reason: error.data.reason }; @@ -149,8 +149,8 @@ implementation: async (context, order) => { return { status: "failed" as const, reason: error.errorName }; }) .with( - tag("@temporal-contract/ActivityError"), - tag("@temporal-contract/ActivityCancelledError"), + P.tag("@temporal-contract/ActivityError"), + P.tag("@temporal-contract/ActivityCancelledError"), (error) => ({ status: "failed" as const, reason: error.message }), ), defect: (cause) => ({ @@ -179,7 +179,7 @@ A workflow whose declared error caused the failure surfaces it as a `WorkflowFailedError`: ```typescript -import { tag } from "unthrown"; +import { P } from "unthrown"; const result = await client.executeWorkflow("processOrder", { workflowId: "order-1", @@ -190,7 +190,7 @@ result.match({ ok: (output) => console.log("done:", output), errCases: (matcher) => matcher - .with(tag("@temporal-contract/ContractError"), (error) => { + .with(P.tag("@temporal-contract/ContractError"), (error) => { switch (error.errorName) { case "EmptyOrder": return console.error("no items on order", error.data.orderId); @@ -199,11 +199,11 @@ result.match({ } }) .with( - tag("@temporal-contract/WorkflowNotFoundError"), - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowAlreadyStartedError"), - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowAlreadyStartedError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (error) => console.error("failed:", error.message), ), defect: (cause) => console.error("unexpected:", cause), diff --git a/docs/how-to/run-child-workflows.md b/docs/how-to/run-child-workflows.md index 92949789..e6a3c8e1 100644 --- a/docs/how-to/run-child-workflows.md +++ b/docs/how-to/run-child-workflows.md @@ -13,7 +13,7 @@ same-contract and cross-contract calls look identical. ```typescript import { declareWorkflow } from "@temporal-contract/worker/workflow"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import { orderContract } from "./contract.js"; @@ -31,9 +31,9 @@ export const processOrder = declareWorkflow({ ok: (output) => ({ status: "completed" as const, transactionId: output.transactionId }), errCases: (matcher) => matcher.with( - tag("@temporal-contract/ChildWorkflowError"), - tag("@temporal-contract/ChildWorkflowCancelledError"), - tag("@temporal-contract/ChildWorkflowNotFoundError"), + P.tag("@temporal-contract/ChildWorkflowError"), + P.tag("@temporal-contract/ChildWorkflowCancelledError"), + P.tag("@temporal-contract/ChildWorkflowNotFoundError"), (error) => ({ status: "failed" as const, reason: error.message }), ), defect: (cause) => ({ diff --git a/docs/how-to/troubleshoot.md b/docs/how-to/troubleshoot.md index e70e0b63..415af1f6 100644 --- a/docs/how-to/troubleshoot.md +++ b/docs/how-to/troubleshoot.md @@ -252,7 +252,7 @@ Handle them in `defect`, `recoverDefect`, or `tapDefect`: ```typescript result.match({ ok: (v) => v, - errCases: (m) => m.with(tag("@temporal-contract/WorkflowFailedError"), handle), + errCases: (m) => m.with(P.tag("@temporal-contract/WorkflowFailedError"), handle), defect: (cause) => { if (cause instanceof RuntimeClientError) return report(cause); throw cause; diff --git a/docs/how-to/upgrade-to-v8.md b/docs/how-to/upgrade-to-v8.md index 55351b60..b3cb5060 100644 --- a/docs/how-to/upgrade-to-v8.md +++ b/docs/how-to/upgrade-to-v8.md @@ -16,9 +16,12 @@ explicitly: ```bash pnpm add @temporal-contract/contract@beta @temporal-contract/worker@beta \ - @temporal-contract/client@beta unthrown@beta + @temporal-contract/client@beta unthrown@^5 ``` +`unthrown` itself is stable — only the `@temporal-contract/*` packages are on +the `beta` tag. + The [stable docs](https://btravstack.github.io/temporal-contract/) document 7.x; you are reading the beta docs. ::: @@ -32,7 +35,7 @@ pnpm add @temporal-contract/contract@beta \ @temporal-contract/worker@beta \ @temporal-contract/client@beta pnpm add -D @temporal-contract/testing@beta -pnpm add unthrown@^5.0.0-beta.7 +pnpm add unthrown@^5.0.0 ``` If an intermediate beta had you install `ts-pattern` as a peer, remove it — @@ -43,6 +46,20 @@ dependencies: pnpm remove ts-pattern ``` +If you already tracked an 8.0 beta, the standalone `tag` export is gone in +unthrown 5.0.0 — it is `P.tag` now. Drop `tag` from the import (keeping or +adding `P`) and prefix the call sites: + +```diff +- import { tag } from "unthrown"; ++ import { P } from "unthrown"; + + result.mapErrCases((matcher) => +- matcher.with(tag("@temporal-contract/WorkflowFailedError"), (error) => handle(error)), ++ matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (error) => handle(error)), + ); +``` + ## 2. Rename the error combinators The bare combinators gained a `Cases` suffix, and their callback now receives a @@ -61,7 +78,7 @@ result.mapErr((error) => new WrappedError(error)); // 8.0 — one arm per tag in the union (abbreviated here; see the note below) result.mapErrCases((matcher) => - matcher.with(tag("@temporal-contract/WorkflowFailedError"), (error) => new WrappedError(error)), + matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (error) => new WrappedError(error)), ); ``` @@ -92,8 +109,8 @@ result.match({ ok: (value) => value, errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowValidationError"), (error) => handle(error), ), defect: (cause) => report(cause), @@ -162,8 +179,8 @@ result.match({ ok: (value) => value, errCases: (matcher) => matcher - .with(tag("@temporal-contract/RuntimeClientError"), (e) => report(e)) // ❌ remove - .with(tag("@temporal-contract/WorkflowFailedError"), (e) => handle(e)), + .with(P.tag("@temporal-contract/RuntimeClientError"), (e) => report(e)) // ❌ remove + .with(P.tag("@temporal-contract/WorkflowFailedError"), (e) => handle(e)), defect: (cause) => report(cause), }); @@ -171,7 +188,7 @@ result.match({ result.match({ ok: (value) => value, errCases: (matcher) => - matcher.with(tag("@temporal-contract/WorkflowFailedError"), (e) => handle(e)), + matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (e) => handle(e)), defect: (cause) => { if (cause instanceof RuntimeClientError) { return report(cause); // handle it here instead @@ -223,13 +240,14 @@ const retryOnce: ClientInterceptor = (args, next) => ## Checklist - [ ] All four `@temporal-contract/*` packages on the same 8.0 version -- [ ] `unthrown` resolves to `^5.0.0-beta.7` +- [ ] `unthrown` resolves to `^5.0.0` - [ ] `ts-pattern` removed if it was added for beta.5 +- [ ] `tag(...)` → `P.tag(...)` if you tracked an earlier 8.0 beta - [ ] `mapErr` / `flatMapErr` / `tapErr` / `recoverErr` → `*Cases` - [ ] `match({ err })` → `match({ errCases })` - [ ] `TypedClient.create` / `createWorker` use `isDefect()` or `.get()` -- [ ] No `tag("@temporal-contract/RuntimeClientError")` or - `tag("@temporal-contract/TechnicalError")` arms remain +- [ ] No `P.tag("@temporal-contract/RuntimeClientError")` or + `P.tag("@temporal-contract/TechnicalError")` arms remain - [ ] `pnpm typecheck` clean The exhaustive matcher does most of the work: once it compiles, the migration is diff --git a/docs/index.md b/docs/index.md index 3bfbd3a7..d0922b3b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -109,7 +109,7 @@ export const processOrder = declareWorkflow({ ```typescript [4. Client] import { TypedClient } from "@temporal-contract/client"; import { Client, Connection } from "@temporalio/client"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import { orderContract } from "./contract.js"; @@ -129,11 +129,11 @@ result.match({ ok: (output) => console.log(output.transactionId), // ✅ typed errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowNotFoundError"), - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowAlreadyStartedError"), - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowAlreadyStartedError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (error) => console.error("failed:", error.message), ), defect: (cause) => console.error("unexpected:", cause), diff --git a/docs/reference/errors.md b/docs/reference/errors.md index 61e46a18..4a3d9582 100644 --- a/docs/reference/errors.md +++ b/docs/reference/errors.md @@ -12,7 +12,7 @@ exhaustive matcher. Tags are namespaced with the package scope library's. `.name` stays the bare class name for readable logs. ```typescript -matcher.with(tag("@temporal-contract/WorkflowFailedError"), (error) => ...); +matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (error) => ...); ``` **`ValidationError` subclasses** extend Temporal's `ApplicationFailure` instead. @@ -58,7 +58,7 @@ declared error; `errorName` is the discriminant. | `cause` | optional | ```typescript -matcher.with(tag("@temporal-contract/ContractError"), (error) => { +matcher.with(P.tag("@temporal-contract/ContractError"), (error) => { switch (error.errorName) { case "CardDeclined": return error.data.reason; diff --git a/docs/tutorial/adding-signals-and-queries.md b/docs/tutorial/adding-signals-and-queries.md index 8313b8c2..c689fb26 100644 --- a/docs/tutorial/adding-signals-and-queries.md +++ b/docs/tutorial/adding-signals-and-queries.md @@ -187,7 +187,7 @@ Replace `src/client.ts`: ```typescript import { TypedClient } from "@temporal-contract/client"; import { Client, Connection } from "@temporalio/client"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import { orderContract } from "./contract.js"; @@ -235,9 +235,9 @@ result.match({ ok: (output) => console.log("charged:", output.transactionId), errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (error) => console.error("workflow failed:", error.message), ), defect: (cause) => console.error("unexpected:", cause), diff --git a/docs/tutorial/your-first-workflow.md b/docs/tutorial/your-first-workflow.md index 9b46c7ba..7bc9b5bd 100644 --- a/docs/tutorial/your-first-workflow.md +++ b/docs/tutorial/your-first-workflow.md @@ -289,7 +289,7 @@ Open a second terminal. Create `src/client.ts`: ```typescript import { TypedClient } from "@temporal-contract/client"; import { Client, Connection } from "@temporalio/client"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import { orderContract } from "./contract.js"; @@ -315,11 +315,11 @@ result.match({ }, errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowNotFoundError"), - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowAlreadyStartedError"), - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowAlreadyStartedError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (error) => console.error("workflow failed:", error.message), ), defect: (cause) => console.error("unexpected:", cause), diff --git a/examples/order-processing-client/src/client.ts b/examples/order-processing-client/src/client.ts index bf583583..33af50b8 100644 --- a/examples/order-processing-client/src/client.ts +++ b/examples/order-processing-client/src/client.ts @@ -4,7 +4,7 @@ import { type OrderSchema, } from "@temporal-contract/sample-order-processing-contract"; import { Client, Connection } from "@temporalio/client"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import type { z } from "zod"; import { logger } from "./logger.js"; @@ -127,28 +127,28 @@ async function run() { }, errCases: (matcher) => matcher - .with(tag("@temporal-contract/WorkflowNotFoundError"), (err) => + .with(P.tag("@temporal-contract/WorkflowNotFoundError"), (err) => logger.error({ error: err, orderId: order.orderId }, "❌ Workflow not found"), ) - .with(tag("@temporal-contract/WorkflowValidationError"), (err) => + .with(P.tag("@temporal-contract/WorkflowValidationError"), (err) => logger.error({ error: err, orderId: order.orderId }, "❌ Workflow validation failed"), ) // Idempotent fast-path: a workflow with this ID is already running (or in // retention). Production callers can re-fetch the existing handle; here we // just log and move on. - .with(tag("@temporal-contract/WorkflowAlreadyStartedError"), (err) => + .with(P.tag("@temporal-contract/WorkflowAlreadyStartedError"), (err) => logger.warn( { error: err, orderId: order.orderId }, "⏭️ Workflow already started — skipping", ), ) - .with(tag("@temporal-contract/WorkflowFailedError"), (err) => + .with(P.tag("@temporal-contract/WorkflowFailedError"), (err) => logger.error( { error: err, orderId: order.orderId, cause: err.cause }, "❌ Workflow completed with failure", ), ) - .with(tag("@temporal-contract/WorkflowExecutionNotFoundError"), (err) => + .with(P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (err) => logger.error( { error: err, orderId: order.orderId }, "❌ Workflow execution not found in namespace", @@ -200,19 +200,19 @@ async function run() { }, errCases: (matcher) => matcher - .with(tag("@temporal-contract/WorkflowNotFoundError"), (err) => + .with(P.tag("@temporal-contract/WorkflowNotFoundError"), (err) => logger.error({ error: err }, "❌ Workflow not found"), ) - .with(tag("@temporal-contract/WorkflowValidationError"), (err) => + .with(P.tag("@temporal-contract/WorkflowValidationError"), (err) => logger.error({ error: err }, "❌ Validation failed"), ) - .with(tag("@temporal-contract/WorkflowAlreadyStartedError"), (err) => + .with(P.tag("@temporal-contract/WorkflowAlreadyStartedError"), (err) => logger.warn({ error: err }, "⏭️ Workflow already started"), ) - .with(tag("@temporal-contract/WorkflowFailedError"), (err) => + .with(P.tag("@temporal-contract/WorkflowFailedError"), (err) => logger.error({ error: err, cause: err.cause }, "❌ Workflow completed with failure"), ) - .with(tag("@temporal-contract/WorkflowExecutionNotFoundError"), (err) => + .with(P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), (err) => logger.error({ error: err }, "❌ Workflow execution not found in namespace"), ), // A defect is an unmodeled failure (a bug) — including technical/ diff --git a/packages/client/package.json b/packages/client/package.json index 68910ecb..defa3f6e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -77,7 +77,7 @@ "peerDependencies": { "@temporalio/client": "^1", "@temporalio/common": "^1", - "unthrown": "^5.0.0-beta.7" + "unthrown": "^5.0.0" }, "engines": { "node": ">=22.19.0" diff --git a/packages/client/src/__tests__/client.spec.ts b/packages/client/src/__tests__/client.spec.ts index 71965904..2964ac99 100644 --- a/packages/client/src/__tests__/client.spec.ts +++ b/packages/client/src/__tests__/client.spec.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url"; import { it as baseIt } from "@temporal-contract/testing/extension"; import { Client } from "@temporalio/client"; import { Worker } from "@temporalio/worker"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import { describe, expect, vi, beforeEach } from "vitest"; import { TypedClient } from "../client.js"; @@ -404,11 +404,11 @@ describe("Client Package - Integration Tests", () => { }, errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowNotFoundError"), - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowAlreadyStartedError"), - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowAlreadyStartedError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), () => { throw new Error("Should not be called"); }, diff --git a/packages/client/src/client.spec.ts b/packages/client/src/client.spec.ts index 0e5d0618..d98c9d1b 100644 --- a/packages/client/src/client.spec.ts +++ b/packages/client/src/client.spec.ts @@ -11,7 +11,7 @@ import { TypedSearchAttributes, WorkflowNotFoundError as TemporalWorkflowNotFoundError, } from "@temporalio/common"; -import { tag } from "unthrown"; +import { P } from "unthrown"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { z } from "zod"; @@ -794,11 +794,11 @@ describe("TypedClient", () => { }, errCases: (matcher) => matcher.with( - tag("@temporal-contract/WorkflowNotFoundError"), - tag("@temporal-contract/WorkflowValidationError"), - tag("@temporal-contract/WorkflowAlreadyStartedError"), - tag("@temporal-contract/WorkflowFailedError"), - tag("@temporal-contract/WorkflowExecutionNotFoundError"), + P.tag("@temporal-contract/WorkflowNotFoundError"), + P.tag("@temporal-contract/WorkflowValidationError"), + P.tag("@temporal-contract/WorkflowAlreadyStartedError"), + P.tag("@temporal-contract/WorkflowFailedError"), + P.tag("@temporal-contract/WorkflowExecutionNotFoundError"), () => { throw new Error("Should not be called"); }, diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 81e12742..42120c94 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -393,8 +393,8 @@ export class TypedClient { * ok: async (handle) => { await handle.pause("maintenance"); }, * errCases: (matcher) => * matcher.with( - * tag("@temporal-contract/WorkflowNotFoundError"), - * tag("@temporal-contract/WorkflowValidationError"), + * P.tag("@temporal-contract/WorkflowNotFoundError"), + * P.tag("@temporal-contract/WorkflowValidationError"), * (error) => console.error("schedule create failed", error), * ), * defect: (cause) => console.error("unexpected failure", cause), @@ -530,9 +530,9 @@ export class TypedClient { * }, * errCases: (matcher) => * matcher.with( - * tag('@temporal-contract/WorkflowNotFoundError'), - * tag('@temporal-contract/WorkflowValidationError'), - * tag('@temporal-contract/WorkflowAlreadyStartedError'), + * P.tag('@temporal-contract/WorkflowNotFoundError'), + * P.tag('@temporal-contract/WorkflowValidationError'), + * P.tag('@temporal-contract/WorkflowAlreadyStartedError'), * (error) => console.error('Failed to start:', error), * ), * defect: (cause) => console.error('Unexpected failure:', cause), @@ -623,10 +623,10 @@ export class TypedClient { * ok: (handle) => console.log('signaled run', handle.signaledRunId), * errCases: (matcher) => * matcher.with( - * tag('@temporal-contract/WorkflowNotFoundError'), - * tag('@temporal-contract/WorkflowValidationError'), - * tag('@temporal-contract/SignalValidationError'), - * tag('@temporal-contract/WorkflowAlreadyStartedError'), + * P.tag('@temporal-contract/WorkflowNotFoundError'), + * P.tag('@temporal-contract/WorkflowValidationError'), + * P.tag('@temporal-contract/SignalValidationError'), + * P.tag('@temporal-contract/WorkflowAlreadyStartedError'), * (error) => console.error('signalWithStart failed', error), * ), * defect: (cause) => console.error('unexpected failure', cause), @@ -755,12 +755,12 @@ export class TypedClient { * ok: (output) => console.log('Order processed:', output.status), * errCases: (matcher) => * matcher.with( - * tag('@temporal-contract/ContractError'), - * tag('@temporal-contract/WorkflowNotFoundError'), - * tag('@temporal-contract/WorkflowValidationError'), - * tag('@temporal-contract/WorkflowAlreadyStartedError'), - * tag('@temporal-contract/WorkflowFailedError'), - * tag('@temporal-contract/WorkflowExecutionNotFoundError'), + * P.tag('@temporal-contract/ContractError'), + * P.tag('@temporal-contract/WorkflowNotFoundError'), + * P.tag('@temporal-contract/WorkflowValidationError'), + * P.tag('@temporal-contract/WorkflowAlreadyStartedError'), + * P.tag('@temporal-contract/WorkflowFailedError'), + * P.tag('@temporal-contract/WorkflowExecutionNotFoundError'), * (error) => console.error('Processing failed:', error), * ), * defect: (cause) => console.error('Unexpected failure:', cause), @@ -896,7 +896,7 @@ export class TypedClient { * }, * errCases: (matcher) => * matcher.with( - * tag('@temporal-contract/WorkflowNotFoundError'), + * P.tag('@temporal-contract/WorkflowNotFoundError'), * (error) => console.error('Failed to get handle:', error), * ), * defect: (cause) => console.error('Unexpected failure:', cause), diff --git a/packages/contract/package.json b/packages/contract/package.json index bac66712..00a73075 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -87,7 +87,7 @@ "vitest": "catalog:" }, "peerDependencies": { - "unthrown": "^5.0.0-beta.7" + "unthrown": "^5.0.0" }, "peerDependenciesMeta": { "unthrown": { diff --git a/packages/contract/src/errors.ts b/packages/contract/src/errors.ts index 244cf885..925664aa 100644 --- a/packages/contract/src/errors.ts +++ b/packages/contract/src/errors.ts @@ -64,7 +64,7 @@ export class TechnicalError extends TaggedError("@temporal-contract/TechnicalErr * * The unthrown `_tag` ("@temporal-contract/ContractError") discriminates a * `ContractError` from the other tagged errors in a Result's error channel - * (e.g. via `result.match({ errCases: (m) => m.with(tag("@temporal-contract/ContractError"), …) })`); + * (e.g. via `result.match({ errCases: (m) => m.with(P.tag("@temporal-contract/ContractError"), …) })`); * `errorName` then narrows to the concrete declared error. */ export class ContractError extends TaggedError( diff --git a/packages/worker/README.md b/packages/worker/README.md index 61af1f5b..dbd4df6d 100644 --- a/packages/worker/README.md +++ b/packages/worker/README.md @@ -70,6 +70,7 @@ Execute child workflows with type-safe `AsyncResult`. Supports both same-contrac ```typescript // workflows.ts import { declareWorkflow } from "@temporal-contract/worker/workflow"; +import { P } from "unthrown"; export const parentWorkflow = declareWorkflow({ workflowName: "parentWorkflow", @@ -86,9 +87,9 @@ export const parentWorkflow = declareWorkflow({ ok: (output) => console.log("Payment processed:", output), errCases: (matcher) => matcher.with( - tag("@temporal-contract/ChildWorkflowError"), - tag("@temporal-contract/ChildWorkflowCancelledError"), - tag("@temporal-contract/ChildWorkflowNotFoundError"), + P.tag("@temporal-contract/ChildWorkflowError"), + P.tag("@temporal-contract/ChildWorkflowCancelledError"), + P.tag("@temporal-contract/ChildWorkflowNotFoundError"), (error) => console.error("Payment failed:", error), ), defect: (cause) => console.error("Unexpected failure:", cause), @@ -118,9 +119,9 @@ export const parentWorkflow = declareWorkflow({ }, errCases: (matcher) => matcher.with( - tag("@temporal-contract/ChildWorkflowError"), - tag("@temporal-contract/ChildWorkflowCancelledError"), - tag("@temporal-contract/ChildWorkflowNotFoundError"), + P.tag("@temporal-contract/ChildWorkflowError"), + P.tag("@temporal-contract/ChildWorkflowCancelledError"), + P.tag("@temporal-contract/ChildWorkflowNotFoundError"), (error) => console.error("Failed to start:", error), ), defect: (cause) => console.error("Unexpected failure:", cause), diff --git a/packages/worker/package.json b/packages/worker/package.json index db2e4fb4..d7984f3e 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -96,7 +96,7 @@ "@temporalio/common": "^1", "@temporalio/worker": "^1", "@temporalio/workflow": "^1", - "unthrown": "^5.0.0-beta.7" + "unthrown": "^5.0.0" }, "engines": { "node": ">=22.19.0" diff --git a/packages/worker/src/activity-contract-errors.spec.ts b/packages/worker/src/activity-contract-errors.spec.ts index 557578f0..a9719481 100644 --- a/packages/worker/src/activity-contract-errors.spec.ts +++ b/packages/worker/src/activity-contract-errors.spec.ts @@ -1,6 +1,6 @@ import { defineContract } from "@temporal-contract/contract"; import { ContractError } from "@temporal-contract/contract/errors"; -import { Ok, Err, P, tag, type AsyncResult } from "unthrown"; +import { Ok, Err, P, type AsyncResult } from "unthrown"; /** * Runtime coverage for `declareActivitiesHandler`'s contract-declared typed * errors, middleware chain, and dependency context — the boundary where an @@ -301,7 +301,7 @@ describe("declareActivitiesHandler — middleware", () => { next().tapErrCases((matcher) => matcher.with( P.instanceOf(ApplicationFailure), - tag("@temporal-contract/ContractError"), + P.tag("@temporal-contract/ContractError"), (error) => { observed.push(error); }, diff --git a/packages/worker/src/activity.ts b/packages/worker/src/activity.ts index 7bb6c9f2..b6ec2b5e 100644 --- a/packages/worker/src/activity.ts +++ b/packages/worker/src/activity.ts @@ -29,7 +29,7 @@ import { type ContractErrorInputUnion, } from "@temporal-contract/contract/errors"; import { ApplicationFailure } from "@temporalio/common"; -import { P, tag, type AsyncResult } from "unthrown"; +import { P, type AsyncResult } from "unthrown"; import { contractErrorToApplicationFailure } from "./contract-errors.js"; import { @@ -297,7 +297,7 @@ export type ActivityMiddlewareNext< * next().tapErrCases((matcher) => * matcher.with( * P.instanceOf(ApplicationFailure), - * tag("@temporal-contract/ContractError"), + * P.tag("@temporal-contract/ContractError"), * (error) => { * logger.warn({ activityName, workflowName, error }, "activity failed"); * }, @@ -748,7 +748,7 @@ export function declareActivitiesHandler< errCases: (matcher) => matcher.with( P.instanceOf(ApplicationFailure), - tag("@temporal-contract/ContractError"), + P.tag("@temporal-contract/ContractError"), async (error) => { if (error instanceof ContractError) { throw await contractErrorToApplicationFailure( diff --git a/packages/worker/src/cancellation.ts b/packages/worker/src/cancellation.ts index 18411e06..97db19e7 100644 --- a/packages/worker/src/cancellation.ts +++ b/packages/worker/src/cancellation.ts @@ -38,7 +38,7 @@ import { makeAsyncResult } from "./internal.js"; * result.match({ * ok: (output) => { ... }, * errCases: (matcher) => - * matcher.with(tag("@temporal-contract/WorkflowCancelledError"), (error) => { + * matcher.with(P.tag("@temporal-contract/WorkflowCancelledError"), (error) => { * // error instanceof WorkflowCancelledError — graceful exit * }), * defect: (cause) => { diff --git a/packages/worker/src/workflow.ts b/packages/worker/src/workflow.ts index 7d15ad24..4a944cd2 100644 --- a/packages/worker/src/workflow.ts +++ b/packages/worker/src/workflow.ts @@ -629,9 +629,9 @@ type WorkflowContext< * }, * errCases: (matcher) => * matcher.with( - * tag('@temporal-contract/ChildWorkflowError'), - * tag('@temporal-contract/ChildWorkflowCancelledError'), - * tag('@temporal-contract/ChildWorkflowNotFoundError'), + * P.tag('@temporal-contract/ChildWorkflowError'), + * P.tag('@temporal-contract/ChildWorkflowCancelledError'), + * P.tag('@temporal-contract/ChildWorkflowNotFoundError'), * (error) => console.error('Failed to start:', error), * ), * defect: (cause) => console.error('Unexpected failure:', cause), @@ -675,9 +675,9 @@ type WorkflowContext< * ok: (output) => console.log('Payment processed:', output), * errCases: (matcher) => * matcher.with( - * tag('@temporal-contract/ChildWorkflowError'), - * tag('@temporal-contract/ChildWorkflowCancelledError'), - * tag('@temporal-contract/ChildWorkflowNotFoundError'), + * P.tag('@temporal-contract/ChildWorkflowError'), + * P.tag('@temporal-contract/ChildWorkflowCancelledError'), + * P.tag('@temporal-contract/ChildWorkflowNotFoundError'), * (error) => console.error('Processing failed:', error), * ), * defect: (cause) => console.error('Unexpected failure:', cause), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ccfa82b..6904c859 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,8 +52,8 @@ catalogs: specifier: 26.1.1 version: 26.1.1 '@unthrown/vitest': - specifier: 5.0.0-beta.7 - version: 5.0.0-beta.7 + specifier: 5.0.0 + version: 5.0.0 '@vitest/coverage-v8': specifier: 4.1.10 version: 4.1.10 @@ -100,8 +100,8 @@ catalogs: specifier: 6.0.3 version: 6.0.3 unthrown: - specifier: 5.0.0-beta.7 - version: 5.0.0-beta.7 + specifier: 5.0.0 + version: 5.0.0 valibot: specifier: 1.4.2 version: 1.4.2 @@ -211,7 +211,7 @@ importers: version: 13.1.3 unthrown: specifier: 'catalog:' - version: 5.0.0-beta.7 + version: 5.0.0 zod: specifier: 'catalog:' version: 4.4.3 @@ -273,7 +273,7 @@ importers: version: 13.1.3 unthrown: specifier: 'catalog:' - version: 5.0.0-beta.7 + version: 5.0.0 zod: specifier: 'catalog:' version: 4.4.3 @@ -295,7 +295,7 @@ importers: version: 26.1.1 '@unthrown/vitest': specifier: 'catalog:' - version: 5.0.0-beta.7(unthrown@5.0.0-beta.7)(vitest@4.1.10) + version: 5.0.0(unthrown@5.0.0)(vitest@4.1.10) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.10(vitest@4.1.10) @@ -344,7 +344,7 @@ importers: version: 26.1.1 '@unthrown/vitest': specifier: 'catalog:' - version: 5.0.0-beta.7(unthrown@5.0.0-beta.7)(vitest@4.1.10) + version: 5.0.0(unthrown@5.0.0)(vitest@4.1.10) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.10(vitest@4.1.10) @@ -362,7 +362,7 @@ importers: version: 6.0.3 unthrown: specifier: 'catalog:' - version: 5.0.0-beta.7 + version: 5.0.0 vitest: specifier: 'catalog:' version: 4.1.10(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0) @@ -390,7 +390,7 @@ importers: version: 26.1.1 '@unthrown/vitest': specifier: 'catalog:' - version: 5.0.0-beta.7(unthrown@5.0.0-beta.7)(vitest@4.1.10) + version: 5.0.0(unthrown@5.0.0)(vitest@4.1.10) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.10(vitest@4.1.10) @@ -411,7 +411,7 @@ importers: version: 6.0.3 unthrown: specifier: 'catalog:' - version: 5.0.0-beta.7 + version: 5.0.0 valibot: specifier: 'catalog:' version: 1.4.2(typescript@6.0.3) @@ -500,7 +500,7 @@ importers: version: 26.1.1 '@unthrown/vitest': specifier: 'catalog:' - version: 5.0.0-beta.7(unthrown@5.0.0-beta.7)(vitest@4.1.10) + version: 5.0.0(unthrown@5.0.0)(vitest@4.1.10) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.10(vitest@4.1.10) @@ -518,7 +518,7 @@ importers: version: 6.0.3 unthrown: specifier: 'catalog:' - version: 5.0.0-beta.7 + version: 5.0.0 vitest: specifier: 'catalog:' version: 4.1.10(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0) @@ -2520,11 +2520,11 @@ packages: '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} - '@unthrown/vitest@5.0.0-beta.7': - resolution: {integrity: sha512-CXT3DwFc8b9Vj/NSF/0Tc7xlDi5syK9+2vVYWSf0U2lmt/Om2z7olP21e67mnvXVmdzEQ8+1RNSGSqRwCtPUOg==} + '@unthrown/vitest@5.0.0': + resolution: {integrity: sha512-YbyqZE+JnPVMkDGRY+cFUY27TEbp7O+mLMF5Clf6zxneTEoU6du0G/xnjQNd/R/Gib8jyfuuLL3tDzwF6YhJiA==} engines: {node: '>=20'} peerDependencies: - unthrown: ^5.0.0-beta.7 + unthrown: ^5.0.0 vitest: ^4 '@upsetjs/venn.js@2.0.0': @@ -4796,8 +4796,8 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} - unthrown@5.0.0-beta.7: - resolution: {integrity: sha512-/eLkvTAg21BwtCQydLLok2lWYTn0CwvU/QSxJ7ugacszL3GOjZEh/rvimAjlz5B7D47u3A55ZLykX06Zyp9TCg==} + unthrown@5.0.0: + resolution: {integrity: sha512-zI93lFwRJ6Lw25HH/T7PNIWiX2bOFfF+0L3regQsrDWFARcqH83a0MLtUPmGHnFKUFXj7ZPpmyLSZOdwQbFu+g==} engines: {node: '>=20'} update-browserslist-db@1.2.3: @@ -6810,9 +6810,9 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@unthrown/vitest@5.0.0-beta.7(unthrown@5.0.0-beta.7)(vitest@4.1.10)': + '@unthrown/vitest@5.0.0(unthrown@5.0.0)(vitest@4.1.10)': dependencies: - unthrown: 5.0.0-beta.7 + unthrown: 5.0.0 vitest: 4.1.10(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0) '@upsetjs/venn.js@2.0.0': @@ -9201,7 +9201,7 @@ snapshots: universalify@0.1.2: {} - unthrown@5.0.0-beta.7: {} + unthrown@5.0.0: {} update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c9199f84..a1e272ed 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -25,7 +25,7 @@ catalog: "@temporalio/worker": 1.20.3 "@temporalio/workflow": 1.20.3 "@types/node": 26.1.1 - "@unthrown/vitest": 5.0.0-beta.7 + "@unthrown/vitest": 5.0.0 "@vitest/coverage-v8": 4.1.10 arktype: 2.2.3 knip: 6.27.0 @@ -41,7 +41,7 @@ catalog: typedoc: 0.28.20 typedoc-plugin-markdown: 4.12.0 typescript: 6.0.3 - unthrown: 5.0.0-beta.7 + unthrown: 5.0.0 valibot: 1.4.2 vitest: 4.1.10 zod: 4.4.3 From de2acdccf523fec15fe4f15441d8f6c5e3bf2799 Mon Sep 17 00:00:00 2001 From: Benoit Travers Date: Wed, 29 Jul 2026 18:40:56 +0200 Subject: [PATCH 2/2] docs: show where P comes from in P.tag snippets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review feedback: several snippets call P.tag(...) without establishing that P is unthrown's pattern namespace. Standalone and random-access contexts now import it explicitly — the eight TSDoc @example blocks (each renders as an isolated card in the API docs), the root README client example, and the troubleshoot.md entry readers reach by symptom rather than by reading top to bottom. The linear how-to guides already import P at their first use, so their later blocks are left alone. reference/errors.md keeps its one-line shape fragments free of imports — no snippet on that page has ever carried one — and states P's origin in prose instead. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 ++ docs/how-to/troubleshoot.md | 2 ++ docs/reference/errors.md | 3 +++ packages/client/src/client.ts | 10 ++++++++++ packages/worker/src/activity.ts | 3 +++ packages/worker/src/cancellation.ts | 2 ++ packages/worker/src/workflow.ts | 4 ++++ 7 files changed, 26 insertions(+) diff --git a/README.md b/README.md index 907aa61b..128f8d11 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,8 @@ export const activities = declareActivitiesHandler({ Call it — names, arguments, and results all typed, and validated at runtime: ```typescript +import { P } from "unthrown"; + const result = await client.executeWorkflow("processOrder", { workflowId: "order-123", args: { orderId: "ORD-1", customerId: "CUST-1", amount: 99.99 }, diff --git a/docs/how-to/troubleshoot.md b/docs/how-to/troubleshoot.md index 415af1f6..8f5013d3 100644 --- a/docs/how-to/troubleshoot.md +++ b/docs/how-to/troubleshoot.md @@ -250,6 +250,8 @@ Since 8.0, `TechnicalError` and `RuntimeClientError` ride the defect channel. Handle them in `defect`, `recoverDefect`, or `tapDefect`: ```typescript +import { P } from "unthrown"; + result.match({ ok: (v) => v, errCases: (m) => m.with(P.tag("@temporal-contract/WorkflowFailedError"), handle), diff --git a/docs/reference/errors.md b/docs/reference/errors.md index 4a3d9582..2421eeee 100644 --- a/docs/reference/errors.md +++ b/docs/reference/errors.md @@ -11,6 +11,9 @@ exhaustive matcher. Tags are namespaced with the package scope (`"@temporal-contract/…"`) so they never collide with your own or another library's. `.name` stays the bare class name for readable logs. +The snippets on this page are shape fragments, not runnable programs. `P` is +unthrown's pattern namespace throughout — `import { P } from "unthrown"`. + ```typescript matcher.with(P.tag("@temporal-contract/WorkflowFailedError"), (error) => ...); ``` diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 42120c94..a88e6b47 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -383,6 +383,8 @@ export class TypedClient { * * @example * ```ts + * import { P } from "unthrown"; + * * const result = await client.schedule.create("processOrder", { * scheduleId: "daily-sweep", * spec: { cronExpressions: ["0 2 * * *"] }, @@ -516,6 +518,8 @@ export class TypedClient { * * @example * ```ts + * import { P } from "unthrown"; + * * const handleResult = await client.startWorkflow('processOrder', { * workflowId: 'order-123', * args: { orderId: 'ORD-123' }, @@ -612,6 +616,8 @@ export class TypedClient { * * @example * ```ts + * import { P } from "unthrown"; + * * const result = await client.signalWithStart('processOrder', { * workflowId: 'order-123', * args: { orderId: 'ORD-123', customerId: 'CUST-1' }, @@ -744,6 +750,8 @@ export class TypedClient { * * @example * ```ts + * import { P } from "unthrown"; + * * const result = await client.executeWorkflow('processOrder', { * workflowId: 'order-123', * args: { orderId: 'ORD-123' }, @@ -888,6 +896,8 @@ export class TypedClient { * * @example * ```ts + * import { P } from "unthrown"; + * * const handleResult = await client.getHandle('processOrder', 'order-123'); * await handleResult.match({ * ok: async (handle) => { diff --git a/packages/worker/src/activity.ts b/packages/worker/src/activity.ts index b6ec2b5e..a69865a2 100644 --- a/packages/worker/src/activity.ts +++ b/packages/worker/src/activity.ts @@ -293,6 +293,9 @@ export type ActivityMiddlewareNext< * * @example Log every activity invocation and its outcome (read-only) * ```ts + * import { ApplicationFailure } from '@temporal-contract/worker/activity'; + * import { P } from "unthrown"; + * * const logging: ActivityMiddleware = ({ activityName, workflowName }, next) => * next().tapErrCases((matcher) => * matcher.with( diff --git a/packages/worker/src/cancellation.ts b/packages/worker/src/cancellation.ts index 97db19e7..f19de678 100644 --- a/packages/worker/src/cancellation.ts +++ b/packages/worker/src/cancellation.ts @@ -31,6 +31,8 @@ import { makeAsyncResult } from "./internal.js"; * * @example * ```ts + * import { P } from "unthrown"; + * * const result = await context.cancellableScope(async () => { * return await context.activities.processStep(...); * }); diff --git a/packages/worker/src/workflow.ts b/packages/worker/src/workflow.ts index 4a944cd2..26bd08f7 100644 --- a/packages/worker/src/workflow.ts +++ b/packages/worker/src/workflow.ts @@ -610,6 +610,8 @@ type WorkflowContext< * * @example * ```ts + * import { P } from "unthrown"; + * * // Same contract child workflow * const childResult = await context.startChildWorkflow(myContract, 'processPayment', { * workflowId: 'payment-123', @@ -659,6 +661,8 @@ type WorkflowContext< * * @example * ```ts + * import { P } from "unthrown"; + * * // Same contract child workflow * const result = await context.executeChildWorkflow(myContract, 'processPayment', { * workflowId: 'payment-123',