pure-effect 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +330 -104
- package/examples/opentelemetry-example.js +173 -0
- package/examples/recording-example.js +170 -0
- package/index.d.ts +1067 -1089
- package/index.js +84 -23
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pure-effect are recorded here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and from 1.0.0 the project follows [Semantic Versioning](https://semver.org/). Before 1.0.0 a minor version could change behaviour; each such change is marked below.
|
|
4
4
|
|
|
5
|
+
## [0.16.0] - 2026-10-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`commandName` gives a Command's name as a trace records it:** `meta.name`, else the function's name, else `'anonymous'`. A test that walks a flow checks each step with it. Reading `step.cmd.name`, as the README's tests did, could not check a Command named through `meta.name` whose function is an inline arrow, since such a function has no name. Anything but a Command throws an `EffectTypeError`.
|
|
10
|
+
- **`timeTravel` warns about anonymous steps.** A trace whose steps include any named `'anonymous'`, usually inline arrow Commands, gets a warning in the narration, since a replay tells steps apart by name and two such steps that trade places after a refactor replay without complaint.
|
|
11
|
+
- **`recordEffect`'s trace keeps the input and context types.** `TraceLog` takes the input and context as type parameters, and `recordEffect` and `recorder().toTrace` fill them in, so `replayEffect(flow(trace.initialInput), trace)` needs no cast straight after recording. A trace read back from storage is typed as before.
|
|
12
|
+
- **`EffectValue`, `EffectError` and `EffectContext` read the value, error and context of an Effect**, as in `EffectError<ReturnType<typeof checkoutFlow>>`. `Parallel`'s declaration is built on them, alongside `ParallelBranches`, `ParallelValues` and `ParallelContext`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **`replayEffect` refuses `onMissing: 'execute'` on a trace that dropped entries.** A trace whose `dropped` is above 0 now rejects with a `ReplayError` before anything runs; replay it without the option to stop at the first step it lacks, or record the flow with a higher `maxEntries`. The message for a missing step now says whether the cap may have dropped it, and `'execute'` is documented for Commands that reach test doubles or only read.
|
|
17
|
+
- **An error union survives a pipeline without return annotations.** A step that cannot return a `Failure`, such as a Command with the default `next`, a Command whose `next` only succeeds, or a pure step that only returns `Success`, declared its error as `unknown`, which absorbed every other step's. So the Quick Start written in TypeScript typed `result.error` as `unknown`, and so did every pipeline that retried a Command in the recommended shape. Such a step now declares `never`, and the union holds the `Failure`s the steps can return: `Retry(Command(fn))` declares `RetryExhaustedError` alone, and a `Retry` whose fallback cannot fail declares nothing of its own. A `Command` call that gives some type arguments, such as `Command<User | null>(fetchJson, next)`, keeps the types it had, since TypeScript infers none of the others in such a call. A `Command` without a `next` takes only its result's type as a type argument. `Ask` and `Retry` given only some type arguments, as in `Ask<Product>(...)`, now refuse a step that can fail; give every type argument or none. A variable that holds the results of two different flows, which both used to type as `FailureState<unknown>`, may need a wider annotation. A Command whose function throws still ends the run with a `Failure` the union does not name, so where a flow's declared error is now only strings, or nothing, `result.error instanceof Error` stops compiling: copy the error into a variable typed `unknown` first, as in `const error: unknown = result.error`.
|
|
18
|
+
- **A `Parallel` declares its branches' errors.** Its branches were typed with one shared error that nothing inferred, so every `Parallel` declared `unknown`, and so did every pipeline holding one, including every loop written as the README writes it. A `Parallel` now declares each branch's error together with what `next` can return, and each outcome a settled `Parallel` hands `next` carries its own branch's error. A settled `Parallel` declares only `next`'s error, since a branch's failure reaches `next` as an outcome rather than escaping. `ParallelOutcomes` takes the branches instead of the values and one error, so `ParallelOutcomes<[User, Order], MyError>` is now written `ParallelOutcomes<[Effect<User, MyError>, Effect<Order, MyError>]>`, and `ParallelState` takes a sixth, optional parameter for the branches' error. `Parallel`'s type parameter is now the branches rather than their values, so an explicit `Parallel<[number]>(...)` stops compiling; leave it to be inferred.
|
|
19
|
+
- **The type declarations need TypeScript 5.1 or later.** The new `Parallel` declaration relies on 5.1, the first version that treats a mapped type over the branches as an array. TypeScript 5.1 is now checked on every change.
|
|
20
|
+
- **`Failure` keeps the literal type of its error.** Its type parameter is `const`, so `Failure('invalid_email')` is typed `'invalid_email'` rather than `string`, and an inferred error union stays exact with no `as const`. An object error is typed as readonly with literal properties, which is still assignable to a mutable object type, and an array error as a readonly tuple, which a mutable array type does not accept. A value typed `string` stays `string`.
|
|
21
|
+
- **A `next` that can only fail no longer widens a step's value.** `Command` with an explicit `next` declared its function's result as its value when `next` could only fail, and `Ask`, `Parallel` and `effectPipe` declared `unknown`, so a step that either succeeds or compensates and then fails, as in `ok ? Success(order) : Command(refund, () => Failure('declined'))`, did not compile in a pipeline, and a compensation written as a sub-pipeline failed the same way. Such a `next` now declares `never`, which adds nothing to the step's value.
|
|
22
|
+
- **`Parallel` refuses a third argument after its options.** Nothing reads a third argument once the options come second, so `Parallel(effects, { limit: 1 }, next)` ran without its `next` and handed the branch values on as they were, which only TypeScript refused. It now throws an `EffectTypeError` that gives the order, and so does a second options object passed there. `undefined` or `null` in that place still counts as absent.
|
|
23
|
+
- **An error for a `next` that returned something other than an Effect names whose `next` it was.** It said only that the flow or a continuation had returned it, so finding the culprit meant reading every `next`. It now reads, for example, `The next of Command 'cmdChargeCard' returned the number 2`, naming a Command by its name and an `Ask` or `Parallel` by its type.
|
|
24
|
+
- **Node 22 or later.** `engines` says `>=22`, where it said `>=18`: Node 18 and 20 have reached their end of life, and the tests run on 22, 24 and 26. Nothing in the library needs a newer Node, so code on an older one keeps running, but it is no longer tested there.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **A `Parallel` requires the context its branches read.** The same shared type made its context `unknown`, so `runEffect` accepted a flow with no context, or the wrong one, whenever only a `Parallel`'s branches read it, and `Ask` then got an empty object: a query filtered on `ctx.tenant` received `undefined`. A `Parallel` now needs every context its branches and its `next` read, as a pipeline needs its steps', whether the branches are listed, built with `map`, settled, or inside a pipeline.
|
|
29
|
+
- **A function that returns `Failure`s of different shapes compiles without an annotation.** A pipeline step, a `next` given to `Command`, `Ask` or `Parallel`, or an `onExhausted` fallback that could fail with, say, `Failure('cart_empty')` and `Failure({ code: 'invalid_quantity', sku })` did not compile unless its return type was written out. Such a function is now typed from its whole return, so its errors join into a union, each kept as written, and its values join the same way. Explicit type arguments type a call as before. A step that returns something other than an Effect is now reported as not assignable to `AnyEffect`, the type every step returns.
|
|
30
|
+
- **A stored trace imported as a JSON module type-checks.** `TraceEntry.threw` was declared `true`, and a JSON import widens `true` to `boolean`, so `replayEffect(flow(incident.initialInput), incident)` with `incident` imported from a trace file was refused whenever the trace held a failed step, which is how the README turns an incident into a regression test. It is declared `boolean` now; the recorder still writes only `true`.
|
|
31
|
+
- **A typed context accepts `flowName`.** `runEffect`, `recordEffect`, `replayEffect` and `timeTravel` refused `{ tenant, flowName }` for a flow whose context type does not declare `flowName`, though that key is how a run is named in traces and spans, so the README's `recordEffect` example did not compile against a typed flow. The context they take is now `RunContext`, the flow's context with an optional `flowName`.
|
|
32
|
+
- **A test walking a flow through a `Retry` can hand its `next` what the retried Command returned.** A `Retry` that starts a pipeline typed its `next` as taking the pipeline's final value, although it receives the retried Command's, so such a walk did not compile without a cast. `RetryState` takes a fourth, optional parameter for the value `next` receives, which defaults to the first.
|
|
33
|
+
|
|
5
34
|
## [0.15.0] - 2026-09-27
|
|
6
35
|
|
|
7
36
|
### Added
|
|
@@ -194,6 +223,7 @@ All notable changes to pure-effect are recorded here. The format follows [Keep a
|
|
|
194
223
|
|
|
195
224
|
- Initial release: `Success`, `Failure`, `Command`, `effectPipe`, and `runEffect`.
|
|
196
225
|
|
|
226
|
+
[0.16.0]: https://github.com/aycangulez/pure-effect/compare/v0.15.0...v0.16.0
|
|
197
227
|
[0.15.0]: https://github.com/aycangulez/pure-effect/compare/v0.14.0...v0.15.0
|
|
198
228
|
[0.14.0]: https://github.com/aycangulez/pure-effect/compare/v0.13.0...v0.14.0
|
|
199
229
|
[0.13.0]: https://github.com/aycangulez/pure-effect/compare/v0.12.0...v0.13.0
|