ts-data-forge 14.4.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.
- package/README.md +6 -0
- package/dist/array/impl/array-utils-element-access.mjs +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs +3 -3
- package/dist/array/impl/array-utils-search.d.mts.map +1 -1
- package/dist/array/impl/array-utils-search.mjs +1 -1
- package/dist/array/impl/array-utils-search.mjs.map +1 -1
- package/dist/collections/imap-mapped.mjs +1 -1
- package/dist/collections/imap.mjs +1 -1
- package/dist/entry-point.mjs +8 -6
- package/dist/entry-point.mjs.map +1 -1
- package/dist/functional/async-result/async-result.d.mts +16 -0
- package/dist/functional/async-result/async-result.d.mts.map +1 -0
- package/dist/functional/async-result/async-result.mjs +3 -0
- package/dist/functional/async-result/async-result.mjs.map +1 -0
- package/dist/functional/async-result/impl/async-result-flat-map.d.mts +39 -0
- package/dist/functional/async-result/impl/async-result-flat-map.d.mts.map +1 -0
- package/dist/functional/async-result/impl/async-result-flat-map.mjs +25 -0
- package/dist/functional/async-result/impl/async-result-flat-map.mjs.map +1 -0
- package/dist/functional/async-result/impl/async-result-from-promise.d.mts +47 -0
- package/dist/functional/async-result/impl/async-result-from-promise.d.mts.map +1 -0
- package/dist/functional/async-result/impl/async-result-from-promise.mjs +11 -0
- package/dist/functional/async-result/impl/async-result-from-promise.mjs.map +1 -0
- package/dist/functional/async-result/impl/async-result-from-throwable.d.mts +53 -0
- package/dist/functional/async-result/impl/async-result-from-throwable.d.mts.map +1 -0
- package/dist/functional/async-result/impl/async-result-from-throwable.mjs +17 -0
- package/dist/functional/async-result/impl/async-result-from-throwable.mjs.map +1 -0
- package/dist/functional/async-result/impl/async-result-map-err.d.mts +35 -0
- package/dist/functional/async-result/impl/async-result-map-err.d.mts.map +1 -0
- package/dist/functional/async-result/impl/async-result-map-err.mjs +23 -0
- package/dist/functional/async-result/impl/async-result-map-err.mjs.map +1 -0
- package/dist/functional/async-result/impl/async-result-map.d.mts +45 -0
- package/dist/functional/async-result/impl/async-result-map.d.mts.map +1 -0
- package/dist/functional/async-result/impl/async-result-map.mjs +23 -0
- package/dist/functional/async-result/impl/async-result-map.mjs.map +1 -0
- package/dist/functional/async-result/impl/async-result-unwrap-or.d.mts +34 -0
- package/dist/functional/async-result/impl/async-result-unwrap-or.d.mts.map +1 -0
- package/dist/functional/async-result/impl/async-result-unwrap-or.mjs +10 -0
- package/dist/functional/async-result/impl/async-result-unwrap-or.mjs.map +1 -0
- package/dist/functional/async-result/impl/index.d.mts +8 -0
- package/dist/functional/async-result/impl/index.d.mts.map +1 -0
- package/dist/functional/async-result/impl/index.mjs +7 -0
- package/dist/functional/async-result/impl/index.mjs.map +1 -0
- package/dist/functional/async-result/impl/types.d.mts +45 -0
- package/dist/functional/async-result/impl/types.d.mts.map +1 -0
- package/dist/functional/async-result/impl/types.mjs +2 -0
- package/dist/functional/async-result/impl/types.mjs.map +1 -0
- package/dist/functional/async-result/index.d.mts +2 -0
- package/dist/functional/async-result/index.d.mts.map +1 -0
- package/dist/functional/async-result/index.mjs +3 -0
- package/dist/functional/async-result/index.mjs.map +1 -0
- package/dist/functional/index.d.mts +1 -0
- package/dist/functional/index.d.mts.map +1 -1
- package/dist/functional/index.mjs +8 -6
- package/dist/functional/index.mjs.map +1 -1
- package/dist/functional/optional/impl/index.d.mts +1 -0
- package/dist/functional/optional/impl/index.d.mts.map +1 -1
- package/dist/functional/optional/impl/index.mjs +1 -0
- package/dist/functional/optional/impl/index.mjs.map +1 -1
- package/dist/functional/optional/impl/optional-match.d.mts +48 -0
- package/dist/functional/optional/impl/optional-match.d.mts.map +1 -0
- package/dist/functional/optional/impl/optional-match.mjs +22 -0
- package/dist/functional/optional/impl/optional-match.mjs.map +1 -0
- package/dist/functional/result/impl/index.d.mts +4 -0
- package/dist/functional/result/impl/index.d.mts.map +1 -1
- package/dist/functional/result/impl/index.mjs +4 -0
- package/dist/functional/result/impl/index.mjs.map +1 -1
- package/dist/functional/result/impl/result-from-optional.d.mts +41 -0
- package/dist/functional/result/impl/result-from-optional.d.mts.map +1 -0
- package/dist/functional/result/impl/result-from-optional.mjs +24 -0
- package/dist/functional/result/impl/result-from-optional.mjs.map +1 -0
- package/dist/functional/result/impl/result-from-promise.d.mts +5 -0
- package/dist/functional/result/impl/result-from-promise.d.mts.map +1 -1
- package/dist/functional/result/impl/result-from-promise.mjs.map +1 -1
- package/dist/functional/result/impl/result-match.d.mts +47 -0
- package/dist/functional/result/impl/result-match.d.mts.map +1 -0
- package/dist/functional/result/impl/result-match.mjs +24 -0
- package/dist/functional/result/impl/result-match.mjs.map +1 -0
- package/dist/functional/result/impl/result-safe-try.d.mts +51 -0
- package/dist/functional/result/impl/result-safe-try.d.mts.map +1 -0
- package/dist/functional/result/impl/result-safe-try.mjs +24 -0
- package/dist/functional/result/impl/result-safe-try.mjs.map +1 -0
- package/dist/functional/result/impl/result-safe-unwrap.d.mts +36 -0
- package/dist/functional/result/impl/result-safe-unwrap.d.mts.map +1 -0
- package/dist/functional/result/impl/result-safe-unwrap.mjs +14 -0
- package/dist/functional/result/impl/result-safe-unwrap.mjs.map +1 -0
- package/dist/json/json.mjs +2 -2
- package/dist/number/refined-number-utils.mjs +1 -1
- package/dist/object/object.mjs +1 -1
- package/dist/others/debounce-function.d.mts.map +1 -1
- package/dist/others/debounce-function.mjs.map +1 -1
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +1 -1
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/package.json +10 -10
- package/src/array/impl/array-utils-modification.test.mts +3 -3
- package/src/array/impl/array-utils-search.mts +22 -8
- package/src/array/impl/array-utils-shape-invariants.test.mts +3 -3
- package/src/array/impl/array-utils-transformation.test.mts +1 -1
- package/src/functional/async-result/async-result.mts +17 -0
- package/src/functional/async-result/impl/async-result-flat-map.mts +99 -0
- package/src/functional/async-result/impl/async-result-from-promise.mts +69 -0
- package/src/functional/async-result/impl/async-result-from-throwable.mts +78 -0
- package/src/functional/async-result/impl/async-result-map-err.mts +84 -0
- package/src/functional/async-result/impl/async-result-map.mts +94 -0
- package/src/functional/async-result/impl/async-result-unwrap-or.mts +42 -0
- package/src/functional/async-result/impl/index.mts +7 -0
- package/src/functional/async-result/impl/types.mts +51 -0
- package/src/functional/async-result/index.mts +1 -0
- package/src/functional/async-result.test.mts +290 -0
- package/src/functional/index.mts +1 -0
- package/src/functional/optional/impl/index.mts +1 -0
- package/src/functional/optional/impl/optional-match.mts +108 -0
- package/src/functional/optional.test.mts +79 -0
- package/src/functional/result/impl/index.mts +4 -0
- package/src/functional/result/impl/result-from-optional.mts +80 -0
- package/src/functional/result/impl/result-from-promise.mts +5 -0
- package/src/functional/result/impl/result-match.mts +108 -0
- package/src/functional/result/impl/result-safe-try.mts +110 -0
- package/src/functional/result/impl/result-safe-unwrap.mts +50 -0
- package/src/functional/result.test.mts +331 -0
- package/src/guard/is-error.test.mts +7 -7
- package/src/others/debounce-function.mts +5 -1
- package/src/others/fast-deep-equal.mts +9 -2
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { isOk } from './result-is-ok.mjs';
|
|
2
|
+
|
|
3
|
+
function match(...args) {
|
|
4
|
+
switch (args.length) {
|
|
5
|
+
case 2: {
|
|
6
|
+
const [result, cases] = args;
|
|
7
|
+
return matchImpl(result, cases);
|
|
8
|
+
}
|
|
9
|
+
case 1: {
|
|
10
|
+
const [cases] = args;
|
|
11
|
+
return (result) => matchImpl(result, cases);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const matchImpl = (result, cases) => isOk(result) ? (
|
|
16
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
17
|
+
cases.ok(result.value)
|
|
18
|
+
) : (
|
|
19
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
20
|
+
cases.err(result.value)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export { match };
|
|
24
|
+
//# sourceMappingURL=result-match.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-match.mjs","sources":["../../../../src/functional/result/impl/result-match.mts"],"sourcesContent":["import { type UnknownResult } from '../result.mjs';\nimport { isOk } from './result-is-ok.mjs';\nimport { type UnwrapErr, type UnwrapOk } from './types.mjs';\n\n/**\n * Folds a `Result` into a plain value by applying one of two case handlers:\n * `ok` if the `Result` is `Ok`, `err` otherwise. This is the function form of\n * pattern matching on a `Result`.\n *\n * Unlike {@link Result.fold}, which maps both sides and returns another\n * `Result`, `match` unwraps: both branches produce the final value directly.\n *\n * @example\n *\n * ```ts\n * const doubledOrZero = Result.match(Result.ok(21), {\n * ok: (value) => value * 2,\n * err: () => 0,\n * });\n *\n * assert.deepStrictEqual(doubledOrZero, 42);\n *\n * const matcher = Result.match({\n * ok: (value: number) => value + 1,\n * err: (error: string) => error.length,\n * });\n *\n * assert.deepStrictEqual(matcher(Result.ok(1)), 2);\n *\n * assert.deepStrictEqual(matcher(Result.err('oops')), 4);\n * ```\n *\n * @template R The input `UnknownResult` type.\n * @template S2 The type returned by the `ok` case.\n * @template E2 The type returned by the `err` case.\n * @param result The `Result` to match on.\n * @param cases An object with an `ok` handler receiving the success value and\n * an `err` handler receiving the error value.\n * @returns The value produced by whichever case handler was applied.\n */\nexport function match<R extends UnknownResult, S2, E2>(\n result: R,\n cases: Readonly<{\n ok: (value: UnwrapOk<R>) => S2;\n err: (error: UnwrapErr<R>) => E2;\n }>,\n): E2 | S2;\n\n// Curried version\n//\n// The returned function is generic over the *whole* result, mirroring the\n// direct overload above; see the comment on the curried overload of\n// `Result.map` for why the parameter is a conditional type instead of a plain\n// `Result<S, E>`. Both sides have to be checked here, hence the nested\n// conditional.\nexport function match<S, E, S2, E2>(\n cases: Readonly<{\n ok: (value: S) => S2;\n err: (error: E) => E2;\n }>,\n): <R extends UnknownResult>(\n result: UnwrapOk<R> extends S ? (UnwrapErr<R> extends E ? R : never) : never,\n) => E2 | S2;\n\nexport function match<R extends UnknownResult, S2, E2>(\n ...args:\n | readonly [\n result: R,\n cases: Readonly<{\n ok: (value: UnwrapOk<R>) => S2;\n err: (error: UnwrapErr<R>) => E2;\n }>,\n ]\n | readonly [\n cases: Readonly<{\n ok: (value: UnwrapOk<R>) => S2;\n err: (error: UnwrapErr<R>) => E2;\n }>,\n ]\n): E2 | S2 | ((result: R) => E2 | S2) {\n switch (args.length) {\n case 2: {\n const [result, cases] = args;\n\n return matchImpl(result, cases);\n }\n\n case 1: {\n // Curried version: the only argument is the case handlers\n const [cases] = args;\n\n return (result: R) => matchImpl(result, cases);\n }\n }\n}\n\nconst matchImpl = <R extends UnknownResult, S2, E2>(\n result: R,\n cases: Readonly<{\n ok: (value: UnwrapOk<R>) => S2;\n err: (error: UnwrapErr<R>) => E2;\n }>,\n): E2 | S2 =>\n isOk(result)\n ? // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n cases.ok(result.value as UnwrapOk<R>)\n : // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n cases.err(result.value as UnwrapErr<R>);\n"],"names":[],"mappings":";;AAgEO,SAAS,SACX,IAAA,EAciC;AACpC,EAAA,QAAQ,KAAK,MAAA;AAAQ,IACnB,KAAK,CAAA,EAAG;AACN,MAAA,MAAM,CAAC,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA;AAExB,MAAA,OAAO,SAAA,CAAU,QAAQ,KAAK,CAAA;AAAA,IAChC;AAAA,IAEA,KAAK,CAAA,EAAG;AAEN,MAAA,MAAM,CAAC,KAAK,CAAA,GAAI,IAAA;AAEhB,MAAA,OAAO,CAAC,MAAA,KAAc,SAAA,CAAU,MAAA,EAAQ,KAAK,CAAA;AAAA,IAC/C;AAAA;AAEJ;AAEA,MAAM,SAAA,GAAY,CAChB,MAAA,EACA,KAAA,KAKA,KAAK,MAAM,CAAA;AAAA;AAAA,EAEP,KAAA,CAAM,EAAA,CAAG,MAAA,CAAO,KAAoB;AAAA;AAAA;AAAA,EAEpC,KAAA,CAAM,GAAA,CAAI,MAAA,CAAO,KAAqB;AAAA,CAAA;;;;"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Err } from '../../../types.mjs';
|
|
2
|
+
import { type Result, type UnknownResult } from '../result.mjs';
|
|
3
|
+
import { type UnwrapErr, type UnwrapOk } from './types.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* Runs a generator body in which `yield* Result.safeUnwrap(...)` unwraps `Ok`
|
|
6
|
+
* values and short-circuits on the first `Err`, emulating Rust's `?` operator
|
|
7
|
+
* (early-return error propagation).
|
|
8
|
+
*
|
|
9
|
+
* The body must return a `Result`. If every unwrapped `Result` is `Ok`, the
|
|
10
|
+
* returned value is the body's `Result`; otherwise it is the first `Err`
|
|
11
|
+
* encountered, and the rest of the body never runs — though the generator is
|
|
12
|
+
* closed rather than dropped, so `finally` blocks and `using` disposals
|
|
13
|
+
* pending in the body still run. An exception thrown by the body is not
|
|
14
|
+
* converted to an `Err`: it propagates out of `safeTry` (as a rejected
|
|
15
|
+
* `Promise` for an async body). Use {@link Result.fromThrowable} for that.
|
|
16
|
+
*
|
|
17
|
+
* Passing an async generator (`async function*`) returns a
|
|
18
|
+
* `Promise<Result<...>>` instead, so `await` can be used inside the body.
|
|
19
|
+
* Note that `yield*` inside an `async function*` awaits what it receives, so
|
|
20
|
+
* unwrapping an `Ok` whose value is itself a `Promise` yields the awaited
|
|
21
|
+
* value there while the static type still says `Promise<...>`; keep promises
|
|
22
|
+
* out of the `Ok` side of a `Result` unwrapped in an async body.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* const parseTwo = (a: string, b: string): Result<number, Error> =>
|
|
28
|
+
* Result.safeTry(function* () {
|
|
29
|
+
* const x = yield* Result.safeUnwrap(Num.safeParseInt(a));
|
|
30
|
+
*
|
|
31
|
+
* const y = yield* Result.safeUnwrap(Num.safeParseInt(b));
|
|
32
|
+
*
|
|
33
|
+
* return Result.ok(x + y);
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* assert.deepStrictEqual(parseTwo('1', '2'), Result.ok(3));
|
|
37
|
+
*
|
|
38
|
+
* assert.isTrue(Result.isErr(parseTwo('1', 'not a number')));
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @template Y The union of `Err` types unwrapped via
|
|
42
|
+
* {@link Result.safeUnwrap} (`never` if nothing is unwrapped).
|
|
43
|
+
* @template R2 The `Result` type returned by the body.
|
|
44
|
+
* @param body A generator function whose `yield*` expressions delegate to
|
|
45
|
+
* {@link Result.safeUnwrap} and whose return value is a `Result`.
|
|
46
|
+
* @returns The body's `Result`, or the first `Err` short-circuited out of it
|
|
47
|
+
* (wrapped in a `Promise` for an async generator body).
|
|
48
|
+
*/
|
|
49
|
+
export declare function safeTry<Y extends Err<unknown>, R2 extends UnknownResult>(body: () => Generator<Y, R2>): Result<UnwrapOk<R2>, UnwrapErr<R2> | UnwrapErr<Y>>;
|
|
50
|
+
export declare function safeTry<Y extends Err<unknown>, R2 extends UnknownResult>(body: () => AsyncGenerator<Y, R2>): Promise<Result<UnwrapOk<R2>, UnwrapErr<R2> | UnwrapErr<Y>>>;
|
|
51
|
+
//# sourceMappingURL=result-safe-try.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-safe-try.d.mts","sourceRoot":"","sources":["../../../../src/functional/result/impl/result-safe-try.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,aAAa,EACtE,IAAI,EAAE,MAAM,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAC3B,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtD,wBAAgB,OAAO,CAAC,CAAC,SAAS,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,aAAa,EACtE,IAAI,EAAE,MAAM,cAAc,CAAC,CAAC,EAAE,EAAE,CAAC,GAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { hasKey } from '../../../guard/has-key.mjs';
|
|
2
|
+
import { isRecord } from '../../../guard/is-record.mjs';
|
|
3
|
+
|
|
4
|
+
function safeTry(body) {
|
|
5
|
+
const generator = body();
|
|
6
|
+
const step = generator.next();
|
|
7
|
+
if (isSyncStep(step)) {
|
|
8
|
+
if (step.done !== true && isSyncGenerator(generator, step)) {
|
|
9
|
+
generator.return(step.value);
|
|
10
|
+
}
|
|
11
|
+
return step.value;
|
|
12
|
+
}
|
|
13
|
+
return step.then(async (asyncStep) => {
|
|
14
|
+
if (asyncStep.done !== true) {
|
|
15
|
+
await generator.return(asyncStep.value);
|
|
16
|
+
}
|
|
17
|
+
return asyncStep.value;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const isSyncStep = (value) => isRecord(value) && hasKey(value, "done");
|
|
21
|
+
const isSyncGenerator = (_generator, step) => isSyncStep(step);
|
|
22
|
+
|
|
23
|
+
export { safeTry };
|
|
24
|
+
//# sourceMappingURL=result-safe-try.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-safe-try.mjs","sources":["../../../../src/functional/result/impl/result-safe-try.mts"],"sourcesContent":["import { hasKey, isRecord } from '../../../guard/index.mjs';\nimport { type Err } from '../../../types.mjs';\nimport { type Result, type UnknownResult } from '../result.mjs';\nimport { type UnwrapErr, type UnwrapOk } from './types.mjs';\n\n/**\n * Runs a generator body in which `yield* Result.safeUnwrap(...)` unwraps `Ok`\n * values and short-circuits on the first `Err`, emulating Rust's `?` operator\n * (early-return error propagation).\n *\n * The body must return a `Result`. If every unwrapped `Result` is `Ok`, the\n * returned value is the body's `Result`; otherwise it is the first `Err`\n * encountered, and the rest of the body never runs — though the generator is\n * closed rather than dropped, so `finally` blocks and `using` disposals\n * pending in the body still run. An exception thrown by the body is not\n * converted to an `Err`: it propagates out of `safeTry` (as a rejected\n * `Promise` for an async body). Use {@link Result.fromThrowable} for that.\n *\n * Passing an async generator (`async function*`) returns a\n * `Promise<Result<...>>` instead, so `await` can be used inside the body.\n * Note that `yield*` inside an `async function*` awaits what it receives, so\n * unwrapping an `Ok` whose value is itself a `Promise` yields the awaited\n * value there while the static type still says `Promise<...>`; keep promises\n * out of the `Ok` side of a `Result` unwrapped in an async body.\n *\n * @example\n *\n * ```ts\n * const parseTwo = (a: string, b: string): Result<number, Error> =>\n * Result.safeTry(function* () {\n * const x = yield* Result.safeUnwrap(Num.safeParseInt(a));\n *\n * const y = yield* Result.safeUnwrap(Num.safeParseInt(b));\n *\n * return Result.ok(x + y);\n * });\n *\n * assert.deepStrictEqual(parseTwo('1', '2'), Result.ok(3));\n *\n * assert.isTrue(Result.isErr(parseTwo('1', 'not a number')));\n * ```\n *\n * @template Y The union of `Err` types unwrapped via\n * {@link Result.safeUnwrap} (`never` if nothing is unwrapped).\n * @template R2 The `Result` type returned by the body.\n * @param body A generator function whose `yield*` expressions delegate to\n * {@link Result.safeUnwrap} and whose return value is a `Result`.\n * @returns The body's `Result`, or the first `Err` short-circuited out of it\n * (wrapped in a `Promise` for an async generator body).\n */\nexport function safeTry<Y extends Err<unknown>, R2 extends UnknownResult>(\n body: () => Generator<Y, R2>,\n): Result<UnwrapOk<R2>, UnwrapErr<R2> | UnwrapErr<Y>>;\n\nexport function safeTry<Y extends Err<unknown>, R2 extends UnknownResult>(\n body: () => AsyncGenerator<Y, R2>,\n): Promise<Result<UnwrapOk<R2>, UnwrapErr<R2> | UnwrapErr<Y>>>;\n\nexport function safeTry(\n body:\n | (() => AsyncGenerator<UnknownResult, UnknownResult>)\n | (() => Generator<UnknownResult, UnknownResult>),\n): Promise<UnknownResult> | UnknownResult {\n const generator = body();\n\n // A yielded `Err` and a returned `Result` are both already the final\n // answer, so the first step's `value` is the result in either case: the\n // generator is closed rather than resumed after yielding.\n const step = generator.next();\n\n if (isSyncStep(step)) {\n // Dropping a suspended generator would skip the `finally` blocks and\n // `using` disposals still pending in the body; closing it runs them\n // before the short-circuited `Err` is handed back. `isSyncGenerator` is\n // what makes `return()` here synchronous rather than a floating promise.\n if (step.done !== true && isSyncGenerator(generator, step)) {\n generator.return(step.value);\n }\n\n return step.value;\n }\n\n return step.then(async (asyncStep) => {\n if (asyncStep.done !== true) {\n await generator.return(asyncStep.value);\n }\n\n return asyncStep.value;\n });\n}\n\n// A sync generator's `next()` returns a plain `{ value, done }` object with\n// own properties, while an async generator's returns a `Promise`, whose\n// `then` lives on the prototype — so an own-key check on `done` separates the\n// two.\nconst isSyncStep = (\n value: unknown,\n): value is IteratorResult<UnknownResult, UnknownResult> =>\n isRecord(value) && hasKey(value, 'done');\n\n// The generator-side counterpart of `isSyncStep`: a step in the sync form can\n// only have come from a sync generator, so the step is what narrows the\n// generator that produced it — hence the predicate reads its second argument\n// and not its first.\nconst isSyncGenerator = (\n _generator:\n | AsyncGenerator<UnknownResult, UnknownResult>\n | Generator<UnknownResult, UnknownResult>,\n step: unknown,\n): _generator is Generator<UnknownResult, UnknownResult> => isSyncStep(step);\n"],"names":[],"mappings":";;;AA0DO,SAAS,QACd,IAAA,EAGwC;AACxC,EAAA,MAAM,YAAY,IAAA,EAAK;AAKvB,EAAA,MAAM,IAAA,GAAO,UAAU,IAAA,EAAK;AAE5B,EAAA,IAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AAKpB,IAAA,IAAI,KAAK,IAAA,KAAS,IAAA,IAAQ,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA,EAAG;AAC1D,MAAA,SAAA,CAAU,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,IAC7B;AAEA,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AAEA,EAAA,OAAO,IAAA,CAAK,IAAA,CAAK,OAAO,SAAA,KAAc;AACpC,IAAA,IAAI,SAAA,CAAU,SAAS,IAAA,EAAM;AAC3B,MAAA,MAAM,SAAA,CAAU,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA;AAAA,IACxC;AAEA,IAAA,OAAO,SAAA,CAAU,KAAA;AAAA,EACnB,CAAC,CAAA;AACH;AAMA,MAAM,UAAA,GAAa,CACjB,KAAA,KAEA,QAAA,CAAS,KAAK,CAAA,IAAK,MAAA,CAAO,OAAO,MAAM,CAAA;AAMzC,MAAM,eAAA,GAAkB,CACtB,UAAA,EAGA,IAAA,KAC0D,WAAW,IAAI,CAAA;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type UnknownResult } from '../result.mjs';
|
|
2
|
+
import { type NarrowToErr, type UnwrapOk } from './types.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Unwraps a `Result` inside a {@link Result.safeTry} generator body via
|
|
5
|
+
* `yield*`, emulating Rust's `?` operator.
|
|
6
|
+
*
|
|
7
|
+
* If the `Result` is `Ok`, the `yield*` expression evaluates to the contained
|
|
8
|
+
* value and execution continues. If it is `Err`, the error is yielded to
|
|
9
|
+
* {@link Result.safeTry}, which returns it immediately — the rest of the
|
|
10
|
+
* generator body never runs.
|
|
11
|
+
*
|
|
12
|
+
* This generator must only be delegated to (`yield*`) inside a
|
|
13
|
+
* {@link Result.safeTry} body; resuming it after it has yielded an `Err`
|
|
14
|
+
* throws.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const result = Result.safeTry(function* () {
|
|
20
|
+
* const x = yield* Result.safeUnwrap(Result.ok(2));
|
|
21
|
+
*
|
|
22
|
+
* const y = yield* Result.safeUnwrap(Result.ok(3));
|
|
23
|
+
*
|
|
24
|
+
* return Result.ok(x + y);
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* assert.deepStrictEqual(result, Result.ok(5));
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @template R The input `UnknownResult` type.
|
|
31
|
+
* @param result The `Result` to unwrap.
|
|
32
|
+
* @returns A generator that yields the `Err` (if any) and returns the `Ok`
|
|
33
|
+
* value.
|
|
34
|
+
*/
|
|
35
|
+
export declare function safeUnwrap<R extends UnknownResult>(result: R): Generator<NarrowToErr<R>, UnwrapOk<R>, unknown>;
|
|
36
|
+
//# sourceMappingURL=result-safe-unwrap.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-safe-unwrap.d.mts","sourceRoot":"","sources":["../../../../src/functional/result/impl/result-safe-unwrap.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAiB,UAAU,CAAC,CAAC,SAAS,aAAa,EACjD,MAAM,EAAE,CAAC,GACR,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAWjD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isErr } from './result-is-err.mjs';
|
|
2
|
+
|
|
3
|
+
function* safeUnwrap(result) {
|
|
4
|
+
if (isErr(result)) {
|
|
5
|
+
yield result;
|
|
6
|
+
throw new Error(
|
|
7
|
+
"`safeUnwrap()` generator was resumed after yielding an `Err`; it must only be used via `yield*` inside `safeTry()`"
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
return result.value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { safeUnwrap };
|
|
14
|
+
//# sourceMappingURL=result-safe-unwrap.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-safe-unwrap.mjs","sources":["../../../../src/functional/result/impl/result-safe-unwrap.mts"],"sourcesContent":["import { type UnknownResult } from '../result.mjs';\nimport { isErr } from './result-is-err.mjs';\nimport { type NarrowToErr, type UnwrapOk } from './types.mjs';\n\n/**\n * Unwraps a `Result` inside a {@link Result.safeTry} generator body via\n * `yield*`, emulating Rust's `?` operator.\n *\n * If the `Result` is `Ok`, the `yield*` expression evaluates to the contained\n * value and execution continues. If it is `Err`, the error is yielded to\n * {@link Result.safeTry}, which returns it immediately — the rest of the\n * generator body never runs.\n *\n * This generator must only be delegated to (`yield*`) inside a\n * {@link Result.safeTry} body; resuming it after it has yielded an `Err`\n * throws.\n *\n * @example\n *\n * ```ts\n * const result = Result.safeTry(function* () {\n * const x = yield* Result.safeUnwrap(Result.ok(2));\n *\n * const y = yield* Result.safeUnwrap(Result.ok(3));\n *\n * return Result.ok(x + y);\n * });\n *\n * assert.deepStrictEqual(result, Result.ok(5));\n * ```\n *\n * @template R The input `UnknownResult` type.\n * @param result The `Result` to unwrap.\n * @returns A generator that yields the `Err` (if any) and returns the `Ok`\n * value.\n */\nexport function* safeUnwrap<R extends UnknownResult>(\n result: R,\n): Generator<NarrowToErr<R>, UnwrapOk<R>, unknown> {\n if (isErr(result)) {\n yield result;\n\n throw new Error(\n '`safeUnwrap()` generator was resumed after yielding an `Err`; it must only be used via `yield*` inside `safeTry()`',\n );\n }\n\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n return result.value as UnwrapOk<R>;\n}\n"],"names":[],"mappings":";;AAoCO,UAAU,WACf,MAAA,EACiD;AACjD,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG;AACjB,IAAA,MAAM,MAAA;AAEN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;;;;"}
|
package/dist/json/json.mjs
CHANGED
|
@@ -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';
|
package/dist/object/object.mjs
CHANGED
|
@@ -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';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debounce-function.d.mts","sourceRoot":"","sources":["../../src/others/debounce-function.mts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAI,IAAI,SAAS,SAAS,OAAO,EAAE,QAChD,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,oBACX,MAAM,KACvB,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"debounce-function.d.mts","sourceRoot":"","sources":["../../src/others/debounce-function.mts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAI,IAAI,SAAS,SAAS,OAAO,EAAE,QAChD,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,oBACX,MAAM,KACvB,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAc1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debounce-function.mjs","sources":["../../src/others/debounce-function.mts"],"sourcesContent":["/**\n * Creates a debounced function that delays invoking `func` until after `waitMilliseconds`\n * have elapsed since the last time the debounced function was invoked.\n *\n * @template F The type of the function to debounce.\n * @param func The function to debounce.\n * @param waitMilliseconds The number of milliseconds to delay.\n * @returns The new debounced function.\n */\nexport const debounce = <Args extends readonly unknown[]>(\n func: (...args: Args) => void,\n waitMilliseconds: number,\n): ((...args: Args) => void) => {\n let mut_timeoutId:
|
|
1
|
+
{"version":3,"file":"debounce-function.mjs","sources":["../../src/others/debounce-function.mts"],"sourcesContent":["/**\n * Creates a debounced function that delays invoking `func` until after `waitMilliseconds`\n * have elapsed since the last time the debounced function was invoked.\n *\n * @template F The type of the function to debounce.\n * @param func The function to debounce.\n * @param waitMilliseconds The number of milliseconds to delay.\n * @returns The new debounced function.\n */\nexport const debounce = <Args extends readonly unknown[]>(\n func: (...args: Args) => void,\n waitMilliseconds: number,\n): ((...args: Args) => void) => {\n // Not `ReturnType<typeof setTimeout>`: with both the DOM and Node\n // declarations in scope that resolves to `unknown`, which `clearTimeout`\n // will not take. Naming what `clearTimeout` accepts says the same thing and\n // stays true in either environment.\n let mut_timeoutId: Parameters<typeof clearTimeout>[0];\n\n return (...args: Args): void => {\n clearTimeout(mut_timeoutId);\n\n mut_timeoutId = setTimeout(() => {\n func(...args);\n }, waitMilliseconds);\n };\n};\n"],"names":[],"mappings":"AASO,MAAM,QAAA,GAAW,CACtB,IAAA,EACA,gBAAA,KAC8B;AAK9B,EAAA,IAAI,aAAA;AAEJ,EAAA,OAAO,IAAI,IAAA,KAAqB;AAC9B,IAAA,YAAA,CAAa,aAAa,CAAA;AAE1B,IAAA,aAAA,GAAgB,WAAW,MAAM;AAC/B,MAAA,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,IACd,GAAG,gBAAgB,CAAA;AAAA,EACrB,CAAA;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.d.mts","sourceRoot":"","sources":["../../src/others/fast-deep-equal.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,KAAM,CAAC,KAAK,CAAC,KAAG,
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.d.mts","sourceRoot":"","sources":["../../src/others/fast-deep-equal.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,KAAM,CAAC,KAAK,CAAC,KAAG,OAgG9C,CAAC"}
|
|
@@ -49,7 +49,7 @@ const fastDeepEqual = (a, b) => {
|
|
|
49
49
|
!(key === "_owner" && a["$$typeof"]) && fastDeepEqual(a[key], b[key])
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
|
-
return Number.isNaN(a) && Number.isNaN(b);
|
|
52
|
+
return typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b);
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
export { fastDeepEqual };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":["import { isMap, isRegExp, isSet, isTypedArray } from '@sindresorhus/is';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * The fastest deep equal with ES6 Map, Set and Typed arrays support.\n * Checks equality of Date and RegExp objects by value.\n *\n * Forked from https://github.com/epoberezkin/fast-deep-equal/blob/v3.1.3/src/index.jst\n */\nexport const fastDeepEqual = <T,>(a: T, b: T): boolean => {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n // Arrays are handled before `isRecord`, which excludes them.\n if (Array.isArray(a)) {\n return (\n Array.isArray(b) &&\n a.constructor === b.constructor &&\n a.length === b.length &&\n a.every((ai, index) => fastDeepEqual(ai, b[index]))\n );\n }\n\n if (isRecord(a)) {\n if (!isRecord(b)) {\n return false;\n }\n\n if (a.constructor !== b.constructor) {\n return false;\n }\n\n if (isMap(a)) {\n return (\n isMap(b) &&\n a.size === b.size &&\n a\n .entries()\n .every(\n ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),\n )\n );\n }\n\n if (isSet(a)) {\n return (\n isSet(b) &&\n a.size === b.size &&\n a.entries().every(([value]) => b.has(value))\n );\n }\n\n if (isTypedArray(a)) {\n return (\n isTypedArray(b) &&\n a.length === b.length &&\n a.every((value, index) => value === b[index])\n );\n }\n\n if (isRegExp(a)) {\n return isRegExp(b) && a.source === b.source && a.flags === b.flags;\n }\n\n if (a.valueOf !== Object.prototype.valueOf) {\n return a.valueOf() === b.valueOf();\n }\n\n if (a.toString !== Object.prototype.toString) {\n return a.toString() === b.toString();\n }\n\n const aKeys: readonly string[] = Object.keys(a);\n\n const bKeys: readonly string[] = Object.keys(b);\n\n return (\n aKeys.length === bKeys.length &&\n aKeys.every(\n (key) =>\n hasKey(b, key) &&\n // React-specific: avoid traversing React elements' _owner.\n // _owner contains circular references\n // and is not needed when comparing the actual elements (and not their owners)\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n !(key === '_owner' && a['$$typeof']) &&\n fastDeepEqual(a[key], b[key]),\n )\n );\n }\n\n // true if both NaN, false otherwise\n return Number.isNaN(a)
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":["import { isMap, isRegExp, isSet, isTypedArray } from '@sindresorhus/is';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * The fastest deep equal with ES6 Map, Set and Typed arrays support.\n * Checks equality of Date and RegExp objects by value.\n *\n * Forked from https://github.com/epoberezkin/fast-deep-equal/blob/v3.1.3/src/index.jst\n */\nexport const fastDeepEqual = <T,>(a: T, b: T): boolean => {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n // Arrays are handled before `isRecord`, which excludes them.\n if (Array.isArray(a)) {\n return (\n Array.isArray(b) &&\n a.constructor === b.constructor &&\n a.length === b.length &&\n a.every((ai, index) => fastDeepEqual(ai, b[index]))\n );\n }\n\n if (isRecord(a)) {\n if (!isRecord(b)) {\n return false;\n }\n\n if (a.constructor !== b.constructor) {\n return false;\n }\n\n if (isMap(a)) {\n return (\n isMap(b) &&\n a.size === b.size &&\n a\n .entries()\n .every(\n ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),\n )\n );\n }\n\n if (isSet(a)) {\n return (\n isSet(b) &&\n a.size === b.size &&\n a.entries().every(([value]) => b.has(value))\n );\n }\n\n if (isTypedArray(a)) {\n return (\n isTypedArray(b) &&\n a.length === b.length &&\n a.every((value, index) => value === b[index])\n );\n }\n\n if (isRegExp(a)) {\n return isRegExp(b) && a.source === b.source && a.flags === b.flags;\n }\n\n if (a.valueOf !== Object.prototype.valueOf) {\n return a.valueOf() === b.valueOf();\n }\n\n if (a.toString !== Object.prototype.toString) {\n return a.toString() === b.toString();\n }\n\n const aKeys: readonly string[] = Object.keys(a);\n\n const bKeys: readonly string[] = Object.keys(b);\n\n return (\n aKeys.length === bKeys.length &&\n aKeys.every(\n (key) =>\n hasKey(b, key) &&\n // React-specific: avoid traversing React elements' _owner.\n // _owner contains circular references\n // and is not needed when comparing the actual elements (and not their owners)\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n !(key === '_owner' && a['$$typeof']) &&\n fastDeepEqual(a[key], b[key]),\n )\n );\n }\n\n // true if both NaN, false otherwise. `Number.isNaN` takes a number: called\n // with anything else it is always false, so the check belongs behind a\n // `typeof` guard rather than relying on that.\n return (\n typeof a === 'number' &&\n typeof b === 'number' &&\n Number.isNaN(a) &&\n Number.isNaN(b)\n );\n};\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-data-forge",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -37,29 +37,29 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@sindresorhus/is": "^8.1.0",
|
|
40
|
-
"ts-type-forge": "~9.2.
|
|
40
|
+
"ts-type-forge": "~9.2.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@emotion/react": "11.14.0",
|
|
44
44
|
"@emotion/styled": "11.14.1",
|
|
45
45
|
"@mui/material": "9.3.1",
|
|
46
46
|
"@types/react": "19.2.18",
|
|
47
|
-
"@vitest/browser-playwright": "4.1.
|
|
47
|
+
"@vitest/browser-playwright": "4.1.11",
|
|
48
48
|
"dedent": "1.7.2",
|
|
49
|
-
"
|
|
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
|
+
"immer": "11.1.18",
|
|
50
53
|
"playwright": "1.62.1",
|
|
51
54
|
"prettier": "3.9.6",
|
|
52
55
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
53
56
|
"prettier-plugin-packagejson": "3.0.2",
|
|
54
57
|
"react": "19.2.8",
|
|
55
|
-
"rollup": "4.62.
|
|
58
|
+
"rollup": "4.62.5",
|
|
59
|
+
"ts-repo-utils": "10.3.3",
|
|
56
60
|
"typedoc": "0.28.20",
|
|
57
61
|
"typedoc-github-theme": "0.4.0",
|
|
58
|
-
"vite": "8.2.
|
|
59
|
-
"eslint-plugin-ts-fortress": "1.0.4",
|
|
60
|
-
"eslint-config-typed": "5.9.3",
|
|
61
|
-
"ts-repo-utils": "10.2.0",
|
|
62
|
-
"eslint-plugin-ts-type-forge": "0.6.6"
|
|
62
|
+
"vite": "8.2.2"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"typescript": ">=4.8"
|
|
@@ -257,7 +257,7 @@ describe('Arr modifications', () => {
|
|
|
257
257
|
xs: T,
|
|
258
258
|
): Readonly<{ [K in keyof T]: 0 }> => toFilled(xs, 0);
|
|
259
259
|
|
|
260
|
-
const result = blank([1, 2, 3]
|
|
260
|
+
const result = blank([1, 2, 3]);
|
|
261
261
|
|
|
262
262
|
expectType<typeof result, FixedLengthTuple<3, 0>>('=');
|
|
263
263
|
|
|
@@ -269,7 +269,7 @@ describe('Arr modifications', () => {
|
|
|
269
269
|
xs: T,
|
|
270
270
|
): Readonly<{ [K in keyof T]: 0 }> => toFilled(0)(xs);
|
|
271
271
|
|
|
272
|
-
const result = blank([1, 2]
|
|
272
|
+
const result = blank([1, 2]);
|
|
273
273
|
|
|
274
274
|
expectType<typeof result, FixedLengthTuple<2, 0>>('=');
|
|
275
275
|
|
|
@@ -319,7 +319,7 @@ describe('Arr modifications', () => {
|
|
|
319
319
|
xs: T,
|
|
320
320
|
): Readonly<{ [K in keyof T]: number }> => toRangeFilled(xs, 0, [1, 2]);
|
|
321
321
|
|
|
322
|
-
const result = blankMiddle([1, 2, 3]
|
|
322
|
+
const result = blankMiddle([1, 2, 3]);
|
|
323
323
|
|
|
324
324
|
expectType<typeof result, FixedLengthTuple<3, number>>('=');
|
|
325
325
|
|
|
@@ -79,10 +79,17 @@ const findImpl = <E,>(
|
|
|
79
79
|
predicate as () => boolean,
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
// Left as a comment rather than asserted. This pinned the shape of the
|
|
83
|
+
// standard library's `findIndex`, and the shape differs between the stock
|
|
84
|
+
// library and the strict one — `(value, index, obj: this) => boolean` there
|
|
85
|
+
// against `(value, index, arr: readonly E[]) => unknown` here. `src` ships
|
|
86
|
+
// to consumers so that "Go to Definition" reaches it, so an assertion that
|
|
87
|
+
// only holds under one of them shows up as an error in their editor.
|
|
88
|
+
//
|
|
89
|
+
// expectType<
|
|
90
|
+
// Parameters<typeof array.findIndex>[0],
|
|
91
|
+
// (value: E, index: number, arr: readonly E[]) => unknown
|
|
92
|
+
// >('=');
|
|
86
93
|
|
|
87
94
|
expectType<
|
|
88
95
|
typeof predicate,
|
|
@@ -162,10 +169,17 @@ const findLastImpl = <E,>(
|
|
|
162
169
|
predicate as () => boolean,
|
|
163
170
|
);
|
|
164
171
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
// Left as a comment rather than asserted. This pinned the shape of the
|
|
173
|
+
// standard library's `findIndex`, and the shape differs between the stock
|
|
174
|
+
// library and the strict one — `(value, index, obj: this) => boolean` there
|
|
175
|
+
// against `(value, index, arr: readonly E[]) => unknown` here. `src` ships
|
|
176
|
+
// to consumers so that "Go to Definition" reaches it, so an assertion that
|
|
177
|
+
// only holds under one of them shows up as an error in their editor.
|
|
178
|
+
//
|
|
179
|
+
// expectType<
|
|
180
|
+
// Parameters<typeof array.findIndex>[0],
|
|
181
|
+
// (value: E, index: number, arr: readonly E[]) => unknown
|
|
182
|
+
// >('=');
|
|
169
183
|
|
|
170
184
|
expectType<
|
|
171
185
|
typeof predicate,
|
|
@@ -267,7 +267,7 @@ describe('Arr shape invariants', () => {
|
|
|
267
267
|
);
|
|
268
268
|
|
|
269
269
|
assert.deepStrictEqual<readonly string[]>(
|
|
270
|
-
toSortedNonNumberInGenericContext(['c', 'a', 'b']
|
|
270
|
+
toSortedNonNumberInGenericContext(['c', 'a', 'b']),
|
|
271
271
|
['a', 'b', 'c'],
|
|
272
272
|
);
|
|
273
273
|
|
|
@@ -313,12 +313,12 @@ describe('Arr shape invariants', () => {
|
|
|
313
313
|
);
|
|
314
314
|
|
|
315
315
|
assert.deepStrictEqual<readonly number[]>(
|
|
316
|
-
uniqInGenericContext([1, 1, 2]
|
|
316
|
+
uniqInGenericContext([1, 1, 2]),
|
|
317
317
|
[1, 2],
|
|
318
318
|
);
|
|
319
319
|
|
|
320
320
|
assert.deepStrictEqual<readonly number[]>(
|
|
321
|
-
uniqByInGenericContext([1, 1, 2]
|
|
321
|
+
uniqByInGenericContext([1, 1, 2]),
|
|
322
322
|
[1, 2],
|
|
323
323
|
);
|
|
324
324
|
});
|
|
@@ -77,7 +77,7 @@ describe('Arr transformations', () => {
|
|
|
77
77
|
boxes: T,
|
|
78
78
|
): Readonly<{ [K in keyof T]: unknown }> => map(boxes, (b) => b.v);
|
|
79
79
|
|
|
80
|
-
const result = mapValues([{ v: 1 }, { v: 'a' }]
|
|
80
|
+
const result = mapValues([{ v: 1 }, { v: 'a' }]);
|
|
81
81
|
|
|
82
82
|
expectType<typeof result, FixedLengthTuple<2, unknown>>('=');
|
|
83
83
|
|
|
@@ -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)));
|