ts-data-forge 14.5.0 → 14.6.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.
Files changed (71) hide show
  1. package/dist/array/impl/array-utils-element-access.mjs +1 -1
  2. package/dist/array/impl/array-utils-reducing-value.mjs +3 -3
  3. package/dist/array/impl/array-utils-search.mjs +1 -1
  4. package/dist/collections/imap-mapped.mjs +1 -1
  5. package/dist/collections/imap.mjs +1 -1
  6. package/dist/entry-point.mjs +8 -6
  7. package/dist/entry-point.mjs.map +1 -1
  8. package/dist/functional/async-result/async-result.d.mts +16 -0
  9. package/dist/functional/async-result/async-result.d.mts.map +1 -0
  10. package/dist/functional/async-result/async-result.mjs +3 -0
  11. package/dist/functional/async-result/async-result.mjs.map +1 -0
  12. package/dist/functional/async-result/impl/async-result-flat-map.d.mts +39 -0
  13. package/dist/functional/async-result/impl/async-result-flat-map.d.mts.map +1 -0
  14. package/dist/functional/async-result/impl/async-result-flat-map.mjs +25 -0
  15. package/dist/functional/async-result/impl/async-result-flat-map.mjs.map +1 -0
  16. package/dist/functional/async-result/impl/async-result-from-promise.d.mts +47 -0
  17. package/dist/functional/async-result/impl/async-result-from-promise.d.mts.map +1 -0
  18. package/dist/functional/async-result/impl/async-result-from-promise.mjs +11 -0
  19. package/dist/functional/async-result/impl/async-result-from-promise.mjs.map +1 -0
  20. package/dist/functional/async-result/impl/async-result-from-throwable.d.mts +53 -0
  21. package/dist/functional/async-result/impl/async-result-from-throwable.d.mts.map +1 -0
  22. package/dist/functional/async-result/impl/async-result-from-throwable.mjs +17 -0
  23. package/dist/functional/async-result/impl/async-result-from-throwable.mjs.map +1 -0
  24. package/dist/functional/async-result/impl/async-result-map-err.d.mts +35 -0
  25. package/dist/functional/async-result/impl/async-result-map-err.d.mts.map +1 -0
  26. package/dist/functional/async-result/impl/async-result-map-err.mjs +23 -0
  27. package/dist/functional/async-result/impl/async-result-map-err.mjs.map +1 -0
  28. package/dist/functional/async-result/impl/async-result-map.d.mts +45 -0
  29. package/dist/functional/async-result/impl/async-result-map.d.mts.map +1 -0
  30. package/dist/functional/async-result/impl/async-result-map.mjs +23 -0
  31. package/dist/functional/async-result/impl/async-result-map.mjs.map +1 -0
  32. package/dist/functional/async-result/impl/async-result-unwrap-or.d.mts +34 -0
  33. package/dist/functional/async-result/impl/async-result-unwrap-or.d.mts.map +1 -0
  34. package/dist/functional/async-result/impl/async-result-unwrap-or.mjs +10 -0
  35. package/dist/functional/async-result/impl/async-result-unwrap-or.mjs.map +1 -0
  36. package/dist/functional/async-result/impl/index.d.mts +8 -0
  37. package/dist/functional/async-result/impl/index.d.mts.map +1 -0
  38. package/dist/functional/async-result/impl/index.mjs +7 -0
  39. package/dist/functional/async-result/impl/index.mjs.map +1 -0
  40. package/dist/functional/async-result/impl/types.d.mts +45 -0
  41. package/dist/functional/async-result/impl/types.d.mts.map +1 -0
  42. package/dist/functional/async-result/impl/types.mjs +2 -0
  43. package/dist/functional/async-result/impl/types.mjs.map +1 -0
  44. package/dist/functional/async-result/index.d.mts +2 -0
  45. package/dist/functional/async-result/index.d.mts.map +1 -0
  46. package/dist/functional/async-result/index.mjs +3 -0
  47. package/dist/functional/async-result/index.mjs.map +1 -0
  48. package/dist/functional/index.d.mts +1 -0
  49. package/dist/functional/index.d.mts.map +1 -1
  50. package/dist/functional/index.mjs +8 -6
  51. package/dist/functional/index.mjs.map +1 -1
  52. package/dist/functional/result/impl/result-from-promise.d.mts +5 -0
  53. package/dist/functional/result/impl/result-from-promise.d.mts.map +1 -1
  54. package/dist/functional/result/impl/result-from-promise.mjs.map +1 -1
  55. package/dist/json/json.mjs +2 -2
  56. package/dist/number/refined-number-utils.mjs +1 -1
  57. package/dist/object/object.mjs +1 -1
  58. package/package.json +7 -7
  59. package/src/functional/async-result/async-result.mts +17 -0
  60. package/src/functional/async-result/impl/async-result-flat-map.mts +99 -0
  61. package/src/functional/async-result/impl/async-result-from-promise.mts +69 -0
  62. package/src/functional/async-result/impl/async-result-from-throwable.mts +78 -0
  63. package/src/functional/async-result/impl/async-result-map-err.mts +84 -0
  64. package/src/functional/async-result/impl/async-result-map.mts +94 -0
  65. package/src/functional/async-result/impl/async-result-unwrap-or.mts +42 -0
  66. package/src/functional/async-result/impl/index.mts +7 -0
  67. package/src/functional/async-result/impl/types.mts +51 -0
  68. package/src/functional/async-result/index.mts +1 -0
  69. package/src/functional/async-result.test.mts +290 -0
  70. package/src/functional/index.mts +1 -0
  71. package/src/functional/result/impl/result-from-promise.mts +5 -0
@@ -0,0 +1,45 @@
1
+ import { type Err, type Ok } from '../../../types.mjs';
2
+ import { type UnknownResult } from '../../result/index.mjs';
3
+ import { type AsyncResult } from '../async-result.mjs';
4
+ /**
5
+ * Base type for any `AsyncResult`, used for generic constraints. Represents
6
+ * an `AsyncResult` with unknown success and error types.
7
+ */
8
+ export type UnknownAsyncResult = AsyncResult<unknown, unknown>;
9
+ /**
10
+ * Extracts the success value type `S` from a `Result.Ok<S>`, or from an
11
+ * `AsyncResult` resolving to one. Distributes over unions, so an
12
+ * `Ok<S> | Err<E>` union resolves to `S`.
13
+ *
14
+ * @template R The `Result` or `AsyncResult` type to unwrap.
15
+ */
16
+ export type UnwrapOk<R extends UnknownAsyncResult | UnknownResult> = UnwrapOkImpl<SyncResultOf<R>>;
17
+ /**
18
+ * Extracts the error value type `E` from a `Result.Err<E>`, or from an
19
+ * `AsyncResult` resolving to one. Distributes over unions, so an
20
+ * `Ok<S> | Err<E>` union resolves to `E`.
21
+ *
22
+ * @template R The `Result` or `AsyncResult` type to unwrap.
23
+ */
24
+ export type UnwrapErr<R extends UnknownAsyncResult | UnknownResult> = UnwrapErrImpl<SyncResultOf<R>>;
25
+ /**
26
+ * @template R The `Result` or `AsyncResult` type.
27
+ * @internal
28
+ * Resolves an `AsyncResult` to its `Result`, leaving a plain `Result`
29
+ * unchanged. Distributive.
30
+ */
31
+ type SyncResultOf<R> = R extends Promise<infer P> ? P : R;
32
+ /**
33
+ * @template R The `Result` type to unwrap.
34
+ * @internal
35
+ * Distributive extraction of the `Ok` value type.
36
+ */
37
+ type UnwrapOkImpl<R> = R extends Ok<infer S> ? S : never;
38
+ /**
39
+ * @template R The `Result` type to unwrap.
40
+ * @internal
41
+ * Distributive extraction of the `Err` value type.
42
+ */
43
+ type UnwrapErrImpl<R> = R extends Err<infer E> ? E : never;
44
+ export {};
45
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../../../src/functional/async-result/impl/types.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,EAAE,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,kBAAkB,GAAG,aAAa,IAC/D,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,kBAAkB,GAAG,aAAa,IAChE,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AAEjC;;;;;GAKG;AACH,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE1D;;;;GAIG;AACH,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEzD;;;;GAIG;AACH,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC"}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export * from './async-result.mjs';
2
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/functional/async-result/index.mts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import * as index from './impl/index.mjs';
2
+ export { index as AsyncResult };
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,3 +1,4 @@
1
+ export * from './async-result/index.mjs';
1
2
  export * from './match.mjs';
2
3
  export * from './optional/index.mjs';
3
4
  export * from './pipe.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/functional/index.mts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/functional/index.mts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC"}
@@ -1,9 +1,11 @@
1
+ import * as index from './async-result/impl/index.mjs';
2
+ export { index as AsyncResult };
1
3
  export { match } from './match.mjs';
2
- import * as index from './optional/impl/index.mjs';
3
- export { index as Optional };
4
+ import * as index$1 from './optional/impl/index.mjs';
5
+ export { index$1 as Optional };
4
6
  export { pipe } from './pipe.mjs';
5
- import * as index$1 from './result/impl/index.mjs';
6
- export { index$1 as Result };
7
- import * as index$2 from './ternary-result/impl/index.mjs';
8
- export { index$2 as TernaryResult };
7
+ import * as index$2 from './result/impl/index.mjs';
8
+ export { index$2 as Result };
9
+ import * as index$3 from './ternary-result/impl/index.mjs';
10
+ export { index$3 as TernaryResult };
9
11
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -11,6 +11,11 @@ type UnwrapPromise<P extends Promise<unknown>> = P extends Promise<infer V> ? V
11
11
  * input Promise rejects, the `Result` will be `Err` with the rejection
12
12
  * reason.
13
13
  *
14
+ * The error channel is `unknown`, since the rejection reason is carried
15
+ * through untouched. For a concrete error type, use `AsyncResult.fromPromise`
16
+ * with a `mapError` function; called without one it behaves exactly as this
17
+ * function does.
18
+ *
14
19
  * @example
15
20
  *
16
21
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"result-from-promise.d.mts","sourceRoot":"","sources":["../../../../src/functional/result/impl/result-from-promise.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C;;;;GAIG;AACH,KAAK,aAAa,CAAC,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,IAC3C,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,WAC3C,CAAC,KACT,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAEmB,CAAC"}
1
+ {"version":3,"file":"result-from-promise.d.mts","sourceRoot":"","sources":["../../../../src/functional/result/impl/result-from-promise.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C;;;;GAIG;AACH,KAAK,aAAa,CAAC,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,IAC3C,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,OAAO,CAAC,OAAO,CAAC,WAC3C,CAAC,KACT,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAEmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"result-from-promise.mjs","sources":["../../../../src/functional/result/impl/result-from-promise.mts"],"sourcesContent":["import { type Ok } from '../../../types.mjs';\nimport { type Result } from '../result.mjs';\nimport { err } from './result-err.mjs';\nimport { ok } from './result-ok.mjs';\n\n/**\n * @template P The Promise type.\n * @internal\n * Utility type to extract the resolved value type from a Promise.\n */\ntype UnwrapPromise<P extends Promise<unknown>> =\n P extends Promise<infer V> ? V : never;\n\n/**\n * Converts a Promise into a Promise that resolves to a `Result`. If the input\n * Promise resolves, the `Result` will be `Ok` with the resolved value. If the\n * input Promise rejects, the `Result` will be `Err` with the rejection\n * reason.\n *\n * @example\n *\n * ```ts\n * const successPromise = Result.fromPromise(Promise.resolve('ok'));\n *\n * const failurePromise = Result.fromPromise(\n * Promise.reject(new Error('fail')),\n * );\n *\n * const resolved = await successPromise;\n *\n * const rejected = await failurePromise;\n *\n * assert.deepStrictEqual(resolved, Result.ok('ok'));\n *\n * assert.isTrue(Result.isErr(rejected));\n * ```\n *\n * @template P The type of the input Promise.\n * @param promise The Promise to convert.\n * @returns A Promise that resolves to `Result<UnwrapPromise<P>, unknown>`.\n */\nexport const fromPromise = <P extends Promise<unknown>>(\n promise: P,\n): Promise<Result<UnwrapPromise<P>, unknown>> =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n promise.then((v) => ok(v) as Ok<UnwrapPromise<P>>).catch(err);\n"],"names":[],"mappings":";;;AAyCO,MAAM,cAAc,CACzB,OAAA;AAAA;AAAA,EAGA,OAAA,CAAQ,KAAK,CAAC,CAAA,KAAM,GAAG,CAAC,CAAyB,CAAA,CAAE,KAAA,CAAM,GAAG;AAAA;;;;"}
1
+ {"version":3,"file":"result-from-promise.mjs","sources":["../../../../src/functional/result/impl/result-from-promise.mts"],"sourcesContent":["import { type Ok } from '../../../types.mjs';\nimport { type Result } from '../result.mjs';\nimport { err } from './result-err.mjs';\nimport { ok } from './result-ok.mjs';\n\n/**\n * @template P The Promise type.\n * @internal\n * Utility type to extract the resolved value type from a Promise.\n */\ntype UnwrapPromise<P extends Promise<unknown>> =\n P extends Promise<infer V> ? V : never;\n\n/**\n * Converts a Promise into a Promise that resolves to a `Result`. If the input\n * Promise resolves, the `Result` will be `Ok` with the resolved value. If the\n * input Promise rejects, the `Result` will be `Err` with the rejection\n * reason.\n *\n * The error channel is `unknown`, since the rejection reason is carried\n * through untouched. For a concrete error type, use `AsyncResult.fromPromise`\n * with a `mapError` function; called without one it behaves exactly as this\n * function does.\n *\n * @example\n *\n * ```ts\n * const successPromise = Result.fromPromise(Promise.resolve('ok'));\n *\n * const failurePromise = Result.fromPromise(\n * Promise.reject(new Error('fail')),\n * );\n *\n * const resolved = await successPromise;\n *\n * const rejected = await failurePromise;\n *\n * assert.deepStrictEqual(resolved, Result.ok('ok'));\n *\n * assert.isTrue(Result.isErr(rejected));\n * ```\n *\n * @template P The type of the input Promise.\n * @param promise The Promise to convert.\n * @returns A Promise that resolves to `Result<UnwrapPromise<P>, unknown>`.\n */\nexport const fromPromise = <P extends Promise<unknown>>(\n promise: P,\n): Promise<Result<UnwrapPromise<P>, unknown>> =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n promise.then((v) => ok(v) as Ok<UnwrapPromise<P>>).catch(err);\n"],"names":[],"mappings":";;;AA8CO,MAAM,cAAc,CACzB,OAAA;AAAA;AAAA,EAGA,OAAA,CAAQ,KAAK,CAAC,CAAA,KAAM,GAAG,CAAC,CAAyB,CAAA,CAAE,KAAA,CAAM,GAAG;AAAA;;;;"}
@@ -34,13 +34,13 @@ import '../number/enum/int8.mjs';
34
34
  import '../number/enum/uint8.mjs';
35
35
  import '../number/num.mjs';
36
36
  import '../number/refined-number-utils.mjs';
37
- import { isRecord } from '../guard/is-record.mjs';
38
- import { pipe } from '../functional/pipe.mjs';
39
37
  import { err } from '../functional/result/impl/result-err.mjs';
40
38
  import { ok } from '../functional/result/impl/result-ok.mjs';
39
+ import { isRecord } from '../guard/is-record.mjs';
41
40
  import '@sindresorhus/is';
42
41
  import { castMutable } from '../others/cast-mutable.mjs';
43
42
  import { unknownToString } from '../others/unknown-to-string.mjs';
43
+ import { pipe } from '../functional/pipe.mjs';
44
44
  import '../collections/imap-mapped.mjs';
45
45
  import '../collections/imap.mjs';
46
46
  import '../collections/iset-mapped.mjs';
@@ -1,5 +1,5 @@
1
- import { pipe } from '../functional/pipe.mjs';
2
1
  import '@sindresorhus/is';
2
+ import { pipe } from '../functional/pipe.mjs';
3
3
  import { Num } from './num.mjs';
4
4
 
5
5
  var TsDataForgeInternals;
@@ -34,9 +34,9 @@ import '../number/enum/int8.mjs';
34
34
  import '../number/enum/uint8.mjs';
35
35
  import '../number/num.mjs';
36
36
  import '../number/refined-number-utils.mjs';
37
+ import { isSome } from '../functional/optional/impl/optional-is-some.mjs';
37
38
  import { hasKey } from '../guard/has-key.mjs';
38
39
  import { isRecord } from '../guard/is-record.mjs';
39
- import { isSome } from '../functional/optional/impl/optional-is-some.mjs';
40
40
  import '@sindresorhus/is';
41
41
  import { isNonEmpty } from '../array/impl/array-utils-length-bounded-array-guard.mjs';
42
42
  import '../collections/imap-mapped.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-data-forge",
3
- "version": "14.5.0",
3
+ "version": "14.6.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript",
@@ -46,20 +46,20 @@
46
46
  "@types/react": "19.2.18",
47
47
  "@vitest/browser-playwright": "4.1.11",
48
48
  "dedent": "1.7.2",
49
- "eslint-config-typed": "5.10.0",
50
- "eslint-plugin-ts-fortress": "1.0.5",
51
- "eslint-plugin-ts-type-forge": "0.7.1",
49
+ "eslint-config-typed": "5.10.1",
50
+ "eslint-plugin-ts-fortress": "1.0.6",
51
+ "eslint-plugin-ts-type-forge": "0.7.2",
52
52
  "immer": "11.1.18",
53
53
  "playwright": "1.62.1",
54
54
  "prettier": "3.9.6",
55
55
  "prettier-plugin-organize-imports": "4.3.0",
56
56
  "prettier-plugin-packagejson": "3.0.2",
57
57
  "react": "19.2.8",
58
- "rollup": "4.62.4",
59
- "ts-repo-utils": "10.3.2",
58
+ "rollup": "4.62.5",
59
+ "ts-repo-utils": "10.3.3",
60
60
  "typedoc": "0.28.20",
61
61
  "typedoc-github-theme": "0.4.0",
62
- "vite": "8.2.1"
62
+ "vite": "8.2.2"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "typescript": ">=4.8"
@@ -0,0 +1,17 @@
1
+ import { type Result } from '../result/index.mjs';
2
+
3
+ /**
4
+ * Represents an asynchronous computation that can either succeed (`Ok`) or
5
+ * fail (`Err`) — a `Promise` that resolves to a `Result<S, E>`.
6
+ *
7
+ * Unlike a raw `Promise`, an `AsyncResult` never rejects for expected
8
+ * failures: errors are carried in the `Err` channel with a concrete type `E`,
9
+ * so they can be handled with the same combinators as a synchronous
10
+ * `Result`.
11
+ *
12
+ * @template S The type of the success value.
13
+ * @template E The type of the error value.
14
+ */
15
+ export type AsyncResult<S, E> = Promise<Result<S, E>>;
16
+
17
+ export * as AsyncResult from './impl/index.mjs';
@@ -0,0 +1,99 @@
1
+ import { Result, type UnknownResult } from '../../result/index.mjs';
2
+ import { type AsyncResult } from '../async-result.mjs';
3
+ import {
4
+ type UnknownAsyncResult,
5
+ type UnwrapErr,
6
+ type UnwrapOk,
7
+ } from './types.mjs';
8
+
9
+ /**
10
+ * Applies a function that returns an `AsyncResult` or a `Result` to the
11
+ * success value of an `AsyncResult`. If the input resolves to `Err`, the
12
+ * original `Err` is passed through and the function is not called. This is
13
+ * the monadic bind operation for `AsyncResult`.
14
+ *
15
+ * @example
16
+ *
17
+ * ```ts
18
+ * const parseNumber = (input: string): Result<number, string> => {
19
+ * const num = Number.parseInt(input, 10);
20
+ *
21
+ * return Number.isNaN(num) ? Result.err('not a number') : Result.ok(num);
22
+ * };
23
+ *
24
+ * const input = AsyncResult.fromPromise(
25
+ * Promise.resolve('42'),
26
+ * () => 'failed',
27
+ * );
28
+ *
29
+ * const parsed = await AsyncResult.flatMap(input, parseNumber);
30
+ *
31
+ * assert.deepStrictEqual(parsed, Result.ok(42));
32
+ * ```
33
+ *
34
+ * @template S The type of the success value of the input.
35
+ * @template E The type of the error value of the input.
36
+ * @template R2 The `AsyncResult` or `Result` type returned by the function.
37
+ * @param asyncResult The `AsyncResult` to flat map.
38
+ * @param flatMapFn The function to apply that returns an `AsyncResult` or a
39
+ * `Result`.
40
+ * @returns The result of applying the function, or the original `Err`.
41
+ */
42
+ export function flatMap<S, E, R2 extends UnknownAsyncResult | UnknownResult>(
43
+ asyncResult: AsyncResult<S, E>,
44
+ flatMapFn: (value: S) => R2,
45
+ ): AsyncResult<UnwrapOk<R2>, E | UnwrapErr<R2>>;
46
+
47
+ // Curried version
48
+ //
49
+ // The function's return type is captured as a whole (`R2`) and unwrapped with
50
+ // `UnwrapOk` / `UnwrapErr`, instead of being written as
51
+ // `AsyncResult<S2, E2> | Result<S2, E2>` with separate `S2` / `E2` type
52
+ // parameters. With the union form, inference for `E2` collects a candidate
53
+ // from matching `Ok<S2>` against `Err<E2>` (both have a `value` property), so
54
+ // `E2` ends up as the success type and the call fails to type-check even for
55
+ // a function annotated as returning `Result<S2, E2>`.
56
+ export function flatMap<S, R2 extends UnknownAsyncResult | UnknownResult>(
57
+ flatMapFn: (value: S) => R2,
58
+ ): <E>(
59
+ asyncResult: AsyncResult<S, E>,
60
+ ) => AsyncResult<UnwrapOk<R2>, E | UnwrapErr<R2>>;
61
+
62
+ export function flatMap<S, E, R2 extends UnknownAsyncResult | UnknownResult>(
63
+ ...args:
64
+ | readonly [asyncResult: AsyncResult<S, E>, flatMapFn: (value: S) => R2]
65
+ | readonly [flatMapFn: (value: S) => R2]
66
+ ):
67
+ | AsyncResult<UnwrapOk<R2>, E | UnwrapErr<R2>>
68
+ | ((
69
+ asyncResult: AsyncResult<S, E>,
70
+ ) => AsyncResult<UnwrapOk<R2>, E | UnwrapErr<R2>>) {
71
+ switch (args.length) {
72
+ case 2: {
73
+ // Direct version: first argument is asyncResult
74
+ const [asyncResult, flatMapFn] = args;
75
+
76
+ return flatMapImpl(asyncResult, flatMapFn);
77
+ }
78
+
79
+ case 1: {
80
+ // Curried version
81
+ const [flatMapFn] = args;
82
+
83
+ return (asyncResult: AsyncResult<S, E>) =>
84
+ flatMapImpl(asyncResult, flatMapFn);
85
+ }
86
+ }
87
+ }
88
+
89
+ const flatMapImpl = async <S, E, R2 extends UnknownAsyncResult | UnknownResult>(
90
+ asyncResult: AsyncResult<S, E>,
91
+ flatMapFn: (value: S) => R2,
92
+ ): AsyncResult<UnwrapOk<R2>, E | UnwrapErr<R2>> => {
93
+ const result = await asyncResult;
94
+
95
+ return Result.isErr(result)
96
+ ? result
97
+ : // eslint-disable-next-line total-functions/no-unsafe-type-assertion
98
+ ((await flatMapFn(result.value)) as Result<UnwrapOk<R2>, UnwrapErr<R2>>);
99
+ };
@@ -0,0 +1,69 @@
1
+ import { Result } from '../../result/index.mjs';
2
+ import { type AsyncResult } from '../async-result.mjs';
3
+
4
+ /**
5
+ * Converts a `Promise` into an `AsyncResult`. If the input Promise resolves,
6
+ * the `AsyncResult` resolves to `Ok` with the resolved value. If the input
7
+ * Promise rejects, the rejection reason is passed through `mapError` and the
8
+ * `AsyncResult` resolves to `Err` with the mapped error.
9
+ *
10
+ * Passing `mapError` is what gives the error channel a concrete type `E`.
11
+ * Omitting it carries the rejection reason through untouched, leaving the
12
+ * error channel as `unknown` — the same contract as `Result.fromPromise`.
13
+ *
14
+ * @example
15
+ *
16
+ * ```ts
17
+ * const resolved = await AsyncResult.fromPromise(
18
+ * Promise.resolve('ok'),
19
+ * (error) => `failed: ${String(error)}`,
20
+ * );
21
+ *
22
+ * const rejected = await AsyncResult.fromPromise(
23
+ * Promise.reject(new Error('boom')),
24
+ * (error) => (isError(error) ? error.message : 'unknown'),
25
+ * );
26
+ *
27
+ * assert.deepStrictEqual(resolved, Result.ok('ok'));
28
+ *
29
+ * assert.deepStrictEqual(rejected, Result.err('boom'));
30
+ *
31
+ * // Without `mapError`, the rejection reason is carried as-is.
32
+ * const untyped = await AsyncResult.fromPromise(
33
+ * Promise.reject(new Error('boom')),
34
+ * );
35
+ *
36
+ * assert.isTrue(Result.isErr(untyped));
37
+ * ```
38
+ *
39
+ * @template S The type of the success value.
40
+ * @template E The type of the error value.
41
+ * @param promise The Promise to convert.
42
+ * @param mapError The function applied to the rejection reason to produce the
43
+ * error value. Omit it to carry the rejection reason as `unknown`.
44
+ * @returns An `AsyncResult<S, E>` that never rejects for the input Promise's
45
+ * failure.
46
+ */
47
+ export function fromPromise<S>(promise: Promise<S>): AsyncResult<S, unknown>;
48
+
49
+ export function fromPromise<S, E>(
50
+ promise: Promise<S>,
51
+ mapError: (error: unknown) => E,
52
+ ): AsyncResult<S, E>;
53
+
54
+ export function fromPromise<S, E>(
55
+ promise: Promise<S>,
56
+ mapError?: (error: unknown) => E,
57
+ ): AsyncResult<S, E> | AsyncResult<S, unknown> {
58
+ return mapError === undefined
59
+ ? fromPromiseImpl(promise, (error: unknown) => error)
60
+ : fromPromiseImpl(promise, mapError);
61
+ }
62
+
63
+ const fromPromiseImpl = <S, E>(
64
+ promise: Promise<S>,
65
+ mapError: (error: unknown) => E,
66
+ ): AsyncResult<S, E> =>
67
+ promise
68
+ .then((value) => Result.ok(value))
69
+ .catch((error: unknown) => Result.err(mapError(error)));
@@ -0,0 +1,78 @@
1
+ import { Result } from '../../result/index.mjs';
2
+ import { type AsyncResult } from '../async-result.mjs';
3
+
4
+ /**
5
+ * Wraps a function returning a `Promise` in an `AsyncResult`. Both a
6
+ * synchronous `throw` while calling `fn` and an asynchronous rejection of the
7
+ * returned Promise are caught, passed through `mapError`, and carried as
8
+ * `Err`.
9
+ *
10
+ * Passing `mapError` is what gives the error channel a concrete type `E`.
11
+ * Omitting it carries the thrown value through untouched, leaving the error
12
+ * channel as `unknown`.
13
+ *
14
+ * @example
15
+ *
16
+ * ```ts
17
+ * const success = await AsyncResult.fromThrowable(
18
+ * () => Promise.resolve(2),
19
+ * (error) => `failed: ${String(error)}`,
20
+ * );
21
+ *
22
+ * const failure = await AsyncResult.fromThrowable(
23
+ * (): Promise<number> => {
24
+ * throw new Error('boom');
25
+ * },
26
+ * (error) => (isError(error) ? error.message : 'unknown'),
27
+ * );
28
+ *
29
+ * assert.deepStrictEqual(success, Result.ok(2));
30
+ *
31
+ * assert.deepStrictEqual(failure, Result.err('boom'));
32
+ *
33
+ * // Without `mapError`, the thrown value is carried as-is.
34
+ * const untyped = await AsyncResult.fromThrowable(
35
+ * (): Promise<number> => {
36
+ * throw new Error('boom');
37
+ * },
38
+ * );
39
+ *
40
+ * assert.isTrue(Result.isErr(untyped));
41
+ * ```
42
+ *
43
+ * @template S The type of the success value.
44
+ * @template E The type of the error value.
45
+ * @param fn The function to execute; it may throw synchronously or return a
46
+ * rejecting Promise.
47
+ * @param mapError The function applied to the thrown value or rejection
48
+ * reason to produce the error value. Omit it to carry the thrown value as
49
+ * `unknown`.
50
+ * @returns An `AsyncResult<S, E>` containing either the resolved value or the
51
+ * mapped error.
52
+ */
53
+ export function fromThrowable<S>(fn: () => Promise<S>): AsyncResult<S, unknown>;
54
+
55
+ export function fromThrowable<S, E>(
56
+ fn: () => Promise<S>,
57
+ mapError: (error: unknown) => E,
58
+ ): AsyncResult<S, E>;
59
+
60
+ export function fromThrowable<S, E>(
61
+ fn: () => Promise<S>,
62
+ mapError?: (error: unknown) => E,
63
+ ): AsyncResult<S, E> | AsyncResult<S, unknown> {
64
+ return mapError === undefined
65
+ ? fromThrowableImpl(fn, (error: unknown) => error)
66
+ : fromThrowableImpl(fn, mapError);
67
+ }
68
+
69
+ const fromThrowableImpl = async <S, E>(
70
+ fn: () => Promise<S>,
71
+ mapError: (error: unknown) => E,
72
+ ): AsyncResult<S, E> => {
73
+ try {
74
+ return Result.ok(await fn());
75
+ } catch (error) {
76
+ return Result.err(mapError(error));
77
+ }
78
+ };
@@ -0,0 +1,84 @@
1
+ import { Result } from '../../result/index.mjs';
2
+ import { type AsyncResult } from '../async-result.mjs';
3
+
4
+ /**
5
+ * Maps an `AsyncResult<S, E>` to `AsyncResult<S, E2>` by applying a function
6
+ * to the error value. The mapping function may be synchronous or return a
7
+ * `Promise`; both are handled transparently. If the `AsyncResult` resolves to
8
+ * `Ok`, the original `Ok` is passed through and the function is not called.
9
+ *
10
+ * If `mapFn` throws or returns a rejecting Promise, the rejection propagates
11
+ * as-is — it is **not** caught and converted into an `Err`.
12
+ *
13
+ * @example
14
+ *
15
+ * ```ts
16
+ * const errValue = AsyncResult.fromPromise(
17
+ * Promise.reject(new Error('boom')),
18
+ * () => 'failure',
19
+ * );
20
+ *
21
+ * const uppercased = await AsyncResult.mapErr(errValue, (error) =>
22
+ * error.toUpperCase(),
23
+ * );
24
+ *
25
+ * assert.deepStrictEqual(uppercased, Result.err('FAILURE'));
26
+ * ```
27
+ *
28
+ * @template S The type of the success value.
29
+ * @template E The type of the error value of the input.
30
+ * @template E2 The type of the error value returned by the mapping function.
31
+ * @param asyncResult The `AsyncResult` to map.
32
+ * @param mapFn The function to apply to the error value if present.
33
+ * @returns A new `AsyncResult<S, E2>`.
34
+ */
35
+ export function mapErr<S, E, E2>(
36
+ asyncResult: AsyncResult<S, E>,
37
+ mapFn: (error: E) => E2 | Promise<E2>,
38
+ ): AsyncResult<S, E2>;
39
+
40
+ // Curried version
41
+ //
42
+ // Unlike `Result.mapErr`, the returned function can simply be generic over
43
+ // `S`: an `AsyncResult` is a `Promise`, not a discriminated union, so there
44
+ // is no literal-narrowing problem that would require the whole-result
45
+ // generic trick used by the synchronous combinators.
46
+ export function mapErr<E, E2>(
47
+ mapFn: (error: E) => E2 | Promise<E2>,
48
+ ): <S>(asyncResult: AsyncResult<S, E>) => AsyncResult<S, E2>;
49
+
50
+ export function mapErr<S, E, E2>(
51
+ ...args:
52
+ | readonly [
53
+ asyncResult: AsyncResult<S, E>,
54
+ mapFn: (error: E) => E2 | Promise<E2>,
55
+ ]
56
+ | readonly [mapFn: (error: E) => E2 | Promise<E2>]
57
+ ):
58
+ | AsyncResult<S, E2>
59
+ | ((asyncResult: AsyncResult<S, E>) => AsyncResult<S, E2>) {
60
+ switch (args.length) {
61
+ case 2: {
62
+ // Direct version: first argument is asyncResult
63
+ const [asyncResult, mapFn] = args;
64
+
65
+ return mapErrImpl(asyncResult, mapFn);
66
+ }
67
+
68
+ case 1: {
69
+ // Curried version: first argument is mapping function
70
+ const [mapFn] = args;
71
+
72
+ return (asyncResult: AsyncResult<S, E>) => mapErrImpl(asyncResult, mapFn);
73
+ }
74
+ }
75
+ }
76
+
77
+ const mapErrImpl = async <S, E, E2>(
78
+ asyncResult: AsyncResult<S, E>,
79
+ mapFn: (error: E) => E2 | Promise<E2>,
80
+ ): AsyncResult<S, E2> => {
81
+ const result = await asyncResult;
82
+
83
+ return Result.isOk(result) ? result : Result.err(await mapFn(result.value));
84
+ };
@@ -0,0 +1,94 @@
1
+ import { Result } from '../../result/index.mjs';
2
+ import { type AsyncResult } from '../async-result.mjs';
3
+
4
+ /**
5
+ * Maps an `AsyncResult<S, E>` to `AsyncResult<S2, E>` by applying a function
6
+ * to the success value. The mapping function may be synchronous or return a
7
+ * `Promise`; both are handled transparently. If the `AsyncResult` resolves to
8
+ * `Err`, the original `Err` is passed through and the function is not called.
9
+ *
10
+ * If `mapFn` throws or returns a rejecting Promise, the rejection propagates
11
+ * as-is — it is **not** caught and converted into an `Err`. `map` performs no
12
+ * implicit error conversion; write a fallible step as
13
+ * `AsyncResult.flatMap` combined with `AsyncResult.fromThrowable` instead, so
14
+ * that the error type stays explicit.
15
+ *
16
+ * @example
17
+ *
18
+ * ```ts
19
+ * const okValue = AsyncResult.fromPromise(
20
+ * Promise.resolve(5),
21
+ * () => 'failed',
22
+ * );
23
+ *
24
+ * const doubled = await AsyncResult.map(okValue, (value) => value * 2);
25
+ *
26
+ * assert.deepStrictEqual(doubled, Result.ok(10));
27
+ *
28
+ * const double = AsyncResult.map((value: number) => value * 2);
29
+ *
30
+ * const curried = await double(
31
+ * AsyncResult.fromPromise(Promise.resolve(21), () => 'failed'),
32
+ * );
33
+ *
34
+ * assert.deepStrictEqual(curried, Result.ok(42));
35
+ * ```
36
+ *
37
+ * @template S The type of the success value of the input.
38
+ * @template S2 The type of the success value returned by the mapping
39
+ * function.
40
+ * @template E The type of the error value.
41
+ * @param asyncResult The `AsyncResult` to map.
42
+ * @param mapFn The function to apply to the success value if present.
43
+ * @returns A new `AsyncResult<S2, E>`.
44
+ */
45
+ export function map<S, S2, E>(
46
+ asyncResult: AsyncResult<S, E>,
47
+ mapFn: (value: S) => Promise<S2> | S2,
48
+ ): AsyncResult<S2, E>;
49
+
50
+ // Curried version
51
+ //
52
+ // Unlike `Result.map`, the returned function can simply be generic over `E`:
53
+ // an `AsyncResult` is a `Promise`, not a discriminated union, so there is no
54
+ // literal-narrowing problem that would require the whole-result generic
55
+ // trick used by the synchronous combinators.
56
+ export function map<S, S2>(
57
+ mapFn: (value: S) => Promise<S2> | S2,
58
+ ): <E>(asyncResult: AsyncResult<S, E>) => AsyncResult<S2, E>;
59
+
60
+ export function map<S, S2, E>(
61
+ ...args:
62
+ | readonly [
63
+ asyncResult: AsyncResult<S, E>,
64
+ mapFn: (value: S) => Promise<S2> | S2,
65
+ ]
66
+ | readonly [mapFn: (value: S) => Promise<S2> | S2]
67
+ ):
68
+ | AsyncResult<S2, E>
69
+ | ((asyncResult: AsyncResult<S, E>) => AsyncResult<S2, E>) {
70
+ switch (args.length) {
71
+ case 2: {
72
+ // Direct version: first argument is asyncResult
73
+ const [asyncResult, mapFn] = args;
74
+
75
+ return mapImpl(asyncResult, mapFn);
76
+ }
77
+
78
+ case 1: {
79
+ // Curried version
80
+ const [mapFn] = args;
81
+
82
+ return (asyncResult: AsyncResult<S, E>) => mapImpl(asyncResult, mapFn);
83
+ }
84
+ }
85
+ }
86
+
87
+ const mapImpl = async <S, S2, E>(
88
+ asyncResult: AsyncResult<S, E>,
89
+ mapFn: (value: S) => Promise<S2> | S2,
90
+ ): AsyncResult<S2, E> => {
91
+ const result = await asyncResult;
92
+
93
+ return Result.isErr(result) ? result : Result.ok(await mapFn(result.value));
94
+ };