resultar 3.2.1 → 3.3.1

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
@@ -116,11 +118,12 @@ in normal TypeScript.
116
118
  | Expected failures are hidden behind `throw` | `Result<T, E>` and `ResultAsync<T, E>` in function signatures |
117
119
  | Production errors need stable names and metadata | `createTaggedError` classes with `_tag`, template props, `cause`, and `.toJSON()` |
118
120
  | Async flows turn into nested `try/catch` | `tryResultAsync`, `andThen`, `asyncAndThen`, `map`, `orElse`, `safeTry` |
121
+ | Repeated result transforms become noisy | `pipe` with small reusable combinators |
119
122
  | Network calls need timeouts, retries, and fallback policy | `ResultAsync.timeout`, `retry`, `retryOrElse`, `race`, and `raceAll` |
120
123
  | Batch work needs backpressure without a framework | `ResultAsync.forEach` and mapped `validateAll` with `{ concurrency }` |
121
124
  | Resourceful work needs cleanup on every path | `ResultAsync.withResource` and native `AsyncIterable<Result<T, E>>` recipes |
122
125
  | 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 |
126
+ | Results are easy to ignore | `resultar-check` and the `resultar-check` TypeScript plugin |
124
127
  | You do not want another application runtime | A small ESM library around explicit values |
125
128
 
126
129
  Nested `try/catch` often masks the error the caller actually needs:
@@ -156,6 +159,31 @@ const getPosts = (
156
159
  )
157
160
  ```
158
161
 
162
+ The same flow can be written linearly with `safeTry` when the chain gets harder to scan:
163
+
164
+ ```ts
165
+ const getPostsLinear = (
166
+ url: string,
167
+ ): StrictResultAsync<Post[], FetchPostsError | HttpClientError | ParsePostsError> =>
168
+ safeTry(async function* () {
169
+ const response = yield* tryResultAsync(
170
+ fetch(url),
171
+ (cause) => new FetchPostsError({ cause }),
172
+ )
173
+
174
+ if (response.status >= 400) {
175
+ return HttpClientError.err({ status: response.status })
176
+ }
177
+
178
+ const posts = yield* tryResultAsync(
179
+ response.json() as Promise<Post[]>,
180
+ (cause) => new ParsePostsError({ cause }),
181
+ )
182
+
183
+ return ok(posts)
184
+ })
185
+ ```
186
+
159
187
  ## Async Control Without A Runtime
160
188
 
161
189
  The 3.1 async helpers cover common production workflows while keeping plain `Promise` and
@@ -477,6 +505,42 @@ const validateCompanyEmail = (
477
505
  )
478
506
  ```
479
507
 
508
+ Use `pipe` when a transform should be named and reused. A pipe step receives the current
509
+ `Result`/`ResultAsync` and returns the next value in the chain:
510
+
511
+ ```ts
512
+ import type { Result, StrictResult } from 'resultar'
513
+
514
+ const normalizeEmail = <E>(result: Result<string, E>): Result<string, E> =>
515
+ result.map((email) => email.trim().toLowerCase())
516
+
517
+ const requireCompanyDomain = (
518
+ result: Result<string, InvalidEmailError>,
519
+ ): StrictResult<string, InvalidEmailError | InvalidDomainError> =>
520
+ result.filterOrElse(
521
+ (email) => email.endsWith('@company.com'),
522
+ (email) => new InvalidDomainError({ domain: email.split('@')[1] ?? 'unknown' }),
523
+ )
524
+
525
+ const email = validateEmail(input).pipe(normalizeEmail, requireCompanyDomain)
526
+ ```
527
+
528
+ The same pattern works for async results. `pipe` is just composition; it does not catch thrown
529
+ errors from the pipe callbacks:
530
+
531
+ ```ts
532
+ import type { ResultAsync } from 'resultar'
533
+
534
+ const auditUser =
535
+ <E>(result: ResultAsync<User, E>): ResultAsync<User, E> =>
536
+ result.tap((user) => logger.info({ userId: user.id }, 'user created'))
537
+
538
+ const createdEmail = validateEmail(input)
539
+ .asyncAndThen(ensureUserDoesNotExistAsync)
540
+ .andThen(insertUserAsync)
541
+ .pipe(auditUser, (result) => result.map((user) => user.email))
542
+ ```
543
+
480
544
  Use `catchTag` or `catchTags` for local recovery from tagged errors:
481
545
 
482
546
  ```ts
@@ -886,19 +950,19 @@ const config = safeTry({
886
950
 
887
951
  ## No-Discard Validation
888
952
 
889
- Resultar values should not be ignored. Install `resultar-lint` to report discarded `Result` and
890
- `ResultAsync` values.
953
+ Resultar values should not be ignored. Use `resultar-check` to run TypeScript 7 and Resultar
954
+ diagnostics through one command.
891
955
 
892
956
  ```sh
893
- pnpm add -D resultar-lint typescript
957
+ pnpm add -D resultar-check typescript-7@npm:typescript@rc
894
958
  ```
895
959
 
896
- Add a lint-like script:
960
+ Add a check script:
897
961
 
898
962
  ```json
899
963
  {
900
964
  "scripts": {
901
- "lint:resultar": "resultar-lint check --project tsconfig.json"
965
+ "check": "resultar-check -p tsconfig.json --noEmit"
902
966
  }
903
967
  }
904
968
  ```
@@ -922,27 +986,18 @@ The default mode is neverthrow-style `must-use`: it also reports assigned `Resul
922
986
  only passed around and never consumed with `match`, `unwrapOr`, `_unsafeUnwrap`, `isOk`, `isErr`,
923
987
  returned, or explicitly discarded. Use `--mode direct` for the lower-noise expression-only check.
924
988
 
925
- For editor diagnostics, enable the TypeScript language-service plugin:
989
+ Configure Resultar rules in `tsconfig.json`:
926
990
 
927
991
  ```json
928
992
  {
929
993
  "compilerOptions": {
930
- "plugins": [{ "name": "resultar-lint", "noDiscard": "error" }]
994
+ "plugins": [{ "name": "resultar-check", "noDiscard": "error" }]
931
995
  }
932
996
  }
933
997
  ```
934
998
 
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.
999
+ Oxlint is intentionally not part of the Resultar rules path; use it only for general linting if your
1000
+ project wants it.
946
1001
 
947
1002
  ## API Decision Guide
948
1003
 
@@ -1021,27 +1076,26 @@ More focused material:
1021
1076
  - [Full guide](https://github.com/inaiat/resultar/blob/main/DOCUMENTATION.md)
1022
1077
  - [Type-safe error handling article, English](https://github.com/inaiat/resultar/blob/main/articles/en/type-safe.md)
1023
1078
  - [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)
1079
+ - [resultar-check package guide](https://github.com/inaiat/resultar/blob/main/packages/resultar-check/README.md)
1026
1080
 
1027
1081
  ## Repository
1028
1082
 
1029
1083
  This repository is a pnpm workspace:
1030
1084
 
1031
1085
  - `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.
1086
+ - `packages/resultar-check`: TypeScript 7 plus Resultar validation.
1087
+ - `packages/resultar-lint`: deprecated compatibility wrapper.
1088
+ - `packages/resultar-tsgo`: deprecated compatibility wrapper for older installs.
1034
1089
  - `benchmarks`: benchmark package.
1035
1090
  - `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.
1091
+ - `examples/lint`: TypeScript 7 `resultar-check` smoke example.
1038
1092
 
1039
1093
  Common commands:
1040
1094
 
1041
1095
  ```sh
1042
1096
  pnpm install
1043
1097
  pnpm run fmt:check
1044
- pnpm run lint
1098
+ pnpm run check
1045
1099
  pnpm test
1046
1100
  pnpm run test:cov
1047
1101
  pnpm run build