Resolvable<T extends Object> constructor

Resolvable<T extends Object>(
  1. @mustBeAnonymous @mustAwaitAllFutures FutureOr<T> mustAwaitAllFutures(), {
  2. @noFutures TOnErrorCallback<T>? onError,
  3. @noFutures TVoidCallback? onFinalize,
})

Creates a Sync or Async depending on the return type of mustAwaitAllFutures.

IMPORTANT:

Always all futures witin mustAwaitAllFutures to ensure errors are be caught and propagated.

Implementation

factory Resolvable(
  @mustBeAnonymous
  @mustAwaitAllFutures
  FutureOr<T> Function() mustAwaitAllFutures, {
  @noFutures TOnErrorCallback<T>? onError,
  @noFutures TVoidCallback? onFinalize,
}) {
  // The closure invocation must NOT escape — a synchronous throw needs to
  // become an Err on the returned Sync, not propagate to the caller. This
  // is the library's core "absorb all throws" contract.
  Result<T> result;
  try {
    final v = mustAwaitAllFutures();
    if (v is Future<T>) {
      return Async(() => v, onError: onError, onFinalize: onFinalize);
    }
    result = Ok(v);
  } on Err catch (err) {
    // Preserve a user-thrown Err verbatim — statusCode and breadcrumbs are
    // load-bearing for life-critical callers. `onError` does NOT fire for
    // Err throws: an Err is already a structured error value.
    result = err.transfErr<T>();
  } catch (error, stackTrace) {
    // Non-Err throw — route through `onError` if the caller supplied one.
    if (onError == null) {
      result = Err<T>(error, stackTrace: stackTrace);
    } else {
      try {
        result = onError(error, stackTrace);
      } on Err catch (err) {
        result = err.transfErr<T>();
      } catch (error, stackTrace) {
        result = Err<T>(error, stackTrace: stackTrace);
      }
    }
  }
  // Run `onFinalize` separately so its throws are absorbed into `result`
  // instead of escaping this factory. A thrown finalize error overrides
  // whatever `result` held — failed cleanup is a meaningful failure mode
  // that callers need to see, surfaced as `Sync.err(...)` to keep the
  // package's no-throw contract intact.
  if (onFinalize != null) {
    try {
      onFinalize();
    } on Err catch (err) {
      result = err.transfErr<T>();
    } catch (error, stackTrace) {
      result = Err<T>(error, stackTrace: stackTrace);
    }
  }
  return Sync.result(result);
}