Dependency and dead-code hygiene
Two cleanups. zod was a v1 leftover with zero references — removed from dependencies (it stays in the lockfile only transitively via the SDKs). And the fp prelude shipped exports nothing called: identity, constant, tap, assertNever, and all of the AsyncResult combinators except tryCatch.
Down to the type + tryCatch (the only member with a call site).
src/fp/asyncResult.ts · 29 lines
src/fp/asyncResult.ts
1/**
2 * `AsyncResult<E, A>` is just `Promise<Result<E, A>>` — an asynchronous, fallible
3 * computation whose error is a value, not a rejection. `tryCatch` lifts a
4 * promise-returning thunk into one, so the imperative shell can hand the core a
5 * Result instead of a rejection.
6 */
8import * as Result from './result';
10export type AsyncResult<E, A> = Promise<Result.Result<E, A>>;
12/**
13 * Lift a promise-returning thunk into an `AsyncResult`, mapping any failure to the
14 * error channel. Handles both a rejected promise and a *synchronous* throw from the
15 * thunk itself (the latter would otherwise escape `.then` and reject the result).
16 */
17export const tryCatch = <E, A>(
18 thunk: () => Promise<A>,
19 onThrow: (u: unknown) => E,
20): AsyncResult<E, A> => {
21 try {
22 return thunk().then(
23 (value) => Result.ok<A, E>(value),
24 (u: unknown) => Result.err<E, A>(onThrow(u)),
25 );
26 } catch (u) {
27 return Promise.resolve(Result.err<E, A>(onThrow(u)));
28 }
29};
The prelude no longer re-exports the deleted helpers.
src/fp/index.ts · 36 lines
src/fp/index.ts
⋯ 10 lines hidden (lines 1–10)
1/**
2 * The functional prelude. One import surface for the whole core:
3 *
4 * import { pipe, Result, Option, matchTag } from '../fp';
5 *
6 * Effect-laden types (`Result`, `Option`, `AsyncResult`, `Parser`) are exposed as
7 * namespaces so their identically-named combinators (`map`, `flatMap`, `match`)
8 * never collide.
9 */
11export { pipe } from './function';
12export { matchTag, type HasTag } from './match';
⋯ 24 lines hidden (lines 13–36)
13export { type Brand, type Tagged } from './brand';
14export { redact, expose, type Redacted } from './redacted';
15export {
16 type NonEmptyArray,
17 type NonEmptyString,
18 isNonEmptyArray,
19 isNonEmptyString,
20 head,
21 tail,
22} from './nonEmpty';
24// Bare constructors are exposed for ergonomics (no name collisions); the
25// combinators that *do* collide (`map`, `flatMap`, `match`) stay namespaced.
26export { ok, err, isOk, isErr } from './result';
27export { some, none, isSome, isNone } from './option';
29export * as Result from './result';
30export * as Option from './option';
31export * as AsyncResult from './asyncResult';
32export * as P from './parser';
34export type { Result as ResultType } from './result';
35export type { Option as OptionType } from './option';
36export type { AsyncResult as AsyncResultType } from './asyncResult';