All files / remote-iterables/src reader-ref.ts

100% Statements 21/21
100% Branches 10/10
100% Functions 6/6
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92      2x                                       2x       20x 14x 6x 3x 3x 2x   1x   19x                   2x 12x 12x             6x                 3x 2x   1x                 3x 2x   1x               1x        
import { makeExo } from '@endo/exo';
import { M } from '@endo/patterns';
 
export const AsyncIteratorInterface = M.interface(
  'AsyncIterator',
  {},
  {
    defaultGuards: 'passable',
  },
);
 
export type SomehowAsyncIterable<Item> =
  | AsyncIterable<Item>
  | Iterable<Item>
  | { next: () => IteratorResult<Item> };
 
/**
 * Returns the iterator for the given iterable object.
 * Supports both synchronous and asynchronous iterables.
 *
 * @param iterable - The iterable object.
 * @returns The iterator for the given iterable object.
 */
export const asyncIterate = <Item>(
  iterable: SomehowAsyncIterable<Item>,
): AsyncIterator<Item> => {
  let iterator;
  if (iterable[Symbol.asyncIterator as keyof typeof iterable]) {
    iterator = (iterable as AsyncIterable<Item>)[Symbol.asyncIterator]();
  } else if (iterable[Symbol.iterator as keyof typeof iterable]) {
    iterator = (iterable as Iterable<Item>)[Symbol.iterator]();
  } else if ('next' in iterable) {
    iterator = iterable;
  } else {
    throw new Error('Not iterable');
  }
  return iterator as AsyncIterator<Item>;
};
 
/**
 * Make a remotable AsyncIterator.
 *
 * @param iterable - The iterable object.
 * @returns An exo for the iterator.
 */
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export const makeIteratorRef = <Item>(iterable: SomehowAsyncIterable<Item>) => {
  const iterator = asyncIterate(iterable);
  return makeExo('AsyncIterator', AsyncIteratorInterface, {
    /**
     * Gets the next value from the iterator.
     *
     * @returns A promise that resolves to the next iterator result.
     */
    async next() {
      return iterator.next(undefined);
    },
    /**
     * Finish the iterator without error.
     *
     * @param value - The value to return.
     * @returns The result of the return operation.
     */
    async return(value: Item): Promise<IteratorResult<Item>> {
      if (iterator.return !== undefined) {
        return iterator.return(value);
      }
      return harden({ done: true, value: undefined });
    },
    /**
     * Finish the iterator with an error.
     *
     * @param error - The error to throw.
     * @returns The result of the throw operation.
     */
    async throw(error: unknown) {
      if (iterator.throw !== undefined) {
        return iterator.throw(error);
      }
      return harden({ done: true, value: undefined });
    },
    /**
     * Returns the async iterator for use with for-await-of loops.
     *
     * @returns The iterator itself.
     */
    [Symbol.asyncIterator]() {
      return this;
    },
  });
};