···11+---
22+'wonka': patch
33+---
44+55+Improve compatibility of `fromAsyncIterable` and `toAsyncIterable`. The `toAsyncIterable` will now output an object that's both an `AsyncIterator` and an `AsyncIterable`. Both helpers will now use a polyfill for `Symbol.asyncIterator` to improve compatibility with the Hermes engine and Babel transpilation.
···11import { TalkbackFn, TeardownFn, Start, Push, SignalKind } from './types';
2233+declare global {
44+ interface SymbolConstructor {
55+ readonly observable: symbol;
66+ }
77+}
88+39/** Placeholder {@link TeardownFn | teardown functions} that's a no-op.
410 * @see {@link TeardownFn} for the definition and usage of teardowns.
511 * @internal
···3945 0: value,
4046 } as Push<T>;
4147}
4848+4949+/** Returns the well-known symbol specifying the default AsyncIterator.
5050+ * @internal
5151+ */
5252+export const asyncIteratorSymbol = (): typeof Symbol.asyncIterator =>
5353+ (typeof Symbol === 'function' && Symbol.asyncIterator) || ('@@asyncIterator' as any);
5454+5555+/** Returns the well-known symbol specifying the default ES Observable.
5656+ * @privateRemarks
5757+ * This symbol is used to mark an object as a default ES Observable. By the specification, an object
5858+ * that abides by the default Observable implementation must carry a method set to this well-known
5959+ * symbol that returns the Observable implementation. It's common for this object to be an
6060+ * Observable itself and return itself on this method.
6161+ *
6262+ * @see {@link https://github.com/0no-co/wonka/issues/122} for notes on the intercompatibility
6363+ * between Observable implementations.
6464+ *
6565+ * @internal
6666+ */
6767+export const observableSymbol = (): typeof Symbol.observable =>
6868+ (typeof Symbol === 'function' && Symbol.observable) || ('@@observable' as any);
+1-22
src/observable.ts
···11import { Source, SignalKind, TalkbackKind } from './types';
22-import { push, start, talkbackPlaceholder } from './helpers';
33-44-declare global {
55- interface SymbolConstructor {
66- readonly observable: symbol;
77- }
88-}
22+import { push, start, talkbackPlaceholder, observableSymbol } from './helpers';
93104/** A definition of the ES Observable Subscription type that is returned by
115 * {@link Observable.subscribe}
···117111 /** The well-known symbol specifying the default ES Observable for an object. */
118112 [Symbol.observable](): Observable<T>;
119113}
120120-121121-/** Returns the well-known symbol specifying the default ES Observable.
122122- * @privateRemarks
123123- * This symbol is used to mark an object as a default ES Observable. By the specification, an object
124124- * that abides by the default Observable implementation must carry a method set to this well-known
125125- * symbol that returns the Observable implementation. It's common for this object to be an
126126- * Observable itself and return itself on this method.
127127- *
128128- * @see {@link https://github.com/0no-co/wonka/issues/122} for notes on the intercompatibility
129129- * between Observable implementations.
130130- *
131131- * @internal
132132- */
133133-const observableSymbol = (): typeof Symbol.observable =>
134134- Symbol.observable || ('@@observable' as any);
135114136115/** Converts an ES Observable to a {@link Source}.
137116 * @param input - The {@link ObservableLike} object that will be converted.
+51-40
src/sinks.ts
···11-import { Source, Subscription, TalkbackKind, SignalKind } from './types';
22-import { talkbackPlaceholder } from './helpers';
11+import { Source, Subscription, TalkbackKind, SignalKind, SourceIterable } from './types';
22+import { talkbackPlaceholder, asyncIteratorSymbol } 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.
···124124 * }
125125 * ```
126126 */
127127-export const toAsyncIterable = <T>(source: Source<T>): AsyncIterable<T> => ({
128128- [Symbol.asyncIterator](): AsyncIterator<T> {
129129- const buffer: T[] = [];
127127+export const toAsyncIterable = <T>(source: Source<T>): SourceIterable<T> => {
128128+ const buffer: T[] = [];
130129131131- let ended = false;
132132- let talkback = talkbackPlaceholder;
133133- let next: ((value: IteratorResult<T>) => void) | void;
130130+ let ended = false;
131131+ let started = false;
132132+ let pulled = false;
133133+ let talkback = talkbackPlaceholder;
134134+ let next: ((value: IteratorResult<T>) => void) | void;
134135135135- source(signal => {
136136- if (ended) {
137137- /*noop*/
138138- } else if (signal === SignalKind.End) {
139139- if (next) next = next(doneResult);
140140- ended = true;
141141- } else if (signal.tag === SignalKind.Start) {
142142- (talkback = signal[0])(TalkbackKind.Pull);
143143- } else if (next) {
144144- next = next({ value: signal[0], done: false });
145145- } else {
146146- buffer.push(signal[0]);
136136+ return {
137137+ async next(): Promise<IteratorResult<T>> {
138138+ if (!started) {
139139+ started = true;
140140+ source(signal => {
141141+ if (ended) {
142142+ /*noop*/
143143+ } else if (signal === SignalKind.End) {
144144+ if (next) next = next(doneResult);
145145+ ended = true;
146146+ } else if (signal.tag === SignalKind.Start) {
147147+ pulled = true;
148148+ (talkback = signal[0])(TalkbackKind.Pull);
149149+ } else {
150150+ pulled = false;
151151+ if (next) {
152152+ next = next({ value: signal[0], done: false });
153153+ } else {
154154+ buffer.push(signal[0]);
155155+ }
156156+ }
157157+ });
147158 }
148148- });
149159150150- return {
151151- async next(): Promise<IteratorResult<T>> {
152152- if (ended && !buffer.length) {
153153- return doneResult;
154154- } else if (!ended && buffer.length <= 1) {
155155- talkback(TalkbackKind.Pull);
156156- }
160160+ if (ended && !buffer.length) {
161161+ return doneResult;
162162+ } else if (!ended && !pulled && buffer.length <= 1) {
163163+ pulled = true;
164164+ talkback(TalkbackKind.Pull);
165165+ }
157166158158- return buffer.length
159159- ? { value: buffer.shift()!, done: false }
160160- : new Promise(resolve => (next = resolve));
161161- },
162162- async return(): Promise<IteratorReturnResult<void>> {
163163- if (!ended) next = talkback(TalkbackKind.Close);
164164- ended = true;
165165- return doneResult;
166166- },
167167- };
168168- },
169169-});
167167+ return buffer.length
168168+ ? { value: buffer.shift()!, done: false }
169169+ : new Promise(resolve => (next = resolve));
170170+ },
171171+ async return(): Promise<IteratorReturnResult<void>> {
172172+ if (!ended) next = talkback(TalkbackKind.Close);
173173+ ended = true;
174174+ return doneResult;
175175+ },
176176+ [asyncIteratorSymbol()](): SourceIterable<T> {
177177+ return this;
178178+ },
179179+ };
180180+};
170181171182/** Subscribes to a given source and collects all synchronous values into an array.
172183 * @param source - A {@link Source}.
+11-3
src/sources.ts
···11import { Source, Sink, SignalKind, TalkbackKind, Observer, Subject, TeardownFn } from './types';
22-import { push, start, talkbackPlaceholder, teardownPlaceholder } from './helpers';
22+import {
33+ push,
44+ start,
55+ talkbackPlaceholder,
66+ teardownPlaceholder,
77+ asyncIteratorSymbol,
88+} from './helpers';
39import { share } from './operators';
410511/** Helper creating a Source from a factory function when it's subscribed to.
···4551 * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols}
4652 * for the JS Iterable protocol.
4753 */
4848-export function fromAsyncIterable<T>(iterable: AsyncIterable<T>): Source<T> {
5454+export function fromAsyncIterable<T>(iterable: AsyncIterable<T> | AsyncIterator<T>): Source<T> {
4955 return sink => {
5050- const iterator = iterable[Symbol.asyncIterator]();
5656+ const iterator: AsyncIterator<T> =
5757+ (iterable[asyncIteratorSymbol()] && iterable[asyncIteratorSymbol()]()) || iterable;
5858+5159 let ended = false;
5260 let looping = false;
5361 let pulled = false;
+5
src/types.d.ts
···200200 /** The {@link Source} that issues the signals as the {@link Observer} methods are called. */
201201 source: Source<T>;
202202}
203203+204204+/** Async Iterable/Iterator after having converted a {@link Source}.
205205+ * @see {@link toAsyncIterable} for a helper that creates this structure.
206206+ */
207207+export interface SourceIterable<T> extends AsyncIterator<T>, AsyncIterable<T> {}