From c8d20d8b94a70f03d49a9f2029c3353482708644 Mon Sep 17 00:00:00 2001 From: Benoit Travers Date: Thu, 3 Sep 2026 09:00:18 +0200 Subject: [PATCH] chore: release packages --- .changeset/prefer-pre-lifted.md | 42 -------------------- .changeset/saga-async.md | 55 -------------------------- packages/boxed/CHANGELOG.md | 2 + packages/boxed/package.json | 2 +- packages/core/CHANGELOG.md | 2 + packages/core/package.json | 2 +- packages/effect/CHANGELOG.md | 2 + packages/effect/package.json | 2 +- packages/neverthrow/CHANGELOG.md | 2 + packages/neverthrow/package.json | 2 +- packages/oxlint/CHANGELOG.md | 43 ++++++++++++++++++++ packages/oxlint/package.json | 2 +- packages/saga/CHANGELOG.md | 57 +++++++++++++++++++++++++++ packages/saga/package.json | 2 +- packages/standard-schema/CHANGELOG.md | 2 + packages/standard-schema/package.json | 2 +- packages/vitest/CHANGELOG.md | 2 + packages/vitest/package.json | 2 +- 18 files changed, 120 insertions(+), 105 deletions(-) delete mode 100644 .changeset/prefer-pre-lifted.md delete mode 100644 .changeset/saga-async.md create mode 100644 packages/saga/CHANGELOG.md diff --git a/.changeset/prefer-pre-lifted.md b/.changeset/prefer-pre-lifted.md deleted file mode 100644 index 906e4e8..0000000 --- a/.changeset/prefer-pre-lifted.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -"@unthrown/oxlint": minor ---- - -`unthrown/prefer-pre-lifted`: ban `.toAsync()` on a freshly constructed -`Ok(...)` / `Err(...)`. - -```ts -Ok(value).toAsync(); // → OkAsync(value) -Err(error).toAsync(); // → ErrAsync(error) -Ok().toAsync(); // → OkAsync() -Ok(undefined).toAsync(); // → OkAsync() -``` - -**The receiver is the whole test**, which is what makes this rule safe where -`prefer-ensure` was not. `prefer-ensure` had to decide whether an `Ok(x)` inside -a callback carried the same `x` the callback was handed — an identity judgement -across a scope, and the source of its false positives. Here the question is -syntactic: a call to the imported `Ok` or `Err`, immediately followed by -`.toAsync()`. So `.toAsync()` on a `Result` that already exists — a variable, a -call's return, a ternary, `fromNullable(...)` — is the combinator doing its -actual job and is never reported. - -Autofixable for the same reason: the pre-lifted name with the arguments -untouched, `Ok()` and `Ok(undefined)` both collapsing to `OkAsync()`, and the -specifier added to the existing `unthrown` import when the name is free. - -**Opt-in**, beside `no-throw` and `no-get-or-throw` — a spelling preference, not -a thesis about correctness. It is a rule rather than a convention because that -is the profile only a linter holds: `btravstack/btravstack` documented this -convention, asserted one violation, and a sweep found thirty-three. It -type-checks, tests stay green, and it is invisible in review. - -The fix never adds a value specifier to an `import type { … }`, and treats a -type-only `OkAsync` binding as taken rather than as already imported — both -would produce code that does not compile. A shadowed `undefined` parameter is -resolved through scope, so only the global collapses to `OkAsync()`. - -This repository now enables it too, and the autofix cleaned 21 sites across -`packages/boxed`, `packages/effect` and four examples. - -Closes #260. diff --git a/.changeset/saga-async.md b/.changeset/saga-async.md deleted file mode 100644 index 97228f0..0000000 --- a/.changeset/saga-async.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -"@unthrown/saga": minor ---- - -`@unthrown/saga`: a sequence whose steps carry compensating undos, unwound LIFO -the moment one fails. - -`DoAsync` sequences steps where a later one needs an earlier one's **value**. -There was nothing for the sequence where a later step's **failure** has to undo -the earlier ones — so every saga was hand-written, with two traps in the -spelling: - -- **Ordering.** The undos must run in reverse of the steps that earned them. - Nothing checked it, and getting it backwards is silent. -- **Eagerness.** An `AsyncResult` starts on construction, so an undo built - outside the failure branch runs whether or not it was needed — the hazard - `unthrown/no-async-result-race` exists for. - -```ts -const fulfilled = await SagaAsync() - .step( - () => place(order), - () => cancelPlacement(order), - ) - .step( - () => reserveStock(order), - () => releaseStock(order), - ) - .step(() => arrangeShipping(order)) - .run(); -// shipping failed → stock released, then placement cancelled, then the Err -``` - -Every argument is a **thunk**, so nothing is built before the saga reaches it. -`run()` answers the last step's value, and a failure — `Err` or `Defect` — comes -back **unchanged**, so a caller triages exactly what it would have without the -saga. `run` takes no argument; an `undo` receives its own step's value, and -either may answer a plain `Result` in place of an `AsyncResult`. An `undo` -answers `unknown` in the Ok channel and `never` in the Err one: compensation -may not invent a new way to fail, because the caller is already handling the -one that triggered it. The single -exception is a **defect inside an undo** — it wins over the failure that -triggered it, since a compensation that broke is the more urgent report, and -every remaining undo still runs first. - -It is pure control flow — no timers, no clock, no randomness — so it replays -deterministically inside a workflow sandbox, which is where its first consumer -runs. - -It ships as a satellite package rather than a core export because it is a -**pattern** built on the public surface — it operates no channel `unthrown` -does not already expose — and core is a finishable library. Installing it is -the opt-in; the compiler holds the boundary, since it imports nothing private. - -Closes #268. diff --git a/packages/boxed/CHANGELOG.md b/packages/boxed/CHANGELOG.md index 018e082..b718372 100644 --- a/packages/boxed/CHANGELOG.md +++ b/packages/boxed/CHANGELOG.md @@ -1,5 +1,7 @@ # @unthrown/boxed +## 5.8.0 + ## 5.7.0 ### Minor Changes diff --git a/packages/boxed/package.json b/packages/boxed/package.json index 0fd9aa8..9953471 100644 --- a/packages/boxed/package.json +++ b/packages/boxed/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/boxed", - "version": "5.7.0", + "version": "5.8.0", "description": "Boxed interop for unthrown", "keywords": [ "boxed", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index b6ecbef..89c2e99 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,7 @@ # unthrown +## 5.8.0 + ## 5.7.0 ## 5.6.0 diff --git a/packages/core/package.json b/packages/core/package.json index fd85916..0519b30 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "unthrown", - "version": "5.7.0", + "version": "5.8.0", "description": "Explicit errors as values, with a separate defect (panic) channel", "keywords": [ "defect", diff --git a/packages/effect/CHANGELOG.md b/packages/effect/CHANGELOG.md index 00252ed..81ba116 100644 --- a/packages/effect/CHANGELOG.md +++ b/packages/effect/CHANGELOG.md @@ -1,5 +1,7 @@ # @unthrown/effect +## 5.8.0 + ## 5.7.0 ### Minor Changes diff --git a/packages/effect/package.json b/packages/effect/package.json index 3ec69ad..012e205 100644 --- a/packages/effect/package.json +++ b/packages/effect/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/effect", - "version": "5.7.0", + "version": "5.8.0", "description": "Effect interop for unthrown", "keywords": [ "effect", diff --git a/packages/neverthrow/CHANGELOG.md b/packages/neverthrow/CHANGELOG.md index f96bb2e..75bb6d0 100644 --- a/packages/neverthrow/CHANGELOG.md +++ b/packages/neverthrow/CHANGELOG.md @@ -1,5 +1,7 @@ # @unthrown/neverthrow +## 5.8.0 + ## 5.7.0 ### Minor Changes diff --git a/packages/neverthrow/package.json b/packages/neverthrow/package.json index 7be3947..1066f6b 100644 --- a/packages/neverthrow/package.json +++ b/packages/neverthrow/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/neverthrow", - "version": "5.7.0", + "version": "5.8.0", "description": "neverthrow interop for unthrown", "keywords": [ "errors-as-values", diff --git a/packages/oxlint/CHANGELOG.md b/packages/oxlint/CHANGELOG.md index 576abe1..903fd89 100644 --- a/packages/oxlint/CHANGELOG.md +++ b/packages/oxlint/CHANGELOG.md @@ -1,5 +1,48 @@ # @unthrown/oxlint +## 5.8.0 + +### Minor Changes + +- 40f7a54: `unthrown/prefer-pre-lifted`: ban `.toAsync()` on a freshly constructed + `Ok(...)` / `Err(...)`. + + ```ts + Ok(value).toAsync(); // → OkAsync(value) + Err(error).toAsync(); // → ErrAsync(error) + Ok().toAsync(); // → OkAsync() + Ok(undefined).toAsync(); // → OkAsync() + ``` + + **The receiver is the whole test**, which is what makes this rule safe where + `prefer-ensure` was not. `prefer-ensure` had to decide whether an `Ok(x)` inside + a callback carried the same `x` the callback was handed — an identity judgement + across a scope, and the source of its false positives. Here the question is + syntactic: a call to the imported `Ok` or `Err`, immediately followed by + `.toAsync()`. So `.toAsync()` on a `Result` that already exists — a variable, a + call's return, a ternary, `fromNullable(...)` — is the combinator doing its + actual job and is never reported. + + Autofixable for the same reason: the pre-lifted name with the arguments + untouched, `Ok()` and `Ok(undefined)` both collapsing to `OkAsync()`, and the + specifier added to the existing `unthrown` import when the name is free. + + **Opt-in**, beside `no-throw` and `no-get-or-throw` — a spelling preference, not + a thesis about correctness. It is a rule rather than a convention because that + is the profile only a linter holds: `btravstack/btravstack` documented this + convention, asserted one violation, and a sweep found thirty-three. It + type-checks, tests stay green, and it is invisible in review. + + The fix never adds a value specifier to an `import type { … }`, and treats a + type-only `OkAsync` binding as taken rather than as already imported — both + would produce code that does not compile. A shadowed `undefined` parameter is + resolved through scope, so only the global collapses to `OkAsync()`. + + This repository now enables it too, and the autofix cleaned 21 sites across + `packages/boxed`, `packages/effect` and four examples. + + Closes #260. + ## 5.7.0 ## 5.6.0 diff --git a/packages/oxlint/package.json b/packages/oxlint/package.json index e1838c3..686ac7c 100644 --- a/packages/oxlint/package.json +++ b/packages/oxlint/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/oxlint", - "version": "5.7.0", + "version": "5.8.0", "description": "oxlint plugin enforcing unthrown's conventions", "keywords": [ "errors-as-values", diff --git a/packages/saga/CHANGELOG.md b/packages/saga/CHANGELOG.md new file mode 100644 index 0000000..dd301bb --- /dev/null +++ b/packages/saga/CHANGELOG.md @@ -0,0 +1,57 @@ +# @unthrown/saga + +## 5.8.0 + +### Minor Changes + +- e06b3b2: `@unthrown/saga`: a sequence whose steps carry compensating undos, unwound LIFO + the moment one fails. + + `DoAsync` sequences steps where a later one needs an earlier one's **value**. + There was nothing for the sequence where a later step's **failure** has to undo + the earlier ones — so every saga was hand-written, with two traps in the + spelling: + + - **Ordering.** The undos must run in reverse of the steps that earned them. + Nothing checked it, and getting it backwards is silent. + - **Eagerness.** An `AsyncResult` starts on construction, so an undo built + outside the failure branch runs whether or not it was needed — the hazard + `unthrown/no-async-result-race` exists for. + + ```ts + const fulfilled = await SagaAsync() + .step( + () => place(order), + () => cancelPlacement(order), + ) + .step( + () => reserveStock(order), + () => releaseStock(order), + ) + .step(() => arrangeShipping(order)) + .run(); + // shipping failed → stock released, then placement cancelled, then the Err + ``` + + Every argument is a **thunk**, so nothing is built before the saga reaches it. + `run()` answers the last step's value, and a failure — `Err` or `Defect` — comes + back **unchanged**, so a caller triages exactly what it would have without the + saga. `run` takes no argument; an `undo` receives its own step's value, and + either may answer a plain `Result` in place of an `AsyncResult`. An `undo` + answers `unknown` in the Ok channel and `never` in the Err one: compensation + may not invent a new way to fail, because the caller is already handling the + one that triggered it. The single + exception is a **defect inside an undo** — it wins over the failure that + triggered it, since a compensation that broke is the more urgent report, and + every remaining undo still runs first. + + It is pure control flow — no timers, no clock, no randomness — so it replays + deterministically inside a workflow sandbox, which is where its first consumer + runs. + + It ships as a satellite package rather than a core export because it is a + **pattern** built on the public surface — it operates no channel `unthrown` + does not already expose — and core is a finishable library. Installing it is + the opt-in; the compiler holds the boundary, since it imports nothing private. + + Closes #268. diff --git a/packages/saga/package.json b/packages/saga/package.json index 68840ab..f914090 100644 --- a/packages/saga/package.json +++ b/packages/saga/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/saga", - "version": "5.7.0", + "version": "5.8.0", "description": "A saga for unthrown's AsyncResult: steps with compensating undos, unwound LIFO", "keywords": [ "compensation", diff --git a/packages/standard-schema/CHANGELOG.md b/packages/standard-schema/CHANGELOG.md index dc78433..01dcfbe 100644 --- a/packages/standard-schema/CHANGELOG.md +++ b/packages/standard-schema/CHANGELOG.md @@ -1,5 +1,7 @@ # @unthrown/standard-schema +## 5.8.0 + ## 5.7.0 ### Minor Changes diff --git a/packages/standard-schema/package.json b/packages/standard-schema/package.json index 7e3a599..8ed3dda 100644 --- a/packages/standard-schema/package.json +++ b/packages/standard-schema/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/standard-schema", - "version": "5.7.0", + "version": "5.8.0", "description": "Standard Schema (Zod, Valibot, ArkType, …) interop for unthrown", "keywords": [ "arktype", diff --git a/packages/vitest/CHANGELOG.md b/packages/vitest/CHANGELOG.md index c272561..f0a0ba8 100644 --- a/packages/vitest/CHANGELOG.md +++ b/packages/vitest/CHANGELOG.md @@ -1,5 +1,7 @@ # @unthrown/vitest +## 5.8.0 + ## 5.7.0 ## 5.6.0 diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 1e29355..9d72fd3 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -1,6 +1,6 @@ { "name": "@unthrown/vitest", - "version": "5.7.0", + "version": "5.8.0", "description": "Vitest matchers for unthrown", "keywords": [ "errors-as-values",