UNPKG

ixfx

Version:

Bundle of ixfx libraries

1,888 lines (1,865 loc) 83 kB
import { n as __exportAll } from "./chunk-CaR5F9JI.js"; import { Et as isEqualDefault, H as continuously, Lt as toStringDefault, Ot as isEqualValueIgnoreOrder, R as elapsedSince, _t as some$3, f as promiseFromEvent, h as intervalToMs, n as sleep, y as compareIterableValuesShallow } from "./src-BUqDa_u7.js"; import { M as resultThrow, v as integerTest } from "./src-C_hvyftg.js"; import { F as isEqual } from "./src-CxEyGbiK.js"; import { a as sum$1, i as rank$1, n as max$4, o as tally$1, r as min$4, t as average$1 } from "./basic-Bepd6Tc6.js"; import { w as numberArrayCompute } from "./src-Cebc3sfq.js"; //#region ../packages/iterables/src/guard.ts const isAsyncIterable = (v) => { if (typeof v !== `object`) return false; if (v === null) return false; return Symbol.asyncIterator in v; }; const isIterable = (v) => { if (typeof v !== `object`) return false; if (v === null) return false; return Symbol.iterator in v; }; //#endregion //#region ../packages/iterables/src/async.ts var async_exports = /* @__PURE__ */ __exportAll({ asCallback: () => asCallback$3, chunks: () => chunks$2, concat: () => concat$2, dropWhile: () => dropWhile$2, equals: () => equals$2, every: () => every$2, fill: () => fill$2, filter: () => filter$3, find: () => find$2, flatten: () => flatten$2, forEach: () => forEach$2, fromArray: () => fromArray$2, fromIterable: () => fromIterable$2, last: () => last$2, map: () => map$2, max: () => max$3, min: () => min$3, nextWithTimeout: () => nextWithTimeout, reduce: () => reduce$3, repeat: () => repeat$1, slice: () => slice$2, some: () => some$2, toArray: () => toArray$2, unique: () => unique$2, uniqueByValue: () => uniqueByValue$2, until: () => until$2, withDelay: () => withDelay, zip: () => zip$2 }); /** * Yield values from `array`, one at a time. * Use `interval` to add time between each item. * The first item is yielded without delay. * * @param array Array of values * @param interval Interval (defaults: 1ms) */ async function* fromArray$2(array, interval = 1) { for (const v of array) { yield v; await sleep(interval); } } /** * Yield values from `iterable`, one at a time. * Use `interval` to add time between each item. * The first item is yielded without delay. * @param iterable Iterable or AsyncIterable * @param [interval=1] Interval to wait between yield */ async function* fromIterable$2(iterable, interval = 1) { for await (const v of iterable) { yield v; await sleep(interval); } } async function* chunks$2(it, size) { let buffer = []; for await (const v of it) { buffer.push(v); if (buffer.length === size) { yield buffer; buffer = []; } } if (buffer.length > 0) yield buffer; } async function* concat$2(...its) { for await (const it of its) yield* it; } async function* dropWhile$2(it, f) { for await (const v of it) if (!f(v)) yield v; } /** * Loops over a generator until it finishes, calling `callback`. * Useful if you don't care about the value generator produces, just the number of loops. * * In this version, we do a `for await of` over `gen`, and also `await callback()`. * ```js * await until(count(5), () => { * // do something 5 times * }); * ``` * * If you want the value from the generator, use a `for of` loop as usual. * * If `callback` explicitly returns _false_, the generator is aborted. * @param it Generator to run * @param callback Code to call for each iteration */ const until$2 = async (it, callback) => { for await (const _ of it) { const value = await callback(); if (typeof value === `boolean` && !value) break; } }; /** * This generator will repeat another generator up until some condition. This is the version * that can handle async generators. * * For example, {@link https://api.ixfx.fun/_ixfx/numbers/count/ @ixfx/numbers.count} will count from 0..number and then finish: * ```js * import { count } from '@ixfx/numbers' * for (const v of count(5)) { * // v: 0, 1, 2, 3, 4 * } * ``` * * But what if we want to repeat the count? We have to provide a function to create the generator, * rather than using the generator directly, since it's "one time use" * ```js * for await (const v of repeat(() => count(5))) { * // v: 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, ... * // warning: never ends * } * ``` * * Limiting the number of repeats can be done by passing in extra parameters * ```js * repeat(generator, { count: 5} ); // Iterate over `generator` five times * ``` * * ```js * const ac = new AbortController(); * repeat(generator, { signal: ac.signal }); // Pass in signal * ... * ac.abort(); // Trigger signal at some point * ``` * @param genCreator * @param repeatsOrSignal */ const repeat$1 = async function* (genCreator, repeatsOrSignal) { const repeats = typeof repeatsOrSignal === `number` ? repeatsOrSignal : Number.POSITIVE_INFINITY; const signal = typeof repeatsOrSignal === `number` ? void 0 : repeatsOrSignal; let count = repeats; while (true) { for await (const v of genCreator()) { yield v; if (signal?.aborted) break; } if (Number.isFinite(repeats)) { count--; if (count === 0) break; } if (signal?.aborted) break; } }; /** * Returns true if items in two iterables are equal, and in the same order. * * Uses === semantics by default. * @param it1 * @param it2 * @param equality * @returns */ async function equals$2(it1, it2, comparerOrKey = isEqualDefault) { const iit1 = it1[Symbol.asyncIterator](); const iit2 = it2[Symbol.asyncIterator](); let isUnknownFunction = true; let eqFunction; let keyFunction; while (true) { const index1 = await iit1.next(); const index2 = await iit2.next(); const v1 = index1.value; const v2 = index2.value; if (isUnknownFunction) { if (typeof comparerOrKey(v1, v2) === `string`) keyFunction = comparerOrKey; else eqFunction = comparerOrKey; isUnknownFunction = false; } if (keyFunction) { if (keyFunction(v1) !== keyFunction(v2)) return false; } else if (eqFunction) { if (!eqFunction(v1, v2)) return false; } else throw new Error(`Something wrong with comparer function?`); if (index1.done ?? index2.done) return index1.done && index2.done; } } async function every$2(it, f) { for await (const v of it) if (!await f(v)) return false; return true; } async function* fill$2(it, v) { for await (const _ of it) yield v; } /** * Filters an iterable, only yielding items which match `f`. * * ```js * filter([1, 2, 3, 4], e => e % 2 == 0); * returns [2, 4] * ``` * @param it * @param f */ async function* filter$3(it, f) { for await (const v of it) { if (!await f(v)) continue; yield v; } } async function find$2(it, f) { for await (const v of it) if (await f(v)) return v; } async function* flatten$2(it) { for await (const v of it) if (typeof v === `object`) { if (Array.isArray(v)) for (const vv of v) yield vv; else if (isAsyncIterable(v)) for await (const vv of v) yield vv; else if (isIterable(v)) for (const vv of v) yield vv; } else yield v; } /** * Iterates over an async iterable or array, calling `fn` for each value, with optional * interval between each loop. If the async `fn` returns _false_, iterator cancels. * * ``` * import { forEach } from "@ixfx/flow.js" * // Prints items from array every second * await forEach([0,1,2,3], i => console.log(i), 1000); * ``` * * ``` * // Retry up to five times, with 5 seconds between each attempt * await forEach(count(5), i=> { * try { * await doSomething(); * return false; // Succeeded, exit early * } catch (ex) { * console.log(ex); * return true; // Keep trying * } * }, 5000); * ``` * @param iterator Iterable thing to loop over * @param fn Function to invoke on each item. If it returns _false_ loop ends. * @param options Options * @typeParam V Type of iterable */ const forEach$2 = async function(iterator, fn, options = {}) { const interval = options.interval; if (Array.isArray(iterator)) for (const x of iterator) { const r = await fn(x); if (typeof r === `boolean` && !r) break; if (interval) await sleep(interval); } else for await (const x of iterator) { const r = await fn(x); if (typeof r === `boolean` && !r) break; if (interval) await sleep(interval); } }; /** * Returns last value from an iterable, or _undefined_ * if no values are generated * @param it */ async function last$2(it, opts = {}) { const abort = opts.abort; let returnValue; for await (const value of it) { if (abort?.aborted) return void 0; returnValue = value; } return returnValue; } /** * Maps an iterable through function `f` * ```js * // For every input value, multiply it by itself * map([1, 2, 3], e => e*e) * // Yields: 1, 4, 9 * ``` * * It can also be used to transform types: * ```js * map([1, 2, 3], e => { value: e }); * // Yields: { value: 1 }, { value: 2 }, { value: 3 } * ``` * @param it * @param f */ async function* map$2(it, f) { for await (const v of it) yield f(v); } async function* max$3(it, gt = ((a, b) => a > b)) { let max; for await (const v of it) { if (max === void 0) { max = v; yield max; continue; } if (gt(v, max)) { max = v; yield v; } } } /** * Returns the minimum seen of an iterable as it changes. * Streaming result: works with endless iterables. * * Note that `gt` function returns true if A is _greater_ than B, even * though we're looking for the minimum. * * ```js * // Rank objects based on 'v' value * const rank = (a,b) => a.v > b.v; * min([ * {i:0,v:1}, * {i:1,v:9}, * {i:2,v:-2} * ], rank); * // Yields: {i:2, v:1}, {i:2,v:-2} * ``` * @param it Iterable * @param gt Should return _true_ if `a` is greater than `b`. * @returns */ async function* min$3(it, gt = (a, b) => a > b) { let min; for await (const v of it) { if (min === void 0) { min = v; yield min; continue; } if (gt(min, v)) { min = v; yield v; } } return min; } async function reduce$3(it, f, start) { for await (const v of it) start = f(start, v); return start; } /** * Calls `callback` whenever the async generator produces a value. * * When using `asCallback`, call it with `await` to let generator * run its course before continuing: * ```js * await asCallback(tick({ interval:1000, loops:5 }), x => { * // Gets called 5 times, with 1000ms interval * }); * console.log(`Hi`); // Prints after 5 seconds * ``` * * Or if you skip the `await`, code continues and callback will still run: * ```js * asCallback(tick({ interval: 1000, loops: 5}), x => { * // Gets called 5 times, with 1000ms interval * }); * console.log(`Hi`); // Prints immediately * ``` * @param input * @param callback */ async function asCallback$3(input, callback, onDone) { for await (const value of input) callback(value); if (onDone) onDone(); } async function* slice$2(it, start = 0, end = Number.POSITIVE_INFINITY) { const iit = it[Symbol.asyncIterator](); if (end < start) throw new Error(`Param 'end' should be more than 'start'`); for (; start > 0; start--, end--) await iit.next(); for await (const v of it) if (end-- > 0) yield v; else break; } /** * Enumerates over an input iterable, with a delay between items. * @param it * @param delay */ async function* withDelay(it, delay) { for (const v of it) { await sleep(delay); yield v; } } /*** * Returns the next IteratorResult, * throwing an error if it does not happen * within `interval` (default: 1s) */ async function nextWithTimeout(it, options) { const ms = intervalToMs(options, 1e3); const value = await Promise.race([(async () => { await sleep({ millis: ms, signal: options.signal }); })(), (async () => { return await it.next(); })()]); if (value === void 0) throw new Error(`Timeout`); return value; } async function some$2(it, f) { for await (const v of it) if (await f(v)) return true; return false; } /** * Returns an array of values from an iterator. * * ```js * const data = await toArray(adsrIterable(opts, 10)); * ``` * * Note: If the iterator is infinite, be sure to provide limits via the options. * ```js * // Return maximum five items * const data = await toArray(iterable, { limit: 5 }); * // Return results for a maximum of 5 seconds * const data = await toArray(iterable, { elapsed: 5000 }); * ``` * Note that limits are ORed, `toArray` will finish if either of them is true. * * @param it Asynchronous iterable * @param options Options when converting to array * @returns */ async function toArray$2(it, options = {}) { const result = []; const iterator = it[Symbol.asyncIterator](); const started = Date.now(); const maxItems = options.limit ?? Number.POSITIVE_INFINITY; const whileFunction = options.while; const maxElapsed = intervalToMs(options.elapsed, Number.POSITIVE_INFINITY); while (result.length < maxItems && Date.now() - started < maxElapsed) { if (whileFunction) { if (!whileFunction(result.length)) break; } const r = await iterator.next(); if (r.done) break; result.push(r.value); } return result; } async function* unique$2(iterable) { const buffer = []; const itera = Array.isArray(iterable) ? iterable : [iterable]; for await (const it of itera) for await (const v of it) { if (buffer.includes(v)) continue; buffer.push(v); yield v; } } async function* uniqueByValue$2(input, toString = toStringDefault, seen = /* @__PURE__ */ new Set()) { for await (const v of input) { const key = toString(v); if (seen.has(key)) continue; seen.add(key); yield v; } } /** * Returns unique items from iterables, given a particular key function * ```js * unique([{i:0,v:2},{i:1,v:3},{i:2,v:2}], e => e.v); * Yields: [{i:0,v:2},{i:1,v:3}] * @param it * @param f */ /** * Combine same-positioned items from several iterables * ```js * zip( [1, 2, 3], [4, 5, 6], [7, 8, 9] ); * Yields: [ [1, 4, 7], [2, 5, 8], [3, 6, 9] ] * ``` * @param its * @returns */ async function* zip$2(...its) { const iits = its.map((it) => it[Symbol.asyncIterator]()); while (true) { const vs = await Promise.all(iits.map((it) => it.next())); if (vs.some((v) => v.done)) return; yield vs.map((v) => v.value); } } //#endregion //#region ../packages/iterables/src/sync/slice.ts function* slice$1(it, start = 0, end = Number.POSITIVE_INFINITY) { if (end < start) throw new Error(`Param 'end' should be more than 'start'`); if (start < 0) throw new Error(`Param 'start' should be at least 0`); let index = 0; for (const v of it) { if (index < start) { index++; continue; } if (index > end) break; yield v; index++; } } //#endregion //#region ../packages/iterables/src/sync/reduce.ts function reduce$2(it, f, start) { for (const v of it) start = f(start, v); return start; } //#endregion //#region ../packages/iterables/src/sync.ts var sync_exports = /* @__PURE__ */ __exportAll({ asCallback: () => asCallback$2, chunks: () => chunks$1, chunksOverlapping: () => chunksOverlapping, concat: () => concat$1, dropWhile: () => dropWhile$1, equals: () => equals$1, every: () => every$1, fill: () => fill$1, filter: () => filter$2, find: () => find$1, first: () => first, flatten: () => flatten$1, forEach: () => forEach$1, fromArray: () => fromArray$1, fromIterable: () => fromIterable$1, last: () => last$1, map: () => map$1, max: () => max$2, min: () => min$2, next: () => next, reduce: () => reduce$2, repeat: () => repeat, slice: () => slice$1, some: () => some$1, toArray: () => toArray$1, unique: () => unique$1, uniqueByValue: () => uniqueByValue$1, until: () => until$1, yieldNumber: () => yieldNumber, zip: () => zip$1 }); function* uniqueByValue$1(input, toString = toStringDefault, seen = /* @__PURE__ */ new Set()) { for (const v of input) { const key = toString(v); if (seen.has(key)) continue; seen.add(key); yield v; } } /** * Calls `callback` whenever the generator produces a value. * * When using `asCallback`, call it with `await` to let generator * run its course before continuing: * ```js * await asCallback(tick({ interval:1000, loops:5 }), x => { * // Gets called 5 times, with 1000ms interval * }); * console.log(`Hi`); // Prints after 5 seconds * ``` * * Or if you skip the `await`, code continues and callback will still run: * ```js * asCallback(tick({ interval: 1000, loops: 5}), x => { * // Gets called 5 times, with 1000ms interval * }); * console.log(`Hi`); // Prints immediately * ``` * @param input * @param callback */ function asCallback$2(input, callback, onDone) { for (const value of input) callback(value); if (onDone) onDone(); } /** * Returns a function that yields a value from a generator. * ```js * const spring = yieldNumber(Oscillators.spring()); * * spring(); // latest value * ``` * * Instead of: * ```js * const spring = Oscillators.spring(); * * spring.next().value * ``` * * A `defaultValue` can be provided if the source generator returns undefined: * ```js * const spring = yieldNumber(Oscillators.spring(), 0); * spring(); // Returns 0 if the generator returns undefined * ``` * @param generator * @param defaultValue * @returns */ function yieldNumber(generator, defaultValue) { return () => { const v = generator.next().value; if (v === void 0) return defaultValue; return v; }; } /** * Return first value from an iterable, or _undefined_ if * no values are generated * @param it * @returns */ function first(it) { for (const value of it) return value; } /** * Returns last value from an iterable, or _undefined_ * if no values are generated * @param it */ function last$1(it) { let returnValue; for (const value of it) returnValue = value; return returnValue; } /** * Yields chunks of the iterable `it` such that the end of a chunk is the * start of the next chunk. * * Eg, with the input [1,2,3,4,5] and a size of 2, we would get back * [1,2], [2,3], [3,4], [4,5]. * * * @param it * @param size * @returns */ function* chunksOverlapping(it, size) { if (size <= 1) throw new Error(`Size should be at least 2`); let buffer = []; for (const v of it) { buffer.push(v); if (buffer.length === size) { yield buffer; buffer = [buffer.at(-1)]; } } if (buffer.length <= 1) return; if (buffer.length > 0) yield buffer; } function* chunks$1(it, size) { let buffer = []; for (const v of it) { buffer.push(v); if (buffer.length === size) { yield buffer; buffer = []; } } if (buffer.length > 0) yield buffer; } function* concat$1(...its) { for (const it of its) yield* it; } function* dropWhile$1(it, f) { for (const v of it) if (!f(v)) yield v; } /** * Loops over a generator until it finishes, calling `callback`. * Useful if you don't care about the value generator produces, just the number of loops. * * ```js * until(count(5), () => { * // do something 5 times * }); * ``` * * If you want the value from the generator, use a `for of` loop as usual. * If `callback` explicitly returns _false_, the generator is aborted. * @param it Generator to run * @param callback Code to call for each iteration */ const until$1 = (it, callback) => { for (const _ of it) { const value = callback(); if (typeof value === `boolean` && !value) break; } }; const next = (it) => { return () => { const r = it.next(); if (r.done) return; return r.value; }; }; /** * Returns true if items in two iterables are equal, by value and order * * By default uses === comparison * @param it1 * @param it2 * @param comparerOrKey Function to produce a key for value or compare two values * @returns */ function equals$1(it1, it2, comparerOrKey = isEqualDefault) { let isUnknownFunction = true; let eqFunction; let keyFunction; while (true) { const index1 = it1.next(); const index2 = it2.next(); const v1 = index1.value; const v2 = index2.value; if (isUnknownFunction) { if (typeof comparerOrKey(v1, v2) === `string`) keyFunction = comparerOrKey; else eqFunction = comparerOrKey; isUnknownFunction = false; } if (keyFunction) { if (keyFunction(v1) !== keyFunction(v2)) return false; } else if (eqFunction) { if (!eqFunction(v1, v2)) return false; } else throw new Error(`Something wrong with comparer function?`); if (index1.done ?? index2.done) return index1.done && index2.done; } } function every$1(it, f) { for (const v of it) if (!f(v)) return false; return true; } function* fill$1(it, v) { for (const _ of it) yield v; } /** * Iterates over `iterator` (iterable/array), calling `fn` for each value. * If `fn` returns _false_, iterator cancels. * * Over the default JS `forEach` function, this one allows you to exit the * iteration early. * * @example * ```js * import { Sync } from "@ixfx/iterables.js" * Sync.forEach(count(5), () => console.log(`Hi`)); // Prints `Hi` 5x * Sync.forEach(count(5), i => console.log(i)); // Prints 0 1 2 3 4 * Sync.forEach([0,1,2,3,4], i => console.log(i)); // Prints 0 1 2 3 4 * ``` * * Use {@link forEach} if you want to use an async `iterator` and async `fn`. * * Alternatives: * * {@link https://api.ixfx.fun/_ixfx/flow/repeat/ @ixfx/flow.repeat}/{@link https://api.ixfx.fun/_ixfx/flow/repeatSync/ @ixfx/flow.repeatSync}: if you want to call something a given number of times and get the result * @param iterator Iterable or array * @typeParam T Type of iterable's values * @param fn Function to call for each item. If function returns _false_, iteration cancels */ function forEach$1(iterator, fn) { for (const v of iterator) { const result = fn(v); if (typeof result === `boolean` && !result) break; } } /** * ```js * filter([1, 2, 3, 4], e => e % 2 == 0); * returns [2, 4] * ``` * @param it * @param f */ function* filter$2(it, f) { for (const v of it) { if (!f(v)) continue; yield v; } } function find$1(it, f) { for (const v of it) if (f(v)) return v; } function* flatten$1(it) { for (const v of it) if (typeof v === `object`) { if (Array.isArray(v)) for (const vv of v) yield vv; else if (isIterable(v)) for (const vv of v) yield vv; } else yield v; } /** * Maps an iterable of type `V` to type `X`. * ```js * map([1, 2, 3], e => e*e) * returns [1, 4, 9] * ``` * @param it * @param f */ function* map$1(it, f) { for (const v of it) yield f(v); } function* max$2(it, gt = (a, b) => a > b) { let max; for (const v of it) { if (max === void 0) { max = v; yield max; continue; } if (gt(v, max)) { max = v; yield max; } } return max; } function* min$2(it, gt = (a, b) => a > b) { let min; for (const v of it) { if (min === void 0) { min = v; yield min; } if (gt(min, v)) { min = v; yield min; } } } function some$1(it, f) { for (const v of it) if (f(v)) return true; return false; } function* repeat(genCreator, repeatsOrSignal) { const repeats = typeof repeatsOrSignal === `number` ? repeatsOrSignal : Number.POSITIVE_INFINITY; const signal = typeof repeatsOrSignal === `number` ? void 0 : repeatsOrSignal; let count = repeats; while (true) { for (const v of genCreator()) { yield v; if (signal?.aborted) break; } if (Number.isFinite(repeats)) { count--; if (count === 0) break; } if (signal?.aborted) break; } } function* unique$1(iterable) { const buffer = []; let itera = []; itera = Array.isArray(iterable) ? iterable : [iterable]; for (const it of itera) for (const v of it) { if (buffer.includes(v)) continue; buffer.push(v); yield v; } } /** * Combine same-positioned items from several iterables * ```js * zip( [1, 2, 3], [4, 5, 6], [7, 8, 9] ); * Yields: [ [1, 4, 7], [2, 5, 8], [3, 6, 9] ] * ``` * @param its * @returns */ function* zip$1(...its) { const iits = its.map((it) => it[Symbol.iterator]()); while (true) { const vs = iits.map((it) => it.next()); if (vs.some((v) => v.done)) return; yield vs.map((v) => v.value); } } function* fromIterable$1(iterable) { for (const v of iterable) yield v; } /** * Returns an array of values from an iterator. * * ```js * const data = await toArray(adsrIterable(opts, 10)); * ``` * * Note: If the iterator is infinite, be sure to provide a limit via the options or the function * will never return. * * @param it Asynchronous iterable * @param options Options when converting to array. * @returns */ function toArray$1(it, options = {}) { const result = []; const started = Date.now(); const whileFunction = options.while; const maxItems = options.limit ?? Number.POSITIVE_INFINITY; const maxElapsed = intervalToMs(options.elapsed, Number.POSITIVE_INFINITY); for (const v of it) { if (whileFunction) { if (!whileFunction(result.length)) break; } if (result.length >= maxItems) break; if (Date.now() - started > maxElapsed) break; result.push(v); } return result; } /** * Yield values from `array`, one at a time. * Use `interval` to add time between each item. * The first item is yielded without delay. * @param array Array of values */ function* fromArray$1(array) { for (const v of array) yield v; } //#endregion //#region ../packages/iterables/src/chain/utility.ts function isGenFactoryNoInput(c) { if (typeof c !== `object` || c === null) return false; if (!(`_type` in c)) return false; if (c._type === `GenFactoryNoInput`) return true; return false; } /** * Wrap the primitive value as generator * @param value */ function* primitiveToGenerator(value) { yield value; } /** * Wrap the primitive value as an async generator * @param value */ async function* primitiveToAsyncGenerator(value) { yield value; await sleep(1); } /** * Resolve the array, data or function to a Generator * @param input * @returns */ function resolveToGen(input) { if (Array.isArray(input)) { const a = input.values(); a._name = `arrayInput`; return a; } else if (typeof input === `number` || typeof input === `boolean` || typeof input === `string`) return primitiveToGenerator(input); else if (typeof input === `function`) return input(); return input; } /** * Resolve the data, primitive or function to an AsyncGenerator * @param input * @returns */ function resolveToAsyncGen(input) { if (input === void 0) return; if (Array.isArray(input)) return fromArray$2(input); else if (typeof input === `number` || typeof input === `boolean` || typeof input === `string`) return primitiveToAsyncGenerator(input); else if (typeof input === `function`) return input(); else if (isAsyncIterable(input)) return input; return fromIterable$2(input); } //#endregion //#region ../packages/iterables/src/util/dom.ts function resolveEl(elOrQuery) { if (typeof elOrQuery === `string`) { const el = document.querySelector(elOrQuery); if (!el) throw new Error(`Element not found '${elOrQuery}'`); return el; } return elOrQuery; } //#endregion //#region ../packages/iterables/src/chain/dom.ts var dom_exports = /* @__PURE__ */ __exportAll({ perValue: () => perValue, query: () => query }); const createMap = (key) => { const keyFunction = key ?? ((value) => value); const map = /* @__PURE__ */ new Map(); return { has(key) { return map.has(keyFunction(key)); }, get(key) { return map.get(keyFunction(key)); }, set(key, value) { map.set(keyFunction(key), value); }, entries() { return map.entries(); }, delete(key) { map.delete(key); } }; }; /** * Creates a HTML element per value. By default compares * values by `JSON.stringify`. Set `byReference:true` to * compare values based on reference. Or provide a toString * function via `key`. * * ```js * // Generate a random number between 0...4 every second * const looper = Generators.interval(() => Math.floor(Math.random()*5), 1000); * * // Make a chain * const ch = Chains.run( * looper, * Chains.Links.delay({before:1000}), * Chains.Dom.perValue() * ); * * setTimeout(async () => { * for await (const v of ch) { * const {el,value} = v; * el.textContent = `${value} - ${Date.now().toString()}`; * } * console.log(`ch iteration done`); * }); * ``` */ function perValue(options = {}) { const byReference = options.byReference; const tagName = options.tagName ?? `div`; if (byReference && options.key) throw new Error(`byReference and key options are mutually exclusive`); const map = createMap(byReference ? void 0 : options.key ?? toStringDefault); const parentEl = resolveEl(options.parentEl ?? document.body); const usedElements = /* @__PURE__ */ new Set(); async function* perValue(input) { for await (const value of resolveToGen(input)) { let el = map.get(value); if (!el) { el = document.createElement(tagName); map.set(value, el); if (options.beforeInsert) options.beforeInsert(el); parentEl.append(el); if (options.afterInsert) options.afterInsert(el); } usedElements.add(el); yield { el, value }; } for (const [id, el] of map.entries()) { if (usedElements.has(el)) continue; if (options.beforeRemove) options.beforeRemove(el); el.remove(); map.delete(id); } } perValue._name = `dom.perValue`; return perValue; } /** * From an input stream of strings, yields an output of HTMLElememnts * @param options * @returns */ function query(options = {}) { const baseElement = options.baseElement ?? document; async function* query(input) { const gen = resolveToGen(input); for await (const value of gen) for (const element of baseElement.querySelectorAll(value)) yield element; } query._name = `dom.query`; return query; } //#endregion //#region ../packages/iterables/src/chain/links.ts var links_exports = /* @__PURE__ */ __exportAll({ average: () => average, chunk: () => chunk, debounce: () => debounce, delay: () => delay, drop: () => drop, duration: () => duration, filter: () => filter$1, max: () => max$1, min: () => min$1, rank: () => rank, rankArray: () => rankArray, reduce: () => reduce$1, sum: () => sum, take: () => take, tally: () => tally, transform: () => transform }); /** * Transform values from one type to another. Just like a map function. * @param transformer * @returns */ function transform(transformer) { async function* transform(input) { input = resolveToGen(input); for await (const value of input) yield transformer(value); } transform._name = `transform`; return transform; } /** * Take `limit` number of results from the stream, before closing * @param limit * @returns */ function take(limit) { async function* take(input) { input = resolveToGen(input); let yielded = 0; for await (const value of input) { if (++yielded > limit) break; yield value; } } take._name = `take`; return take; } /** * Takes an array of values, flattening to a single one * using the provided `reducer` function. * * ```js * // Create a chain that flattens values * const reduce = Chains.reduce(values => Math.max(...values)); * // Feed it a single input (an array), get a single output back: * const result = await Chains.single(reduce, [ 1, 2, 3]); // 3 * ``` * @param reducer Function to reduce array of values to a single value * @returns */ function reduce$1(reducer) { async function* reduce(input) { input = resolveToGen(input); for await (const value of input) yield reducer(value); } reduce._name = `reduce`; return reduce; } /** * Allow values through until a duration has elapsed. After * that, the chain stops. * @param elapsed * @returns */ function duration(elapsed) { const durationMs = intervalToMs(elapsed, 0); async function* duration(input) { input = resolveToGen(input); const elapsed = elapsedSince(); for await (const value of input) { if (elapsed() > durationMs) break; yield value; } } duration._name = `duration`; return duration; } /** * Add delay before/after values are emitted from the input stream. * @param options * @returns */ function delay(options) { const before = intervalToMs(options.before, 0); const after = intervalToMs(options.after, 0); async function* delay(input) { input = resolveToGen(input); for await (const value of input) { if (before > 0) await sleep(before); yield value; if (after > 0) await sleep(after); } } delay._name = `delay`; return delay; } /** * Ensure a minimum length of time between values. * Values being produced too quickly are dropped. * * In the following example, only three values will be let through. * ```js * const chain = Chains.run( * // Produce values every 10ms for 350ms * Chains.From.timestamp({ interval: 10, elapsed: 350 }), * // Only let a value through every 100ms * Chains.Links.debounce(100) * ); * ``` * @param rate * @returns */ function debounce(rate) { const rateMs = intervalToMs(rate, 0); async function* debounce(input) { input = resolveToGen(input); let elapsed = elapsedSince(); for await (const value of input) { if (elapsed() < rateMs) continue; yield value; elapsed = elapsedSince(); } } debounce._name = `debounce`; return debounce; } /** * Returns a running tally of how many items have been * emitted from the input source. * ```js * const ch = Chains.run( * Chains.From.timestamp({ interval: 100 }), * Chains.Links.tally() * ); * * for await (const v of ch) { * // Produces: 1, 2, 3 ... every 100ms * } * ``` * This is different than {@link sum} which adds up numeric values. * By default it adds up individual array items * @returns */ function tally(countArrayItems = true) { async function* tally(input) { input = resolveToGen(input); const p = tally$1(countArrayItems); for await (const v of input) yield p(v); } tally._name = `tally`; return tally; } /** * Returns the smallest value from the input. * Can work with numbers or number[] as input. * Non-numeric data is filtered out. * @returns */ function min$1() { async function* min(input) { input = resolveToGen(input); const p = min$4(); for await (const value of input) { const x = p(value); if (x === void 0) continue; yield x; } } min._name = `min`; return min; } /** * Returns the largest value from the input. * - Non-numeric data is filtered out. * - Looks inside of numeric arrays. * @returns */ function max$1() { async function* max(input) { input = resolveToGen(input); const p = max$4(); for await (const value of input) { const x = p(value); if (x === void 0) continue; yield x; } } max._name = `max`; return max; } /** * Emits the currently ranked 'highest' value from a stream. Only * values exceeding the current highest are emitted. * * eg, if we are ranking on numerical value, an input stream of: * ``` * 4, 1, 6, 10, 2, 4 * ``` * * Results in the output stream of: * ``` * 4, 6, 10 * ``` * * @example * ```js * // Rank based on a field * Chains.Links.rank((a,b) => { * if (a.size > b.size) return `a`; // Signals the first param is highest * if (a.size < b.size) return `b`; // Signals the second param is highest * return `eq`; * }); * ``` * @param options * @returns */ function rank(r, options = {}) { async function* rank(input) { input = resolveToGen(input); const p = rank$1(r, options); for await (const value of input) { const x = p(value); if (x === void 0) continue; yield x; } } rank._name = `rank`; return rank; } /** * Emits the highest-ranked value from amongst an array of values. * * By default, it tracks the highest-ranked _between_ arrays. * * For example: * ```js * // Input * [ [4,5,6], [1,2,3] ] * // Outputs: * [ 6 ] * ``` * * This behaviour can be modified with an option to only compare _within_ arrays. * ``` * // Input * [ [4,5,6], [1,2,3] ] * // Output: * [ 6, 3 ] * ``` * * Uses the `rank` option to determine which is more highly ranked. * ```js * Chains.Links.rankArray( * (a, b) => { * if (a > b) return `a`; // a is higher * else if (b > a) return `b`; // b is higher * return `eq`; // same * } * ) * ``` * @param options * @returns */ function rankArray(r, options = {}) { const includeType = options.includeType; const emitEqualRanked = options.emitEqualRanked ?? false; const emitRepeatHighest = options.emitRepeatHighest ?? false; const withinArrays = options.withinArrays ?? false; async function* rankArray(input) { input = resolveToGen(input); let best; for await (const value of input) { let emit = false; if (withinArrays) best = void 0; for (const subValue of value) { if (includeType && typeof subValue !== includeType) continue; if (best === void 0) { best = subValue; emit = true; } else { const result = r(subValue, best); if (result == `a`) { best = subValue; emit = true; } else if (result === `eq` && emitEqualRanked) emit = true; else if (emitRepeatHighest) emit = true; } } if (emit && best) yield best; } } rankArray._name = `rankArray`; return rankArray; } /** * Returns the average from the input. * Non-numeric values are filtered out. * @returns */ function average() { async function* average(input) { input = resolveToGen(input); const p = average$1(); for await (const value of input) { const x = p(value); if (x === void 0) continue; yield x; } } average._name = `average`; return average; } /** * Returns the total of the numeric values. * Non-numeric values are filtered out. * @returns */ function sum() { async function* total(input) { input = resolveToGen(input); const p = sum$1(); for await (const value of input) { const x = p(value); if (x === void 0) continue; yield x; } } total._name = `total`; return total; } /** * Chunks an input stream into `size` chunks. * * Eg, with a chunk size of 3, the input stream of: * 1, 2, 3, 4, 5, 6 * Yields: * [ 1, 2, 3 ], [ 4, 5, 6 ] * * If `returnRemainders` is _true_ (default), any left over values are returned even if * it's less than `size`. * @param size * @param returnRemainders If true (default) left over data that didn't make a full chunk is also returned * @returns */ function chunk(size, returnRemainders = true) { resultThrow(integerTest(size, `aboveZero`, `size`)); async function* chunk(input) { input = resolveToGen(input); let buffer = []; for await (const value of input) { buffer.push(value); if (buffer.length >= size) { yield buffer; buffer = []; } } if (returnRemainders && buffer.length > 0) yield buffer; } chunk._name = `chunk`; return chunk; } /** * Filters the input source, only allowing through * data for which `predicate` returns _true_ * * {@link drop}, on the other hand excludes values for which predicate is _true_ * @param predicate * @returns */ function filter$1(predicate) { async function* filter(input) { input = resolveToGen(input); for await (const value of input) if (predicate(value)) yield value; } filter._name = `filter`; return filter; } /** * Drops all values from input stream for which `predicate` returns _true_ * * {@link filter}, on the other hand includes values where the predicate is _true_ * @param predicate * @returns */ function drop(predicate) { async function* drop(input) { input = resolveToGen(input); for await (const value of input) if (!predicate(value)) yield value; } drop._name = `drop`; return drop; } //#endregion //#region ../packages/iterables/src/chain/from/array.ts /** * Creates a chain from an array, reading values at a given interval * @param it * @param delay * @returns */ function array(it, delay = 5) { async function* fromArray() { for (const v of it) { await sleep(delay); yield v; } } fromArray._name = `fromArray`; fromArray._type = `GenFactoryNoInput`; return fromArray; } //#endregion //#region ../packages/iterables/src/chain/from/event.ts /** * Create an iterable from an event * @param target Event source (eg HTML element) * @param name Name of event (eg. 'pointermove') * @returns */ function event(target, name) { async function* event() { while (true) yield await promiseFromEvent(target, name); } event._name = `event`; event._type = `GenFactoryNoInput`; return event; } //#endregion //#region ../packages/iterables/src/chain/from/function.ts /** * Produce a value from a callback. When * the callback returns _undefined_ it is considered done. * * ```js * const callback = () => Math.random(); * * const f = Chains.From.func(callback); * for await (const v of f) { * // v is a new random number * } * ``` * * In the context of a chain: * ```js * let produced = 0; * const chain = Chains.chain<number, string>( * // Produce incrementing numbers * Chains.From.func(() => produced++), * // Convert to `x:0`, `x:1` ... * Chains.transform(v => `x:${ v }`), * // Take first 5 results * Chains.cap(5) * ); * const data = await Chains.asArray(chain); * ``` * @param callback * @returns */ function func(callback) { async function* fromFunction() { while (true) { const v = await callback(); if (typeof v === `undefined`) break; yield v; } } fromFunction._name = `fromFunction`; fromFunction._type = `GenFactoryNoInput`; return fromFunction; } //#endregion //#region ../packages/iterables/src/chain/from/iterable.ts /** * Creates a chain from an interable * @param it * @returns */ function iterable(it) { async function* fromIterable() { for await (const v of it) yield v; } fromIterable._name = `fromIterable`; fromIterable._type = `GenFactoryNoInput`; return fromIterable; } //#endregion //#region ../packages/iterables/src/chain/from/ticks.ts /** * Generate timestamp values at `interval` rate. By default it runs forever. * Use `loops` or `elapsed` to set upper limit on how long it should run. * * ```js * const c = Chains.From.timestamp({ interval: 1000 }); * ``` * Options: * - `asClockTime`: If _true_, yielded value will be clock time rather than elapsed milliseconds * @param options * @returns */ function timestamp(options) { const intervalMs = intervalToMs(options.interval, 0); const asClockTime = options.asClockTime ?? false; const loops = options.loops ?? Number.MAX_SAFE_INTEGER; let looped = 0; const durationTime = intervalToMs(options.elapsed, Number.MAX_SAFE_INTEGER); async function* ts() { const elapsed = elapsedSince(); while (looped < loops && elapsed() < durationTime) { yield asClockTime ? Date.now() : elapsed(); const expectedTimeDiff = looped * intervalMs - elapsed(); await sleep(Math.max(0, intervalMs + expectedTimeDiff)); looped++; } } ts._name = `timestamp`; ts._type = `GenFactoryNoInput`; return ts; } //#endregion //#region ../packages/iterables/src/chain/from/index.ts var from_exports = /* @__PURE__ */ __exportAll({ array: () => array, event: () => event, func: () => func, iterable: () => iterable, timestamp: () => timestamp }); //#endregion //#region ../packages/iterables/src/chain/add-to-array.ts /** * Adds values to the provided array as they are produced, * mutating array. * * ```js * const data = []; * addToArray(data, tick({ interval: 1000, loops: 5 })); * // Execution continues immediately, with `data` mutated over time * ``` * @param valueToWrap * @param array */ async function addToArray(array, valueToWrap) { const outputType = typeof valueToWrap === `function` ? valueToWrap() : valueToWrap; for await (const value of outputType) array.push(value); } //#endregion //#region ../packages/iterables/src/chain/as-array.ts /** * Async function that returns the chain as an array of values * ```js * const values = await asArray(tick( { interval: 1000, loops: 5 })); * // After 5 seconds, values will be a set of timestamps. * ``` * * If the chain is infinite, be sure to specify limits: * ```js * // Stop after we have five items * const values = await asArray(chain, { limit: 5 }); * // Stop after 5 seconds has elapsed * const values = await asArray(chain, { elapsed: 5000 }); * ``` * @param valueToWrap * @returns */ async function asArray(valueToWrap, options = {}) { return toArray$2(typeof valueToWrap === `function` ? valueToWrap() : valueToWrap, options); } //#endregion //#region ../packages/iterables/src/chain/as-callback.ts /** * Calls `callback` whenever the chain/generator produces a value. * * When using `asCallback`, call it with `await` to let generator * run its course before continuing: * ```js * await asCallback(tick({ interval:1000, loops:5 }), x => { * // Gets called 5 times, with 1000ms interval * }); * console.log(`Hi`); // Prints after 5 seconds * ``` * * Or if you skip the `await`, code continues and callback will still run: * ```js * asCallback(tick({ interval: 1000, loops: 5}), x => { * // Gets called 5 times, with 1000ms interval * }); * console.log(`Hi`); // Prints immediately * ``` * @param valueToWrap * @param callback */ async function asCallback$1(valueToWrap, callback, onDone) { const outputType = typeof valueToWrap === `function` ? valueToWrap() : valueToWrap; for await (const value of outputType) callback(value); if (onDone) onDone(); } //#endregion //#region ../packages/iterables/src/chain/as-promise.ts /** * Treats the chain/generator as a promise * * ```js * const ticker = asPromise(tick({ interval: 1000 })); * const x = await ticker(); // Waits for 1000ms before giving a value * ``` * * This will only ever return one value. To return multiple values, it's necessary * to call `asPromise` and `await` the result in a loop. * @param valueToWrap * @returns */ function asPromise(valueToWrap) { let lastValue; const outputType = typeof valueToWrap === `function` ? valueToWrap() : valueToWrap; async function asPromise() { const v = await outputType.next(); if (v.done) return; lastValue = v.value; return lastValue; } return asPromise; } //#endregion //#region ../packages/iterables/src/chain/as-value.ts /** * Returns the most recent value from the chain/generator, or * `initialValue` (defaulting to _undefined_) if no value * has been emitted yet. * * ```js * const ticker = asValue(tick({ interval: 1000 })); * x = ticker(); // Get the most recent value * ``` * * Every time it's called, it fetches a new value from the generator, assuming * it isn't already awaiting a result. * * In the meantime, the last value (or `initialValue`) is returned. * @param valueToWrap Value to wrap * @param initialValue Initial value * @returns */ function asValue(valueToWrap, initialValue) { let lastValue = initialValue; let awaiting = false; const outputType = typeof valueToWrap === `function` ? valueToWrap() : valueToWrap; function asValue() { if (!awaiting) { awaiting = true; outputType.next().then((v) => { lastValue = v.value; awaiting = false; }).catch((error) => { awaiting = false; throw error; }); } return lastValue; } return asValue; } //#endregion //#region ../packages/iterables/src/chain/combine-latest-to-array.ts /** * Monitors sources, storing as they happen to an array. * Whenever a new value is emitted, the whole array is sent out, containing current * values from each source, or _undefined_ if not yet emitted. * * The tempo of this stream will be set by the fastest source stream. * See {@link syncToArray} to have pace determined by slowest source, and only * send when each source has produce a new value compared to last time. * * Set `onSourceDone` to choose behaviour if a source stops. The default is * 'break', meaning the whole combined stream stops. * * If a source completes and onSourceDone = 'allow', the option * 'finalValue' sets the logic for what values get returned for the source. * By default the setting is 'undefined', thus _undefined_ results. 'last' will be the last (old) value * from that source. */ async function* combineLatestToArray(sources, options = {}) { const onSourceDone = options.onSourceDone ?? `break`; const finalValue = options.finalValue ?? `undefined`; const afterEmit = options.afterEmit ?? `last`; const inputs = sources.map((source, index) => ({ waiting: void 0, index, gen: resolveToGen(source), done: false, lastValue: void 0 })); const isDone = () => !inputs.some((v) => !v.done); const isWaiting = () => inputs.some((v) => v.waiting !== void 0); const allEmpty = (d) => !d.some((v) => v !== void 0); let lastEmitted = []; while (true) { const promises = []; for (const input of inputs) { if (input.done) continue; if (input.waiting !== void 0) { promises.push(input.waiting); continue; } const p = Promise.resolve((async () => { if (input.done) return input; const v = await input.gen.next(); input.waiting = void 0; if (v.done) { input.done = true; if (finalValue === `undefined`) input.lastValue = void 0; } else input.lastValue = v.value; return input; })()); input.waiting = p; promises.push(p); } const won = await Promise.race(promises); if (`done` in won) { if (won.done && onSourceDone === `break`) break; } else throw new Error(`Missing 'done' property`); const d = inputs.map((v) => v.lastValue); if (d.length === 0) return; const dataEmpty = allEmpty(d); if (dataEmpty && !isWaiting()) return; if (!isEqual(lastEmitted, d) && !dataEmpty) { lastEmitted = d; yield d; } if (afterEmit === `undefined`) for (const input of inputs) { if (input.waiting !== void 0) continue; input.lastValue = void 0; } if (isDone()) break; } } //#endregion //#region ../packages/iterables/src/chain/combine-latest-to-object.ts /** * Monitors sources, storing as they happen to an object. * Whenever a new value is emitted, the object is sent out, containing current * values from each source, or _undefined_ if not yet emitted. * * The tempo of this stream will be set by the fastest source stream. * See {@link syncToObject} to have pace determined by slowest source, and only * send when each source has produce a new value compared to last time. * * Set `onSourceDone` to choose behaviour if a source stops. By default it * is 'break', meaning the whole merged stream stops. * * If a source completes and onSourceDone = 'allow', the option * 'finalValue' sets the logic for what values get returned for the source. * By default the setting is 'undefined', thus _undefined_ results. 'last' will be the last (old) value * from that source. */ async function* combineLatestToObject(sources, options = {}) { const onSourceDone = options.onSourceDone ?? `break`; const finalValue = options.finalValue ?? `undefined`; const afterEmit = options.afterEmit ??