import { Subscriber, IObserver, IntoObservable, IObservable } from "./types" import { Subscription } from "./subscription" import { Emitter } from "./emitter" export class Observable implements IObservable { private _subscriber: Subscriber constructor(subscriber: Subscriber) { if (typeof subscriber != 'function') throw new TypeError(`Subscriber must be a function`) this._subscriber = subscriber } subscribe(observer: IObserver): Subscription subscribe( next: (val: T) => any, error?: (err: Error) => void, complete?: (val: any) => void, ): Subscription subscribe( next: ((val: T) => any) | IObserver, error?: (err: Error) => any, complete?: (val: any) => any, ): Subscription { let observer if (typeof next == 'function') observer = {next, error, complete} else if (next && typeof next == 'object') observer = next else throw new TypeError(`Invalid observer type`) const subscription = new Subscription() const emitter = new Emitter(observer) emitter.next = emitter.next.bind(emitter) emitter.error = emitter.error.bind(emitter) emitter.complete = emitter.complete.bind(emitter) try { if (observer.start) observer.start(subscription) if (!subscription.closed) { const unsubscriber = this._subscriber(emitter) subscription._setUnsubscriber(unsubscriber) emitter._subscription = subscription if (emitter.closed) subscription.unsubscribe() } } catch (err) { emitter.error(err) } return subscription } static of(...xs: T[]): Observable { const Ctor = typeof this == 'function' ? this : Observable return new Ctor(observer => { for (const item of xs) observer.next(item) observer.complete() }) } static from($: IntoObservable): Observable { const Ctor = typeof this == 'function' ? this : Observable if ($ instanceof Ctor) { return $ } if ($ && typeof ($ as IObservable).subscribe == 'function') { return new Ctor(emit => ($ as IObservable).subscribe(emit) ) } else if ($ && typeof ($ as Iterable)[Symbol.iterator] == 'function') { return new Ctor(emit => { for (const item of $ as Iterable) emit.next(item) emit.complete() }) } else if ($ && typeof ($ as AsyncIterable)[Symbol.asyncIterator] == 'function') { return new Ctor(emit => { let closed = false const run = async () => { for await (const item of $ as AsyncIterable) { if (closed) break emit.next(item) } } run() .then(() => { if (!closed) emit.complete() }) .catch(err => { if (!closed) emit.error(err) }) return () => { closed = true } }) } else if ($ && typeof ($ as Promise).then == 'function') { return new Ctor(emit => { ($ as Promise) .then(val => { emit.next(val) emit.complete() }) .catch(err => { emit.error(err) }) }) } else throw new TypeError(`Invalid argument`) } }