Mirror: 🎩 A tiny but capable push & pull stream library for TypeScript and Flow

feat: Add TSDocs to all internal and external utilities and types (#136)

authored by kitten.sh and committed by GitHub 2cfe3e6a 9c062ac3

+5
.changeset/violet-suits-yawn.md
··· 1 + --- 2 + 'wonka': minor 3 + --- 4 + 5 + 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.
+3
.editorconfig
··· 7 7 indent_style = space 8 8 insert_final_newline = true 9 9 trim_trailing_whitespace = true 10 + 11 + [*.ts] 12 + max_line_length = 100
+1
package.json
··· 87 87 "eslint": "^8.29.0", 88 88 "eslint-config-prettier": "^8.5.0", 89 89 "eslint-plugin-prettier": "^4.2.1", 90 + "eslint-plugin-tsdoc": "^0.2.17", 90 91 "flowgen": "^1.21.0", 91 92 "glob": "^8.0.3", 92 93 "husky-v4": "^4.3.8",
+33
pnpm-lock.yaml
··· 18 18 eslint: ^8.29.0 19 19 eslint-config-prettier: ^8.5.0 20 20 eslint-plugin-prettier: ^4.2.1 21 + eslint-plugin-tsdoc: ^0.2.17 21 22 flowgen: ^1.21.0 22 23 glob: ^8.0.3 23 24 husky-v4: ^4.3.8 ··· 51 52 eslint: 8.29.0 52 53 eslint-config-prettier: 8.5.0_eslint@8.29.0 53 54 eslint-plugin-prettier: 4.2.1_nrhoyyjffvfyk4vtlt5destxgm 55 + eslint-plugin-tsdoc: 0.2.17 54 56 flowgen: 1.21.0 55 57 glob: 8.0.3 56 58 husky-v4: 4.3.8 ··· 398 400 fs-extra: 8.1.0 399 401 globby: 11.1.0 400 402 read-yaml-file: 1.1.0 403 + dev: true 404 + 405 + /@microsoft/tsdoc-config/0.16.2: 406 + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} 407 + dependencies: 408 + '@microsoft/tsdoc': 0.14.2 409 + ajv: 6.12.6 410 + jju: 1.4.0 411 + resolve: 1.19.0 412 + dev: true 413 + 414 + /@microsoft/tsdoc/0.14.2: 415 + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} 401 416 dev: true 402 417 403 418 /@nodelib/fs.scandir/2.1.5: ··· 1571 1586 prettier-linter-helpers: 1.0.0 1572 1587 dev: true 1573 1588 1589 + /eslint-plugin-tsdoc/0.2.17: 1590 + resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} 1591 + dependencies: 1592 + '@microsoft/tsdoc': 0.14.2 1593 + '@microsoft/tsdoc-config': 0.16.2 1594 + dev: true 1595 + 1574 1596 /eslint-scope/5.1.1: 1575 1597 resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} 1576 1598 engines: {node: '>=8.0.0'} ··· 2267 2289 2268 2290 /isexe/2.0.0: 2269 2291 resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 2292 + dev: true 2293 + 2294 + /jju/1.4.0: 2295 + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} 2270 2296 dev: true 2271 2297 2272 2298 /js-sdsl/4.2.0: ··· 3070 3096 /resolve-from/5.0.0: 3071 3097 resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 3072 3098 engines: {node: '>=8'} 3099 + dev: true 3100 + 3101 + /resolve/1.19.0: 3102 + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} 3103 + dependencies: 3104 + is-core-module: 2.11.0 3105 + path-parse: 1.0.7 3073 3106 dev: true 3074 3107 3075 3108 /resolve/1.22.1:
+2 -1
scripts/eslint-preset.js
··· 7 7 }, 8 8 }, 9 9 extends: ['prettier'], 10 - plugins: ['prettier'], 10 + plugins: ['prettier', 'tsdoc'], 11 11 ignorePatterns: ['node_modules/', 'dist/', 'coverage/', 'perf/'], 12 12 rules: { 13 13 'sort-keys': 'off', ··· 47 47 '@typescript-eslint/no-empty-function': 'off', 48 48 '@typescript-eslint/no-unused-vars': 'off', 49 49 'prefer-rest-params': 'off', 50 + 'tsdoc/syntax': 'error', 50 51 }, 51 52 }, 52 53 ],
+3 -1
scripts/flow-typings-plugin.mjs
··· 8 8 // NOTE: Computed property names will be omitted 9 9 code = code.replace(/\[Symbol\.\w+\][?()]*:(?:.*);\n?/g, ''); 10 10 11 - let flowdef = compiler.compileDefinitionString(code); 11 + let flowdef = compiler.compileDefinitionString(code, { 12 + jsdoc: false, 13 + }); 12 14 13 15 flowdef = beautify(flowdef); 14 16 flowdef = flowdef.replace(/import/g, 'import type');
+20
src/callbag.ts
··· 1 1 import { Source, SignalKind } from './types'; 2 2 import { push, start } from './helpers'; 3 3 4 + /** A definition of the Callbag type as per its specification. 5 + * @see {@link https://github.com/callbag/callbag} for the Callbag specification. 6 + */ 4 7 interface Callbag<I, O> { 5 8 (t: 0, d: Callbag<O, I>): void; 6 9 (t: 1, d: I): void; 7 10 (t: 2, d?: any): void; 8 11 } 9 12 13 + /** Converts a Callbag to a {@link Source}. 14 + * @param callbag - The {@link Callbag} object that will be converted. 15 + * @returns A {@link Source} wrapping the passed Callbag. 16 + * 17 + * @remarks 18 + * This converts a Callbag to a {@link Source}. When this Source receives a {@link Sink} and 19 + * the subscription starts, internally, it'll subscribe to the passed Callbag, passing through 20 + * all of its emitted values. 21 + */ 10 22 export function fromCallbag<T>(callbag: Callbag<any, T>): Source<T> { 11 23 return sink => { 12 24 callbag(0, (signal: number, data: any) => { ··· 25 37 }; 26 38 } 27 39 40 + /** Converts a {@link Source} to a Callbag. 41 + * @param source - The {@link Source} that will be converted. 42 + * @returns A {@link Callbag} wrapping the passed Source. 43 + * 44 + * @remarks 45 + * This converts a {@link Source} to a {@link Callbag}. When this Callbag is subscribed to, it 46 + * internally subscribes to the Wonka Source and pulls new values. 47 + */ 28 48 export function toCallbag<T>(source: Source<T>): Callbag<any, T> { 29 49 return (signal: number, sink: any) => { 30 50 if (signal === 0) {
+64 -9
src/combine.ts
··· 5 5 ? [TypeOfSource<Head>, ...TypeOfSourceArray<Tail>] 6 6 : []; 7 7 8 - export function zip<Sources extends readonly [...Source<any>[]]>( 9 - sources: [...Sources] 10 - ): Source<TypeOfSourceArray<Sources>>; 8 + /** Combines the latest values of several sources into a Source issuing either tuple or dictionary 9 + * values. 10 + * 11 + * @param sources - Either an array or dictionary object of Sources. 12 + * @returns A {@link Source} issuing a zipped value whenever any input Source updates. 13 + * 14 + * @remarks 15 + * `zip` combines several {@link Source | Sources}. The resulting Source will issue its first value 16 + * once all input Sources have at least issued one value, and will subsequently issue a new value 17 + * each time any of the Sources emits a new value. 18 + * 19 + * Depending on whether an array or dictionary object of Sources is passed to `zip`, its emitted 20 + * values will be arrays or dictionary objects of the Sources' values. 21 + * 22 + * @example 23 + * An example of passing a dictionary object to `zip`. If an array is passed, the resulting 24 + * values will output arrays of the sources' values instead. 25 + * 26 + * ```ts 27 + * pipe( 28 + * zip({ 29 + * x: fromValue(1), 30 + * y: fromArray([2, 3]), 31 + * }), 32 + * subscribe(result => { 33 + * // logs { x: 1, y: 2 } then { x: 1, y: 3 } 34 + * console.log(result); 35 + * }) 36 + * ); 37 + * ``` 38 + */ 39 + interface zip { 40 + <Sources extends readonly [...Source<any>[]]>(sources: [...Sources]): Source< 41 + TypeOfSourceArray<Sources> 42 + >; 11 43 12 - export function zip<Sources extends { [prop: string]: Source<any> }>( 13 - sources: Sources 14 - ): Source<{ [Property in keyof Sources]: TypeOfSource<Sources[Property]> }>; 44 + <Sources extends { [prop: string]: Source<any> }>(sources: Sources): Source<{ 45 + [Property in keyof Sources]: TypeOfSource<Sources[Property]>; 46 + }>; 47 + } 15 48 16 - export function zip<T>( 17 - sources: Source<T>[] | Record<string, Source<T>> 18 - ): Source<T[] | Record<string, T>> { 49 + function zip<T>(sources: Source<T>[] | Record<string, Source<T>>): Source<T[] | Record<string, T>> { 19 50 const size = Object.keys(sources).length; 20 51 return sink => { 21 52 const filled: Set<string | number> = new Set(); ··· 75 106 }; 76 107 } 77 108 109 + export { zip }; 110 + 111 + /** Combines the latest values of all passed sources into a Source issuing tuple values. 112 + * 113 + * @see {@link zip | `zip`} which this helper wraps and uses. 114 + * @param sources - A variadic list of {@link Source} parameters. 115 + * @returns A {@link Source} issuing a zipped value whenever any input Source updates. 116 + * 117 + * @remarks 118 + * `combine` takes one or more {@link Source | Sources} as arguments. Once all input Sources have at 119 + * least issued one value it will issue an array of all of the Sources' values. Subsequently, it 120 + * will issue a new array value whenever any of the Sources update. 121 + * 122 + * @example 123 + * 124 + * ```ts 125 + * pipe( 126 + * combine(fromValue(1), fromValue(2)), 127 + * subscribe(result => { 128 + * console.log(result); // logs [1, 2] 129 + * }) 130 + * ); 131 + * ``` 132 + */ 78 133 export function combine<Sources extends Source<any>[]>( 79 134 ...sources: Sources 80 135 ): Source<TypeOfSourceArray<Sources>> {
+21
src/helpers.ts
··· 1 1 import { TalkbackFn, TeardownFn, Start, Push, SignalKind } from './types'; 2 2 3 + /** Placeholder {@link TeardownFn | teardown functions} that's a no-op. 4 + * @see {@link TeardownFn} for the definition and usage of teardowns. 5 + * @internal 6 + */ 3 7 export const teardownPlaceholder: TeardownFn = () => { 4 8 /*noop*/ 5 9 }; 10 + 11 + /** Placeholder {@link TalkbackFn | talkback function} that's a no-op. 12 + * @privateRemarks 13 + * This is frequently used in the codebase as a no-op initializer value for talkback functions in 14 + * the implementation of {@link Operator | Operators}. This is cheaper than initializing the 15 + * variables of talkbacks to `undefined` or `null` and performing an extra check before calling 16 + * them. Since the {@link Start | Start signal} is assumed to come first and carry a talkback, we can 17 + * use this to our advantage and use a no-op placeholder before {@link Start} is received. 18 + * 19 + * @internal 20 + */ 6 21 export const talkbackPlaceholder: TalkbackFn = teardownPlaceholder; 7 22 23 + /** Wraps the passed {@link TalkbackFn | talkback function} in a {@link Start | Start signal}. 24 + * @internal 25 + */ 8 26 export function start<T>(talkback: TalkbackFn): Start<T> { 9 27 const box: any = [talkback]; 10 28 box.tag = SignalKind.Start; 11 29 return box; 12 30 } 13 31 32 + /** Wraps the passed value in a {@link Push | Push signal}. 33 + * @internal 34 + */ 14 35 export function push<T>(value: T): Push<T> { 15 36 const box: any = [value]; 16 37 box.tag = SignalKind.Push;
+12
src/index.ts
··· 1 + /** 2 + * A tiny but capable push & pull stream library for TypeScript and Flow. 3 + * 4 + * @remarks 5 + * Wonka is a lightweight iterable and observable library and exposes a set of helpers to create 6 + * streams, which are sources emitting multiple values, which allow you to create, transform, and 7 + * consume event streams or iterable sets of data. 8 + * 9 + * It's loosely based on the Callbag spec: {@link https://github.com/callbag/callbag} 10 + * @packageDocumentation 11 + */ 12 + 1 13 export * from './types'; 2 14 export * from './sources'; 3 15 export * from './operators';
+121 -2
src/observable.ts
··· 1 1 import { Source, SignalKind, TalkbackKind } from './types'; 2 2 import { push, start, talkbackPlaceholder } from './helpers'; 3 3 4 + /** A definition of the ES Observable Subscription type that is returned by 5 + * {@link Observable.subscribe} 6 + * 7 + * @remarks 8 + * The Subscription in ES Observables is a handle that is held while the Observable is actively 9 + * streaming values. As such, it's used to indicate with {@link ObservableSubscription.closed} 10 + * whether it's active, and {@link ObservableSubscription.unsubscribe} may be used to cancel the 11 + * ongoing subscription and end the {@link Observable} early. 12 + * 13 + * @see {@link https://github.com/tc39/proposal-observable} for the ES Observable specification. 14 + */ 4 15 interface ObservableSubscription { 16 + /** A boolean flag indicating whether the subscription is closed. 17 + * @remarks 18 + * When `true`, the subscription will not issue new values to the {@link ObservableObserver} and 19 + * has terminated. No new values are expected. 20 + * 21 + * @readonly 22 + */ 5 23 closed?: boolean; 24 + /** Cancels the subscription. 25 + * @remarks 26 + * This cancels the ongoing subscription and the {@link ObservableObserver}'s callbacks will 27 + * subsequently not be called at all. The subscription will be terminated and become inactive. 28 + */ 6 29 unsubscribe(): void; 7 30 } 8 31 32 + /** A definition of the ES Observable Observer type that is used to receive data from an 33 + * {@link Observable}. 34 + * 35 + * @remarks 36 + * The Observer in ES Observables is supplied to {@link Observable.subscribe} to receive events from 37 + * an {@link Observable} as it issues them. 38 + * 39 + * @see {@link https://github.com/tc39/proposal-observable#observer} for the ES Observable 40 + * specification of an Observer. 41 + */ 9 42 interface ObservableObserver<T> { 43 + /** Callback for the Observable issuing new values. 44 + * @param value - The value that the {@link Observable} is sending. 45 + */ 10 46 next(value: T): void; 47 + /** Callback for the Observable encountering an error, terminating it. 48 + * @param error - The error that the {@link Observable} has encountered. 49 + */ 11 50 error?(error: any): void; 51 + /** Callback for the Observable ending, after all values have been issued. */ 12 52 complete?(): void; 13 53 } 14 54 55 + /** A looser definition of ES Observable-like types that is used for interoperability. 56 + * @remarks 57 + * The Observable is often used by multiple libraries supporting or creating streams to provide 58 + * interoperability for push-based streams. When converting from an Observable to a {@link Source}, 59 + * this looser type is accepted as an input. 60 + * 61 + * @see {@link https://github.com/tc39/proposal-observable} for the ES Observable specification. 62 + * @see {@link Observable} for the full ES Observable type. 63 + */ 15 64 interface ObservableLike<T> { 65 + /** 66 + * Subscribes to new signals from an {@link Observable} via callbacks. 67 + * @param observer - An object containing callbacks for the various events of an Observable. 68 + * @returns Subscription handle of type {@link ObservableSubscription}. 69 + * 70 + * @see {@link ObservableObserver} for the callbacks in an object that are called as Observables 71 + * issue events. 72 + */ 16 73 subscribe(observer: ObservableObserver<T>): ObservableSubscription; 74 + 75 + /** The well-known symbol specifying the default ES Observable for an object. */ 17 76 [Symbol.observable]?(): Observable<T>; 18 77 } 19 78 79 + /** An ES Observable type that is a de-facto standard for push-based data sources across the JS 80 + * ecosystem. 81 + * 82 + * @remarks 83 + * The Observable is often used by multiple libraries supporting or creating streams to provide 84 + * interoperability for push-based streams. As Wonka's {@link Source | Sources} are similar in 85 + * functionality to Observables, it provides utilities to cleanly convert to and from Observables. 86 + * 87 + * @see {@link https://github.com/tc39/proposal-observable} for the ES Observable specification. 88 + */ 20 89 interface Observable<T> { 90 + /** Subscribes to new signals from an {@link Observable} via callbacks. 91 + * @param observer - An object containing callbacks for the various events of an Observable. 92 + * @returns Subscription handle of type {@link ObservableSubscription}. 93 + * 94 + * @see {@link ObservableObserver} for the callbacks in an object that are called as Observables 95 + * issue events. 96 + */ 21 97 subscribe(observer: ObservableObserver<T>): ObservableSubscription; 22 98 99 + /** Subscribes to new signals from an {@link Observable} via callbacks. 100 + * @param onNext - Callback for the Observable issuing new values. 101 + * @param onError - Callback for the Observable encountering an error, terminating it. 102 + * @param onComplete - Callback for the Observable ending, after all values have been issued. 103 + * @returns Subscription handle of type {@link ObservableSubscription}. 104 + */ 23 105 subscribe( 24 106 onNext: (value: T) => any, 25 107 onError?: (error: any) => any, 26 108 onComplete?: () => any 27 109 ): ObservableSubscription; 28 110 111 + /** The well-known symbol specifying the default ES Observable for an object. */ 29 112 [Symbol.observable](): Observable<T>; 30 113 } 31 114 115 + /** Returns the well-known symbol specifying the default ES Observable. 116 + * @privateRemarks 117 + * This symbol is used to mark an object as a default ES Observable. By the specification, an object 118 + * that abides by the default Observable implementation must carry a method set to this well-known 119 + * symbol that returns the Observable implementation. It's common for this object to be an 120 + * Observable itself and return itself on this method. 121 + * 122 + * @see {@link https://github.com/0no-co/wonka/issues/122} for notes on the intercompatibility 123 + * between Observable implementations. 124 + * 125 + * @internal 126 + */ 32 127 const observableSymbol = (): typeof Symbol.observable => Symbol.observable || '@@observable'; 33 128 129 + /** Converts an ES Observable to a {@link Source}. 130 + * @param input - The {@link ObservableLike} object that will be converted. 131 + * @returns A {@link Source} wrapping the passed Observable. 132 + * 133 + * @remarks 134 + * This converts an ES Observable to a {@link Source}. When this Source receives a {@link Sink} and 135 + * the subscription starts, internally, it'll subscribe to the passed Observable, passing through 136 + * all of the Observable's values. As such, this utility provides intercompatibility converting from 137 + * standard Observables to Wonka Sources. 138 + * 139 + * @throws 140 + * When the passed ES Observable throws, the error is simply re-thrown as {@link Source} does 141 + * not support or expect errors to be handled by streams. 142 + */ 34 143 export function fromObservable<T>(input: ObservableLike<T>): Source<T> { 35 - input = input[observableSymbol()] ? (input as any)[observableSymbol()]() : input; 36 144 return sink => { 37 - const subscription = input.subscribe({ 145 + const subscription = ( 146 + input[observableSymbol()] ? input[observableSymbol()]!() : input 147 + ).subscribe({ 38 148 next(value: T) { 39 149 sink(push(value)); 40 150 }, ··· 53 163 }; 54 164 } 55 165 166 + /** Converts a {@link Source} to an ES Observable. 167 + * @param source - The {@link Source} that will be converted. 168 + * @returns An {@link Observable} wrapping the passed Source. 169 + * 170 + * @remarks 171 + * This converts a {@link Source} to an {@link Observable}. When this Observable is subscribed to, it 172 + * internally subscribes to the Wonka Source and pulls new values. As such, this utility provides 173 + * intercompatibility converting from Wonka Sources to standard ES Observables. 174 + */ 56 175 export function toObservable<T>(source: Source<T>): Observable<T> { 57 176 return { 58 177 subscribe(
+599
src/operators.ts
··· 4 4 5 5 const identity = <T>(x: T): T => x; 6 6 7 + /** Buffers values and emits the array of bufferd values each time a `notifier` Source emits. 8 + * 9 + * @param notifier - A {@link Source} that releases the current buffer. 10 + * @returns An {@link Operator}. 11 + * 12 + * @remarks 13 + * `buffer` will buffer values from the input {@link Source}. When the passed `notifier` Source 14 + * emits, it will emit an array of all buffered values. 15 + * 16 + * This can be used to group values over time. A buffer will only be emitted when it contains any 17 + * values. 18 + * 19 + * @example 20 + * ```ts 21 + * pipe( 22 + * interval(50), 23 + * buffer(interval(100)), 24 + * subscribe(x => { 25 + * console.log(text); // logs: [0], [1, 2], [3, 4]... 26 + * }) 27 + * ); 28 + * ``` 29 + */ 7 30 export function buffer<S, T>(notifier: Source<S>): Operator<T, T[]> { 8 31 return source => sink => { 9 32 let buffer: T[] = []; ··· 64 87 }; 65 88 } 66 89 90 + /** Emits in order from the Sources returned by a mapping function per value of the Source. 91 + * 92 + * @param map - A function returning a {@link Source} per value. 93 + * @returns An {@link Operator}. 94 + * 95 + * @remarks 96 + * `concatMap` accepts a mapping function which must return a {@link Source} per value. 97 + * The output {@link Source} will emit values from each Source the function returned, in order, 98 + * queuing sources that aren't yet active. 99 + * 100 + * This can be used to issue multiple values per emission of an input {@link Source}, while keeping 101 + * the order of their outputs consistent. 102 + * 103 + * @example 104 + * ```ts 105 + * pipe( 106 + * fromArray([1, 2]), 107 + * concatMap(x => fromArray([x, x * 2])), 108 + * subscribe(x => { 109 + * console.log(text); // logs: 1, 2, 2, 4 110 + * }) 111 + * ); 112 + * ``` 113 + */ 67 114 export function concatMap<In, Out>(map: (value: In) => Source<Out>): Operator<In, Out> { 68 115 return source => sink => { 69 116 const inputQueue: In[] = []; ··· 144 191 }; 145 192 } 146 193 194 + /** Flattens a Source emitting Sources into a single Source emitting the inner values in order. 195 + * 196 + * @see {@link concatMap} which this helper uses and instead accept a mapping function. 197 + * @param source - An {@link Source} emitting {@link Source | Sources}. 198 + * @returns A {@link Source} emitting values from the inner Sources. 199 + * 200 + * @remarks 201 + * `concatAll` accepts a {@link Source} emitting {@link Source | Sources}. 202 + * The output {@link Source} will emit values from each Source, in order, queuing sources that 203 + * aren't yet active. 204 + * 205 + * @example 206 + * ```ts 207 + * pipe( 208 + * fromArray([ 209 + * fromArray([1, 2]), 210 + * fromArray([3, 4]), 211 + * ]), 212 + * concatAll, 213 + * subscribe(x => { 214 + * console.log(text); // logs: 1, 2, 3, 4 215 + * }) 216 + * ); 217 + * ``` 218 + */ 147 219 export function concatAll<T>(source: Source<Source<T>>): Source<T> { 148 220 return concatMap<Source<T>, T>(identity)(source); 149 221 } 150 222 223 + /** Emits values from the passed sources in order. 224 + * 225 + * @param sources - An array of {@link Source | Sources}. 226 + * @returns A {@link Source} emitting values from the input Sources. 227 + * 228 + * @remarks 229 + * `concat` accepts an array of {@link Source | Sources} and will emit values from them, starting 230 + * with the first one and continuing to the next only when the prior source ended. 231 + * 232 + * This can be used to issue combine sources while keeping the order of their values intact. 233 + * 234 + * @example 235 + * ```ts 236 + * pipe( 237 + * concat([ 238 + * fromArray([1, 2]), 239 + * fromArray([3, 4]), 240 + * ]), 241 + * subscribe(x => { 242 + * console.log(text); // logs: 1, 2, 3, 4 243 + * }) 244 + * ); 245 + * ``` 246 + */ 151 247 export function concat<T>(sources: Source<T>[]): Source<T> { 152 248 return concatAll(fromArray(sources)); 153 249 } 154 250 251 + /** Filters out emitted values for which the passed predicate function returns `false`. 252 + * 253 + * @param predicate - A function returning a boolean per value. 254 + * @returns An {@link Operator}. 255 + * 256 + * @remarks 257 + * `filter` will omit values from the {@link Source} for which the passed `predicate` function 258 + * returns `false`. 259 + * 260 + * @example 261 + * ```ts 262 + * pipe( 263 + * fromArray([1, 2, 3]), 264 + * filter(x => x % 2 === 0), 265 + * subscribe(x => { 266 + * console.log(text); // logs: 2 267 + * }) 268 + * ); 269 + * ``` 270 + */ 155 271 export function filter<T>(predicate: (value: T) => boolean): Operator<T, T> { 156 272 return source => sink => { 157 273 let talkback = talkbackPlaceholder; ··· 170 286 }; 171 287 } 172 288 289 + /** Maps emitted values using the passed mapping function. 290 + * 291 + * @param map - A function returning transforming the {@link Source | Source's} values. 292 + * @returns An {@link Operator}. 293 + * 294 + * @remarks 295 + * `map` accepts a transform function and calls it on each emitted value. It then emits 296 + * the values returned by the transform function instead. 297 + * 298 + * @example 299 + * ```ts 300 + * pipe( 301 + * fromArray([1, 2, 3]), 302 + * map(x => x * 2), 303 + * subscribe(x => { 304 + * console.log(text); // logs: 2, 4, 6 305 + * }) 306 + * ); 307 + * ``` 308 + */ 173 309 export function map<In, Out>(map: (value: In) => Out): Operator<In, Out> { 174 310 return source => sink => 175 311 source(signal => { ··· 181 317 }); 182 318 } 183 319 320 + /** Emits from the Sources returned by a mapping function per value of the Source. 321 + * 322 + * @param map - A function returning a {@link Source} per value. 323 + * @returns An {@link Operator}. 324 + * 325 + * @remarks 326 + * `mergeMap` accepts a mapping function which must return a {@link Source} per value. 327 + * The output {@link Source} will emit values from all {@link Source | Sources} the mapping function 328 + * returned. 329 + * 330 + * This can be used to issue multiple values per emission of an input {@link Source}, essentially 331 + * multiplexing all values to multiple Sources. 332 + * 333 + * @example 334 + * ```ts 335 + * pipe( 336 + * interval(50), 337 + * mergeMap(x => pipe( 338 + * fromValue(x), 339 + * delay(100) 340 + * )), 341 + * subscribe(x => { 342 + * console.log(text); // logs: 0, 1, 2... 343 + * }) 344 + * ); 345 + * ``` 346 + */ 184 347 export function mergeMap<In, Out>(map: (value: In) => Source<Out>): Operator<In, Out> { 185 348 return source => sink => { 186 349 let innerTalkbacks: TalkbackFn[] = []; ··· 254 417 }; 255 418 } 256 419 420 + /** Flattens a Source emitting Sources into a single Source emitting the inner values. 421 + * 422 + * @see {@link mergeMap} which this helper uses and instead accept a mapping function. 423 + * @param source - An {@link Source} emitting {@link Source | Sources}. 424 + * @returns A {@link Source} emitting values from the inner Sources. 425 + * 426 + * @remarks 427 + * `mergeAll` accepts a {@link Source} which must emit {@link Source | Sources}. It will subscribe 428 + * to each incoming source immediately and start passing its emitted values through. 429 + * 430 + * @example 431 + * ```ts 432 + * pipe( 433 + * fromArray([ 434 + * interval(50), 435 + * interval(100), 436 + * ]), 437 + * mergeAll, 438 + * subscribe(x => { 439 + * console.log(text); // logs: 0, 0, 1, 2, 1, 3, 4, 2 440 + * }) 441 + * ); 442 + * ``` 443 + */ 257 444 export function mergeAll<T>(source: Source<Source<T>>): Source<T> { 258 445 return mergeMap<Source<T>, T>(identity)(source); 259 446 } 260 447 448 + /** Emits values from the passed sources simultaneously. 449 + * 450 + * @param sources - An array of {@link Source | Sources}. 451 + * @returns A {@link Source} emitting values from the input Sources. 452 + * 453 + * @remarks 454 + * `merge` accepts an array of {@link Source | Sources} and will subscribe to all of them, passing 455 + * through all their emitted values simultaneously. 456 + * 457 + * This can be used to interleave the values of multiple sources. 458 + * 459 + * @example 460 + * ```ts 461 + * pipe( 462 + * merge([ 463 + * interval(50), 464 + * interval(100), 465 + * ]), 466 + * subscribe(x => { 467 + * console.log(text); // logs: 0, 0, 1, 2, 1, 3, 4, 2 468 + * }) 469 + * ); 470 + * ``` 471 + */ 261 472 export function merge<T>(sources: Source<T>[]): Source<T> { 262 473 return mergeAll(fromArray(sources)); 263 474 } 264 475 476 + /** Calls the passed callback function when the Source ends or is closed. 477 + * 478 + * @param callback - A function that is called when the {@link Source} ends. 479 + * @returns An {@link Operator}. 480 + * 481 + * @remarks 482 + * `onEnd` accepts a callback which is called when the {@link Source} either ends 483 + * or is closed. 484 + * 485 + * This operator can be used to add side-effects to a Source. 486 + * 487 + * @example 488 + * ```ts 489 + * pipe( 490 + * fromArray([1, 2, 3]), 491 + * take(1), 492 + * onEnd(() => { 493 + * console.log('end'); 494 + * }), 495 + * publish 496 + * ); 497 + * ``` 498 + */ 265 499 export function onEnd<T>(callback: () => void): Operator<T, T> { 266 500 return source => sink => { 267 501 let ended = false; ··· 292 526 }; 293 527 } 294 528 529 + /** Calls the passed callback function when the Source emits a value. 530 + * 531 + * @param callback - A function that is called with each value the {@link Source} emits. 532 + * @returns An {@link Operator}. 533 + * 534 + * @remarks 535 + * `onPush` accepts a callback which is called for every emitted value of 536 + * the {@link Source}. 537 + * 538 + * This operator can be used to add side-effects to a Source. 539 + * 540 + * @example 541 + * ```ts 542 + * pipe( 543 + * fromArray([1, 2, 3]), 544 + * onPush(value => { 545 + * console.log(value); // logs: 1, 2, 3 546 + * }), 547 + * publish 548 + * ); 549 + * ``` 550 + */ 295 551 export function onPush<T>(callback: (value: T) => void): Operator<T, T> { 296 552 return source => sink => { 297 553 let ended = false; ··· 317 573 }; 318 574 } 319 575 576 + /** Calls the passed callback function when the Source starts. 577 + * 578 + * @param callback - A function that is called when the {@link Source} is started. 579 + * @returns An {@link Operator}. 580 + * 581 + * @remarks 582 + * `onPush` accepts a callback which is called for every emitted value of 583 + * the {@link Source}. 584 + * 585 + * This operator can be used to add side-effects to a Source. 586 + * Specifically, it's useful to add a side-effect for a Source that triggers only once 587 + * the {@link Source} is used and started. 588 + * 589 + * @example 590 + * ```ts 591 + * pipe( 592 + * fromArray([1, 2, 3]), 593 + * onStart(() => { 594 + * console.log('start'); 595 + * }), 596 + * publish 597 + * ); 598 + * ``` 599 + */ 320 600 export function onStart<T>(callback: () => void): Operator<T, T> { 321 601 return source => sink => 322 602 source(signal => { ··· 331 611 }); 332 612 } 333 613 614 + /** Emits the last value the {@link Source} emitted, whenever the notifier Source emits a value. 615 + * 616 + * @param notifier - A {@link Source} that triggers the last value to be emitted. 617 + * @returns An {@link Operator}. 618 + * 619 + * @remarks 620 + * `sample` will store the latest value the {@link Source} emitted. Every time the `notifier` Source 621 + * emits, it will emit the latest value. 622 + * 623 + * This is a back pressure operator that can be used to omit values from a {@link Source} coming in 624 + * too frequently. 625 + * 626 + * {@link Source | Sources} emitting `undefined` are undefined behaviour and these values will be 627 + * ignored. 628 + * 629 + * @example 630 + * ```ts 631 + * pipe( 632 + * interval(50), 633 + * sample(interval(100)), 634 + * subscribe(x => { 635 + * console.log(text); // logs: 0, 2, 4... 636 + * }) 637 + * ); 638 + * ``` 639 + */ 334 640 export function sample<S, T>(notifier: Source<S>): Operator<T, T> { 335 641 return source => sink => { 336 642 let sourceTalkback = talkbackPlaceholder; ··· 389 695 }; 390 696 } 391 697 698 + /** Maps emitted values using the passed reducer function. 699 + * 700 + * @param reducer - A function called with the last value by the `reducer` and the emitted value. 701 + * @param seed - The initial value that is passed to the `reducer`. 702 + * @returns An {@link Operator}. 703 + * 704 + * @remarks 705 + * `scan` accepts a reducer function and a seed value. The reducer will be called initially with the 706 + * seed value and the first emitted value. The {@link Source} will then emit the value returned by 707 + * the reducer function. Subsequently, the `reducer` is called with the last value the `reducer` 708 + * returned and the emitted value. 709 + * 710 + * This operator is similar to `Array.prototype.reduce`, but instead is called over time and emits 711 + * each value of the reducer. 712 + * 713 + * @example 714 + * ```ts 715 + * pipe( 716 + * fromArray([1, 2, 3]), 717 + * scan((acc, x) => acc + x, 0), 718 + * subscribe(x => { 719 + * console.log(text); // logs: 1, 3, 6 720 + * }) 721 + * ); 722 + * ``` 723 + */ 392 724 export function scan<In, Out>(reducer: (acc: Out, value: In) => Out, seed: Out): Operator<In, Out> { 393 725 return source => sink => { 394 726 let acc = seed; ··· 404 736 }; 405 737 } 406 738 739 + /** Shares one underlying subscription to the Source between all Sinks. 740 + * 741 + * @param source - A {@link Source} that should be shared. 742 + * @returns A shared {@link Source}. 743 + * 744 + * @remarks 745 + * `share` accepts a {@link Source} and returns one. It will emit all values as normal, however, it 746 + * will share one subscription to the input source. This allows side-effects on the input 747 + * {@link Source} to only be triggerd once. 748 + */ 407 749 export function share<T>(source: Source<T>): Source<T> { 408 750 let sinks: Sink<T>[] = []; 409 751 let talkback = talkbackPlaceholder; ··· 438 780 }; 439 781 } 440 782 783 + /** Omits `wait` amount of values from the Source and then runs as usual. 784 + * 785 + * @param wait - The number of values to be omitted. 786 + * @returns An {@link Operator}. 787 + * 788 + * @remarks 789 + * `skip` will skip `wait` number of emitted values, then issue all values as normal afterwards. 790 + * This essentially skips a given number of values on the input {@link Source}. 791 + * 792 + * @example 793 + * ```ts 794 + * pipe( 795 + * fromArray([1, 2, 3]), 796 + * skip(2), 797 + * subscribe(x => { 798 + * console.log(text); // logs: 3 799 + * }) 800 + * ); 801 + * ``` 802 + */ 441 803 export function skip<T>(wait: number): Operator<T, T> { 442 804 return source => sink => { 443 805 let talkback = talkbackPlaceholder; ··· 457 819 }; 458 820 } 459 821 822 + /** Omits values from an input Source until a notifier Source emits a value. 823 + * 824 + * @param notifier - A {@link Source} that starts the operator's sent values. 825 + * @returns An {@link Operator}. 826 + * 827 + * @remarks 828 + * `skipUntil` will omit all values from the input {@link Source} until the `notifier` 829 + * Source emits a value of its own. It'll then start passing values from the Source through. 830 + * 831 + * @example 832 + * ```ts 833 + * pipe( 834 + * interval(50), 835 + * skipUntil(interval(150)), 836 + * subscribe(x => { 837 + * console.log(text); // logs: 2, 3... 838 + * }) 839 + * ); 840 + * ``` 841 + */ 460 842 export function skipUntil<S, T>(notifier: Source<S>): Operator<T, T> { 461 843 return source => sink => { 462 844 let sourceTalkback = talkbackPlaceholder; ··· 513 895 }; 514 896 } 515 897 898 + /** Omits values from an input Source until a predicate function returns `false`. 899 + * 900 + * @param predicate - A function returning a boolean per value. 901 + * @returns An {@link Operator}. 902 + * 903 + * @remarks 904 + * `skipWhile` will omit all values from the input {@link Source} until the `predicate` 905 + * function returns `false`. When the `predicate` function returns `false`, the Source's values will 906 + * be passed through. 907 + * 908 + * @example 909 + * ```ts 910 + * pipe( 911 + * fromArray([1, 2, 3]), 912 + * skipWhile(x => x < 2), 913 + * subscribe(x => { 914 + * console.log(text); // logs: 2, 3 915 + * }) 916 + * ); 917 + * ``` 918 + */ 516 919 export function skipWhile<T>(predicate: (value: T) => boolean): Operator<T, T> { 517 920 return source => sink => { 518 921 let talkback = talkbackPlaceholder; ··· 537 940 }; 538 941 } 539 942 943 + /** Emits from the latest Source returned by a mapping function per value of the Source. 944 + * 945 + * @param map - A function returning a {@link Source} per value. 946 + * @returns An {@link Operator}. 947 + * 948 + * @remarks 949 + * `switchMap` accepts a mapping function which must return a {@link Source} per value. 950 + * The output {@link Source} will emit values from the latest Source the mapping function 951 + * returned. If a value is emitted while the last returned Source is still active, the prior Source 952 + * will be closed. 953 + * 954 + * This can be used to issue multiple values per emission of an input {@link Source}, while only 955 + * letting one of these sub-Sources be active at a time. 956 + * 957 + * @example 958 + * ```ts 959 + * pipe( 960 + * interval(100), 961 + * switchMap(() => interval(50)), 962 + * subscribe(x => { 963 + * console.log(text); // logs: 0, 0, 0... 964 + * }) 965 + * ); 966 + * ``` 967 + */ 540 968 export function switchMap<In, Out>(map: (value: In) => Source<Out>): Operator<In, Out> { 541 969 return source => sink => { 542 970 let outerTalkback = talkbackPlaceholder; ··· 619 1047 }; 620 1048 } 621 1049 1050 + /** Flattens a Source emitting Sources into a single Source emitting the inner values. 1051 + * 1052 + * @see {@link switchMap} which this helper uses and instead accept a mapping function. 1053 + * @param source - An {@link Source} emitting {@link Source | Sources}. 1054 + * @returns A {@link Source} emitting values from the inner Sources. 1055 + * 1056 + * @remarks 1057 + * `switchAll` accepts a {@link Source} which must emit {@link Source | Sources}. Each time it 1058 + * receives a {@link Source} it will close its prior subscription and subscribe to the new Source 1059 + * instead, passing through its values. 1060 + * 1061 + * @example 1062 + * ```ts 1063 + * pipe( 1064 + * interval(100), 1065 + * map(() => interval(50)), 1066 + * switchAll, 1067 + * subscribe(x => { 1068 + * console.log(text); // logs: 0, 0, 0... 1069 + * }) 1070 + * ); 1071 + * ``` 1072 + */ 622 1073 export function switchAll<T>(source: Source<Source<T>>): Source<T> { 623 1074 return switchMap<Source<T>, T>(identity)(source); 624 1075 } 625 1076 1077 + /** Emits `max` values from the Source and then ends. 1078 + * 1079 + * @param max - The maximum number of values emitted. 1080 + * @returns An {@link Operator}. 1081 + * 1082 + * @remarks 1083 + * `take` will issue all values as normal until the `max` number of emitted values has been reached. 1084 + * It will then end and close the {@link Source}. 1085 + * 1086 + * @example 1087 + * ```ts 1088 + * pipe( 1089 + * fromArray([1, 2, 3]), 1090 + * take(2), 1091 + * subscribe(x => { 1092 + * console.log(text); // logs: 1, 2 1093 + * }) 1094 + * ); 1095 + * ``` 1096 + */ 626 1097 export function take<T>(max: number): Operator<T, T> { 627 1098 return source => sink => { 628 1099 let talkback = talkbackPlaceholder; ··· 666 1137 }; 667 1138 } 668 1139 1140 + /** Buffers the `max` last values of the Source and emits them once the Source ends. 1141 + * 1142 + * @param max - The maximum number of values buffered. 1143 + * @returns An {@link Operator}. 1144 + * 1145 + * @remarks 1146 + * `takeLast` will buffer values from the input {@link Source} up until the given `max` number. It 1147 + * will only emit values stored in the buffer once the {@link Source} ends. 1148 + * 1149 + * All values in the buffer are emitted like the {@link fromArray | `fromArray`} source would 1150 + * synchronously. 1151 + * 1152 + * @example 1153 + * ```ts 1154 + * pipe( 1155 + * fromArray([1, 2, 3]), 1156 + * takeLast(1), 1157 + * subscribe(x => { 1158 + * console.log(text); // logs: 3 1159 + * }) 1160 + * ); 1161 + * ``` 1162 + */ 669 1163 export function takeLast<T>(max: number): Operator<T, T> { 670 1164 return source => sink => { 671 1165 const queue: T[] = []; ··· 689 1183 }; 690 1184 } 691 1185 1186 + /** Takes values from an input Source until a notifier Source emits a value. 1187 + * 1188 + * @param notifier - A {@link Source} that stops the operator's sent values. 1189 + * @returns An {@link Operator}. 1190 + * 1191 + * @remarks 1192 + * `takeUntil` will issue all values as normal from the input {@link Source} until the `notifier` 1193 + * Source emits a value of its own. It'll then close the {@link Source}. 1194 + * 1195 + * @example 1196 + * ```ts 1197 + * pipe( 1198 + * interval(50), 1199 + * takeUntil(interval(150)), 1200 + * subscribe(x => { 1201 + * console.log(text); // logs: 0, 1 1202 + * }) 1203 + * ); 1204 + * ``` 1205 + */ 692 1206 export function takeUntil<S, T>(notifier: Source<S>): Operator<T, T> { 693 1207 return source => sink => { 694 1208 let sourceTalkback = talkbackPlaceholder; ··· 733 1247 }; 734 1248 } 735 1249 1250 + /** Takes values from an input Source until a predicate function returns `false`. 1251 + * 1252 + * @param predicate - A function returning a boolean per value. 1253 + * @returns An {@link Operator}. 1254 + * 1255 + * @remarks 1256 + * `takeWhile` will issue all values as normal from the input {@link Source} until the `predicate` 1257 + * function returns `false`. When the `predicate` function returns `false`, the current value is 1258 + * omitted and the {@link Source} is closed. 1259 + * 1260 + * @example 1261 + * ```ts 1262 + * pipe( 1263 + * fromArray([1, 2, 3]), 1264 + * takeWhile(x => x < 2), 1265 + * subscribe(x => { 1266 + * console.log(text); // logs: 1 1267 + * }) 1268 + * ); 1269 + * ``` 1270 + */ 736 1271 export function takeWhile<T>(predicate: (value: T) => boolean): Operator<T, T> { 737 1272 return source => sink => { 738 1273 let talkback = talkbackPlaceholder; ··· 757 1292 }; 758 1293 } 759 1294 1295 + /** Debounces a Source by omitting values until a given timeframe has passed. 1296 + * 1297 + * @param timing - A function returning a debounce time (ms) per emitted value. 1298 + * @returns An {@link Operator}. 1299 + * 1300 + * @remarks 1301 + * `debounce` accepts a mapping function that can be used to return a time (in ms) per emitted 1302 + * value. All emitted values issued by the {@link Source} during the returned time will be omitted 1303 + * until the time has passed. 1304 + * 1305 + * Debouncing means that the returned {@link Source} will wait for a minimum time of silence until a 1306 + * value is let through. 1307 + * 1308 + * This is a back pressure operator that can be used to omit values from a {@link Source} coming in 1309 + * too frequently. 1310 + * 1311 + * @example 1312 + * ```ts 1313 + * pipe( 1314 + * interval(50), 1315 + * debounce(() => 100), 1316 + * subscribe(x => { 1317 + * console.log(text); // never logs any value 1318 + * }) 1319 + * ); 1320 + * ``` 1321 + */ 760 1322 export function debounce<T>(timing: (value: T) => number): Operator<T, T> { 761 1323 return source => sink => { 762 1324 let id: any | void; ··· 798 1360 }; 799 1361 } 800 1362 1363 + /** Delays each signal emitted by a Source by given time (ms). 1364 + * 1365 + * @param wait - A time (in ms) by which each {@link SignalKind | signal} is delayed. 1366 + * @returns An {@link Operator}. 1367 + * 1368 + * @remarks 1369 + * `delay` accepts a time (in ms) by which each {@link SignalKind | signal} will be delayed by. 1370 + * This will create a timeout per received signal and delay the emitted values accordingly. 1371 + * 1372 + * Since the operator only calls `setTimeout` per signal, it relies on the timeout implementation to 1373 + * be ordered. Otherwise, signals will arrive in the wrong order at the sink. 1374 + */ 801 1375 export function delay<T>(wait: number): Operator<T, T> { 802 1376 return source => sink => { 803 1377 let active = 0; ··· 817 1391 }; 818 1392 } 819 1393 1394 + /** Throttles a Source by omitting values that are emitted before a given timeout. 1395 + * 1396 + * @param timing - A function returning a throttle time (ms) per emitted value. 1397 + * @returns An {@link Operator}. 1398 + * 1399 + * @remarks 1400 + * `throttle` accepts a mapping function that can be used to return a time (in ms) per emitted 1401 + * value. During the returned timeframe all values issued by the {@link Source} will be omitted and 1402 + * dropped. 1403 + * 1404 + * This is a back pressure operator that can be used to omit values from a {@link Source} coming in 1405 + * too frequently. 1406 + * 1407 + * @example 1408 + * ```ts 1409 + * pipe( 1410 + * interval(50), 1411 + * throttle(() => 100), 1412 + * subscribe(x => { 1413 + * // omits every second value: 0, 2, 4... 1414 + * console.log(text); 1415 + * }) 1416 + * ); 1417 + * ``` 1418 + */ 820 1419 export function throttle<T>(timing: (value: T) => number): Operator<T, T> { 821 1420 return source => sink => { 822 1421 let skip = false;
+155 -130
src/pipe.ts
··· 1 - import { Source } from './types'; 1 + import { Source, Sink, Operator } from './types'; 2 2 3 3 interface UnaryFn<T, R> { 4 4 (source: T): R; 5 5 } 6 6 7 - /* pipe definitions for source + operators composition */ 7 + /** Chain calls operators on a given source and returns the last result. 8 + * @param args - A source, then a variable number of transform functions 9 + * 10 + * @remarks 11 + * The `pipe` utility can be called with a {@link Source} then one or more unary transform functions. 12 + * Each transform function will be called in turn with the last function's return value, starting 13 + * with the source passed as the first argument to `pipe`. 14 + * 15 + * It's used to transform a source with a list of {@link Operator | Operators}. The last argument may 16 + * also be a {@link Sink} that returns something else than a Source. 17 + * 18 + * @example 19 + * 20 + * ```ts 21 + * pipe( 22 + * fromArray([1, 2, 3]), 23 + * map(x => x * 2), 24 + * subscribe(console.log) 25 + * ); 26 + * ``` 27 + * 28 + * @see {@link https://github.com/tc39/proposal-pipeline-operator} for the JS Pipeline Operator spec, for which this is a replacement utility for. 29 + */ 30 + interface pipe { 31 + /* pipe definitions for source + operators composition */ 8 32 9 - function pipe<T, A>(source: Source<T>, op1: UnaryFn<Source<T>, Source<A>>): Source<A>; 33 + <T, A>(source: Source<T>, op1: UnaryFn<Source<T>, Source<A>>): Source<A>; 10 34 11 - function pipe<T, A, B>( 12 - source: Source<T>, 13 - op1: UnaryFn<Source<T>, Source<A>>, 14 - op2: UnaryFn<Source<A>, Source<B>> 15 - ): Source<B>; 35 + <T, A, B>( 36 + source: Source<T>, 37 + op1: UnaryFn<Source<T>, Source<A>>, 38 + op2: UnaryFn<Source<A>, Source<B>> 39 + ): Source<B>; 16 40 17 - function pipe<T, A, B, C>( 18 - source: Source<T>, 19 - op1: UnaryFn<Source<T>, Source<A>>, 20 - op2: UnaryFn<Source<A>, Source<B>>, 21 - op3: UnaryFn<Source<B>, Source<C>> 22 - ): Source<C>; 41 + <T, A, B, C>( 42 + source: Source<T>, 43 + op1: UnaryFn<Source<T>, Source<A>>, 44 + op2: UnaryFn<Source<A>, Source<B>>, 45 + op3: UnaryFn<Source<B>, Source<C>> 46 + ): Source<C>; 23 47 24 - function pipe<T, A, B, C, D>( 25 - source: Source<T>, 26 - op1: UnaryFn<Source<T>, Source<A>>, 27 - op2: UnaryFn<Source<A>, Source<B>>, 28 - op3: UnaryFn<Source<B>, Source<C>>, 29 - op4: UnaryFn<Source<C>, Source<D>> 30 - ): Source<D>; 48 + <T, A, B, C, D>( 49 + source: Source<T>, 50 + op1: UnaryFn<Source<T>, Source<A>>, 51 + op2: UnaryFn<Source<A>, Source<B>>, 52 + op3: UnaryFn<Source<B>, Source<C>>, 53 + op4: UnaryFn<Source<C>, Source<D>> 54 + ): Source<D>; 31 55 32 - function pipe<T, A, B, C, D, E>( 33 - source: Source<T>, 34 - op1: UnaryFn<Source<T>, Source<A>>, 35 - op2: UnaryFn<Source<A>, Source<B>>, 36 - op3: UnaryFn<Source<B>, Source<C>>, 37 - op4: UnaryFn<Source<C>, Source<D>>, 38 - op5: UnaryFn<Source<D>, Source<E>> 39 - ): Source<E>; 56 + <T, A, B, C, D, E>( 57 + source: Source<T>, 58 + op1: UnaryFn<Source<T>, Source<A>>, 59 + op2: UnaryFn<Source<A>, Source<B>>, 60 + op3: UnaryFn<Source<B>, Source<C>>, 61 + op4: UnaryFn<Source<C>, Source<D>>, 62 + op5: UnaryFn<Source<D>, Source<E>> 63 + ): Source<E>; 40 64 41 - function pipe<T, A, B, C, D, E, F>( 42 - source: Source<T>, 43 - op1: UnaryFn<Source<T>, Source<A>>, 44 - op2: UnaryFn<Source<A>, Source<B>>, 45 - op3: UnaryFn<Source<B>, Source<C>>, 46 - op4: UnaryFn<Source<C>, Source<D>>, 47 - op5: UnaryFn<Source<D>, Source<E>>, 48 - op6: UnaryFn<Source<E>, Source<F>> 49 - ): Source<F>; 65 + <T, A, B, C, D, E, F>( 66 + source: Source<T>, 67 + op1: UnaryFn<Source<T>, Source<A>>, 68 + op2: UnaryFn<Source<A>, Source<B>>, 69 + op3: UnaryFn<Source<B>, Source<C>>, 70 + op4: UnaryFn<Source<C>, Source<D>>, 71 + op5: UnaryFn<Source<D>, Source<E>>, 72 + op6: UnaryFn<Source<E>, Source<F>> 73 + ): Source<F>; 50 74 51 - function pipe<T, A, B, C, D, E, F, G>( 52 - source: Source<T>, 53 - op1: UnaryFn<Source<T>, Source<A>>, 54 - op2: UnaryFn<Source<A>, Source<B>>, 55 - op3: UnaryFn<Source<B>, Source<C>>, 56 - op4: UnaryFn<Source<C>, Source<D>>, 57 - op5: UnaryFn<Source<D>, Source<E>>, 58 - op6: UnaryFn<Source<E>, Source<F>>, 59 - op7: UnaryFn<Source<F>, Source<G>> 60 - ): Source<G>; 75 + <T, A, B, C, D, E, F, G>( 76 + source: Source<T>, 77 + op1: UnaryFn<Source<T>, Source<A>>, 78 + op2: UnaryFn<Source<A>, Source<B>>, 79 + op3: UnaryFn<Source<B>, Source<C>>, 80 + op4: UnaryFn<Source<C>, Source<D>>, 81 + op5: UnaryFn<Source<D>, Source<E>>, 82 + op6: UnaryFn<Source<E>, Source<F>>, 83 + op7: UnaryFn<Source<F>, Source<G>> 84 + ): Source<G>; 61 85 62 - function pipe<T, A, B, C, D, E, F, G, H>( 63 - source: Source<T>, 64 - op1: UnaryFn<Source<T>, Source<A>>, 65 - op2: UnaryFn<Source<A>, Source<B>>, 66 - op3: UnaryFn<Source<B>, Source<C>>, 67 - op4: UnaryFn<Source<C>, Source<D>>, 68 - op5: UnaryFn<Source<D>, Source<E>>, 69 - op6: UnaryFn<Source<E>, Source<F>>, 70 - op7: UnaryFn<Source<F>, Source<G>>, 71 - op8: UnaryFn<Source<G>, Source<H>> 72 - ): Source<H>; 86 + <T, A, B, C, D, E, F, G, H>( 87 + source: Source<T>, 88 + op1: UnaryFn<Source<T>, Source<A>>, 89 + op2: UnaryFn<Source<A>, Source<B>>, 90 + op3: UnaryFn<Source<B>, Source<C>>, 91 + op4: UnaryFn<Source<C>, Source<D>>, 92 + op5: UnaryFn<Source<D>, Source<E>>, 93 + op6: UnaryFn<Source<E>, Source<F>>, 94 + op7: UnaryFn<Source<F>, Source<G>>, 95 + op8: UnaryFn<Source<G>, Source<H>> 96 + ): Source<H>; 73 97 74 - /* pipe definitions for source + operators + consumer composition */ 98 + /* pipe definitions for source + operators + consumer composition */ 75 99 76 - function pipe<T, R>(source: Source<T>, consumer: UnaryFn<Source<T>, R>): R; 100 + <T, R>(source: Source<T>, consumer: UnaryFn<Source<T>, R>): R; 77 101 78 - function pipe<T, A, R>( 79 - source: Source<T>, 80 - op1: UnaryFn<Source<T>, Source<A>>, 81 - consumer: UnaryFn<Source<A>, R> 82 - ): R; 102 + <T, A, R>( 103 + source: Source<T>, 104 + op1: UnaryFn<Source<T>, Source<A>>, 105 + consumer: UnaryFn<Source<A>, R> 106 + ): R; 83 107 84 - function pipe<T, A, B, R>( 85 - source: Source<T>, 86 - op1: UnaryFn<Source<T>, Source<A>>, 87 - op2: UnaryFn<Source<A>, Source<B>>, 88 - consumer: UnaryFn<Source<B>, R> 89 - ): R; 108 + <T, A, B, R>( 109 + source: Source<T>, 110 + op1: UnaryFn<Source<T>, Source<A>>, 111 + op2: UnaryFn<Source<A>, Source<B>>, 112 + consumer: UnaryFn<Source<B>, R> 113 + ): R; 90 114 91 - function pipe<T, A, B, C, R>( 92 - source: Source<T>, 93 - op1: UnaryFn<Source<T>, Source<A>>, 94 - op2: UnaryFn<Source<A>, Source<B>>, 95 - op3: UnaryFn<Source<B>, Source<C>>, 96 - consumer: UnaryFn<Source<C>, R> 97 - ): R; 115 + <T, A, B, C, R>( 116 + source: Source<T>, 117 + op1: UnaryFn<Source<T>, Source<A>>, 118 + op2: UnaryFn<Source<A>, Source<B>>, 119 + op3: UnaryFn<Source<B>, Source<C>>, 120 + consumer: UnaryFn<Source<C>, R> 121 + ): R; 98 122 99 - function pipe<T, A, B, C, D, R>( 100 - source: Source<T>, 101 - op1: UnaryFn<Source<T>, Source<A>>, 102 - op2: UnaryFn<Source<A>, Source<B>>, 103 - op3: UnaryFn<Source<B>, Source<C>>, 104 - op4: UnaryFn<Source<C>, Source<D>>, 105 - consumer: UnaryFn<Source<D>, R> 106 - ): R; 123 + <T, A, B, C, D, R>( 124 + source: Source<T>, 125 + op1: UnaryFn<Source<T>, Source<A>>, 126 + op2: UnaryFn<Source<A>, Source<B>>, 127 + op3: UnaryFn<Source<B>, Source<C>>, 128 + op4: UnaryFn<Source<C>, Source<D>>, 129 + consumer: UnaryFn<Source<D>, R> 130 + ): R; 107 131 108 - function pipe<T, A, B, C, D, E, R>( 109 - source: Source<T>, 110 - op1: UnaryFn<Source<T>, Source<A>>, 111 - op2: UnaryFn<Source<A>, Source<B>>, 112 - op3: UnaryFn<Source<B>, Source<C>>, 113 - op4: UnaryFn<Source<C>, Source<D>>, 114 - op5: UnaryFn<Source<D>, Source<E>>, 115 - consumer: UnaryFn<Source<E>, R> 116 - ): R; 132 + <T, A, B, C, D, E, R>( 133 + source: Source<T>, 134 + op1: UnaryFn<Source<T>, Source<A>>, 135 + op2: UnaryFn<Source<A>, Source<B>>, 136 + op3: UnaryFn<Source<B>, Source<C>>, 137 + op4: UnaryFn<Source<C>, Source<D>>, 138 + op5: UnaryFn<Source<D>, Source<E>>, 139 + consumer: UnaryFn<Source<E>, R> 140 + ): R; 117 141 118 - function pipe<T, A, B, C, D, E, F, R>( 119 - source: Source<T>, 120 - op1: UnaryFn<Source<T>, Source<A>>, 121 - op2: UnaryFn<Source<A>, Source<B>>, 122 - op3: UnaryFn<Source<B>, Source<C>>, 123 - op4: UnaryFn<Source<C>, Source<D>>, 124 - op5: UnaryFn<Source<D>, Source<E>>, 125 - op6: UnaryFn<Source<E>, Source<F>>, 126 - consumer: UnaryFn<Source<F>, R> 127 - ): R; 142 + <T, A, B, C, D, E, F, R>( 143 + source: Source<T>, 144 + op1: UnaryFn<Source<T>, Source<A>>, 145 + op2: UnaryFn<Source<A>, Source<B>>, 146 + op3: UnaryFn<Source<B>, Source<C>>, 147 + op4: UnaryFn<Source<C>, Source<D>>, 148 + op5: UnaryFn<Source<D>, Source<E>>, 149 + op6: UnaryFn<Source<E>, Source<F>>, 150 + consumer: UnaryFn<Source<F>, R> 151 + ): R; 128 152 129 - function pipe<T, A, B, C, D, E, F, G, R>( 130 - source: Source<T>, 131 - op1: UnaryFn<Source<T>, Source<A>>, 132 - op2: UnaryFn<Source<A>, Source<B>>, 133 - op3: UnaryFn<Source<B>, Source<C>>, 134 - op4: UnaryFn<Source<C>, Source<D>>, 135 - op5: UnaryFn<Source<D>, Source<E>>, 136 - op6: UnaryFn<Source<E>, Source<F>>, 137 - op7: UnaryFn<Source<F>, Source<G>>, 138 - consumer: UnaryFn<Source<G>, R> 139 - ): R; 153 + <T, A, B, C, D, E, F, G, R>( 154 + source: Source<T>, 155 + op1: UnaryFn<Source<T>, Source<A>>, 156 + op2: UnaryFn<Source<A>, Source<B>>, 157 + op3: UnaryFn<Source<B>, Source<C>>, 158 + op4: UnaryFn<Source<C>, Source<D>>, 159 + op5: UnaryFn<Source<D>, Source<E>>, 160 + op6: UnaryFn<Source<E>, Source<F>>, 161 + op7: UnaryFn<Source<F>, Source<G>>, 162 + consumer: UnaryFn<Source<G>, R> 163 + ): R; 140 164 141 - function pipe<T, A, B, C, D, E, F, G, H, R>( 142 - source: Source<T>, 143 - op1: UnaryFn<Source<T>, Source<A>>, 144 - op2: UnaryFn<Source<A>, Source<B>>, 145 - op3: UnaryFn<Source<B>, Source<C>>, 146 - op4: UnaryFn<Source<C>, Source<D>>, 147 - op5: UnaryFn<Source<D>, Source<E>>, 148 - op6: UnaryFn<Source<E>, Source<F>>, 149 - op7: UnaryFn<Source<F>, Source<G>>, 150 - op8: UnaryFn<Source<G>, Source<H>>, 151 - consumer: UnaryFn<Source<H>, R> 152 - ): R; 165 + <T, A, B, C, D, E, F, G, H, R>( 166 + source: Source<T>, 167 + op1: UnaryFn<Source<T>, Source<A>>, 168 + op2: UnaryFn<Source<A>, Source<B>>, 169 + op3: UnaryFn<Source<B>, Source<C>>, 170 + op4: UnaryFn<Source<C>, Source<D>>, 171 + op5: UnaryFn<Source<D>, Source<E>>, 172 + op6: UnaryFn<Source<E>, Source<F>>, 173 + op7: UnaryFn<Source<F>, Source<G>>, 174 + op8: UnaryFn<Source<G>, Source<H>>, 175 + consumer: UnaryFn<Source<H>, R> 176 + ): R; 177 + } 153 178 154 - function pipe(...args: any[]) { 179 + function pipe(...args: Function[]): any { 155 180 let x = args[0]; 156 181 for (let i = 1, l = args.length; i < l; i++) x = args[i](x); 157 182 return x;
+122
src/sinks.ts
··· 1 1 import { Source, Subscription, TalkbackKind, SignalKind } from './types'; 2 2 import { talkbackPlaceholder } from './helpers'; 3 3 4 + /** Creates a subscription to a given source and invokes a `subscriber` callback for each value. 5 + * @param subscriber - A callback function called for each issued value. 6 + * @returns A function accepting a {@link Source} and returning a {@link Subscription}. 7 + * 8 + * @remarks 9 + * `subscribe` accepts a `subscriber` callback and returns a function accepting a {@link Source}. 10 + * When a source is passed to the returned funtion, the subscription will start and `subscriber` 11 + * will be called for each new value the Source issues. This will also return a {@link Subscription} 12 + * object that can cancel the ongoing {@link Source} early. 13 + * 14 + * @example 15 + * ```ts 16 + * const subscription = pipe( 17 + * fromValue('test'), 18 + * subscribe(text => { 19 + * console.log(text); // 'test' 20 + * }) 21 + * ); 22 + * ``` 23 + */ 4 24 export function subscribe<T>(subscriber: (value: T) => void) { 5 25 return (source: Source<T>): Subscription => { 6 26 let talkback = talkbackPlaceholder; ··· 26 46 }; 27 47 } 28 48 49 + /** Creates a subscription to a given source and invokes a `subscriber` callback for each value. 50 + * @see {@link subscribe} which this helper aliases without returnin a {@link Subscription}. 51 + * @param subscriber - A callback function called for each issued value. 52 + * @returns A function accepting a {@link Source}. 53 + * 54 + * @remarks 55 + * `forEach` accepts a `subscriber` callback and returns a function accepting a {@link Source}. 56 + * When a source is passed to the returned funtion, the subscription will start and `subscriber` 57 + * will be called for each new value the Source issues. Unlike `subscribe` it will not return a 58 + * Subscription object and can't be cancelled early. 59 + * 60 + * @example 61 + * ```ts 62 + * pipe( 63 + * fromValue('test'), 64 + * forEach(text => { 65 + * console.log(text); // 'test' 66 + * }) 67 + * ); // undefined 68 + * ``` 69 + */ 29 70 export function forEach<T>(subscriber: (value: T) => void) { 30 71 return (source: Source<T>): void => { 31 72 subscribe(subscriber)(source); 32 73 }; 33 74 } 34 75 76 + /** Creates a subscription to a given source and invokes a `subscriber` callback for each value. 77 + * @see {@link subscribe} which this helper aliases without accepting parameters or returning a 78 + * {@link Subscription | Subscription}. 79 + * 80 + * @param source - A {@link Source}. 81 + * 82 + * @remarks 83 + * `publish` accepts a {@link Source} and subscribes to it, starting its values. The resulting 84 + * values cannot be observed and the subscription can't be cancelled, as this helper is purely 85 + * intended to start side-effects. 86 + * 87 + * @example 88 + * ```ts 89 + * pipe( 90 + * lazy(() => { 91 + * console.log('test'); // this is called 92 + * return fromValue(123); // this is never used 93 + * }), 94 + * publish 95 + * ); // undefined 96 + * ``` 97 + */ 35 98 export function publish<T>(source: Source<T>): void { 36 99 subscribe(_value => { 37 100 /*noop*/ ··· 40 103 41 104 const doneResult = { done: true } as IteratorReturnResult<void>; 42 105 106 + /** Converts a Source to an AsyncIterable that pulls and issues values from the Source. 107 + * 108 + * @param source - A {@link Source}. 109 + * @returns An {@link AsyncIterable | `AsyncIterable`} issuing values from the Source. 110 + * 111 + * @remarks 112 + * `toAsyncIterable` will create an {@link AsyncIterable} that pulls and issues values from a given 113 + * {@link Source}. This can be used in many interoperability situations, to provide an iterable when 114 + * a consumer requires it. 115 + * 116 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols} 117 + * for the JS Iterable protocol. 118 + * 119 + * @example 120 + * ```ts 121 + * const iterable = toAsyncIterable(fromArray([1, 2, 3])); 122 + * for await (const value of iterable) { 123 + * console.log(value); // outputs: 1, 2, 3 124 + * } 125 + * ``` 126 + */ 43 127 export const toAsyncIterable = <T>(source: Source<T>): AsyncIterable<T> => ({ 44 128 [Symbol.asyncIterator](): AsyncIterator<T> { 45 129 const buffer: T[] = []; ··· 84 168 }, 85 169 }); 86 170 171 + /** Subscribes to a given source and collects all synchronous values into an array. 172 + * @param source - A {@link Source}. 173 + * @returns An array of values collected from the {@link Source}. 174 + * 175 + * @remarks 176 + * `toArray` accepts a {@link Source} and returns an array of all synchronously issued values from 177 + * this Source. It will issue {@link TalkbackKind.Pull | Pull signals} after every value it receives 178 + * and expects the Source to recursively issue values. 179 + * 180 + * Any asynchronously issued values will not be 181 + * added to the array and a {@link TalkbackKind.Close | Close signal} is issued by the sink before 182 + * returning the array. 183 + * 184 + * @example 185 + * ```ts 186 + * toArray(fromArray([1, 2, 3])); // [1, 2, 3] 187 + * ``` 188 + */ 87 189 export function toArray<T>(source: Source<T>): T[] { 88 190 const values: T[] = []; 89 191 let talkback = talkbackPlaceholder; ··· 102 204 return values; 103 205 } 104 206 207 + /** Subscribes to a given source and returns a Promise that will resolve with the last value the 208 + * source issues. 209 + * 210 + * @param source - A {@link Source}. 211 + * @returns A {@link Promise} resolving to the last value of the {@link Source}. 212 + * 213 + * @remarks 214 + * `toPromise` will subscribe to the passed {@link Source} and resolve to the last value of it once 215 + * it receives the last value, as signaled by the {@link SignalKind.End | End signal}. 216 + * 217 + * To keep its implementation simple, padding sources that don't issue any values to `toPromise` is 218 + * undefined behaviour and `toPromise` will issue `undefined` in that case. 219 + * 220 + * The returned {@link Promise} delays its value by a microtick, using `Promise.resolve`. 221 + * 222 + * @example 223 + * ```ts 224 + * toPromise(fromValue('test')); // resolves: 'test' 225 + * ``` 226 + */ 105 227 export function toPromise<T>(source: Source<T>): Promise<T> { 106 228 return new Promise(resolve => { 107 229 let talkback = talkbackPlaceholder;
+206 -4
src/sources.ts
··· 2 2 import { push, start, talkbackPlaceholder, teardownPlaceholder } from './helpers'; 3 3 import { share } from './operators'; 4 4 5 - export function lazy<T>(make: () => Source<T>): Source<T> { 6 - return sink => make()(sink); 5 + /** Helper creating a Source from a factory function when it's subscribed to. 6 + * @param produce - A factory function returning a {@link Source}. 7 + * @returns A {@link Source} lazyily subscribing to the Source returned by the given factory 8 + * function. 9 + * 10 + * @remarks 11 + * At times it's necessary to create a {@link Source} lazily. The time of a {@link Source} being 12 + * created could be different from when it's subscribed to, and hence we may want to split the 13 + * creation and subscription time. This is especially useful when the Source we wrap is "hot" and 14 + * issues values as soon as it's created, which we may then not receive in a subscriber. 15 + * 16 + * @example An example of creating a {@link Source} that issues the timestamp of subscription. Here 17 + * we effectively use `lazy` with the simple {@link fromValue | `fromValue`} source, to quickly 18 + * create a Source that issues the time of its subscription, rather than the time of its creation 19 + * that it would otherwise issue without `lazy`. 20 + * 21 + * ```ts 22 + * lazy(() => fromValue(Date.now())); 23 + * ``` 24 + */ 25 + export function lazy<T>(produce: () => Source<T>): Source<T> { 26 + return sink => produce()(sink); 7 27 } 8 28 29 + /** Converts an AsyncIterable to a Source that pulls and issues values from it as requested. 30 + * 31 + * @see {@link fromIterable | `fromIterable`} for the non-async Iterable version of this helper, 32 + * which calls this helper automatically as needed. 33 + * 34 + * @param iterable - An {@link AsyncIterable | `AsyncIterable`}. 35 + * @returns A {@link Source} issuing values sourced from the Iterable. 36 + * 37 + * @remarks 38 + * `fromAsyncIterable` will create a {@link Source} that pulls and issues values from a given 39 + * {@link AsyncIterable}. This can be used in many interoperability situations, including to consume 40 + * an async generator function. 41 + * 42 + * When the {@link Sink} throws an exception when a new value is pushed, this helper will rethrow it 43 + * using {@link AsyncIterator.throw}, which allows an async generator to recover from the exception. 44 + * 45 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols} 46 + * for the JS Iterable protocol. 47 + */ 9 48 export function fromAsyncIterable<T>(iterable: AsyncIterable<T>): Source<T> { 10 49 return sink => { 11 50 const iterator = iterable[Symbol.asyncIterator](); ··· 46 85 }; 47 86 } 48 87 88 + /** Converts an Iterable to a Source that pulls and issues values from it as requested. 89 + * @see {@link fromAsyncIterable | `fromAsyncIterable`} for the AsyncIterable version of this helper. 90 + * @param iterable - An {@link Iterable | `Iterable`} or an `AsyncIterable` 91 + * @returns A {@link Source} issuing values sourced from the Iterable. 92 + * 93 + * @remarks 94 + * `fromIterable` will create a {@link Source} that pulls and issues values from a given 95 + * {@link Iterable | JS Iterable}. As iterables are the common standard for any lazily iterated list 96 + * of values in JS it can be applied to many different JS data types, including a JS Generator 97 + * function. 98 + * 99 + * This Source will only call {@link Iterator.next} on the iterator when the subscribing {@link Sink} 100 + * has pulled a new value with the {@link TalkbackKind.Pull | Pull signal}. `fromIterable` can 101 + * therefore also be applied to "infinite" iterables, without a predefined end. 102 + * 103 + * This helper will call {@link fromAsyncIterable | `fromAsyncIterable`} automatically when the 104 + * passed object also implements the async iterator protocol. 105 + * 106 + * When the {@link Sink} throws an exception when a new value is pushed, this helper will rethrow it 107 + * using {@link Iterator.throw}, which allows a generator to recover from the exception. 108 + * 109 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol} 110 + * for the JS Iterable protocol. 111 + */ 49 112 export function fromIterable<T>(iterable: Iterable<T> | AsyncIterable<T>): Source<T> { 50 113 if (iterable[Symbol.asyncIterator]) return fromAsyncIterable(iterable as AsyncIterable<T>); 51 114 return sink => { ··· 87 150 }; 88 151 } 89 152 153 + /** Creates a Source that issues a each value of a given array synchronously. 154 + * @see {@link fromIterable} which `fromArray` aliases. 155 + * @param array - The array whose values will be issued one by one. 156 + * @returns A {@link Source} issuing the array's values. 157 + * 158 + * @remarks 159 + * `fromArray` will create a {@link Source} that issues the values of a given JS array one by one. It 160 + * will issue values as they're pulled and is hence a "cold" source, not eagerly emitting values. It 161 + * will end and issue the {@link SignalKind.End | End signal} when the array is exhausted of values. 162 + * 163 + * @example 164 + * ```ts 165 + * fromArray([1, 2, 3]); 166 + * ``` 167 + */ 90 168 export const fromArray: <T>(array: T[]) => Source<T> = fromIterable; 91 169 170 + /** Creates a Source that issues a single value and ends immediately after. 171 + * @param value - The value that will be issued. 172 + * @returns A {@link Source} issuing the single value. 173 + * 174 + * @example 175 + * ```ts 176 + * fromValue('test'); 177 + * ``` 178 + */ 92 179 export function fromValue<T>(value: T): Source<T> { 93 180 return sink => { 94 181 let ended = false; ··· 106 193 }; 107 194 } 108 195 109 - export function make<T>(produce: (observer: Observer<T>) => TeardownFn): Source<T> { 196 + /** Creates a new Source from scratch from a passed `subscriber` function. 197 + * @param subscriber - A callback that is called when the {@link Source} is subscribed to. 198 + * @returns A {@link Source} created from the `subscriber` parameter. 199 + * 200 + * @remarks 201 + * `make` is used to create a new, arbitrary {@link Source} from scratch. It calls the passed 202 + * `subscriber` function when it's subscribed to. 203 + * 204 + * The `subscriber` function receives an {@link Observer}. You may call {@link Observer.next} to 205 + * issue values on the Source, and {@link Observer.complete} to end the Source. 206 + * 207 + * Your `subscribr` function must return a {@link TeardownFn | teardown function} which is only 208 + * called when your source is cancelled — not when you invoke `complete` yourself. As this creates a 209 + * "cold" source, every time this source is subscribed to, it will invoke the `subscriber` function 210 + * again and create a new source. 211 + * 212 + * @example 213 + * 214 + * ```ts 215 + * make(observer => { 216 + * const frame = requestAnimationFrame(() => { 217 + * observer.next('animate!'); 218 + * }); 219 + * return () => { 220 + * cancelAnimationFrame(frame); 221 + * }; 222 + * }); 223 + * ``` 224 + */ 225 + export function make<T>(subscriber: (observer: Observer<T>) => TeardownFn): Source<T> { 110 226 return sink => { 111 227 let ended = false; 112 - const teardown = produce({ 228 + const teardown = subscriber({ 113 229 next(value: T) { 114 230 if (!ended) sink(push(value)); 115 231 }, ··· 131 247 }; 132 248 } 133 249 250 + /** Creates a new Subject which can be used as an IO event hub. 251 + * @returns A new {@link Subject}. 252 + * 253 + * @remarks 254 + * `makeSubject` creates a new {@link Subject}. A Subject is a {@link Source} and an {@link Observer} 255 + * combined in one interface, as the Observer is used to send new signals to the Source. This means 256 + * that it's "hot" and hence all subscriptions to {@link Subject.source} share the same underlying 257 + * signals coming from {@link Subject.next} and {@link Subject.complete}. 258 + * 259 + * @example 260 + * ```ts 261 + * const subject = makeSubject(); 262 + * pipe(subject.source, subscribe(console.log)); 263 + * // This will log the string on the above subscription 264 + * subject.next('hello subject!'); 265 + * ``` 266 + */ 134 267 export function makeSubject<T>(): Subject<T> { 135 268 let next: Subject<T>['next'] | void; 136 269 let complete: Subject<T>['complete'] | void; ··· 151 284 }; 152 285 } 153 286 287 + /** A {@link Source} that immediately ends. 288 + * @remarks 289 + * `empty` is a {@link Source} that immediately issues an {@link SignalKind.End | End signal} when 290 + * it's subscribed to, ending immediately. 291 + * 292 + * @see {@link never | `never`} for a source that instead never ends. 293 + */ 154 294 export const empty: Source<any> = (sink: Sink<any>): void => { 155 295 let ended = false; 156 296 sink( ··· 165 305 ); 166 306 }; 167 307 308 + /** A {@link Source} without values that never ends. 309 + * @remarks 310 + * `never` is a {@link Source} that never issues any signals and neither sends values nor ends. 311 + * 312 + * @see {@link empty | `empty`} for a source that instead ends immediately. 313 + */ 168 314 export const never: Source<any> = (sink: Sink<any>): void => { 169 315 sink(start(talkbackPlaceholder)); 170 316 }; 171 317 318 + /** Creates a Source that issues an incrementing integer in intervals. 319 + * @param ms - The interval in milliseconds. 320 + * @returns A {@link Source} issuing an incrementing count on each interval. 321 + * 322 + * @remarks 323 + * `interval` will create a {@link Source} that issues an incrementing counter each time the `ms` 324 + * interval expires. 325 + * 326 + * It'll only stop when it's cancelled by a {@link TalkbackKind.Close | Close signal}. 327 + * 328 + * @example 329 + * An example printing `0`, then `1`, and so on, in intervals of 50ms. 330 + * 331 + * ```ts 332 + * pipe(interval(50), subscribe(console.log)); 333 + * ``` 334 + */ 172 335 export function interval(ms: number): Source<number> { 173 336 return make(observer => { 174 337 let i = 0; ··· 177 340 }); 178 341 } 179 342 343 + /** Converts DOM Events to a Source given an `HTMLElement` and an event's name. 344 + * @param element - The {@link HTMLElement} to listen to. 345 + * @param event - The DOM Event name to listen to. 346 + * @returns A {@link Source} issuing the {@link Event | DOM Events} as they're issued by the DOM. 347 + * 348 + * @remarks 349 + * `fromDomEvent` will create a {@link Source} that listens to the given element's events and issues 350 + * them as values on the source. This source will only stop when it's cancelled by a 351 + * {@link TalkbackKind.Close | Close signal}. 352 + * 353 + * @example 354 + * An example printing `'clicked!'` when the given `#root` element is clicked. 355 + * 356 + * ```ts 357 + * const element = document.getElementById('root'); 358 + * pipe( 359 + * fromDomEvent(element, 'click'), 360 + * subscribe(() => console.log('clicked!')) 361 + * ); 362 + * ``` 363 + */ 180 364 export function fromDomEvent(element: HTMLElement, event: string): Source<Event> { 181 365 return make(observer => { 182 366 element.addEventListener(event, observer.next); ··· 184 368 }); 185 369 } 186 370 371 + /** Converts a Promise to a Source that issues the resolving Promise's value and then ends. 372 + * @param promise - The promise that will be wrapped. 373 + * @returns A {@link Source} issuing the promise's value when it resolves. 374 + * 375 + * @remarks 376 + * `fromPromise` will create a {@link Source} that issues the {@link Promise}'s resolving value 377 + * asynchronously and ends immediately after resolving. 378 + * 379 + * This helper will not handle the promise's exceptions, and will cause uncaught errors if the 380 + * promise rejects without a value. 381 + * 382 + * @example 383 + * An example printing `'resolved!'` when the given promise resolves after a tick. 384 + * 385 + * ```ts 386 + * pipe(fromPromise(Promise.resolve('resolved!')), subscribe(console.log)); 387 + * ``` 388 + */ 187 389 export function fromPromise<T>(promise: Promise<T>): Source<T> { 188 390 return make(observer => { 189 391 promise.then(value => {
+161 -9
src/types.ts
··· 1 - /** 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 */ 1 + /** 2 + * Talkback signal that sends instructions from a sink to a source. 3 + * 4 + * @remarks 5 + * This signal is issued via {@link TalkbackFn | talkback functions} that a {@link Sink} receives via 6 + * the {@link Start} signal, to tell a {@link Source} to either send a new value (pulling) or stop 7 + * sending values altogether (cancellation). 8 + */ 2 9 export const enum TalkbackKind { 10 + /** Instructs the {@link Source} to send the next value. */ 3 11 Pull = 0, 12 + /** Instructs the {@link Source} to stop sending values and cancels it. */ 4 13 Close = 1, 5 14 } 6 15 7 - /** A talkback callback is sent to the sink with the [Start] signal to communicate signals back to the source. */ 16 + /** 17 + * Talkback callback that sends instructions to a source. 18 + * 19 + * @remarks 20 + * This function sends a {@link TalkbackKind} signal to the source to instruct it to send a new value 21 + * (pulling) or to be cancelled and stop sending values altogether. 22 + */ 8 23 export type TalkbackFn = (signal: TalkbackKind) => void; 24 + 25 + /** 26 + * Callback that is called when a source is cancelled. 27 + * 28 + * @remarks 29 + * This is used, in particular, in the {@link make | make Source} and is a returned function that is 30 + * called when the {@link TalkbackKind.Close} signal is received by the source. 31 + */ 9 32 export type TeardownFn = () => void; 10 33 34 + /** 35 + * Tag enum that is used to on signals that are sent from a source to a sink. 36 + * 37 + * @remarks 38 + * This signal is issued by a {@link Source} and {@link Sink | Sinks} are called with it. The signals 39 + * carrying values ({@link Start} and {@link Push}) are sent as a unary `[T]` tuple tagged with 40 + * {@link Tag}. The {@link End} signal carries no value and is sent as a raw `0` value. 41 + * @see {@link Start} for the data structure of the start signal. 42 + * @see {@link Push} for the data structure of the push signal, carrying values. 43 + */ 11 44 export const enum SignalKind { 45 + /** 46 + * Informs the {@link Sink} that it's being called by a {@link Source}. 47 + * 48 + * @remarks 49 + * This starts the stream of values and carries a {@link TalkbackFn | talkback function} with it 50 + * that is used by the {@link Sink} to communicate back to the {@link Source}. 51 + * @see {@link Start} for the data structure of the signal. 52 + */ 12 53 Start = 0, 54 + /** 55 + * Informs the {@link Sink} of a new values that's incoming from the {@link Source}. 56 + * 57 + * @remarks 58 + * This informs the {@link Sink} of new values that are sent by the {@link Source}. 59 + * @see {@link Push} for the data structure of the signal. 60 + */ 13 61 Push = 1, 62 + /** 63 + * Informs the {@link Sink} that the {@link Source} has ended and that it won't send more values. 64 + * 65 + * @remarks 66 + * This signal signifies that the stream has stopped and that no more values are expected. Some 67 + * sources don't have a set end or limit on how many values will be sent. This signal is not sent 68 + * when the {@link Source} is cancelled with a {@link TalkbackKind.Close | Close talkback signal}. 69 + */ 14 70 End = 0, 15 71 } 16 72 73 + /** 74 + * The tag property that's put on unary `[T]` tuple to turn them into signals carrying values. 75 + * 76 + * @internal 77 + */ 17 78 export interface Tag<T> { 18 79 tag: T; 19 80 } 20 81 21 - /** The start [Signal] is the first signal and carries a callback (talkback) so the sink can send signals to the source */ 82 + /** 83 + * Indicates the start of a stream to a {@link Sink}. 84 + * 85 + * @remarks 86 + * This signal is sent from a {@link Source} to a {@link Sink} at the start of a stream to inform it 87 + * that values can be pulled and/or will be sent. This signal carries a 88 + * {@link TalkbackFn | talkback function} that is used by the {@link Sink} to communicate back to the 89 + * {@link Source} as a callback. The talkback accepts {@link TalkbackKind.Pull | Pull} and 90 + * {@link TalkbackKind.Close | Close} signals. 91 + */ 22 92 export type Start<_T> = Tag<SignalKind.Start> & [TalkbackFn]; 23 - /** The Push [Signal] carries new values to the sink, like in an event emitter */ 93 + 94 + /** 95 + * Sends a new value to a {@link Sink}. 96 + * 97 + * @remarks 98 + * This signal is sent from a {@link Source} to a {@link Sink} to send a new value to it. This is 99 + * essentially the signal that wraps new values coming in, like an event. Values are carried on 100 + * unary tuples and can be accessed using `signal[0]`. 101 + */ 24 102 export type Push<T> = Tag<SignalKind.Push> & [T]; 25 103 26 - /** A signal that communicates new events to a sink. */ 104 + /** 105 + * Signals are sent from {@link Source | Sources} to {@link Sink | Sinks} to inform them of changes. 106 + * 107 + * @remarks 108 + * A {@link Source}, when consumed, sends a sequence of events to {@link Sink | Sinks}. In order, a 109 + * {@link SignalKind.Start | Start} signal will always be sent first, followed optionally by one or 110 + * more {@link SignalKind.Push | Push signals}, carrying values and representing the stream. A 111 + * {@link Source} will send the {@link SignalKind.End | End signal} when it runs out of values. The 112 + * End signal will be omitted if the Source is cancelled by a 113 + * {@link TalkbackKind.Close | Close signal}, sent back from the {@link Sink}. 114 + * @see {@link SignalKind} for the kinds signals sent by {@link Source | Sources}. 115 + * @see {@link Start} for the data structure of the start signal. 116 + * @see {@link Push} for the data structure of the push signal. 117 + */ 27 118 export type Signal<T> = Start<T> | Push<T> | SignalKind.End; 28 119 29 - /** 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. */ 120 + /** 121 + * Callback function that is called by a {@link Source} with {@link Signal | Signals}. 122 + * 123 + * @remarks 124 + * A Sink is a function that is called repeatedly with signals from a {@link Source}. It represents 125 + * the receiver of the stream of signals/events coming from a {@link Source}. 126 + * @see {@link Signal} for the data structure of signals. 127 + */ 30 128 export type Sink<T> = (signal: Signal<T>) => void; 31 - /** A source is a function that accepts a [Sink] and then starts sending [Signal]s to it. */ 129 + 130 + /** Factory function that calls {@link Sink | Sinks} with {@link Signal | Signals} when invoked. 131 + * @remarks 132 + * A Source is a factory function that when invoked with a {@link Sink}, calls it with 133 + * {@link Signal | Signals} to create a stream of events, informing it of new values and the 134 + * potential end of the stream of values. The first signal a Source sends is always a 135 + * {@link Start | Start signal} that sends a talkback function to the {@link Sink}, so it may request 136 + * new values or cancel the source. 137 + * 138 + * @see {@link Signal} for the data structure of signals. 139 + * @see {@link Sink} for the data structure of sinks. 140 + */ 32 141 export type Source<T> = (sink: Sink<T>) => void; 33 - /** An operator transforms a [Source] and returns a new [Source], potentially with different timings or output types. */ 142 + 143 + /** Transform function that accepts a {@link Source} and returns a new one. 144 + * @remarks 145 + * Wonka comes with several helper operators that transform a given {@link Source} into a new one, 146 + * potentially changing its outputs, or the outputs' timing. An "operator" in Wonka typically 147 + * accepts arguments and then returns this kind of function, so they can be chained and composed. 148 + * 149 + * @see {@link pipe | `pipe`} for the helper used to compose operators. 150 + */ 34 151 export type Operator<In, Out> = (a: Source<In>) => Source<Out>; 35 152 36 - /** Extracts the type of a given Source */ 153 + /** Type utility to determine the type of a {@link Source}. */ 37 154 export type TypeOfSource<T> = T extends Source<infer U> ? U : never; 38 155 156 + /** Subscription object that can be used to cancel a {@link Source}. 157 + * @see {@link subscribe | subscribe sink} for a helper that returns this structure. 158 + */ 39 159 export interface Subscription { 160 + /** 161 + * Cancels a {@link Source} to stop the subscription from receiving new values. 162 + * 163 + * @see {@link TalkbackKind.Close | Close signal} This uses the {@link TalkbackFn | talkback function} to send a {@link TalkbackKind.Close | Close signal} 164 + * to the subscribed-to {@link Source} to stop it from sending new values. This cleans up the subscription 165 + * and ends it immediately. 166 + */ 40 167 unsubscribe(): void; 41 168 } 42 169 170 + /** An Observer represents sending signals manually to a {@link Sink}. 171 + * @remarks 172 + * The Observer is used whenever a utility allows for signals to be sent manually as a {@link Source} 173 + * would send them. 174 + * 175 + * @see {@link make | `make` source} for a helper that uses this structure. 176 + */ 43 177 export interface Observer<T> { 178 + /** Sends a new value to the receiving Sink. 179 + * @remarks 180 + * This creates a {@link Push | Push signal} that is sent to a {@link Sink}. 181 + */ 44 182 next(value: T): void; 183 + /** Indicates to the receiving Sink that no more values will be sent. 184 + * @remarks 185 + * This creates an {@link SignalKind.End | End signal} that is sent to a {@link Sink}. The Observer 186 + * will accept no more values via {@link Observer.next | `next` calls} once this method has been 187 + * invoked. 188 + */ 45 189 complete(): void; 46 190 } 47 191 192 + /** Subjects combine a {@link Source} with the {@link Observer} that is used to send values on said Source. 193 + * @remarks 194 + * A Subject is used whenever an event hub-like structure is needed, as it both provides the 195 + * {@link Observer}'s methods to send signals, as well as the `source` to receive said signals. 196 + * 197 + * @see {@link makeSubject | `makeSubject` source} for a helper that creates this structure. 198 + */ 48 199 export interface Subject<T> extends Observer<T> { 200 + /** The {@link Source} that issues the signals as the {@link Observer} methods are called. */ 49 201 source: Source<T>; 50 202 }