···11+---
22+'wonka': minor
33+---
44+55+Add extensive TSDoc documentation for all `wonka` internals and exports. This will replace the documentation and give consumers more guidance on each of the library's extensive utilities.
···88 // NOTE: Computed property names will be omitted
99 code = code.replace(/\[Symbol\.\w+\][?()]*:(?:.*);\n?/g, '');
10101111- let flowdef = compiler.compileDefinitionString(code);
1111+ let flowdef = compiler.compileDefinitionString(code, {
1212+ jsdoc: false,
1313+ });
12141315 flowdef = beautify(flowdef);
1416 flowdef = flowdef.replace(/import/g, 'import type');
+20
src/callbag.ts
···11import { Source, SignalKind } from './types';
22import { push, start } from './helpers';
3344+/** A definition of the Callbag type as per its specification.
55+ * @see {@link https://github.com/callbag/callbag} for the Callbag specification.
66+ */
47interface Callbag<I, O> {
58 (t: 0, d: Callbag<O, I>): void;
69 (t: 1, d: I): void;
710 (t: 2, d?: any): void;
811}
9121313+/** Converts a Callbag to a {@link Source}.
1414+ * @param callbag - The {@link Callbag} object that will be converted.
1515+ * @returns A {@link Source} wrapping the passed Callbag.
1616+ *
1717+ * @remarks
1818+ * This converts a Callbag to a {@link Source}. When this Source receives a {@link Sink} and
1919+ * the subscription starts, internally, it'll subscribe to the passed Callbag, passing through
2020+ * all of its emitted values.
2121+ */
1022export function fromCallbag<T>(callbag: Callbag<any, T>): Source<T> {
1123 return sink => {
1224 callbag(0, (signal: number, data: any) => {
···2537 };
2638}
27394040+/** Converts a {@link Source} to a Callbag.
4141+ * @param source - The {@link Source} that will be converted.
4242+ * @returns A {@link Callbag} wrapping the passed Source.
4343+ *
4444+ * @remarks
4545+ * This converts a {@link Source} to a {@link Callbag}. When this Callbag is subscribed to, it
4646+ * internally subscribes to the Wonka Source and pulls new values.
4747+ */
2848export function toCallbag<T>(source: Source<T>): Callbag<any, T> {
2949 return (signal: number, sink: any) => {
3050 if (signal === 0) {
+64-9
src/combine.ts
···55 ? [TypeOfSource<Head>, ...TypeOfSourceArray<Tail>]
66 : [];
7788-export function zip<Sources extends readonly [...Source<any>[]]>(
99- sources: [...Sources]
1010-): Source<TypeOfSourceArray<Sources>>;
88+/** Combines the latest values of several sources into a Source issuing either tuple or dictionary
99+ * values.
1010+ *
1111+ * @param sources - Either an array or dictionary object of Sources.
1212+ * @returns A {@link Source} issuing a zipped value whenever any input Source updates.
1313+ *
1414+ * @remarks
1515+ * `zip` combines several {@link Source | Sources}. The resulting Source will issue its first value
1616+ * once all input Sources have at least issued one value, and will subsequently issue a new value
1717+ * each time any of the Sources emits a new value.
1818+ *
1919+ * Depending on whether an array or dictionary object of Sources is passed to `zip`, its emitted
2020+ * values will be arrays or dictionary objects of the Sources' values.
2121+ *
2222+ * @example
2323+ * An example of passing a dictionary object to `zip`. If an array is passed, the resulting
2424+ * values will output arrays of the sources' values instead.
2525+ *
2626+ * ```ts
2727+ * pipe(
2828+ * zip({
2929+ * x: fromValue(1),
3030+ * y: fromArray([2, 3]),
3131+ * }),
3232+ * subscribe(result => {
3333+ * // logs { x: 1, y: 2 } then { x: 1, y: 3 }
3434+ * console.log(result);
3535+ * })
3636+ * );
3737+ * ```
3838+ */
3939+interface zip {
4040+ <Sources extends readonly [...Source<any>[]]>(sources: [...Sources]): Source<
4141+ TypeOfSourceArray<Sources>
4242+ >;
11431212-export function zip<Sources extends { [prop: string]: Source<any> }>(
1313- sources: Sources
1414-): Source<{ [Property in keyof Sources]: TypeOfSource<Sources[Property]> }>;
4444+ <Sources extends { [prop: string]: Source<any> }>(sources: Sources): Source<{
4545+ [Property in keyof Sources]: TypeOfSource<Sources[Property]>;
4646+ }>;
4747+}
15481616-export function zip<T>(
1717- sources: Source<T>[] | Record<string, Source<T>>
1818-): Source<T[] | Record<string, T>> {
4949+function zip<T>(sources: Source<T>[] | Record<string, Source<T>>): Source<T[] | Record<string, T>> {
1950 const size = Object.keys(sources).length;
2051 return sink => {
2152 const filled: Set<string | number> = new Set();
···75106 };
76107}
77108109109+export { zip };
110110+111111+/** Combines the latest values of all passed sources into a Source issuing tuple values.
112112+ *
113113+ * @see {@link zip | `zip`} which this helper wraps and uses.
114114+ * @param sources - A variadic list of {@link Source} parameters.
115115+ * @returns A {@link Source} issuing a zipped value whenever any input Source updates.
116116+ *
117117+ * @remarks
118118+ * `combine` takes one or more {@link Source | Sources} as arguments. Once all input Sources have at
119119+ * least issued one value it will issue an array of all of the Sources' values. Subsequently, it
120120+ * will issue a new array value whenever any of the Sources update.
121121+ *
122122+ * @example
123123+ *
124124+ * ```ts
125125+ * pipe(
126126+ * combine(fromValue(1), fromValue(2)),
127127+ * subscribe(result => {
128128+ * console.log(result); // logs [1, 2]
129129+ * })
130130+ * );
131131+ * ```
132132+ */
78133export function combine<Sources extends Source<any>[]>(
79134 ...sources: Sources
80135): Source<TypeOfSourceArray<Sources>> {
+21
src/helpers.ts
···11import { TalkbackFn, TeardownFn, Start, Push, SignalKind } from './types';
2233+/** Placeholder {@link TeardownFn | teardown functions} that's a no-op.
44+ * @see {@link TeardownFn} for the definition and usage of teardowns.
55+ * @internal
66+ */
37export const teardownPlaceholder: TeardownFn = () => {
48 /*noop*/
59};
1010+1111+/** Placeholder {@link TalkbackFn | talkback function} that's a no-op.
1212+ * @privateRemarks
1313+ * This is frequently used in the codebase as a no-op initializer value for talkback functions in
1414+ * the implementation of {@link Operator | Operators}. This is cheaper than initializing the
1515+ * variables of talkbacks to `undefined` or `null` and performing an extra check before calling
1616+ * them. Since the {@link Start | Start signal} is assumed to come first and carry a talkback, we can
1717+ * use this to our advantage and use a no-op placeholder before {@link Start} is received.
1818+ *
1919+ * @internal
2020+ */
621export const talkbackPlaceholder: TalkbackFn = teardownPlaceholder;
7222323+/** Wraps the passed {@link TalkbackFn | talkback function} in a {@link Start | Start signal}.
2424+ * @internal
2525+ */
826export function start<T>(talkback: TalkbackFn): Start<T> {
927 const box: any = [talkback];
1028 box.tag = SignalKind.Start;
1129 return box;
1230}
13313232+/** Wraps the passed value in a {@link Push | Push signal}.
3333+ * @internal
3434+ */
1435export function push<T>(value: T): Push<T> {
1536 const box: any = [value];
1637 box.tag = SignalKind.Push;
+12
src/index.ts
···11+/**
22+ * A tiny but capable push & pull stream library for TypeScript and Flow.
33+ *
44+ * @remarks
55+ * Wonka is a lightweight iterable and observable library and exposes a set of helpers to create
66+ * streams, which are sources emitting multiple values, which allow you to create, transform, and
77+ * consume event streams or iterable sets of data.
88+ *
99+ * It's loosely based on the Callbag spec: {@link https://github.com/callbag/callbag}
1010+ * @packageDocumentation
1111+ */
1212+113export * from './types';
214export * from './sources';
315export * from './operators';
+121-2
src/observable.ts
···11import { Source, SignalKind, TalkbackKind } from './types';
22import { push, start, talkbackPlaceholder } from './helpers';
3344+/** A definition of the ES Observable Subscription type that is returned by
55+ * {@link Observable.subscribe}
66+ *
77+ * @remarks
88+ * The Subscription in ES Observables is a handle that is held while the Observable is actively
99+ * streaming values. As such, it's used to indicate with {@link ObservableSubscription.closed}
1010+ * whether it's active, and {@link ObservableSubscription.unsubscribe} may be used to cancel the
1111+ * ongoing subscription and end the {@link Observable} early.
1212+ *
1313+ * @see {@link https://github.com/tc39/proposal-observable} for the ES Observable specification.
1414+ */
415interface ObservableSubscription {
1616+ /** A boolean flag indicating whether the subscription is closed.
1717+ * @remarks
1818+ * When `true`, the subscription will not issue new values to the {@link ObservableObserver} and
1919+ * has terminated. No new values are expected.
2020+ *
2121+ * @readonly
2222+ */
523 closed?: boolean;
2424+ /** Cancels the subscription.
2525+ * @remarks
2626+ * This cancels the ongoing subscription and the {@link ObservableObserver}'s callbacks will
2727+ * subsequently not be called at all. The subscription will be terminated and become inactive.
2828+ */
629 unsubscribe(): void;
730}
8313232+/** A definition of the ES Observable Observer type that is used to receive data from an
3333+ * {@link Observable}.
3434+ *
3535+ * @remarks
3636+ * The Observer in ES Observables is supplied to {@link Observable.subscribe} to receive events from
3737+ * an {@link Observable} as it issues them.
3838+ *
3939+ * @see {@link https://github.com/tc39/proposal-observable#observer} for the ES Observable
4040+ * specification of an Observer.
4141+ */
942interface ObservableObserver<T> {
4343+ /** Callback for the Observable issuing new values.
4444+ * @param value - The value that the {@link Observable} is sending.
4545+ */
1046 next(value: T): void;
4747+ /** Callback for the Observable encountering an error, terminating it.
4848+ * @param error - The error that the {@link Observable} has encountered.
4949+ */
1150 error?(error: any): void;
5151+ /** Callback for the Observable ending, after all values have been issued. */
1252 complete?(): void;
1353}
14545555+/** A looser definition of ES Observable-like types that is used for interoperability.
5656+ * @remarks
5757+ * The Observable is often used by multiple libraries supporting or creating streams to provide
5858+ * interoperability for push-based streams. When converting from an Observable to a {@link Source},
5959+ * this looser type is accepted as an input.
6060+ *
6161+ * @see {@link https://github.com/tc39/proposal-observable} for the ES Observable specification.
6262+ * @see {@link Observable} for the full ES Observable type.
6363+ */
1564interface ObservableLike<T> {
6565+ /**
6666+ * Subscribes to new signals from an {@link Observable} via callbacks.
6767+ * @param observer - An object containing callbacks for the various events of an Observable.
6868+ * @returns Subscription handle of type {@link ObservableSubscription}.
6969+ *
7070+ * @see {@link ObservableObserver} for the callbacks in an object that are called as Observables
7171+ * issue events.
7272+ */
1673 subscribe(observer: ObservableObserver<T>): ObservableSubscription;
7474+7575+ /** The well-known symbol specifying the default ES Observable for an object. */
1776 [Symbol.observable]?(): Observable<T>;
1877}
19787979+/** An ES Observable type that is a de-facto standard for push-based data sources across the JS
8080+ * ecosystem.
8181+ *
8282+ * @remarks
8383+ * The Observable is often used by multiple libraries supporting or creating streams to provide
8484+ * interoperability for push-based streams. As Wonka's {@link Source | Sources} are similar in
8585+ * functionality to Observables, it provides utilities to cleanly convert to and from Observables.
8686+ *
8787+ * @see {@link https://github.com/tc39/proposal-observable} for the ES Observable specification.
8888+ */
2089interface Observable<T> {
9090+ /** Subscribes to new signals from an {@link Observable} via callbacks.
9191+ * @param observer - An object containing callbacks for the various events of an Observable.
9292+ * @returns Subscription handle of type {@link ObservableSubscription}.
9393+ *
9494+ * @see {@link ObservableObserver} for the callbacks in an object that are called as Observables
9595+ * issue events.
9696+ */
2197 subscribe(observer: ObservableObserver<T>): ObservableSubscription;
22989999+ /** Subscribes to new signals from an {@link Observable} via callbacks.
100100+ * @param onNext - Callback for the Observable issuing new values.
101101+ * @param onError - Callback for the Observable encountering an error, terminating it.
102102+ * @param onComplete - Callback for the Observable ending, after all values have been issued.
103103+ * @returns Subscription handle of type {@link ObservableSubscription}.
104104+ */
23105 subscribe(
24106 onNext: (value: T) => any,
25107 onError?: (error: any) => any,
26108 onComplete?: () => any
27109 ): ObservableSubscription;
28110111111+ /** The well-known symbol specifying the default ES Observable for an object. */
29112 [Symbol.observable](): Observable<T>;
30113}
31114115115+/** Returns the well-known symbol specifying the default ES Observable.
116116+ * @privateRemarks
117117+ * This symbol is used to mark an object as a default ES Observable. By the specification, an object
118118+ * that abides by the default Observable implementation must carry a method set to this well-known
119119+ * symbol that returns the Observable implementation. It's common for this object to be an
120120+ * Observable itself and return itself on this method.
121121+ *
122122+ * @see {@link https://github.com/0no-co/wonka/issues/122} for notes on the intercompatibility
123123+ * between Observable implementations.
124124+ *
125125+ * @internal
126126+ */
32127const observableSymbol = (): typeof Symbol.observable => Symbol.observable || '@@observable';
33128129129+/** Converts an ES Observable to a {@link Source}.
130130+ * @param input - The {@link ObservableLike} object that will be converted.
131131+ * @returns A {@link Source} wrapping the passed Observable.
132132+ *
133133+ * @remarks
134134+ * This converts an ES Observable to a {@link Source}. When this Source receives a {@link Sink} and
135135+ * the subscription starts, internally, it'll subscribe to the passed Observable, passing through
136136+ * all of the Observable's values. As such, this utility provides intercompatibility converting from
137137+ * standard Observables to Wonka Sources.
138138+ *
139139+ * @throws
140140+ * When the passed ES Observable throws, the error is simply re-thrown as {@link Source} does
141141+ * not support or expect errors to be handled by streams.
142142+ */
34143export function fromObservable<T>(input: ObservableLike<T>): Source<T> {
3535- input = input[observableSymbol()] ? (input as any)[observableSymbol()]() : input;
36144 return sink => {
3737- const subscription = input.subscribe({
145145+ const subscription = (
146146+ input[observableSymbol()] ? input[observableSymbol()]!() : input
147147+ ).subscribe({
38148 next(value: T) {
39149 sink(push(value));
40150 },
···53163 };
54164}
55165166166+/** Converts a {@link Source} to an ES Observable.
167167+ * @param source - The {@link Source} that will be converted.
168168+ * @returns An {@link Observable} wrapping the passed Source.
169169+ *
170170+ * @remarks
171171+ * This converts a {@link Source} to an {@link Observable}. When this Observable is subscribed to, it
172172+ * internally subscribes to the Wonka Source and pulls new values. As such, this utility provides
173173+ * intercompatibility converting from Wonka Sources to standard ES Observables.
174174+ */
56175export function toObservable<T>(source: Source<T>): Observable<T> {
57176 return {
58177 subscribe(
+599
src/operators.ts
···4455const identity = <T>(x: T): T => x;
6677+/** Buffers values and emits the array of bufferd values each time a `notifier` Source emits.
88+ *
99+ * @param notifier - A {@link Source} that releases the current buffer.
1010+ * @returns An {@link Operator}.
1111+ *
1212+ * @remarks
1313+ * `buffer` will buffer values from the input {@link Source}. When the passed `notifier` Source
1414+ * emits, it will emit an array of all buffered values.
1515+ *
1616+ * This can be used to group values over time. A buffer will only be emitted when it contains any
1717+ * values.
1818+ *
1919+ * @example
2020+ * ```ts
2121+ * pipe(
2222+ * interval(50),
2323+ * buffer(interval(100)),
2424+ * subscribe(x => {
2525+ * console.log(text); // logs: [0], [1, 2], [3, 4]...
2626+ * })
2727+ * );
2828+ * ```
2929+ */
730export function buffer<S, T>(notifier: Source<S>): Operator<T, T[]> {
831 return source => sink => {
932 let buffer: T[] = [];
···6487 };
6588}
66899090+/** Emits in order from the Sources returned by a mapping function per value of the Source.
9191+ *
9292+ * @param map - A function returning a {@link Source} per value.
9393+ * @returns An {@link Operator}.
9494+ *
9595+ * @remarks
9696+ * `concatMap` accepts a mapping function which must return a {@link Source} per value.
9797+ * The output {@link Source} will emit values from each Source the function returned, in order,
9898+ * queuing sources that aren't yet active.
9999+ *
100100+ * This can be used to issue multiple values per emission of an input {@link Source}, while keeping
101101+ * the order of their outputs consistent.
102102+ *
103103+ * @example
104104+ * ```ts
105105+ * pipe(
106106+ * fromArray([1, 2]),
107107+ * concatMap(x => fromArray([x, x * 2])),
108108+ * subscribe(x => {
109109+ * console.log(text); // logs: 1, 2, 2, 4
110110+ * })
111111+ * );
112112+ * ```
113113+ */
67114export function concatMap<In, Out>(map: (value: In) => Source<Out>): Operator<In, Out> {
68115 return source => sink => {
69116 const inputQueue: In[] = [];
···144191 };
145192}
146193194194+/** Flattens a Source emitting Sources into a single Source emitting the inner values in order.
195195+ *
196196+ * @see {@link concatMap} which this helper uses and instead accept a mapping function.
197197+ * @param source - An {@link Source} emitting {@link Source | Sources}.
198198+ * @returns A {@link Source} emitting values from the inner Sources.
199199+ *
200200+ * @remarks
201201+ * `concatAll` accepts a {@link Source} emitting {@link Source | Sources}.
202202+ * The output {@link Source} will emit values from each Source, in order, queuing sources that
203203+ * aren't yet active.
204204+ *
205205+ * @example
206206+ * ```ts
207207+ * pipe(
208208+ * fromArray([
209209+ * fromArray([1, 2]),
210210+ * fromArray([3, 4]),
211211+ * ]),
212212+ * concatAll,
213213+ * subscribe(x => {
214214+ * console.log(text); // logs: 1, 2, 3, 4
215215+ * })
216216+ * );
217217+ * ```
218218+ */
147219export function concatAll<T>(source: Source<Source<T>>): Source<T> {
148220 return concatMap<Source<T>, T>(identity)(source);
149221}
150222223223+/** Emits values from the passed sources in order.
224224+ *
225225+ * @param sources - An array of {@link Source | Sources}.
226226+ * @returns A {@link Source} emitting values from the input Sources.
227227+ *
228228+ * @remarks
229229+ * `concat` accepts an array of {@link Source | Sources} and will emit values from them, starting
230230+ * with the first one and continuing to the next only when the prior source ended.
231231+ *
232232+ * This can be used to issue combine sources while keeping the order of their values intact.
233233+ *
234234+ * @example
235235+ * ```ts
236236+ * pipe(
237237+ * concat([
238238+ * fromArray([1, 2]),
239239+ * fromArray([3, 4]),
240240+ * ]),
241241+ * subscribe(x => {
242242+ * console.log(text); // logs: 1, 2, 3, 4
243243+ * })
244244+ * );
245245+ * ```
246246+ */
151247export function concat<T>(sources: Source<T>[]): Source<T> {
152248 return concatAll(fromArray(sources));
153249}
154250251251+/** Filters out emitted values for which the passed predicate function returns `false`.
252252+ *
253253+ * @param predicate - A function returning a boolean per value.
254254+ * @returns An {@link Operator}.
255255+ *
256256+ * @remarks
257257+ * `filter` will omit values from the {@link Source} for which the passed `predicate` function
258258+ * returns `false`.
259259+ *
260260+ * @example
261261+ * ```ts
262262+ * pipe(
263263+ * fromArray([1, 2, 3]),
264264+ * filter(x => x % 2 === 0),
265265+ * subscribe(x => {
266266+ * console.log(text); // logs: 2
267267+ * })
268268+ * );
269269+ * ```
270270+ */
155271export function filter<T>(predicate: (value: T) => boolean): Operator<T, T> {
156272 return source => sink => {
157273 let talkback = talkbackPlaceholder;
···170286 };
171287}
172288289289+/** Maps emitted values using the passed mapping function.
290290+ *
291291+ * @param map - A function returning transforming the {@link Source | Source's} values.
292292+ * @returns An {@link Operator}.
293293+ *
294294+ * @remarks
295295+ * `map` accepts a transform function and calls it on each emitted value. It then emits
296296+ * the values returned by the transform function instead.
297297+ *
298298+ * @example
299299+ * ```ts
300300+ * pipe(
301301+ * fromArray([1, 2, 3]),
302302+ * map(x => x * 2),
303303+ * subscribe(x => {
304304+ * console.log(text); // logs: 2, 4, 6
305305+ * })
306306+ * );
307307+ * ```
308308+ */
173309export function map<In, Out>(map: (value: In) => Out): Operator<In, Out> {
174310 return source => sink =>
175311 source(signal => {
···181317 });
182318}
183319320320+/** Emits from the Sources returned by a mapping function per value of the Source.
321321+ *
322322+ * @param map - A function returning a {@link Source} per value.
323323+ * @returns An {@link Operator}.
324324+ *
325325+ * @remarks
326326+ * `mergeMap` accepts a mapping function which must return a {@link Source} per value.
327327+ * The output {@link Source} will emit values from all {@link Source | Sources} the mapping function
328328+ * returned.
329329+ *
330330+ * This can be used to issue multiple values per emission of an input {@link Source}, essentially
331331+ * multiplexing all values to multiple Sources.
332332+ *
333333+ * @example
334334+ * ```ts
335335+ * pipe(
336336+ * interval(50),
337337+ * mergeMap(x => pipe(
338338+ * fromValue(x),
339339+ * delay(100)
340340+ * )),
341341+ * subscribe(x => {
342342+ * console.log(text); // logs: 0, 1, 2...
343343+ * })
344344+ * );
345345+ * ```
346346+ */
184347export function mergeMap<In, Out>(map: (value: In) => Source<Out>): Operator<In, Out> {
185348 return source => sink => {
186349 let innerTalkbacks: TalkbackFn[] = [];
···254417 };
255418}
256419420420+/** Flattens a Source emitting Sources into a single Source emitting the inner values.
421421+ *
422422+ * @see {@link mergeMap} which this helper uses and instead accept a mapping function.
423423+ * @param source - An {@link Source} emitting {@link Source | Sources}.
424424+ * @returns A {@link Source} emitting values from the inner Sources.
425425+ *
426426+ * @remarks
427427+ * `mergeAll` accepts a {@link Source} which must emit {@link Source | Sources}. It will subscribe
428428+ * to each incoming source immediately and start passing its emitted values through.
429429+ *
430430+ * @example
431431+ * ```ts
432432+ * pipe(
433433+ * fromArray([
434434+ * interval(50),
435435+ * interval(100),
436436+ * ]),
437437+ * mergeAll,
438438+ * subscribe(x => {
439439+ * console.log(text); // logs: 0, 0, 1, 2, 1, 3, 4, 2
440440+ * })
441441+ * );
442442+ * ```
443443+ */
257444export function mergeAll<T>(source: Source<Source<T>>): Source<T> {
258445 return mergeMap<Source<T>, T>(identity)(source);
259446}
260447448448+/** Emits values from the passed sources simultaneously.
449449+ *
450450+ * @param sources - An array of {@link Source | Sources}.
451451+ * @returns A {@link Source} emitting values from the input Sources.
452452+ *
453453+ * @remarks
454454+ * `merge` accepts an array of {@link Source | Sources} and will subscribe to all of them, passing
455455+ * through all their emitted values simultaneously.
456456+ *
457457+ * This can be used to interleave the values of multiple sources.
458458+ *
459459+ * @example
460460+ * ```ts
461461+ * pipe(
462462+ * merge([
463463+ * interval(50),
464464+ * interval(100),
465465+ * ]),
466466+ * subscribe(x => {
467467+ * console.log(text); // logs: 0, 0, 1, 2, 1, 3, 4, 2
468468+ * })
469469+ * );
470470+ * ```
471471+ */
261472export function merge<T>(sources: Source<T>[]): Source<T> {
262473 return mergeAll(fromArray(sources));
263474}
264475476476+/** Calls the passed callback function when the Source ends or is closed.
477477+ *
478478+ * @param callback - A function that is called when the {@link Source} ends.
479479+ * @returns An {@link Operator}.
480480+ *
481481+ * @remarks
482482+ * `onEnd` accepts a callback which is called when the {@link Source} either ends
483483+ * or is closed.
484484+ *
485485+ * This operator can be used to add side-effects to a Source.
486486+ *
487487+ * @example
488488+ * ```ts
489489+ * pipe(
490490+ * fromArray([1, 2, 3]),
491491+ * take(1),
492492+ * onEnd(() => {
493493+ * console.log('end');
494494+ * }),
495495+ * publish
496496+ * );
497497+ * ```
498498+ */
265499export function onEnd<T>(callback: () => void): Operator<T, T> {
266500 return source => sink => {
267501 let ended = false;
···292526 };
293527}
294528529529+/** Calls the passed callback function when the Source emits a value.
530530+ *
531531+ * @param callback - A function that is called with each value the {@link Source} emits.
532532+ * @returns An {@link Operator}.
533533+ *
534534+ * @remarks
535535+ * `onPush` accepts a callback which is called for every emitted value of
536536+ * the {@link Source}.
537537+ *
538538+ * This operator can be used to add side-effects to a Source.
539539+ *
540540+ * @example
541541+ * ```ts
542542+ * pipe(
543543+ * fromArray([1, 2, 3]),
544544+ * onPush(value => {
545545+ * console.log(value); // logs: 1, 2, 3
546546+ * }),
547547+ * publish
548548+ * );
549549+ * ```
550550+ */
295551export function onPush<T>(callback: (value: T) => void): Operator<T, T> {
296552 return source => sink => {
297553 let ended = false;
···317573 };
318574}
319575576576+/** Calls the passed callback function when the Source starts.
577577+ *
578578+ * @param callback - A function that is called when the {@link Source} is started.
579579+ * @returns An {@link Operator}.
580580+ *
581581+ * @remarks
582582+ * `onPush` accepts a callback which is called for every emitted value of
583583+ * the {@link Source}.
584584+ *
585585+ * This operator can be used to add side-effects to a Source.
586586+ * Specifically, it's useful to add a side-effect for a Source that triggers only once
587587+ * the {@link Source} is used and started.
588588+ *
589589+ * @example
590590+ * ```ts
591591+ * pipe(
592592+ * fromArray([1, 2, 3]),
593593+ * onStart(() => {
594594+ * console.log('start');
595595+ * }),
596596+ * publish
597597+ * );
598598+ * ```
599599+ */
320600export function onStart<T>(callback: () => void): Operator<T, T> {
321601 return source => sink =>
322602 source(signal => {
···331611 });
332612}
333613614614+/** Emits the last value the {@link Source} emitted, whenever the notifier Source emits a value.
615615+ *
616616+ * @param notifier - A {@link Source} that triggers the last value to be emitted.
617617+ * @returns An {@link Operator}.
618618+ *
619619+ * @remarks
620620+ * `sample` will store the latest value the {@link Source} emitted. Every time the `notifier` Source
621621+ * emits, it will emit the latest value.
622622+ *
623623+ * This is a back pressure operator that can be used to omit values from a {@link Source} coming in
624624+ * too frequently.
625625+ *
626626+ * {@link Source | Sources} emitting `undefined` are undefined behaviour and these values will be
627627+ * ignored.
628628+ *
629629+ * @example
630630+ * ```ts
631631+ * pipe(
632632+ * interval(50),
633633+ * sample(interval(100)),
634634+ * subscribe(x => {
635635+ * console.log(text); // logs: 0, 2, 4...
636636+ * })
637637+ * );
638638+ * ```
639639+ */
334640export function sample<S, T>(notifier: Source<S>): Operator<T, T> {
335641 return source => sink => {
336642 let sourceTalkback = talkbackPlaceholder;
···389695 };
390696}
391697698698+/** Maps emitted values using the passed reducer function.
699699+ *
700700+ * @param reducer - A function called with the last value by the `reducer` and the emitted value.
701701+ * @param seed - The initial value that is passed to the `reducer`.
702702+ * @returns An {@link Operator}.
703703+ *
704704+ * @remarks
705705+ * `scan` accepts a reducer function and a seed value. The reducer will be called initially with the
706706+ * seed value and the first emitted value. The {@link Source} will then emit the value returned by
707707+ * the reducer function. Subsequently, the `reducer` is called with the last value the `reducer`
708708+ * returned and the emitted value.
709709+ *
710710+ * This operator is similar to `Array.prototype.reduce`, but instead is called over time and emits
711711+ * each value of the reducer.
712712+ *
713713+ * @example
714714+ * ```ts
715715+ * pipe(
716716+ * fromArray([1, 2, 3]),
717717+ * scan((acc, x) => acc + x, 0),
718718+ * subscribe(x => {
719719+ * console.log(text); // logs: 1, 3, 6
720720+ * })
721721+ * );
722722+ * ```
723723+ */
392724export function scan<In, Out>(reducer: (acc: Out, value: In) => Out, seed: Out): Operator<In, Out> {
393725 return source => sink => {
394726 let acc = seed;
···404736 };
405737}
406738739739+/** Shares one underlying subscription to the Source between all Sinks.
740740+ *
741741+ * @param source - A {@link Source} that should be shared.
742742+ * @returns A shared {@link Source}.
743743+ *
744744+ * @remarks
745745+ * `share` accepts a {@link Source} and returns one. It will emit all values as normal, however, it
746746+ * will share one subscription to the input source. This allows side-effects on the input
747747+ * {@link Source} to only be triggerd once.
748748+ */
407749export function share<T>(source: Source<T>): Source<T> {
408750 let sinks: Sink<T>[] = [];
409751 let talkback = talkbackPlaceholder;
···438780 };
439781}
440782783783+/** Omits `wait` amount of values from the Source and then runs as usual.
784784+ *
785785+ * @param wait - The number of values to be omitted.
786786+ * @returns An {@link Operator}.
787787+ *
788788+ * @remarks
789789+ * `skip` will skip `wait` number of emitted values, then issue all values as normal afterwards.
790790+ * This essentially skips a given number of values on the input {@link Source}.
791791+ *
792792+ * @example
793793+ * ```ts
794794+ * pipe(
795795+ * fromArray([1, 2, 3]),
796796+ * skip(2),
797797+ * subscribe(x => {
798798+ * console.log(text); // logs: 3
799799+ * })
800800+ * );
801801+ * ```
802802+ */
441803export function skip<T>(wait: number): Operator<T, T> {
442804 return source => sink => {
443805 let talkback = talkbackPlaceholder;
···457819 };
458820}
459821822822+/** Omits values from an input Source until a notifier Source emits a value.
823823+ *
824824+ * @param notifier - A {@link Source} that starts the operator's sent values.
825825+ * @returns An {@link Operator}.
826826+ *
827827+ * @remarks
828828+ * `skipUntil` will omit all values from the input {@link Source} until the `notifier`
829829+ * Source emits a value of its own. It'll then start passing values from the Source through.
830830+ *
831831+ * @example
832832+ * ```ts
833833+ * pipe(
834834+ * interval(50),
835835+ * skipUntil(interval(150)),
836836+ * subscribe(x => {
837837+ * console.log(text); // logs: 2, 3...
838838+ * })
839839+ * );
840840+ * ```
841841+ */
460842export function skipUntil<S, T>(notifier: Source<S>): Operator<T, T> {
461843 return source => sink => {
462844 let sourceTalkback = talkbackPlaceholder;
···513895 };
514896}
515897898898+/** Omits values from an input Source until a predicate function returns `false`.
899899+ *
900900+ * @param predicate - A function returning a boolean per value.
901901+ * @returns An {@link Operator}.
902902+ *
903903+ * @remarks
904904+ * `skipWhile` will omit all values from the input {@link Source} until the `predicate`
905905+ * function returns `false`. When the `predicate` function returns `false`, the Source's values will
906906+ * be passed through.
907907+ *
908908+ * @example
909909+ * ```ts
910910+ * pipe(
911911+ * fromArray([1, 2, 3]),
912912+ * skipWhile(x => x < 2),
913913+ * subscribe(x => {
914914+ * console.log(text); // logs: 2, 3
915915+ * })
916916+ * );
917917+ * ```
918918+ */
516919export function skipWhile<T>(predicate: (value: T) => boolean): Operator<T, T> {
517920 return source => sink => {
518921 let talkback = talkbackPlaceholder;
···537940 };
538941}
539942943943+/** Emits from the latest Source returned by a mapping function per value of the Source.
944944+ *
945945+ * @param map - A function returning a {@link Source} per value.
946946+ * @returns An {@link Operator}.
947947+ *
948948+ * @remarks
949949+ * `switchMap` accepts a mapping function which must return a {@link Source} per value.
950950+ * The output {@link Source} will emit values from the latest Source the mapping function
951951+ * returned. If a value is emitted while the last returned Source is still active, the prior Source
952952+ * will be closed.
953953+ *
954954+ * This can be used to issue multiple values per emission of an input {@link Source}, while only
955955+ * letting one of these sub-Sources be active at a time.
956956+ *
957957+ * @example
958958+ * ```ts
959959+ * pipe(
960960+ * interval(100),
961961+ * switchMap(() => interval(50)),
962962+ * subscribe(x => {
963963+ * console.log(text); // logs: 0, 0, 0...
964964+ * })
965965+ * );
966966+ * ```
967967+ */
540968export function switchMap<In, Out>(map: (value: In) => Source<Out>): Operator<In, Out> {
541969 return source => sink => {
542970 let outerTalkback = talkbackPlaceholder;
···6191047 };
6201048}
621104910501050+/** Flattens a Source emitting Sources into a single Source emitting the inner values.
10511051+ *
10521052+ * @see {@link switchMap} which this helper uses and instead accept a mapping function.
10531053+ * @param source - An {@link Source} emitting {@link Source | Sources}.
10541054+ * @returns A {@link Source} emitting values from the inner Sources.
10551055+ *
10561056+ * @remarks
10571057+ * `switchAll` accepts a {@link Source} which must emit {@link Source | Sources}. Each time it
10581058+ * receives a {@link Source} it will close its prior subscription and subscribe to the new Source
10591059+ * instead, passing through its values.
10601060+ *
10611061+ * @example
10621062+ * ```ts
10631063+ * pipe(
10641064+ * interval(100),
10651065+ * map(() => interval(50)),
10661066+ * switchAll,
10671067+ * subscribe(x => {
10681068+ * console.log(text); // logs: 0, 0, 0...
10691069+ * })
10701070+ * );
10711071+ * ```
10721072+ */
6221073export function switchAll<T>(source: Source<Source<T>>): Source<T> {
6231074 return switchMap<Source<T>, T>(identity)(source);
6241075}
625107610771077+/** Emits `max` values from the Source and then ends.
10781078+ *
10791079+ * @param max - The maximum number of values emitted.
10801080+ * @returns An {@link Operator}.
10811081+ *
10821082+ * @remarks
10831083+ * `take` will issue all values as normal until the `max` number of emitted values has been reached.
10841084+ * It will then end and close the {@link Source}.
10851085+ *
10861086+ * @example
10871087+ * ```ts
10881088+ * pipe(
10891089+ * fromArray([1, 2, 3]),
10901090+ * take(2),
10911091+ * subscribe(x => {
10921092+ * console.log(text); // logs: 1, 2
10931093+ * })
10941094+ * );
10951095+ * ```
10961096+ */
6261097export function take<T>(max: number): Operator<T, T> {
6271098 return source => sink => {
6281099 let talkback = talkbackPlaceholder;
···6661137 };
6671138}
668113911401140+/** Buffers the `max` last values of the Source and emits them once the Source ends.
11411141+ *
11421142+ * @param max - The maximum number of values buffered.
11431143+ * @returns An {@link Operator}.
11441144+ *
11451145+ * @remarks
11461146+ * `takeLast` will buffer values from the input {@link Source} up until the given `max` number. It
11471147+ * will only emit values stored in the buffer once the {@link Source} ends.
11481148+ *
11491149+ * All values in the buffer are emitted like the {@link fromArray | `fromArray`} source would
11501150+ * synchronously.
11511151+ *
11521152+ * @example
11531153+ * ```ts
11541154+ * pipe(
11551155+ * fromArray([1, 2, 3]),
11561156+ * takeLast(1),
11571157+ * subscribe(x => {
11581158+ * console.log(text); // logs: 3
11591159+ * })
11601160+ * );
11611161+ * ```
11621162+ */
6691163export function takeLast<T>(max: number): Operator<T, T> {
6701164 return source => sink => {
6711165 const queue: T[] = [];
···6891183 };
6901184}
691118511861186+/** Takes values from an input Source until a notifier Source emits a value.
11871187+ *
11881188+ * @param notifier - A {@link Source} that stops the operator's sent values.
11891189+ * @returns An {@link Operator}.
11901190+ *
11911191+ * @remarks
11921192+ * `takeUntil` will issue all values as normal from the input {@link Source} until the `notifier`
11931193+ * Source emits a value of its own. It'll then close the {@link Source}.
11941194+ *
11951195+ * @example
11961196+ * ```ts
11971197+ * pipe(
11981198+ * interval(50),
11991199+ * takeUntil(interval(150)),
12001200+ * subscribe(x => {
12011201+ * console.log(text); // logs: 0, 1
12021202+ * })
12031203+ * );
12041204+ * ```
12051205+ */
6921206export function takeUntil<S, T>(notifier: Source<S>): Operator<T, T> {
6931207 return source => sink => {
6941208 let sourceTalkback = talkbackPlaceholder;
···7331247 };
7341248}
735124912501250+/** Takes values from an input Source until a predicate function returns `false`.
12511251+ *
12521252+ * @param predicate - A function returning a boolean per value.
12531253+ * @returns An {@link Operator}.
12541254+ *
12551255+ * @remarks
12561256+ * `takeWhile` will issue all values as normal from the input {@link Source} until the `predicate`
12571257+ * function returns `false`. When the `predicate` function returns `false`, the current value is
12581258+ * omitted and the {@link Source} is closed.
12591259+ *
12601260+ * @example
12611261+ * ```ts
12621262+ * pipe(
12631263+ * fromArray([1, 2, 3]),
12641264+ * takeWhile(x => x < 2),
12651265+ * subscribe(x => {
12661266+ * console.log(text); // logs: 1
12671267+ * })
12681268+ * );
12691269+ * ```
12701270+ */
7361271export function takeWhile<T>(predicate: (value: T) => boolean): Operator<T, T> {
7371272 return source => sink => {
7381273 let talkback = talkbackPlaceholder;
···7571292 };
7581293}
759129412951295+/** Debounces a Source by omitting values until a given timeframe has passed.
12961296+ *
12971297+ * @param timing - A function returning a debounce time (ms) per emitted value.
12981298+ * @returns An {@link Operator}.
12991299+ *
13001300+ * @remarks
13011301+ * `debounce` accepts a mapping function that can be used to return a time (in ms) per emitted
13021302+ * value. All emitted values issued by the {@link Source} during the returned time will be omitted
13031303+ * until the time has passed.
13041304+ *
13051305+ * Debouncing means that the returned {@link Source} will wait for a minimum time of silence until a
13061306+ * value is let through.
13071307+ *
13081308+ * This is a back pressure operator that can be used to omit values from a {@link Source} coming in
13091309+ * too frequently.
13101310+ *
13111311+ * @example
13121312+ * ```ts
13131313+ * pipe(
13141314+ * interval(50),
13151315+ * debounce(() => 100),
13161316+ * subscribe(x => {
13171317+ * console.log(text); // never logs any value
13181318+ * })
13191319+ * );
13201320+ * ```
13211321+ */
7601322export function debounce<T>(timing: (value: T) => number): Operator<T, T> {
7611323 return source => sink => {
7621324 let id: any | void;
···7981360 };
7991361}
800136213631363+/** Delays each signal emitted by a Source by given time (ms).
13641364+ *
13651365+ * @param wait - A time (in ms) by which each {@link SignalKind | signal} is delayed.
13661366+ * @returns An {@link Operator}.
13671367+ *
13681368+ * @remarks
13691369+ * `delay` accepts a time (in ms) by which each {@link SignalKind | signal} will be delayed by.
13701370+ * This will create a timeout per received signal and delay the emitted values accordingly.
13711371+ *
13721372+ * Since the operator only calls `setTimeout` per signal, it relies on the timeout implementation to
13731373+ * be ordered. Otherwise, signals will arrive in the wrong order at the sink.
13741374+ */
8011375export function delay<T>(wait: number): Operator<T, T> {
8021376 return source => sink => {
8031377 let active = 0;
···8171391 };
8181392}
819139313941394+/** Throttles a Source by omitting values that are emitted before a given timeout.
13951395+ *
13961396+ * @param timing - A function returning a throttle time (ms) per emitted value.
13971397+ * @returns An {@link Operator}.
13981398+ *
13991399+ * @remarks
14001400+ * `throttle` accepts a mapping function that can be used to return a time (in ms) per emitted
14011401+ * value. During the returned timeframe all values issued by the {@link Source} will be omitted and
14021402+ * dropped.
14031403+ *
14041404+ * This is a back pressure operator that can be used to omit values from a {@link Source} coming in
14051405+ * too frequently.
14061406+ *
14071407+ * @example
14081408+ * ```ts
14091409+ * pipe(
14101410+ * interval(50),
14111411+ * throttle(() => 100),
14121412+ * subscribe(x => {
14131413+ * // omits every second value: 0, 2, 4...
14141414+ * console.log(text);
14151415+ * })
14161416+ * );
14171417+ * ```
14181418+ */
8201419export function throttle<T>(timing: (value: T) => number): Operator<T, T> {
8211420 return source => sink => {
8221421 let skip = false;
···11import { Source, Subscription, TalkbackKind, SignalKind } from './types';
22import { talkbackPlaceholder } from './helpers';
3344+/** Creates a subscription to a given source and invokes a `subscriber` callback for each value.
55+ * @param subscriber - A callback function called for each issued value.
66+ * @returns A function accepting a {@link Source} and returning a {@link Subscription}.
77+ *
88+ * @remarks
99+ * `subscribe` accepts a `subscriber` callback and returns a function accepting a {@link Source}.
1010+ * When a source is passed to the returned funtion, the subscription will start and `subscriber`
1111+ * will be called for each new value the Source issues. This will also return a {@link Subscription}
1212+ * object that can cancel the ongoing {@link Source} early.
1313+ *
1414+ * @example
1515+ * ```ts
1616+ * const subscription = pipe(
1717+ * fromValue('test'),
1818+ * subscribe(text => {
1919+ * console.log(text); // 'test'
2020+ * })
2121+ * );
2222+ * ```
2323+ */
424export function subscribe<T>(subscriber: (value: T) => void) {
525 return (source: Source<T>): Subscription => {
626 let talkback = talkbackPlaceholder;
···2646 };
2747}
28484949+/** Creates a subscription to a given source and invokes a `subscriber` callback for each value.
5050+ * @see {@link subscribe} which this helper aliases without returnin a {@link Subscription}.
5151+ * @param subscriber - A callback function called for each issued value.
5252+ * @returns A function accepting a {@link Source}.
5353+ *
5454+ * @remarks
5555+ * `forEach` accepts a `subscriber` callback and returns a function accepting a {@link Source}.
5656+ * When a source is passed to the returned funtion, the subscription will start and `subscriber`
5757+ * will be called for each new value the Source issues. Unlike `subscribe` it will not return a
5858+ * Subscription object and can't be cancelled early.
5959+ *
6060+ * @example
6161+ * ```ts
6262+ * pipe(
6363+ * fromValue('test'),
6464+ * forEach(text => {
6565+ * console.log(text); // 'test'
6666+ * })
6767+ * ); // undefined
6868+ * ```
6969+ */
2970export function forEach<T>(subscriber: (value: T) => void) {
3071 return (source: Source<T>): void => {
3172 subscribe(subscriber)(source);
3273 };
3374}
34757676+/** Creates a subscription to a given source and invokes a `subscriber` callback for each value.
7777+ * @see {@link subscribe} which this helper aliases without accepting parameters or returning a
7878+ * {@link Subscription | Subscription}.
7979+ *
8080+ * @param source - A {@link Source}.
8181+ *
8282+ * @remarks
8383+ * `publish` accepts a {@link Source} and subscribes to it, starting its values. The resulting
8484+ * values cannot be observed and the subscription can't be cancelled, as this helper is purely
8585+ * intended to start side-effects.
8686+ *
8787+ * @example
8888+ * ```ts
8989+ * pipe(
9090+ * lazy(() => {
9191+ * console.log('test'); // this is called
9292+ * return fromValue(123); // this is never used
9393+ * }),
9494+ * publish
9595+ * ); // undefined
9696+ * ```
9797+ */
3598export function publish<T>(source: Source<T>): void {
3699 subscribe(_value => {
37100 /*noop*/
···4010341104const doneResult = { done: true } as IteratorReturnResult<void>;
42105106106+/** Converts a Source to an AsyncIterable that pulls and issues values from the Source.
107107+ *
108108+ * @param source - A {@link Source}.
109109+ * @returns An {@link AsyncIterable | `AsyncIterable`} issuing values from the Source.
110110+ *
111111+ * @remarks
112112+ * `toAsyncIterable` will create an {@link AsyncIterable} that pulls and issues values from a given
113113+ * {@link Source}. This can be used in many interoperability situations, to provide an iterable when
114114+ * a consumer requires it.
115115+ *
116116+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols}
117117+ * for the JS Iterable protocol.
118118+ *
119119+ * @example
120120+ * ```ts
121121+ * const iterable = toAsyncIterable(fromArray([1, 2, 3]));
122122+ * for await (const value of iterable) {
123123+ * console.log(value); // outputs: 1, 2, 3
124124+ * }
125125+ * ```
126126+ */
43127export const toAsyncIterable = <T>(source: Source<T>): AsyncIterable<T> => ({
44128 [Symbol.asyncIterator](): AsyncIterator<T> {
45129 const buffer: T[] = [];
···84168 },
85169});
86170171171+/** Subscribes to a given source and collects all synchronous values into an array.
172172+ * @param source - A {@link Source}.
173173+ * @returns An array of values collected from the {@link Source}.
174174+ *
175175+ * @remarks
176176+ * `toArray` accepts a {@link Source} and returns an array of all synchronously issued values from
177177+ * this Source. It will issue {@link TalkbackKind.Pull | Pull signals} after every value it receives
178178+ * and expects the Source to recursively issue values.
179179+ *
180180+ * Any asynchronously issued values will not be
181181+ * added to the array and a {@link TalkbackKind.Close | Close signal} is issued by the sink before
182182+ * returning the array.
183183+ *
184184+ * @example
185185+ * ```ts
186186+ * toArray(fromArray([1, 2, 3])); // [1, 2, 3]
187187+ * ```
188188+ */
87189export function toArray<T>(source: Source<T>): T[] {
88190 const values: T[] = [];
89191 let talkback = talkbackPlaceholder;
···102204 return values;
103205}
104206207207+/** Subscribes to a given source and returns a Promise that will resolve with the last value the
208208+ * source issues.
209209+ *
210210+ * @param source - A {@link Source}.
211211+ * @returns A {@link Promise} resolving to the last value of the {@link Source}.
212212+ *
213213+ * @remarks
214214+ * `toPromise` will subscribe to the passed {@link Source} and resolve to the last value of it once
215215+ * it receives the last value, as signaled by the {@link SignalKind.End | End signal}.
216216+ *
217217+ * To keep its implementation simple, padding sources that don't issue any values to `toPromise` is
218218+ * undefined behaviour and `toPromise` will issue `undefined` in that case.
219219+ *
220220+ * The returned {@link Promise} delays its value by a microtick, using `Promise.resolve`.
221221+ *
222222+ * @example
223223+ * ```ts
224224+ * toPromise(fromValue('test')); // resolves: 'test'
225225+ * ```
226226+ */
105227export function toPromise<T>(source: Source<T>): Promise<T> {
106228 return new Promise(resolve => {
107229 let talkback = talkbackPlaceholder;
+206-4
src/sources.ts
···22import { push, start, talkbackPlaceholder, teardownPlaceholder } from './helpers';
33import { share } from './operators';
4455-export function lazy<T>(make: () => Source<T>): Source<T> {
66- return sink => make()(sink);
55+/** Helper creating a Source from a factory function when it's subscribed to.
66+ * @param produce - A factory function returning a {@link Source}.
77+ * @returns A {@link Source} lazyily subscribing to the Source returned by the given factory
88+ * function.
99+ *
1010+ * @remarks
1111+ * At times it's necessary to create a {@link Source} lazily. The time of a {@link Source} being
1212+ * created could be different from when it's subscribed to, and hence we may want to split the
1313+ * creation and subscription time. This is especially useful when the Source we wrap is "hot" and
1414+ * issues values as soon as it's created, which we may then not receive in a subscriber.
1515+ *
1616+ * @example An example of creating a {@link Source} that issues the timestamp of subscription. Here
1717+ * we effectively use `lazy` with the simple {@link fromValue | `fromValue`} source, to quickly
1818+ * create a Source that issues the time of its subscription, rather than the time of its creation
1919+ * that it would otherwise issue without `lazy`.
2020+ *
2121+ * ```ts
2222+ * lazy(() => fromValue(Date.now()));
2323+ * ```
2424+ */
2525+export function lazy<T>(produce: () => Source<T>): Source<T> {
2626+ return sink => produce()(sink);
727}
8282929+/** Converts an AsyncIterable to a Source that pulls and issues values from it as requested.
3030+ *
3131+ * @see {@link fromIterable | `fromIterable`} for the non-async Iterable version of this helper,
3232+ * which calls this helper automatically as needed.
3333+ *
3434+ * @param iterable - An {@link AsyncIterable | `AsyncIterable`}.
3535+ * @returns A {@link Source} issuing values sourced from the Iterable.
3636+ *
3737+ * @remarks
3838+ * `fromAsyncIterable` will create a {@link Source} that pulls and issues values from a given
3939+ * {@link AsyncIterable}. This can be used in many interoperability situations, including to consume
4040+ * an async generator function.
4141+ *
4242+ * When the {@link Sink} throws an exception when a new value is pushed, this helper will rethrow it
4343+ * using {@link AsyncIterator.throw}, which allows an async generator to recover from the exception.
4444+ *
4545+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols}
4646+ * for the JS Iterable protocol.
4747+ */
948export function fromAsyncIterable<T>(iterable: AsyncIterable<T>): Source<T> {
1049 return sink => {
1150 const iterator = iterable[Symbol.asyncIterator]();
···4685 };
4786}
48878888+/** Converts an Iterable to a Source that pulls and issues values from it as requested.
8989+ * @see {@link fromAsyncIterable | `fromAsyncIterable`} for the AsyncIterable version of this helper.
9090+ * @param iterable - An {@link Iterable | `Iterable`} or an `AsyncIterable`
9191+ * @returns A {@link Source} issuing values sourced from the Iterable.
9292+ *
9393+ * @remarks
9494+ * `fromIterable` will create a {@link Source} that pulls and issues values from a given
9595+ * {@link Iterable | JS Iterable}. As iterables are the common standard for any lazily iterated list
9696+ * of values in JS it can be applied to many different JS data types, including a JS Generator
9797+ * function.
9898+ *
9999+ * This Source will only call {@link Iterator.next} on the iterator when the subscribing {@link Sink}
100100+ * has pulled a new value with the {@link TalkbackKind.Pull | Pull signal}. `fromIterable` can
101101+ * therefore also be applied to "infinite" iterables, without a predefined end.
102102+ *
103103+ * This helper will call {@link fromAsyncIterable | `fromAsyncIterable`} automatically when the
104104+ * passed object also implements the async iterator protocol.
105105+ *
106106+ * When the {@link Sink} throws an exception when a new value is pushed, this helper will rethrow it
107107+ * using {@link Iterator.throw}, which allows a generator to recover from the exception.
108108+ *
109109+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol}
110110+ * for the JS Iterable protocol.
111111+ */
49112export function fromIterable<T>(iterable: Iterable<T> | AsyncIterable<T>): Source<T> {
50113 if (iterable[Symbol.asyncIterator]) return fromAsyncIterable(iterable as AsyncIterable<T>);
51114 return sink => {
···87150 };
88151}
89152153153+/** Creates a Source that issues a each value of a given array synchronously.
154154+ * @see {@link fromIterable} which `fromArray` aliases.
155155+ * @param array - The array whose values will be issued one by one.
156156+ * @returns A {@link Source} issuing the array's values.
157157+ *
158158+ * @remarks
159159+ * `fromArray` will create a {@link Source} that issues the values of a given JS array one by one. It
160160+ * will issue values as they're pulled and is hence a "cold" source, not eagerly emitting values. It
161161+ * will end and issue the {@link SignalKind.End | End signal} when the array is exhausted of values.
162162+ *
163163+ * @example
164164+ * ```ts
165165+ * fromArray([1, 2, 3]);
166166+ * ```
167167+ */
90168export const fromArray: <T>(array: T[]) => Source<T> = fromIterable;
91169170170+/** Creates a Source that issues a single value and ends immediately after.
171171+ * @param value - The value that will be issued.
172172+ * @returns A {@link Source} issuing the single value.
173173+ *
174174+ * @example
175175+ * ```ts
176176+ * fromValue('test');
177177+ * ```
178178+ */
92179export function fromValue<T>(value: T): Source<T> {
93180 return sink => {
94181 let ended = false;
···106193 };
107194}
108195109109-export function make<T>(produce: (observer: Observer<T>) => TeardownFn): Source<T> {
196196+/** Creates a new Source from scratch from a passed `subscriber` function.
197197+ * @param subscriber - A callback that is called when the {@link Source} is subscribed to.
198198+ * @returns A {@link Source} created from the `subscriber` parameter.
199199+ *
200200+ * @remarks
201201+ * `make` is used to create a new, arbitrary {@link Source} from scratch. It calls the passed
202202+ * `subscriber` function when it's subscribed to.
203203+ *
204204+ * The `subscriber` function receives an {@link Observer}. You may call {@link Observer.next} to
205205+ * issue values on the Source, and {@link Observer.complete} to end the Source.
206206+ *
207207+ * Your `subscribr` function must return a {@link TeardownFn | teardown function} which is only
208208+ * called when your source is cancelled — not when you invoke `complete` yourself. As this creates a
209209+ * "cold" source, every time this source is subscribed to, it will invoke the `subscriber` function
210210+ * again and create a new source.
211211+ *
212212+ * @example
213213+ *
214214+ * ```ts
215215+ * make(observer => {
216216+ * const frame = requestAnimationFrame(() => {
217217+ * observer.next('animate!');
218218+ * });
219219+ * return () => {
220220+ * cancelAnimationFrame(frame);
221221+ * };
222222+ * });
223223+ * ```
224224+ */
225225+export function make<T>(subscriber: (observer: Observer<T>) => TeardownFn): Source<T> {
110226 return sink => {
111227 let ended = false;
112112- const teardown = produce({
228228+ const teardown = subscriber({
113229 next(value: T) {
114230 if (!ended) sink(push(value));
115231 },
···131247 };
132248}
133249250250+/** Creates a new Subject which can be used as an IO event hub.
251251+ * @returns A new {@link Subject}.
252252+ *
253253+ * @remarks
254254+ * `makeSubject` creates a new {@link Subject}. A Subject is a {@link Source} and an {@link Observer}
255255+ * combined in one interface, as the Observer is used to send new signals to the Source. This means
256256+ * that it's "hot" and hence all subscriptions to {@link Subject.source} share the same underlying
257257+ * signals coming from {@link Subject.next} and {@link Subject.complete}.
258258+ *
259259+ * @example
260260+ * ```ts
261261+ * const subject = makeSubject();
262262+ * pipe(subject.source, subscribe(console.log));
263263+ * // This will log the string on the above subscription
264264+ * subject.next('hello subject!');
265265+ * ```
266266+ */
134267export function makeSubject<T>(): Subject<T> {
135268 let next: Subject<T>['next'] | void;
136269 let complete: Subject<T>['complete'] | void;
···151284 };
152285}
153286287287+/** A {@link Source} that immediately ends.
288288+ * @remarks
289289+ * `empty` is a {@link Source} that immediately issues an {@link SignalKind.End | End signal} when
290290+ * it's subscribed to, ending immediately.
291291+ *
292292+ * @see {@link never | `never`} for a source that instead never ends.
293293+ */
154294export const empty: Source<any> = (sink: Sink<any>): void => {
155295 let ended = false;
156296 sink(
···165305 );
166306};
167307308308+/** A {@link Source} without values that never ends.
309309+ * @remarks
310310+ * `never` is a {@link Source} that never issues any signals and neither sends values nor ends.
311311+ *
312312+ * @see {@link empty | `empty`} for a source that instead ends immediately.
313313+ */
168314export const never: Source<any> = (sink: Sink<any>): void => {
169315 sink(start(talkbackPlaceholder));
170316};
171317318318+/** Creates a Source that issues an incrementing integer in intervals.
319319+ * @param ms - The interval in milliseconds.
320320+ * @returns A {@link Source} issuing an incrementing count on each interval.
321321+ *
322322+ * @remarks
323323+ * `interval` will create a {@link Source} that issues an incrementing counter each time the `ms`
324324+ * interval expires.
325325+ *
326326+ * It'll only stop when it's cancelled by a {@link TalkbackKind.Close | Close signal}.
327327+ *
328328+ * @example
329329+ * An example printing `0`, then `1`, and so on, in intervals of 50ms.
330330+ *
331331+ * ```ts
332332+ * pipe(interval(50), subscribe(console.log));
333333+ * ```
334334+ */
172335export function interval(ms: number): Source<number> {
173336 return make(observer => {
174337 let i = 0;
···177340 });
178341}
179342343343+/** Converts DOM Events to a Source given an `HTMLElement` and an event's name.
344344+ * @param element - The {@link HTMLElement} to listen to.
345345+ * @param event - The DOM Event name to listen to.
346346+ * @returns A {@link Source} issuing the {@link Event | DOM Events} as they're issued by the DOM.
347347+ *
348348+ * @remarks
349349+ * `fromDomEvent` will create a {@link Source} that listens to the given element's events and issues
350350+ * them as values on the source. This source will only stop when it's cancelled by a
351351+ * {@link TalkbackKind.Close | Close signal}.
352352+ *
353353+ * @example
354354+ * An example printing `'clicked!'` when the given `#root` element is clicked.
355355+ *
356356+ * ```ts
357357+ * const element = document.getElementById('root');
358358+ * pipe(
359359+ * fromDomEvent(element, 'click'),
360360+ * subscribe(() => console.log('clicked!'))
361361+ * );
362362+ * ```
363363+ */
180364export function fromDomEvent(element: HTMLElement, event: string): Source<Event> {
181365 return make(observer => {
182366 element.addEventListener(event, observer.next);
···184368 });
185369}
186370371371+/** Converts a Promise to a Source that issues the resolving Promise's value and then ends.
372372+ * @param promise - The promise that will be wrapped.
373373+ * @returns A {@link Source} issuing the promise's value when it resolves.
374374+ *
375375+ * @remarks
376376+ * `fromPromise` will create a {@link Source} that issues the {@link Promise}'s resolving value
377377+ * asynchronously and ends immediately after resolving.
378378+ *
379379+ * This helper will not handle the promise's exceptions, and will cause uncaught errors if the
380380+ * promise rejects without a value.
381381+ *
382382+ * @example
383383+ * An example printing `'resolved!'` when the given promise resolves after a tick.
384384+ *
385385+ * ```ts
386386+ * pipe(fromPromise(Promise.resolve('resolved!')), subscribe(console.log));
387387+ * ```
388388+ */
187389export function fromPromise<T>(promise: Promise<T>): Source<T> {
188390 return make(observer => {
189391 promise.then(value => {
+161-9
src/types.ts
···11-/** A talkback signal is used to tell a [Source] that either the [Sink] is ready for new values or that the stream should be cancelled */
11+/**
22+ * Talkback signal that sends instructions from a sink to a source.
33+ *
44+ * @remarks
55+ * This signal is issued via {@link TalkbackFn | talkback functions} that a {@link Sink} receives via
66+ * the {@link Start} signal, to tell a {@link Source} to either send a new value (pulling) or stop
77+ * sending values altogether (cancellation).
88+ */
29export const enum TalkbackKind {
1010+ /** Instructs the {@link Source} to send the next value. */
311 Pull = 0,
1212+ /** Instructs the {@link Source} to stop sending values and cancels it. */
413 Close = 1,
514}
61577-/** A talkback callback is sent to the sink with the [Start] signal to communicate signals back to the source. */
1616+/**
1717+ * Talkback callback that sends instructions to a source.
1818+ *
1919+ * @remarks
2020+ * This function sends a {@link TalkbackKind} signal to the source to instruct it to send a new value
2121+ * (pulling) or to be cancelled and stop sending values altogether.
2222+ */
823export type TalkbackFn = (signal: TalkbackKind) => void;
2424+2525+/**
2626+ * Callback that is called when a source is cancelled.
2727+ *
2828+ * @remarks
2929+ * This is used, in particular, in the {@link make | make Source} and is a returned function that is
3030+ * called when the {@link TalkbackKind.Close} signal is received by the source.
3131+ */
932export type TeardownFn = () => void;
10333434+/**
3535+ * Tag enum that is used to on signals that are sent from a source to a sink.
3636+ *
3737+ * @remarks
3838+ * This signal is issued by a {@link Source} and {@link Sink | Sinks} are called with it. The signals
3939+ * carrying values ({@link Start} and {@link Push}) are sent as a unary `[T]` tuple tagged with
4040+ * {@link Tag}. The {@link End} signal carries no value and is sent as a raw `0` value.
4141+ * @see {@link Start} for the data structure of the start signal.
4242+ * @see {@link Push} for the data structure of the push signal, carrying values.
4343+ */
1144export const enum SignalKind {
4545+ /**
4646+ * Informs the {@link Sink} that it's being called by a {@link Source}.
4747+ *
4848+ * @remarks
4949+ * This starts the stream of values and carries a {@link TalkbackFn | talkback function} with it
5050+ * that is used by the {@link Sink} to communicate back to the {@link Source}.
5151+ * @see {@link Start} for the data structure of the signal.
5252+ */
1253 Start = 0,
5454+ /**
5555+ * Informs the {@link Sink} of a new values that's incoming from the {@link Source}.
5656+ *
5757+ * @remarks
5858+ * This informs the {@link Sink} of new values that are sent by the {@link Source}.
5959+ * @see {@link Push} for the data structure of the signal.
6060+ */
1361 Push = 1,
6262+ /**
6363+ * Informs the {@link Sink} that the {@link Source} has ended and that it won't send more values.
6464+ *
6565+ * @remarks
6666+ * This signal signifies that the stream has stopped and that no more values are expected. Some
6767+ * sources don't have a set end or limit on how many values will be sent. This signal is not sent
6868+ * when the {@link Source} is cancelled with a {@link TalkbackKind.Close | Close talkback signal}.
6969+ */
1470 End = 0,
1571}
16727373+/**
7474+ * The tag property that's put on unary `[T]` tuple to turn them into signals carrying values.
7575+ *
7676+ * @internal
7777+ */
1778export interface Tag<T> {
1879 tag: T;
1980}
20812121-/** The start [Signal] is the first signal and carries a callback (talkback) so the sink can send signals to the source */
8282+/**
8383+ * Indicates the start of a stream to a {@link Sink}.
8484+ *
8585+ * @remarks
8686+ * This signal is sent from a {@link Source} to a {@link Sink} at the start of a stream to inform it
8787+ * that values can be pulled and/or will be sent. This signal carries a
8888+ * {@link TalkbackFn | talkback function} that is used by the {@link Sink} to communicate back to the
8989+ * {@link Source} as a callback. The talkback accepts {@link TalkbackKind.Pull | Pull} and
9090+ * {@link TalkbackKind.Close | Close} signals.
9191+ */
2292export type Start<_T> = Tag<SignalKind.Start> & [TalkbackFn];
2323-/** The Push [Signal] carries new values to the sink, like in an event emitter */
9393+9494+/**
9595+ * Sends a new value to a {@link Sink}.
9696+ *
9797+ * @remarks
9898+ * This signal is sent from a {@link Source} to a {@link Sink} to send a new value to it. This is
9999+ * essentially the signal that wraps new values coming in, like an event. Values are carried on
100100+ * unary tuples and can be accessed using `signal[0]`.
101101+ */
24102export type Push<T> = Tag<SignalKind.Push> & [T];
251032626-/** A signal that communicates new events to a sink. */
104104+/**
105105+ * Signals are sent from {@link Source | Sources} to {@link Sink | Sinks} to inform them of changes.
106106+ *
107107+ * @remarks
108108+ * A {@link Source}, when consumed, sends a sequence of events to {@link Sink | Sinks}. In order, a
109109+ * {@link SignalKind.Start | Start} signal will always be sent first, followed optionally by one or
110110+ * more {@link SignalKind.Push | Push signals}, carrying values and representing the stream. A
111111+ * {@link Source} will send the {@link SignalKind.End | End signal} when it runs out of values. The
112112+ * End signal will be omitted if the Source is cancelled by a
113113+ * {@link TalkbackKind.Close | Close signal}, sent back from the {@link Sink}.
114114+ * @see {@link SignalKind} for the kinds signals sent by {@link Source | Sources}.
115115+ * @see {@link Start} for the data structure of the start signal.
116116+ * @see {@link Push} for the data structure of the push signal.
117117+ */
27118export type Signal<T> = Start<T> | Push<T> | SignalKind.End;
281192929-/** A sink accepts new values from a [Source], like [Push], [Start], and an end signal. The [Start] is used to receive a callback to send talkback signals back to the source. */
120120+/**
121121+ * Callback function that is called by a {@link Source} with {@link Signal | Signals}.
122122+ *
123123+ * @remarks
124124+ * A Sink is a function that is called repeatedly with signals from a {@link Source}. It represents
125125+ * the receiver of the stream of signals/events coming from a {@link Source}.
126126+ * @see {@link Signal} for the data structure of signals.
127127+ */
30128export type Sink<T> = (signal: Signal<T>) => void;
3131-/** A source is a function that accepts a [Sink] and then starts sending [Signal]s to it. */
129129+130130+/** Factory function that calls {@link Sink | Sinks} with {@link Signal | Signals} when invoked.
131131+ * @remarks
132132+ * A Source is a factory function that when invoked with a {@link Sink}, calls it with
133133+ * {@link Signal | Signals} to create a stream of events, informing it of new values and the
134134+ * potential end of the stream of values. The first signal a Source sends is always a
135135+ * {@link Start | Start signal} that sends a talkback function to the {@link Sink}, so it may request
136136+ * new values or cancel the source.
137137+ *
138138+ * @see {@link Signal} for the data structure of signals.
139139+ * @see {@link Sink} for the data structure of sinks.
140140+ */
32141export type Source<T> = (sink: Sink<T>) => void;
3333-/** An operator transforms a [Source] and returns a new [Source], potentially with different timings or output types. */
142142+143143+/** Transform function that accepts a {@link Source} and returns a new one.
144144+ * @remarks
145145+ * Wonka comes with several helper operators that transform a given {@link Source} into a new one,
146146+ * potentially changing its outputs, or the outputs' timing. An "operator" in Wonka typically
147147+ * accepts arguments and then returns this kind of function, so they can be chained and composed.
148148+ *
149149+ * @see {@link pipe | `pipe`} for the helper used to compose operators.
150150+ */
34151export type Operator<In, Out> = (a: Source<In>) => Source<Out>;
351523636-/** Extracts the type of a given Source */
153153+/** Type utility to determine the type of a {@link Source}. */
37154export type TypeOfSource<T> = T extends Source<infer U> ? U : never;
38155156156+/** Subscription object that can be used to cancel a {@link Source}.
157157+ * @see {@link subscribe | subscribe sink} for a helper that returns this structure.
158158+ */
39159export interface Subscription {
160160+ /**
161161+ * Cancels a {@link Source} to stop the subscription from receiving new values.
162162+ *
163163+ * @see {@link TalkbackKind.Close | Close signal} This uses the {@link TalkbackFn | talkback function} to send a {@link TalkbackKind.Close | Close signal}
164164+ * to the subscribed-to {@link Source} to stop it from sending new values. This cleans up the subscription
165165+ * and ends it immediately.
166166+ */
40167 unsubscribe(): void;
41168}
42169170170+/** An Observer represents sending signals manually to a {@link Sink}.
171171+ * @remarks
172172+ * The Observer is used whenever a utility allows for signals to be sent manually as a {@link Source}
173173+ * would send them.
174174+ *
175175+ * @see {@link make | `make` source} for a helper that uses this structure.
176176+ */
43177export interface Observer<T> {
178178+ /** Sends a new value to the receiving Sink.
179179+ * @remarks
180180+ * This creates a {@link Push | Push signal} that is sent to a {@link Sink}.
181181+ */
44182 next(value: T): void;
183183+ /** Indicates to the receiving Sink that no more values will be sent.
184184+ * @remarks
185185+ * This creates an {@link SignalKind.End | End signal} that is sent to a {@link Sink}. The Observer
186186+ * will accept no more values via {@link Observer.next | `next` calls} once this method has been
187187+ * invoked.
188188+ */
45189 complete(): void;
46190}
47191192192+/** Subjects combine a {@link Source} with the {@link Observer} that is used to send values on said Source.
193193+ * @remarks
194194+ * A Subject is used whenever an event hub-like structure is needed, as it both provides the
195195+ * {@link Observer}'s methods to send signals, as well as the `source` to receive said signals.
196196+ *
197197+ * @see {@link makeSubject | `makeSubject` source} for a helper that creates this structure.
198198+ */
48199export interface Subject<T> extends Observer<T> {
200200+ /** The {@link Source} that issues the signals as the {@link Observer} methods are called. */
49201 source: Source<T>;
50202}