Skip to content

feat: unified API — AsyncResult overloads, pipeAsync/flowAsync, integration #321

@codewizdave

Description

@codewizdave

feat: unified API — AsyncResult overloads, pipeAsync/flowAsync, integration

Summary

Complete the unified API by ensuring all functions (map, flatMap, tap, getOrElse, match, etc.) work natively with AsyncResult without explicit .then() unwrapping, and add pipeAsync/flowAsync for full async pipeline support.

Motivation

The project defines a unified API in index.ts where functions like map, flatMap, tap, getOrElse, match should work transparently on both Result and AsyncResult. This issue completes that integration.

Status: Blocked

This issue is blocked by all other implementation issues:

  • retry-policy-builder — retry functions need unified API treatment
  • composition-additionstapBoth, dual() pattern needed
  • repeat — repeat functions need unified API treatment
  • timeout-standalone — timeout functions need unified API treatment
  • serialization — serialize/deserialize need unified API treatment
  • error-panic-and-matchpartialmatchErrorPartial needed

Do not start this issue until all 6 other issues are completed.

Deliverables

1. Native AsyncResult support in unified API

Currently some functions work with AsyncResult implicitly via thenable pattern, but not all overloads are complete. Ensure:

import { Result, pipe, AsyncResult } from '@deessejs/fp';

// These should work directly without explicit .then() unwrapping
const result: AsyncResult<User, NetworkError> = fetchUser(id);

pipe(
  result,
  Result.map(user => user.email),           // works on AsyncResult
  Result.tap(email => console.log(email)),   // works on AsyncResult
  Result.flatMap(email => fetchProfile(email)), // works on AsyncResult
  Result.getOrElse('anonymous@example.com')  // works on AsyncResult
);

Functions requiring unified overloads:

Function Must work on AsyncResult
Result.map
Result.flatMap / andThen
Result.mapErr
Result.tap
Result.tapErr
Result.tapBoth
Result.getOrElse
Result.getOrCompute
Result.unwrap
Result.unwrapOr
Result.unwrapOrCompute
Result.orElse
Result.match
Result.isOk
Result.isErr
Result.toNullable
Result.toUndefined
Result.toMaybe
Result.swap
Result.all
Result.race
Result.traverse
Result.allSettled

2. pipeAsync / flowAsync for async chains

import { pipeAsync, flowAsync, Result } from '@deessejs/fp';

// Async pipeline — awaits promises automatically
const result = await pipeAsync(
  userId,
  fetchUser,                    // Promise<Result<User, E>>
  Result.tap(user => log(user)),
  Result.map(user => user.email),
  Result.flatMap(email => fetchProfile(email)),  // Promise<Result<Profile, E>>
  Result.getOrElse('anonymous@example.com')
);

// flowAsync creates a reusable async pipeline
const processEmail = flowAsync(
  fetchUser,
  Result.tap(user => log(user)),
  Result.map(user => user.email)
);

Key difference from pipe:

  • pipe passes values synchronously through functions
  • pipeAsync detects when a function returns a Promise and awaits it automatically
  • Both Ok and Err paths are handled correctly through async chains

3. Integration tests

Comprehensive tests that verify the unified API works end-to-end across all modules:

describe('Unified API', () => {
  it('map works on AsyncResult', async () => {
    const asyncResult: AsyncResult<User, E> = Promise.resolve(ok({ id: '1', name: 'Alice' }));
    const result = await pipeAsync(
      asyncResult,
      Result.map(user => user.name)
    );
    expect(result).toEqual(ok('Alice'));
  });

  it('flatMap chains AsyncResults correctly', async () => { /* ... */ });
  it('tapBoth observes AsyncResult correctly', async () => { /* ... */ });
  it('retry result works with unified API', async () => { /* ... */ });
  it('repeat result works with unified API', async () => { /* ... */ });
  it('timeout result works with unified API', async () => { /* ... */ });
  it('serialized result roundtrips correctly', async () => { /* ... */ });
});

4. Result.gen / Result.await integration

Ensure Result.gen and Result.await work correctly with all error types created in the new modules:

const result = await Result.gen(async function* () {
  const user = yield* Result.await(fetchUser(id));  // AsyncResult
  const profile = yield* Result.await(fetchProfile(user.id));
  return { user, profile };
});
// Result<{ user: User, profile: Profile }, NetworkError | ValidationError | ...>

Dependencies

All 6 other issues must be completed first:

  1. retry-policy-builder
  2. composition-additions
  3. repeat
  4. timeout-standalone
  5. serialization
  6. error-panic-and-matchpartial

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions