Mirror of https://github.com/roostorg/coop
github.com/roostorg/coop
1import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
2
3export function filterNullOrUndefined<T>(
4 array: T[],
5): Exclude<T, null | undefined>[];
6export function filterNullOrUndefined<T>(
7 array: readonly T[],
8): readonly Exclude<T, null | undefined>[];
9export function filterNullOrUndefined<T>(array: readonly T[]) {
10 return array.filter(
11 (it): it is Exclude<T, null | undefined> => it !== null && it !== undefined,
12 );
13}
14
15export function moveArrayElement<T>(
16 array: T[],
17 fromIndex: number,
18 toIndex: number,
19) {
20 if (
21 fromIndex === toIndex ||
22 fromIndex < 0 ||
23 toIndex < 0 ||
24 fromIndex >= array.length ||
25 toIndex >= array.length
26 ) {
27 return array.slice();
28 }
29
30 const element = array[fromIndex]; // Get the element at the original index
31 const newArray = array.slice(0, fromIndex).concat(array.slice(fromIndex + 1)); // Remove the element from the original index
32 newArray.splice(toIndex, 0, element); // Insert the element at the new index
33 return newArray;
34}
35
36export async function asyncIterableToArray<T>(
37 it: AsyncIterable<T>,
38): Promise<T[]> {
39 const result = [];
40 for await (const x of it) {
41 result.push(x);
42 }
43 return result;
44}
45
46/**
47 * Gets an iterator from the given iterable and iterates it until the timeout is
48 * hit, at which point it returns an array of all the items that have been
49 * yielded up until that moment. When the timeout is hit, it also calls return()
50 * on the iterator to signal disinterest in further consumption.
51 */
52export async function asyncIterableToArrayWithTimeout<T>(
53 it: AsyncIterable<T>,
54 timeoutMs: number,
55): Promise<T[]> {
56 const items: T[] = [];
57 let timeoutReached = false;
58
59 const timeoutPromise = setTimeoutPromise(timeoutMs).then(() => {
60 timeoutReached = true;
61 });
62
63 const itemsIterationPromise = (async () => {
64 for await (const item of it) {
65 items.push(item);
66
67 // Once timeout's reached, stop awaiting more items. This will implicitly
68 // call iterator.return() to let the iterator know we're done consuming.
69 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
70 if (timeoutReached) {
71 break;
72 }
73 }
74 })();
75
76 await Promise.race([timeoutPromise, itemsIterationPromise]);
77 return items;
78}
79
80/**
81 * Same as above, except this function also takes a limit to the number of items
82 * the caller wants, and returns as soon as that limit is reached.
83 */
84export async function asyncIterableToArrayWithTimeoutAndLimit<T>(
85 it: AsyncIterable<T>,
86 timeoutMs: number,
87 limit: number,
88): Promise<T[]> {
89 const items: T[] = [];
90 let timeoutReached = false;
91
92 const timeoutPromise = setTimeoutPromise(timeoutMs).then(() => {
93 timeoutReached = true;
94 });
95
96 const itemsIterationPromise = (async () => {
97 for await (const item of it) {
98 items.push(item);
99
100 // Once timeout's reached or the limit is reached, stop awaiting more items. This will implicitly
101 // call iterator.return() to let the iterator know we're done consuming.
102 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
103 if (timeoutReached || items.length >= limit) {
104 break;
105 }
106 }
107 })();
108
109 await Promise.race([timeoutPromise, itemsIterationPromise]);
110 return items;
111}