massaman 0.0.1-rc.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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/array/index.d.mts +2 -0
- package/dist/array/index.mjs +2 -0
- package/dist/array-C8MqjyiP.mjs +191 -0
- package/dist/array-C8MqjyiP.mjs.map +1 -0
- package/dist/control/index.d.mts +2 -0
- package/dist/control/index.mjs +2 -0
- package/dist/control-B4mDPBn6.mjs +164 -0
- package/dist/control-B4mDPBn6.mjs.map +1 -0
- package/dist/conversion/index.d.mts +2 -0
- package/dist/conversion/index.mjs +2 -0
- package/dist/conversion-ByBXBR5i.mjs +189 -0
- package/dist/conversion-ByBXBR5i.mjs.map +1 -0
- package/dist/error/index.d.mts +2 -0
- package/dist/error/index.mjs +2 -0
- package/dist/function/index.d.mts +2 -0
- package/dist/function/index.mjs +2 -0
- package/dist/function-2-6QB4m7.mjs +122 -0
- package/dist/function-2-6QB4m7.mjs.map +1 -0
- package/dist/index-BkyNt8th.d.mts +20 -0
- package/dist/index-BkyNt8th.d.mts.map +1 -0
- package/dist/index-Br9PO6mU.d.mts +155 -0
- package/dist/index-Br9PO6mU.d.mts.map +1 -0
- package/dist/index-BuBpCB73.d.mts +197 -0
- package/dist/index-BuBpCB73.d.mts.map +1 -0
- package/dist/index-COFf0Pih.d.mts +128 -0
- package/dist/index-COFf0Pih.d.mts.map +1 -0
- package/dist/index-DqsBQ7Pp.d.mts +120 -0
- package/dist/index-DqsBQ7Pp.d.mts.map +1 -0
- package/dist/index-Dw_tuZdb.d.mts +134 -0
- package/dist/index-Dw_tuZdb.d.mts.map +1 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.mjs +12 -0
- package/dist/math/index.d.mts +2 -0
- package/dist/math/index.mjs +2 -0
- package/dist/object/index.d.mts +2 -0
- package/dist/object/index.mjs +2 -0
- package/dist/object-BHnp9Sr9.mjs +27 -0
- package/dist/object-BHnp9Sr9.mjs.map +1 -0
- package/dist/pattern/index.d.mts +2 -0
- package/dist/pattern/index.mjs +2 -0
- package/dist/predicate/index.d.mts +2 -0
- package/dist/predicate/index.mjs +2 -0
- package/dist/predicate-CYzSOMLW.mjs +108 -0
- package/dist/predicate-CYzSOMLW.mjs.map +1 -0
- package/dist/promise/index.d.mts +2 -0
- package/dist/promise/index.mjs +2 -0
- package/dist/string/index.d.mts +2 -0
- package/dist/string/index.mjs +2 -0
- package/package.json +108 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zac Rosenbauer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="assets/banner.svg" alt="tadka" width="100%" />
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
# tadka
|
|
6
|
+
|
|
7
|
+
A comprehensive functional programming library for TypeScript — a curated unified surface over [es-toolkit](https://es-toolkit.slash.page) and [ts-pattern](https://github.com/gvergnaud/ts-pattern), with custom utilities for composition, predicates, Result-style error handling, and pattern matching.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install tadka
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# pnpm
|
|
17
|
+
pnpm add tadka
|
|
18
|
+
|
|
19
|
+
# yarn
|
|
20
|
+
yarn add tadka
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
| Feature | Description |
|
|
26
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| **Unified FP surface** | Array, object, string, function, math, predicate, promise — one curated import root, tree-shakeable. |
|
|
28
|
+
| **Result-style error handling** | `attempt` / `attemptAsync` / `ok` / `err` / `isOk` / `isErr` / `unwrap` — no thrown exceptions. |
|
|
29
|
+
| **Pattern matching** | Full [ts-pattern](https://github.com/gvergnaud/ts-pattern) re-export under `tadka/pattern`. |
|
|
30
|
+
| **Variadic type-guard narrowing** | `allPass([isString, isNotEmpty])` returns a guard that narrows to `string` — composes any arity. |
|
|
31
|
+
| **Async-aware pipelines** | `flowAsync` chains promise-returning functions with end-to-end type inference (up to 7 steps). |
|
|
32
|
+
| **Safe error normalization** | `toError` + `stringify` handle non-`Error` throws, circular refs, Maps/Sets — no more `[object Object]`. |
|
|
33
|
+
| **Branching combinators** | `when` / `unless` / `ifElse` for point-free conditionals inside `flow` pipelines. |
|
|
34
|
+
| **100% test coverage** | Enforced by CI — every line, branch, and function is verified. |
|
|
35
|
+
| **ESM-only, `sideEffects: false`** | 12 subpath exports, tree-shakeable, zero side effects. |
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { flow, compact, uniq, toArray } from 'tadka'
|
|
41
|
+
|
|
42
|
+
const normalize = flow(toArray, compact, uniq)
|
|
43
|
+
normalize([1, null, 2, 1, null]) // [1, 2]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { match, P } from 'tadka/pattern'
|
|
48
|
+
|
|
49
|
+
const label = match(status)
|
|
50
|
+
.with('active', () => 'Live')
|
|
51
|
+
.with('draft', () => 'Draft')
|
|
52
|
+
.with(P._, () => 'N/A')
|
|
53
|
+
.exhaustive()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { attempt, isOk } from 'tadka/control'
|
|
58
|
+
|
|
59
|
+
const result = attempt(() => JSON.parse(raw))
|
|
60
|
+
if (isOk(result)) {
|
|
61
|
+
console.log(result.value)
|
|
62
|
+
} else {
|
|
63
|
+
console.error(result.error)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { allPass } from 'tadka/predicate'
|
|
69
|
+
import { isString } from 'es-toolkit/predicate'
|
|
70
|
+
import { isNotEmpty } from 'tadka/predicate'
|
|
71
|
+
|
|
72
|
+
const isNonEmptyString = allPass([isString, isNotEmpty])
|
|
73
|
+
if (isNonEmptyString(x)) {
|
|
74
|
+
// x is narrowed to string
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Error-safe composition
|
|
79
|
+
|
|
80
|
+
`flow` and `flowAsync` don't catch throws — wrap the pipeline with `attempt` / `attemptAsync` to recover:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { flow, attempt, isOk } from 'tadka'
|
|
84
|
+
|
|
85
|
+
const parseUser = flow(JSON.parse, (u) => u.name.trim())
|
|
86
|
+
|
|
87
|
+
const result = attempt(() => parseUser(rawInput))
|
|
88
|
+
if (isOk(result)) {
|
|
89
|
+
return result.value
|
|
90
|
+
}
|
|
91
|
+
return 'unknown'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Modules
|
|
95
|
+
|
|
96
|
+
| Subpath | Description |
|
|
97
|
+
| ------------------ | -------------------------------------------------------------------------- |
|
|
98
|
+
| `tadka` | Root barrel — re-exports everything below |
|
|
99
|
+
| `tadka/array` | Array utilities (chunk, groupBy, sortWith, scan, unfold, …) |
|
|
100
|
+
| `tadka/object` | Object utilities (evolve, pick, omit, merge, mapKeys, …) |
|
|
101
|
+
| `tadka/function` | Composition (flow, flowAsync, tap, call, curry, when, unless, ifElse, …) |
|
|
102
|
+
| `tadka/predicate` | Type guards, combinators with variadic narrowing |
|
|
103
|
+
| `tadka/conversion` | Coercion + safe stringification (toError, stringify, toNumber, toArray, …) |
|
|
104
|
+
| `tadka/string` | String transforms (camelCase, kebabCase, trim, …) |
|
|
105
|
+
| `tadka/math` | Numeric utilities (clamp, sum, mean, range, …) |
|
|
106
|
+
| `tadka/promise` | Async helpers (delay, timeout, Mutex, Semaphore) |
|
|
107
|
+
| `tadka/control` | Result-style error handling (attempt, ok, err, isOk, isErr, unwrap, …) |
|
|
108
|
+
| `tadka/pattern` | Pattern matching (match, P, isMatching) |
|
|
109
|
+
| `tadka/error` | Error types (AbortError, TimeoutError) |
|
|
110
|
+
|
|
111
|
+
## Custom Utilities
|
|
112
|
+
|
|
113
|
+
Utilities that aren't in es-toolkit or ts-pattern — implemented in this package:
|
|
114
|
+
|
|
115
|
+
| Module | Utility | Description |
|
|
116
|
+
| ------------ | ------------------------------------------------ | -------------------------------------------------------------- |
|
|
117
|
+
| `array` | `adjust`, `scan`, `unfold`, `dropRepeats` | Index update, accumulator scan, seed unfold, consecutive dedup |
|
|
118
|
+
| `array` | `reduceWhile`, `ascend`, `descend`, `sortWith` | Short-circuit reduce, comparator factories, multi-key sort |
|
|
119
|
+
| `function` | `flowAsync`, `tap` | Async composition, side-effect-in-pipeline |
|
|
120
|
+
| `function` | `when`, `unless`, `ifElse`, `call`, `callAsync` | Point-free conditionals, named application |
|
|
121
|
+
| `object` | `evolve` | Apply transforms per key |
|
|
122
|
+
| `predicate` | `allPass`, `anyPass`, `both`, `either` | Variadic-narrowing predicate combinators |
|
|
123
|
+
| `predicate` | `isArray`, `isObject`, `isEmpty`, `isNotEmpty` | Type guards + emptiness |
|
|
124
|
+
| `predicate` | `isFiniteNumber`, `isInteger`, `isNaN` | Strict numeric guards (shadowing the broken global `isNaN`) |
|
|
125
|
+
| `control` | `attempt`, `attemptAsync` | Wrap throwing/rejecting code into a `Result` |
|
|
126
|
+
| `control` | `ok`, `err`, `isOk`, `isErr`, `unwrap` | Construct, narrow, and unwrap a `Result` |
|
|
127
|
+
| `conversion` | `toError`, `stringify` | Normalize unknown thrown values + safe JSON of any value |
|
|
128
|
+
| `conversion` | `toNumber`, `toString`, `toBoolean`, `toInteger` | Stable coercion primitives |
|
|
129
|
+
| `conversion` | `toFinite`, `toArray` | Safe coercion with fallback semantics |
|
|
130
|
+
|
|
131
|
+
## Requirements
|
|
132
|
+
|
|
133
|
+
- Node.js >= 24.0.0
|
|
134
|
+
- ESM only (`require()` / CommonJS is not supported)
|
|
135
|
+
- TypeScript >= 5.9 recommended (variadic narrowing benefits from recent inference)
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT © Zac Rosenbauer
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { $ as unionWith, A as last, B as remove, C as initial, D as isSubset, E as intersectionWith, F as orderBy, G as tail, H as sampleSize, I as partition, J as takeRightWhile, K as take, L as pull, M as mapAsync, N as maxBy, O as isSubsetWith, P as minBy, Q as unionBy, R as pullAt, S as head, T as intersectionBy, U as shuffle, V as sample, W as sortBy, X as toFilled, Y as takeWhile, Z as union, _ as flatten, _t as scan, a as difference, at as windowed, b as forEachRight, bt as adjust, c as drop, ct as xorBy, d as dropWhile, dt as zipObject, et as uniq, f as fill, ft as zipWith, g as flatMapDeep, gt as sortWith, h as flatMapAsync, ht as descend, i as countBy, it as unzipWith, j as limitAsync, k as keyBy, l as dropRight, lt as xorWith, m as flatMap, mt as ascend, n as chunk, nt as uniqWith, o as differenceBy, ot as without, p as filterAsync, pt as unfold, q as takeRight, r as compact, rt as unzip, s as differenceWith, st as xor, t as at, tt as uniqBy, u as dropRightWhile, ut as zip, v as flattenDeep, vt as reduceWhile, w as intersection, x as groupBy, y as forEachAsync, yt as dropRepeats, z as reduceAsync } from "../index-Dw_tuZdb.mjs";
|
|
2
|
+
export { adjust, ascend, at, chunk, compact, countBy, descend, difference, differenceBy, differenceWith, drop, dropRepeats, dropRight, dropRightWhile, dropWhile, fill, filterAsync, flatMap, flatMapAsync, flatMapDeep, flatten, flattenDeep, forEachAsync, forEachRight, groupBy, head, initial, intersection, intersectionBy, intersectionWith, isSubset, isSubsetWith, keyBy, last, limitAsync, mapAsync, maxBy, minBy, orderBy, partition, pull, pullAt, reduceAsync, reduceWhile, remove, sample, sampleSize, scan, shuffle, sortBy, sortWith, tail, take, takeRight, takeRightWhile, takeWhile, toFilled, unfold, union, unionBy, unionWith, uniq, uniqBy, uniqWith, unzip, unzipWith, windowed, without, xor, xorBy, xorWith, zip, zipObject, zipWith };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { $ as unionWith, A as last, B as remove, C as initial, D as isSubset, E as intersectionWith, F as orderBy, G as tail, H as sampleSize, I as partition, J as takeRightWhile, K as take, L as pull, M as mapAsync, N as maxBy, O as isSubsetWith, P as minBy, Q as unionBy, R as pullAt, S as head, T as intersectionBy, U as shuffle, V as sample, W as sortBy, X as toFilled, Y as takeWhile, Z as union, _ as flatten, _t as scan, a as difference, at as windowed, b as forEachRight, bt as adjust, c as drop, ct as xorBy, d as dropWhile, dt as zipObject, et as uniq, f as fill, ft as zipWith, g as flatMapDeep, gt as sortWith, h as flatMapAsync, ht as descend, i as countBy, it as unzipWith, j as limitAsync, k as keyBy, l as dropRight, lt as xorWith, m as flatMap, mt as ascend, n as chunk, nt as uniqWith, o as differenceBy, ot as without, p as filterAsync, pt as unfold, q as takeRight, r as compact, rt as unzip, s as differenceWith, st as xor, t as at, tt as uniqBy, u as dropRightWhile, ut as zip, v as flattenDeep, vt as reduceWhile, w as intersection, x as groupBy, y as forEachAsync, yt as dropRepeats, z as reduceAsync } from "../array-C8MqjyiP.mjs";
|
|
2
|
+
export { adjust, ascend, at, chunk, compact, countBy, descend, difference, differenceBy, differenceWith, drop, dropRepeats, dropRight, dropRightWhile, dropWhile, fill, filterAsync, flatMap, flatMapAsync, flatMapDeep, flatten, flattenDeep, forEachAsync, forEachRight, groupBy, head, initial, intersection, intersectionBy, intersectionWith, isSubset, isSubsetWith, keyBy, last, limitAsync, mapAsync, maxBy, minBy, orderBy, partition, pull, pullAt, reduceAsync, reduceWhile, remove, sample, sampleSize, scan, shuffle, sortBy, sortWith, tail, take, takeRight, takeRightWhile, takeWhile, toFilled, unfold, union, unionBy, unionWith, uniq, uniqBy, uniqWith, unzip, unzipWith, windowed, without, xor, xorBy, xorWith, zip, zipObject, zipWith };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { at, chunk, compact, countBy, difference, differenceBy, differenceWith, drop, dropRight, dropRightWhile, dropWhile, fill, filterAsync, flatMap, flatMapAsync, flatMapDeep, flatten, flattenDeep, forEachAsync, forEachRight, groupBy, head, initial, intersection, intersectionBy, intersectionWith, isSubset, isSubsetWith, keyBy, last, limitAsync, mapAsync, maxBy, minBy, orderBy, partition, pull, pullAt, reduceAsync, remove, sample, sampleSize, shuffle, sortBy, tail, take, takeRight, takeRightWhile, takeWhile, toFilled, union, unionBy, unionWith, uniq, uniqBy, uniqWith, unzip, unzipWith, windowed, without, xor, xorBy, xorWith, zip, zipObject, zipWith } from "es-toolkit/array";
|
|
2
|
+
//#region src/array/adjust.ts
|
|
3
|
+
/**
|
|
4
|
+
* Applies a function to the element at the given index, returning a new array.
|
|
5
|
+
*
|
|
6
|
+
* The original array is not modified. If the index is out of bounds, the
|
|
7
|
+
* array is returned unchanged.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* adjust([1, 2, 3], 1, (n) => n * 10)
|
|
12
|
+
* // [1, 20, 3]
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
function adjust(array, index, fn) {
|
|
16
|
+
const result = [...array];
|
|
17
|
+
if (index >= 0 && index < array.length) result[index] = fn(result[index]);
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/array/dropRepeats.ts
|
|
22
|
+
/**
|
|
23
|
+
* Removes consecutively repeated elements from an array using strict
|
|
24
|
+
* equality (`===`).
|
|
25
|
+
*
|
|
26
|
+
* Only adjacent duplicates are removed — non-adjacent duplicates are kept.
|
|
27
|
+
* Use {@link uniq} from es-toolkit to remove all duplicates regardless of position.
|
|
28
|
+
*
|
|
29
|
+
* Quirks (inherent to `===`):
|
|
30
|
+
* - `NaN !== NaN`, so adjacent `NaN`s are *not* collapsed.
|
|
31
|
+
* - `-0 === 0`, so adjacent `-0` and `0` *are* collapsed.
|
|
32
|
+
* - Object/array elements compare by reference, not structure.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* dropRepeats([1, 1, 2, 3, 3, 2, 1])
|
|
37
|
+
* // [1, 2, 3, 2, 1]
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function dropRepeats(array) {
|
|
41
|
+
return array.reduce((acc, item, index) => {
|
|
42
|
+
if (index === 0 || item !== array[index - 1]) acc.push(item);
|
|
43
|
+
return acc;
|
|
44
|
+
}, []);
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/array/reduceWhile.ts
|
|
48
|
+
/**
|
|
49
|
+
* Like `reduce` but stops early when the predicate returns `false`.
|
|
50
|
+
*
|
|
51
|
+
* The predicate receives the current accumulator and element. When it
|
|
52
|
+
* returns `false`, the current accumulator is returned without processing
|
|
53
|
+
* the remaining elements.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // Sum until we hit a negative number
|
|
58
|
+
* reduceWhile(
|
|
59
|
+
* [1, 2, 3, -1, 5],
|
|
60
|
+
* (acc, n) => n >= 0,
|
|
61
|
+
* (acc, n) => acc + n,
|
|
62
|
+
* 0,
|
|
63
|
+
* )
|
|
64
|
+
* // 6
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
function reduceWhile(array, predicate, fn, initial) {
|
|
68
|
+
let acc = initial;
|
|
69
|
+
for (let index = 0; index < array.length; index += 1) {
|
|
70
|
+
const value = array[index];
|
|
71
|
+
if (!predicate(acc, value)) return acc;
|
|
72
|
+
acc = fn(acc, value, index);
|
|
73
|
+
}
|
|
74
|
+
return acc;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/array/scan.ts
|
|
78
|
+
/**
|
|
79
|
+
* Like `reduce` but returns an array of all intermediate accumulator values.
|
|
80
|
+
*
|
|
81
|
+
* The result array starts with the initial value and ends with the final
|
|
82
|
+
* accumulator, so its length is `array.length + 1`.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* scan([1, 2, 3, 4], (acc, n) => acc + n, 0)
|
|
87
|
+
* // [0, 1, 3, 6, 10]
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
function scan(array, fn, initial) {
|
|
91
|
+
return array.reduce((result, item, index) => {
|
|
92
|
+
const prev = result[result.length - 1];
|
|
93
|
+
result.push(fn(prev, item, index));
|
|
94
|
+
return result;
|
|
95
|
+
}, [initial]);
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/array/sorting.ts
|
|
99
|
+
/**
|
|
100
|
+
* Creates an ascending comparator from an accessor function.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* users.sort(ascend(u => u.name))
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function ascend(fn) {
|
|
108
|
+
return (a, b) => {
|
|
109
|
+
const aa = fn(a);
|
|
110
|
+
const bb = fn(b);
|
|
111
|
+
if (aa < bb) return -1;
|
|
112
|
+
if (aa > bb) return 1;
|
|
113
|
+
return 0;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Creates a descending comparator from an accessor function.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* users.sort(descend(u => u.age))
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
function descend(fn) {
|
|
125
|
+
return (a, b) => {
|
|
126
|
+
const aa = fn(a);
|
|
127
|
+
const bb = fn(b);
|
|
128
|
+
if (aa > bb) return -1;
|
|
129
|
+
if (aa < bb) return 1;
|
|
130
|
+
return 0;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Sorts an array using multiple comparators in priority order.
|
|
135
|
+
*
|
|
136
|
+
* The first comparator has highest priority. When it returns 0 (tie),
|
|
137
|
+
* the next comparator is used, and so on.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* sortWith(users, [
|
|
142
|
+
* ascend(u => u.department),
|
|
143
|
+
* descend(u => u.age),
|
|
144
|
+
* ])
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
function sortWith(array, comparators) {
|
|
148
|
+
return [...array].toSorted((a, b) => {
|
|
149
|
+
for (const cmp of comparators) {
|
|
150
|
+
const result = cmp(a, b);
|
|
151
|
+
if (result !== 0) return result;
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/array/unfold.ts
|
|
158
|
+
/**
|
|
159
|
+
* Builds an array from a seed value using an iterator function.
|
|
160
|
+
*
|
|
161
|
+
* The iterator receives the current seed and returns either a `[value, nextSeed]`
|
|
162
|
+
* tuple to continue, or `false` to stop.
|
|
163
|
+
*
|
|
164
|
+
* Dual of `reduce` — reduce collapses a list into a value, unfold expands
|
|
165
|
+
* a value into a list.
|
|
166
|
+
*
|
|
167
|
+
* **Termination is the caller's responsibility.** If `fn` never returns
|
|
168
|
+
* `false`, `unfold` will run until memory is exhausted. For bounded
|
|
169
|
+
* generation, encode a counter or limit into the seed.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* unfold((n) => (n > 0 ? [n, n - 1] : false), 5)
|
|
174
|
+
* // [5, 4, 3, 2, 1]
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
function unfold(fn, seed) {
|
|
178
|
+
const result = [];
|
|
179
|
+
let current = seed;
|
|
180
|
+
let pair = fn(current);
|
|
181
|
+
while (pair !== false) {
|
|
182
|
+
result.push(pair[0]);
|
|
183
|
+
current = pair[1];
|
|
184
|
+
pair = fn(current);
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
export { unionWith as $, last as A, remove as B, initial as C, isSubset as D, intersectionWith as E, orderBy as F, tail as G, sampleSize as H, partition as I, takeRightWhile as J, take as K, pull as L, mapAsync as M, maxBy as N, isSubsetWith as O, minBy as P, unionBy as Q, pullAt as R, head as S, intersectionBy as T, shuffle as U, sample as V, sortBy as W, toFilled as X, takeWhile as Y, union as Z, flatten as _, scan as _t, difference as a, windowed as at, forEachRight as b, adjust as bt, drop as c, xorBy as ct, dropWhile as d, zipObject as dt, uniq as et, fill as f, zipWith as ft, flatMapDeep as g, sortWith as gt, flatMapAsync as h, descend as ht, countBy as i, unzipWith as it, limitAsync as j, keyBy as k, dropRight as l, xorWith as lt, flatMap as m, ascend as mt, chunk as n, uniqWith as nt, differenceBy as o, without as ot, filterAsync as p, unfold as pt, takeRight as q, compact as r, unzip as rt, differenceWith as s, xor as st, at as t, uniqBy as tt, dropRightWhile as u, zip as ut, flattenDeep as v, reduceWhile as vt, intersection as w, groupBy as x, forEachAsync as y, dropRepeats as yt, reduceAsync as z };
|
|
190
|
+
|
|
191
|
+
//# sourceMappingURL=array-C8MqjyiP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array-C8MqjyiP.mjs","names":[],"sources":["../src/array/adjust.ts","../src/array/dropRepeats.ts","../src/array/reduceWhile.ts","../src/array/scan.ts","../src/array/sorting.ts","../src/array/unfold.ts"],"sourcesContent":["/**\n * Applies a function to the element at the given index, returning a new array.\n *\n * The original array is not modified. If the index is out of bounds, the\n * array is returned unchanged.\n *\n * @example\n * ```ts\n * adjust([1, 2, 3], 1, (n) => n * 10)\n * // [1, 20, 3]\n * ```\n */\nexport function adjust<T>(array: readonly T[], index: number, fn: (value: T) => T): T[] {\n const result = [...array]\n if (index >= 0 && index < array.length) {\n // oxlint-disable-next-line security/detect-object-injection -- Numeric array index parameter, not user-controlled key\n result[index] = fn(result[index])\n }\n return result\n}\n","/**\n * Removes consecutively repeated elements from an array using strict\n * equality (`===`).\n *\n * Only adjacent duplicates are removed — non-adjacent duplicates are kept.\n * Use {@link uniq} from es-toolkit to remove all duplicates regardless of position.\n *\n * Quirks (inherent to `===`):\n * - `NaN !== NaN`, so adjacent `NaN`s are *not* collapsed.\n * - `-0 === 0`, so adjacent `-0` and `0` *are* collapsed.\n * - Object/array elements compare by reference, not structure.\n *\n * @example\n * ```ts\n * dropRepeats([1, 1, 2, 3, 3, 2, 1])\n * // [1, 2, 3, 2, 1]\n * ```\n */\nexport function dropRepeats<T>(array: readonly T[]): T[] {\n return array.reduce<T[]>((acc, item, index) => {\n if (index === 0 || item !== array[index - 1]) {\n acc.push(item)\n }\n return acc\n }, [])\n}\n","/* oxlint-disable functional/no-let -- iterative impl avoids stack overflow on large arrays */\n\n/**\n * Like `reduce` but stops early when the predicate returns `false`.\n *\n * The predicate receives the current accumulator and element. When it\n * returns `false`, the current accumulator is returned without processing\n * the remaining elements.\n *\n * @example\n * ```ts\n * // Sum until we hit a negative number\n * reduceWhile(\n * [1, 2, 3, -1, 5],\n * (acc, n) => n >= 0,\n * (acc, n) => acc + n,\n * 0,\n * )\n * // 6\n * ```\n */\nexport function reduceWhile<T, R>(\n array: readonly T[],\n predicate: (accumulator: R, value: T) => boolean,\n fn: (accumulator: R, value: T, index: number) => R,\n initial: R\n): R {\n let acc = initial\n for (let index = 0; index < array.length; index += 1) {\n // oxlint-disable-next-line security/detect-object-injection -- Locally computed numeric array index\n const value = array[index]\n if (!predicate(acc, value)) {\n return acc\n }\n acc = fn(acc, value, index)\n }\n return acc\n}\n","/**\n * Like `reduce` but returns an array of all intermediate accumulator values.\n *\n * The result array starts with the initial value and ends with the final\n * accumulator, so its length is `array.length + 1`.\n *\n * @example\n * ```ts\n * scan([1, 2, 3, 4], (acc, n) => acc + n, 0)\n * // [0, 1, 3, 6, 10]\n * ```\n */\nexport function scan<T, R>(\n array: readonly T[],\n fn: (accumulator: R, value: T, index: number) => R,\n initial: R\n): R[] {\n return array.reduce<R[]>(\n (result, item, index) => {\n const prev = result[result.length - 1]\n result.push(fn(prev, item, index))\n return result\n },\n [initial]\n )\n}\n","/**\n * Creates an ascending comparator from an accessor function.\n *\n * @example\n * ```ts\n * users.sort(ascend(u => u.name))\n * ```\n */\nexport function ascend<T>(fn: (value: T) => number | string): (a: T, b: T) => number {\n return (a: T, b: T) => {\n const aa = fn(a)\n const bb = fn(b)\n if (aa < bb) {\n return -1\n }\n if (aa > bb) {\n return 1\n }\n return 0\n }\n}\n\n/**\n * Creates a descending comparator from an accessor function.\n *\n * @example\n * ```ts\n * users.sort(descend(u => u.age))\n * ```\n */\nexport function descend<T>(fn: (value: T) => number | string): (a: T, b: T) => number {\n return (a: T, b: T) => {\n const aa = fn(a)\n const bb = fn(b)\n if (aa > bb) {\n return -1\n }\n if (aa < bb) {\n return 1\n }\n return 0\n }\n}\n\n/**\n * Sorts an array using multiple comparators in priority order.\n *\n * The first comparator has highest priority. When it returns 0 (tie),\n * the next comparator is used, and so on.\n *\n * @example\n * ```ts\n * sortWith(users, [\n * ascend(u => u.department),\n * descend(u => u.age),\n * ])\n * ```\n */\nexport function sortWith<T>(\n array: readonly T[],\n comparators: ReadonlyArray<(a: T, b: T) => number>\n): T[] {\n return [...array].toSorted((a, b) => {\n for (const cmp of comparators) {\n const result = cmp(a, b)\n if (result !== 0) {\n return result\n }\n }\n return 0\n })\n}\n","/* oxlint-disable functional/no-let -- iterative impl avoids stack overflow on large outputs */\n\n/**\n * Builds an array from a seed value using an iterator function.\n *\n * The iterator receives the current seed and returns either a `[value, nextSeed]`\n * tuple to continue, or `false` to stop.\n *\n * Dual of `reduce` — reduce collapses a list into a value, unfold expands\n * a value into a list.\n *\n * **Termination is the caller's responsibility.** If `fn` never returns\n * `false`, `unfold` will run until memory is exhausted. For bounded\n * generation, encode a counter or limit into the seed.\n *\n * @example\n * ```ts\n * unfold((n) => (n > 0 ? [n, n - 1] : false), 5)\n * // [5, 4, 3, 2, 1]\n * ```\n */\nexport function unfold<T, R>(fn: (seed: T) => [R, T] | false, seed: T): R[] {\n const result: R[] = []\n let current = seed\n let pair = fn(current)\n while (pair !== false) {\n result.push(pair[0])\n current = pair[1]\n pair = fn(current)\n }\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;AAYA,SAAgB,OAAU,OAAqB,OAAe,IAA0B;CACtF,MAAM,SAAS,CAAC,GAAG,MAAM;CACzB,IAAI,SAAS,KAAK,QAAQ,MAAM,QAE9B,OAAO,SAAS,GAAG,OAAO,OAAO;CAEnC,OAAO;;;;;;;;;;;;;;;;;;;;;;ACAT,SAAgB,YAAe,OAA0B;CACvD,OAAO,MAAM,QAAa,KAAK,MAAM,UAAU;EAC7C,IAAI,UAAU,KAAK,SAAS,MAAM,QAAQ,IACxC,IAAI,KAAK,KAAK;EAEhB,OAAO;IACN,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;ACHR,SAAgB,YACd,OACA,WACA,IACA,SACG;CACH,IAAI,MAAM;CACV,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EAEpD,MAAM,QAAQ,MAAM;EACpB,IAAI,CAAC,UAAU,KAAK,MAAM,EACxB,OAAO;EAET,MAAM,GAAG,KAAK,OAAO,MAAM;;CAE7B,OAAO;;;;;;;;;;;;;;;;ACxBT,SAAgB,KACd,OACA,IACA,SACK;CACL,OAAO,MAAM,QACV,QAAQ,MAAM,UAAU;EACvB,MAAM,OAAO,OAAO,OAAO,SAAS;EACpC,OAAO,KAAK,GAAG,MAAM,MAAM,MAAM,CAAC;EAClC,OAAO;IAET,CAAC,QAAQ,CACV;;;;;;;;;;;;AChBH,SAAgB,OAAU,IAA2D;CACnF,QAAQ,GAAM,MAAS;EACrB,MAAM,KAAK,GAAG,EAAE;EAChB,MAAM,KAAK,GAAG,EAAE;EAChB,IAAI,KAAK,IACP,OAAO;EAET,IAAI,KAAK,IACP,OAAO;EAET,OAAO;;;;;;;;;;;AAYX,SAAgB,QAAW,IAA2D;CACpF,QAAQ,GAAM,MAAS;EACrB,MAAM,KAAK,GAAG,EAAE;EAChB,MAAM,KAAK,GAAG,EAAE;EAChB,IAAI,KAAK,IACP,OAAO;EAET,IAAI,KAAK,IACP,OAAO;EAET,OAAO;;;;;;;;;;;;;;;;;AAkBX,SAAgB,SACd,OACA,aACK;CACL,OAAO,CAAC,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM;EACnC,KAAK,MAAM,OAAO,aAAa;GAC7B,MAAM,SAAS,IAAI,GAAG,EAAE;GACxB,IAAI,WAAW,GACb,OAAO;;EAGX,OAAO;GACP;;;;;;;;;;;;;;;;;;;;;;;ACjDJ,SAAgB,OAAa,IAAiC,MAAc;CAC1E,MAAM,SAAc,EAAE;CACtB,IAAI,UAAU;CACd,IAAI,OAAO,GAAG,QAAQ;CACtB,OAAO,SAAS,OAAO;EACrB,OAAO,KAAK,KAAK,GAAG;EACpB,UAAU,KAAK;EACf,OAAO,GAAG,QAAQ;;CAEpB,OAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as isOk, c as attempt, d as Ok, f as Result, i as isErr, l as attemptAsync, n as invariant, o as ok, r as err, s as unwrap, t as assert, u as Err } from "../index-Br9PO6mU.mjs";
|
|
2
|
+
export { Err, Ok, Result, assert, attempt, attemptAsync, err, invariant, isErr, isOk, ok, unwrap };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { isNil } from "es-toolkit/predicate";
|
|
2
|
+
import { assert, invariant } from "es-toolkit/util";
|
|
3
|
+
//#region src/control/result.ts
|
|
4
|
+
/**
|
|
5
|
+
* Minimal error coercion used internally by `err()`. Kept local to avoid
|
|
6
|
+
* pulling the full conversion module into the `tadka/control` bundle.
|
|
7
|
+
* For richer stringification (Maps, Sets, Errors with own props, circular
|
|
8
|
+
* refs), import `toError` from `tadka/conversion`.
|
|
9
|
+
*/
|
|
10
|
+
function coerceError(thrown) {
|
|
11
|
+
if (thrown instanceof Error) return thrown;
|
|
12
|
+
if (typeof thrown === "string") return new Error(thrown);
|
|
13
|
+
try {
|
|
14
|
+
const message = JSON.stringify(thrown) ?? String(thrown);
|
|
15
|
+
return new Error(message, { cause: thrown });
|
|
16
|
+
} catch {
|
|
17
|
+
return new Error(String(thrown), { cause: thrown });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a success result wrapping the given value.
|
|
22
|
+
*
|
|
23
|
+
* @param value - The success value
|
|
24
|
+
* @returns An `Ok` result containing the value
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const result = ok(42)
|
|
29
|
+
* // { ok: true, value: 42 }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function ok(value) {
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
value,
|
|
36
|
+
error: null
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates a failure result wrapping the given error.
|
|
41
|
+
*
|
|
42
|
+
* @param error - The error value
|
|
43
|
+
* @returns An `Err` result containing the error
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const result = err(new Error('fail'))
|
|
48
|
+
* // { ok: false, error: Error('fail') }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function err(error) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
value: null,
|
|
55
|
+
error: coerceError(error)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Type guard that narrows a `Result` to `Ok`.
|
|
60
|
+
*
|
|
61
|
+
* @param result - The result to check
|
|
62
|
+
* @returns `true` if the result is `Ok`
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const result = attempt(() => JSON.parse('{}'))
|
|
67
|
+
* if (isOk(result)) {
|
|
68
|
+
* console.log(result.value)
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function isOk(result) {
|
|
73
|
+
return result.ok === true;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Type guard that narrows a `Result` to `Err`.
|
|
77
|
+
*
|
|
78
|
+
* @param result - The result to check
|
|
79
|
+
* @returns `true` if the result is `Err`
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const result = attempt(() => JSON.parse('bad'))
|
|
84
|
+
* if (isErr(result)) {
|
|
85
|
+
* console.error(result.error)
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
function isErr(result) {
|
|
90
|
+
return result.ok === false;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract the value from an `Ok` result, or throw on `Err`.
|
|
94
|
+
*
|
|
95
|
+
* When called without a message, throws the original error.
|
|
96
|
+
* When called with a message, throws a new Error with that message
|
|
97
|
+
* and the original error as `cause` (like Rust's `expect`).
|
|
98
|
+
*
|
|
99
|
+
* @param result - The result to unwrap
|
|
100
|
+
* @param message - Optional custom error message (Rust `expect` behavior)
|
|
101
|
+
* @returns The unwrapped value
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* const value = unwrap(ok(42)) // 42
|
|
106
|
+
* unwrap(err('fail')) // throws Error('fail')
|
|
107
|
+
* unwrap(err('fail'), 'config required') // throws Error('config required', { cause: Error('fail') })
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
function unwrap(result, message) {
|
|
111
|
+
if (result.ok) return result.value;
|
|
112
|
+
if (!isNil(message)) throw new Error(message, { cause: result.error });
|
|
113
|
+
throw result.error;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/control/attempt.ts
|
|
117
|
+
/**
|
|
118
|
+
* Executes a synchronous function and wraps the outcome in a `Result`.
|
|
119
|
+
* Returns `Ok` with the return value on success, `Err` with the thrown value on failure.
|
|
120
|
+
*
|
|
121
|
+
* @param fn - The function to execute
|
|
122
|
+
* @returns A `Result` containing either the value or the error
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* const result = attempt(() => JSON.parse('{"a":1}'))
|
|
127
|
+
* if (isOk(result)) {
|
|
128
|
+
* console.log(result.value) // { a: 1 }
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
function attempt(fn) {
|
|
133
|
+
try {
|
|
134
|
+
return ok(fn());
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return err(error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Executes an asynchronous function and wraps the outcome in a `Result`.
|
|
141
|
+
* Returns `Ok` with the resolved value on success, `Err` with the rejection reason on failure.
|
|
142
|
+
*
|
|
143
|
+
* @param fn - The async function to execute
|
|
144
|
+
* @returns A promise resolving to a `Result` containing either the value or the error
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* const result = await attemptAsync(() => fetch('/api/data'))
|
|
149
|
+
* if (isErr(result)) {
|
|
150
|
+
* console.error(result.error)
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
async function attemptAsync(fn) {
|
|
155
|
+
try {
|
|
156
|
+
return ok(await fn());
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return err(error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
export { err as a, ok as c, attemptAsync as i, unwrap as l, invariant as n, isErr as o, attempt as r, isOk as s, assert as t };
|
|
163
|
+
|
|
164
|
+
//# sourceMappingURL=control-B4mDPBn6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-B4mDPBn6.mjs","names":[],"sources":["../src/control/result.ts","../src/control/attempt.ts"],"sourcesContent":["import { isNil } from 'es-toolkit/predicate'\n\nimport type { Err, Ok, Result } from './types.js'\n\n/**\n * Minimal error coercion used internally by `err()`. Kept local to avoid\n * pulling the full conversion module into the `tadka/control` bundle.\n * For richer stringification (Maps, Sets, Errors with own props, circular\n * refs), import `toError` from `tadka/conversion`.\n */\nfunction coerceError(thrown: unknown): Error {\n if (thrown instanceof Error) {\n return thrown\n }\n if (typeof thrown === 'string') {\n return new Error(thrown)\n }\n try {\n const message = JSON.stringify(thrown) ?? String(thrown)\n return new Error(message, { cause: thrown })\n } catch {\n return new Error(String(thrown), { cause: thrown })\n }\n}\n\n/**\n * Creates a success result wrapping the given value.\n *\n * @param value - The success value\n * @returns An `Ok` result containing the value\n *\n * @example\n * ```ts\n * const result = ok(42)\n * // { ok: true, value: 42 }\n * ```\n */\nexport function ok<T>(value: T): Ok<T> {\n return { ok: true, value, error: null }\n}\n\n/**\n * Creates a failure result wrapping the given error.\n *\n * @param error - The error value\n * @returns An `Err` result containing the error\n *\n * @example\n * ```ts\n * const result = err(new Error('fail'))\n * // { ok: false, error: Error('fail') }\n * ```\n */\nexport function err(error: unknown): Err {\n return { ok: false, value: null, error: coerceError(error) }\n}\n\n/**\n * Type guard that narrows a `Result` to `Ok`.\n *\n * @param result - The result to check\n * @returns `true` if the result is `Ok`\n *\n * @example\n * ```ts\n * const result = attempt(() => JSON.parse('{}'))\n * if (isOk(result)) {\n * console.log(result.value)\n * }\n * ```\n */\nexport function isOk<T>(result: Result<T>): result is Ok<T> {\n return result.ok === true\n}\n\n/**\n * Type guard that narrows a `Result` to `Err`.\n *\n * @param result - The result to check\n * @returns `true` if the result is `Err`\n *\n * @example\n * ```ts\n * const result = attempt(() => JSON.parse('bad'))\n * if (isErr(result)) {\n * console.error(result.error)\n * }\n * ```\n */\nexport function isErr<T>(result: Result<T>): result is Err {\n return result.ok === false\n}\n\n/**\n * Extract the value from an `Ok` result, or throw on `Err`.\n *\n * When called without a message, throws the original error.\n * When called with a message, throws a new Error with that message\n * and the original error as `cause` (like Rust's `expect`).\n *\n * @param result - The result to unwrap\n * @param message - Optional custom error message (Rust `expect` behavior)\n * @returns The unwrapped value\n *\n * @example\n * ```ts\n * const value = unwrap(ok(42)) // 42\n * unwrap(err('fail')) // throws Error('fail')\n * unwrap(err('fail'), 'config required') // throws Error('config required', { cause: Error('fail') })\n * ```\n */\nexport function unwrap<T>(result: Result<T>, message?: string): T {\n if (result.ok) {\n return result.value\n }\n if (!isNil(message)) {\n throw new Error(message, { cause: result.error })\n }\n throw result.error\n}\n","import { err, ok } from './result.js'\nimport type { Result } from './types.js'\n\n/**\n * Executes a synchronous function and wraps the outcome in a `Result`.\n * Returns `Ok` with the return value on success, `Err` with the thrown value on failure.\n *\n * @param fn - The function to execute\n * @returns A `Result` containing either the value or the error\n *\n * @example\n * ```ts\n * const result = attempt(() => JSON.parse('{\"a\":1}'))\n * if (isOk(result)) {\n * console.log(result.value) // { a: 1 }\n * }\n * ```\n */\nexport function attempt<T>(fn: () => T): Result<T> {\n try {\n return ok(fn())\n } catch (error) {\n return err(error)\n }\n}\n\n/**\n * Executes an asynchronous function and wraps the outcome in a `Result`.\n * Returns `Ok` with the resolved value on success, `Err` with the rejection reason on failure.\n *\n * @param fn - The async function to execute\n * @returns A promise resolving to a `Result` containing either the value or the error\n *\n * @example\n * ```ts\n * const result = await attemptAsync(() => fetch('/api/data'))\n * if (isErr(result)) {\n * console.error(result.error)\n * }\n * ```\n */\nexport async function attemptAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {\n try {\n return ok(await fn())\n } catch (error) {\n return err(error)\n }\n}\n"],"mappings":";;;;;;;;;AAUA,SAAS,YAAY,QAAwB;CAC3C,IAAI,kBAAkB,OACpB,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO,IAAI,MAAM,OAAO;CAE1B,IAAI;EACF,MAAM,UAAU,KAAK,UAAU,OAAO,IAAI,OAAO,OAAO;EACxD,OAAO,IAAI,MAAM,SAAS,EAAE,OAAO,QAAQ,CAAC;SACtC;EACN,OAAO,IAAI,MAAM,OAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,CAAC;;;;;;;;;;;;;;;AAgBvD,SAAgB,GAAM,OAAiB;CACrC,OAAO;EAAE,IAAI;EAAM;EAAO,OAAO;EAAM;;;;;;;;;;;;;;AAezC,SAAgB,IAAI,OAAqB;CACvC,OAAO;EAAE,IAAI;EAAO,OAAO;EAAM,OAAO,YAAY,MAAM;EAAE;;;;;;;;;;;;;;;;AAiB9D,SAAgB,KAAQ,QAAoC;CAC1D,OAAO,OAAO,OAAO;;;;;;;;;;;;;;;;AAiBvB,SAAgB,MAAS,QAAkC;CACzD,OAAO,OAAO,OAAO;;;;;;;;;;;;;;;;;;;;AAqBvB,SAAgB,OAAU,QAAmB,SAAqB;CAChE,IAAI,OAAO,IACT,OAAO,OAAO;CAEhB,IAAI,CAAC,MAAM,QAAQ,EACjB,MAAM,IAAI,MAAM,SAAS,EAAE,OAAO,OAAO,OAAO,CAAC;CAEnD,MAAM,OAAO;;;;;;;;;;;;;;;;;;;ACpGf,SAAgB,QAAW,IAAwB;CACjD,IAAI;EACF,OAAO,GAAG,IAAI,CAAC;UACR,OAAO;EACd,OAAO,IAAI,MAAM;;;;;;;;;;;;;;;;;;AAmBrB,eAAsB,aAAgB,IAA0C;CAC9E,IAAI;EACF,OAAO,GAAG,MAAM,IAAI,CAAC;UACd,OAAO;EACd,OAAO,IAAI,MAAM"}
|