resultar 3.2.1 → 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 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 6
47
- support, and ESM-only packaging.
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 6+
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-lint check` and the `resultar-lint` TypeScript plugin |
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. Install `resultar-lint` to report discarded `Result` and
890
- `ResultAsync` values.
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-lint typescript
947
+ pnpm add -D resultar-check typescript-7@npm:typescript@rc
894
948
  ```
895
949
 
896
- Add a lint-like script:
950
+ Add a check script:
897
951
 
898
952
  ```json
899
953
  {
900
954
  "scripts": {
901
- "lint:resultar": "resultar-lint check --project tsconfig.json"
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
- For editor diagnostics, enable the TypeScript language-service plugin:
979
+ Configure Resultar rules in `tsconfig.json`:
926
980
 
927
981
  ```json
928
982
  {
929
983
  "compilerOptions": {
930
- "plugins": [{ "name": "resultar-lint", "noDiscard": "error" }]
984
+ "plugins": [{ "name": "resultar-check", "noDiscard": "error" }]
931
985
  }
932
986
  }
933
987
  ```
934
988
 
935
- TypeScript language-service plugins are editor-only by default. To make `tsc` report Resultar
936
- diagnostics during builds, patch the local TypeScript installation:
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-lint package guide](https://github.com/inaiat/resultar/blob/main/packages/resultar-lint/README.md)
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-lint`: TypeScript language-service diagnostics and no-discard CLI.
1033
- - `packages/resultar-tsgo`: TypeScript 7 `typescript@rc` wrapper plus Resultar lint validation.
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 6 Resultar lint, patched `tsc`, and Vite+ / Oxlint smoke example.
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 lint
1088
+ pnpm run check
1045
1089
  pnpm test
1046
1090
  pnpm run test:cov
1047
1091
  pnpm run build