resultar 3.2.0 → 3.3.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/README.md +73 -29
- package/dist/index.js +135 -138
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ hidden timers, or helper code that throws.
|
|
|
17
17
|
`{ concurrency }`, and explicit unbounded execution for batch processing and validation.
|
|
18
18
|
- `AbortError` / `isAbortError` and redacted tagged-error props with `redact`, `isRedacted`, and
|
|
19
19
|
`revealRedacted` make cancellation and sensitive error metadata visible at the type boundary.
|
|
20
|
+
- `pipe` lets teams package reusable result combinators without hiding the underlying `Result` /
|
|
21
|
+
`ResultAsync` type.
|
|
20
22
|
|
|
21
23
|
## Main Functionalities
|
|
22
24
|
|
|
@@ -43,8 +45,8 @@ Use it when expected failures should be impossible to miss:
|
|
|
43
45
|
- Ignored `Result` values can be reported by a type-aware no-discard check.
|
|
44
46
|
|
|
45
47
|
Resultar began as an initial fork of `neverthrow`. The v3 line keeps the explicit wrapper model,
|
|
46
|
-
then leans into Resultar-specific tagged errors, strict service-boundary types, TypeScript
|
|
47
|
-
|
|
48
|
+
then leans into Resultar-specific tagged errors, strict service-boundary types, TypeScript 7-first
|
|
49
|
+
diagnostics, and ESM-only packaging.
|
|
48
50
|
|
|
49
51
|
## Install
|
|
50
52
|
|
|
@@ -59,7 +61,7 @@ npm install resultar
|
|
|
59
61
|
## Requirements
|
|
60
62
|
|
|
61
63
|
- Node.js 24+
|
|
62
|
-
- TypeScript
|
|
64
|
+
- TypeScript 7 for the canonical Resultar diagnostics workflow
|
|
63
65
|
- ESM only
|
|
64
66
|
|
|
65
67
|
```ts
|
|
@@ -69,6 +71,21 @@ import type { StrictResult } from 'resultar'
|
|
|
69
71
|
|
|
70
72
|
CommonJS `require('resultar')` is not exported.
|
|
71
73
|
|
|
74
|
+
## Mutation Testing
|
|
75
|
+
|
|
76
|
+
The core runtime package has a scoped Stryker setup for mutation testing:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
pnpm --filter resultar test:mutation
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The setup lives in [`stryker.conf.json`](./stryker.conf.json) and is intentionally separate from
|
|
83
|
+
`check:full`. It mutates the central runtime files under `src/`, uses the Vitest runner with
|
|
84
|
+
per-test coverage, stores incremental state under `reports/`, and keeps baseline thresholds
|
|
85
|
+
report-only while the mutation score is established. The dedicated
|
|
86
|
+
[`vitest-stryker.config.ts`](./vitest-stryker.config.ts) maps `vite-plus/test` to `vitest` so
|
|
87
|
+
Stryker can run the same tests without the `vp test` bootstrap.
|
|
88
|
+
|
|
72
89
|
## Why Teams Use It
|
|
73
90
|
|
|
74
91
|
This signature hides an expected failure:
|
|
@@ -116,11 +133,12 @@ in normal TypeScript.
|
|
|
116
133
|
| Expected failures are hidden behind `throw` | `Result<T, E>` and `ResultAsync<T, E>` in function signatures |
|
|
117
134
|
| Production errors need stable names and metadata | `createTaggedError` classes with `_tag`, template props, `cause`, and `.toJSON()` |
|
|
118
135
|
| Async flows turn into nested `try/catch` | `tryResultAsync`, `andThen`, `asyncAndThen`, `map`, `orElse`, `safeTry` |
|
|
136
|
+
| Repeated result transforms become noisy | `pipe` with small reusable combinators |
|
|
119
137
|
| Network calls need timeouts, retries, and fallback policy | `ResultAsync.timeout`, `retry`, `retryOrElse`, `race`, and `raceAll` |
|
|
120
138
|
| Batch work needs backpressure without a framework | `ResultAsync.forEach` and mapped `validateAll` with `{ concurrency }` |
|
|
121
139
|
| Resourceful work needs cleanup on every path | `ResultAsync.withResource` and native `AsyncIterable<Result<T, E>>` recipes |
|
|
122
140
|
| Boundary handlers miss cases | `matchTags` for exhaustive tagged-error handling |
|
|
123
|
-
| Results are easy to ignore | `resultar-
|
|
141
|
+
| Results are easy to ignore | `resultar-check` and the `resultar-check` TypeScript plugin |
|
|
124
142
|
| You do not want another application runtime | A small ESM library around explicit values |
|
|
125
143
|
|
|
126
144
|
Nested `try/catch` often masks the error the caller actually needs:
|
|
@@ -477,6 +495,42 @@ const validateCompanyEmail = (
|
|
|
477
495
|
)
|
|
478
496
|
```
|
|
479
497
|
|
|
498
|
+
Use `pipe` when a transform should be named and reused. A pipe step receives the current
|
|
499
|
+
`Result`/`ResultAsync` and returns the next value in the chain:
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
import type { Result, StrictResult } from 'resultar'
|
|
503
|
+
|
|
504
|
+
const normalizeEmail = <E>(result: Result<string, E>): Result<string, E> =>
|
|
505
|
+
result.map((email) => email.trim().toLowerCase())
|
|
506
|
+
|
|
507
|
+
const requireCompanyDomain = (
|
|
508
|
+
result: Result<string, InvalidEmailError>,
|
|
509
|
+
): StrictResult<string, InvalidEmailError | InvalidDomainError> =>
|
|
510
|
+
result.filterOrElse(
|
|
511
|
+
(email) => email.endsWith('@company.com'),
|
|
512
|
+
(email) => new InvalidDomainError({ domain: email.split('@')[1] ?? 'unknown' }),
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
const email = validateEmail(input).pipe(normalizeEmail, requireCompanyDomain)
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The same pattern works for async results. `pipe` is just composition; it does not catch thrown
|
|
519
|
+
errors from the pipe callbacks:
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
import type { ResultAsync } from 'resultar'
|
|
523
|
+
|
|
524
|
+
const auditUser =
|
|
525
|
+
<E>(result: ResultAsync<User, E>): ResultAsync<User, E> =>
|
|
526
|
+
result.tap((user) => logger.info({ userId: user.id }, 'user created'))
|
|
527
|
+
|
|
528
|
+
const createdEmail = validateEmail(input)
|
|
529
|
+
.asyncAndThen(ensureUserDoesNotExistAsync)
|
|
530
|
+
.andThen(insertUserAsync)
|
|
531
|
+
.pipe(auditUser, (result) => result.map((user) => user.email))
|
|
532
|
+
```
|
|
533
|
+
|
|
480
534
|
Use `catchTag` or `catchTags` for local recovery from tagged errors:
|
|
481
535
|
|
|
482
536
|
```ts
|
|
@@ -886,19 +940,19 @@ const config = safeTry({
|
|
|
886
940
|
|
|
887
941
|
## No-Discard Validation
|
|
888
942
|
|
|
889
|
-
Resultar values should not be ignored.
|
|
890
|
-
|
|
943
|
+
Resultar values should not be ignored. Use `resultar-check` to run TypeScript 7 and Resultar
|
|
944
|
+
diagnostics through one command.
|
|
891
945
|
|
|
892
946
|
```sh
|
|
893
|
-
pnpm add -D resultar-
|
|
947
|
+
pnpm add -D resultar-check typescript-7@npm:typescript@rc
|
|
894
948
|
```
|
|
895
949
|
|
|
896
|
-
Add a
|
|
950
|
+
Add a check script:
|
|
897
951
|
|
|
898
952
|
```json
|
|
899
953
|
{
|
|
900
954
|
"scripts": {
|
|
901
|
-
"
|
|
955
|
+
"check": "resultar-check -p tsconfig.json --noEmit"
|
|
902
956
|
}
|
|
903
957
|
}
|
|
904
958
|
```
|
|
@@ -922,27 +976,18 @@ The default mode is neverthrow-style `must-use`: it also reports assigned `Resul
|
|
|
922
976
|
only passed around and never consumed with `match`, `unwrapOr`, `_unsafeUnwrap`, `isOk`, `isErr`,
|
|
923
977
|
returned, or explicitly discarded. Use `--mode direct` for the lower-noise expression-only check.
|
|
924
978
|
|
|
925
|
-
|
|
979
|
+
Configure Resultar rules in `tsconfig.json`:
|
|
926
980
|
|
|
927
981
|
```json
|
|
928
982
|
{
|
|
929
983
|
"compilerOptions": {
|
|
930
|
-
"plugins": [{ "name": "resultar-
|
|
984
|
+
"plugins": [{ "name": "resultar-check", "noDiscard": "error" }]
|
|
931
985
|
}
|
|
932
986
|
}
|
|
933
987
|
```
|
|
934
988
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
```sh
|
|
939
|
-
pnpm exec resultar-lint patch
|
|
940
|
-
pnpm exec resultar-lint doctor
|
|
941
|
-
pnpm exec resultar-lint unpatch
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
For TypeScript 7 projects using `typescript@rc`, `resultar-tsgo` exposes a `tsgo` wrapper that runs
|
|
945
|
-
native TypeScript and then Resultar lint validation.
|
|
989
|
+
Oxlint is intentionally not part of the Resultar rules path; use it only for general linting if your
|
|
990
|
+
project wants it.
|
|
946
991
|
|
|
947
992
|
## API Decision Guide
|
|
948
993
|
|
|
@@ -1021,27 +1066,26 @@ More focused material:
|
|
|
1021
1066
|
- [Full guide](https://github.com/inaiat/resultar/blob/main/DOCUMENTATION.md)
|
|
1022
1067
|
- [Type-safe error handling article, English](https://github.com/inaiat/resultar/blob/main/articles/en/type-safe.md)
|
|
1023
1068
|
- [Artigo sobre tratamento de erros type-safe, Portuguese](https://github.com/inaiat/resultar/blob/main/articles/pt/type-safe.md)
|
|
1024
|
-
- [resultar-
|
|
1025
|
-
- [resultar-tsgo package guide](https://github.com/inaiat/resultar/blob/main/packages/resultar-tsgo/README.md)
|
|
1069
|
+
- [resultar-check package guide](https://github.com/inaiat/resultar/blob/main/packages/resultar-check/README.md)
|
|
1026
1070
|
|
|
1027
1071
|
## Repository
|
|
1028
1072
|
|
|
1029
1073
|
This repository is a pnpm workspace:
|
|
1030
1074
|
|
|
1031
1075
|
- `packages/resultar`: Resultar runtime package.
|
|
1032
|
-
- `packages/resultar-
|
|
1033
|
-
- `packages/resultar-
|
|
1076
|
+
- `packages/resultar-check`: TypeScript 7 plus Resultar validation.
|
|
1077
|
+
- `packages/resultar-lint`: deprecated compatibility wrapper.
|
|
1078
|
+
- `packages/resultar-tsgo`: deprecated compatibility wrapper for older installs.
|
|
1034
1079
|
- `benchmarks`: benchmark package.
|
|
1035
1080
|
- `examples/resultar`: runnable core Resultar cookbook.
|
|
1036
|
-
- `examples/lint`: TypeScript
|
|
1037
|
-
- `examples/tsgo-lint`: TypeScript 7 `typescript@rc` Resultar lint smoke example.
|
|
1081
|
+
- `examples/lint`: TypeScript 7 `resultar-check` smoke example.
|
|
1038
1082
|
|
|
1039
1083
|
Common commands:
|
|
1040
1084
|
|
|
1041
1085
|
```sh
|
|
1042
1086
|
pnpm install
|
|
1043
1087
|
pnpm run fmt:check
|
|
1044
|
-
pnpm run
|
|
1088
|
+
pnpm run check
|
|
1045
1089
|
pnpm test
|
|
1046
1090
|
pnpm run test:cov
|
|
1047
1091
|
pnpm run build
|