reactor-core-ts
Version:
The Reactive-Streams based implementation of Reactor-Core
1 lines • 113 kB
Source Map (JSON)
{"version":3,"sources":["../src/publishers/Publisher.ts","../src/sinks/BackpressureSink.ts","../src/sinks/OneSink.ts","../src/sinks/ManySink.ts","../src/sinks/ReplaySink.ts","../src/sinks/ReplayAllSink.ts","../src/sinks/ReplayLatestSink.ts","../src/sinks/ReplayLimitSink.ts","../src/sinks/index.ts","../src/publishers/PipePublisher.ts","../src/utils/index.ts","../src/schedulers/MicroScheduler.ts","../src/schedulers/DelayScheduler.ts","../src/publishers/Flux.ts","../src/publishers/Mono.ts","../src/schedulers/ImmediateScheduler.ts","../src/schedulers/MacroScheduler.ts","../src/schedulers/IntervalScheduler.ts","../src/schedulers/index.ts"],"sourcesContent":["import {EmitAction} from \"@/sinks/BackpressureSink\";\nimport {Subscriber} from \"@/subscriptions/Subscriber\";\nimport {Subscription} from \"@/subscriptions/Subscription\";\n\n/**\n * Represents a generic publisher that can subscribe to a data stream.\n * @template T - The type of data being published.\n */\nexport interface Publisher<T> {\n /**\n * Subscribes a subscriber to the publisher.\n * @param {Subscriber<T>} subscriber - The subscriber to receive published data.\n * @returns {Subscription} A subscription object to manage the subscriber's lifecycle.\n */\n subscribe(subscriber: Subscriber<T>): Subscription\n}\n\n/**\n * A publisher that supports backpressure handling.\n * Ensures that the subscriber receives data only as requested, preventing data loss or overflow.\n * @template T - The type of data being published.\n */\nexport class BackpressurePublisher<T> implements Publisher<T> {\n private backpressure: Array<EmitAction<T>> = []\n private subscriber?: Subscriber<T>\n private requested: number = 0;\n private subscription: Subscription\n\n public constructor(sink: Publisher<T>) {\n this.subscription = sink.subscribe({\n onNext: (value: T) => {\n this.backpressure.push({emit: 'next', data: value})\n this.flush()\n },\n onError: (error: Error) => {\n this.backpressure.push({emit: 'error', data: error})\n this.flush()\n },\n onComplete: () => {\n this.subscription.unsubscribe()\n this.backpressure.push({emit: 'complete'})\n this.flush()\n }\n })\n this.subscription.request(Number.MAX_SAFE_INTEGER)\n }\n\n /**\n * Subscribes a subscriber to this publisher.\n * Throws an error if a subscriber is already registered (unicast).\n * @param {Subscriber<T>} subscriber - The subscriber to receive data.\n * @returns {Subscription} A subscription for managing data flow and lifecycle.\n * @throws {Error} If the publisher already has a subscriber.\n */\n public subscribe(subscriber: Subscriber<T>): Subscription {\n if (this.subscriber != null) throw new Error(\"Backpressure unicast publisher is not accepting new subscribers\")\n this.subscriber = subscriber;\n return {\n request: (count: number) => {\n this.requested += count\n this.flush()\n },\n unsubscribe: () => {\n this.backpressure = []\n this.subscription.unsubscribe()\n }\n };\n }\n\n private emit(action: EmitAction<T>) {\n switch (action.emit) {\n case \"next\": {\n try {\n this.subscriber?.onNext(action.data as T)\n } catch (error) {\n this.subscriber?.onError(error as Error)\n }\n break;\n }\n case \"error\": {\n this.subscriber?.onError(action.data as Error)\n break;\n }\n case \"complete\": {\n this.subscriber?.onComplete()\n }\n }\n }\n\n private flush() {\n while (this.requested > 0 && this.backpressure.length > 0) {\n this.requested--\n this.emit(this.backpressure.shift() as EmitAction<T>)\n }\n if (this.subscriber != null && this.backpressure[0]?.emit == 'complete') {\n this.emit(this.backpressure.shift() as EmitAction<T>)\n }\n }\n\n}","import {Publisher} from \"@/publishers/Publisher\";\nimport {Sink} from \"@/sinks/Sink\";\nimport {Subscriber} from \"@/subscriptions/Subscriber\";\nimport {Subscription} from \"@/subscriptions/Subscription\";\n\n/**\n * Represents a backpressure control structure.\n * Stores the subscriber, buffered data, and the number of requested items.\n *\n * @template T - The type of data being processed.\n */\ntype Backpressure<T> = {\n subscriber: Subscriber<T>\n data: EmitAction<T>[]\n requested: number;\n}\n\n/**\n * Represents an action emitted by the sink.\n * Contains the type of emission (next, error, complete) and the associated data.\n *\n * @template T - The type of data being emitted.\n */\nexport type EmitAction<T> = {\n emit: 'next' | 'error' | 'complete'\n data?: T | Error\n}\n\n/**\n * An abstract sink that supports backpressure handling.\n * Manages the flow of data to multiple subscribers while respecting backpressure demands.\n *\n * @template T - The type of data being emitted.\n */\nexport abstract class BackpressureSink<T> implements Sink<T>, Publisher<T> {\n protected readonly subscribers = new Set<Backpressure<T>>()\n protected completed = false\n\n /**\n * Subscribes a subscriber to the sink with backpressure handling.\n *\n * @param {Subscriber<T>} subscriber - The subscriber to add.\n * @returns {Subscription} The subscription object for managing the subscriber's lifecycle.\n */\n public subscribe(subscriber: Subscriber<T>): Subscription {\n // if (this.completed) throw new Error('The completed sink is not accepting new subscribers.')\n const backpressure: Backpressure<T> = {\n subscriber: subscriber,\n data: [],\n requested: 0,\n }\n\n this.subscribers.add(backpressure)\n\n return {\n /**\n * Requests a specific number of items from the sink.\n * Increments the requested count and triggers data flushing.\n * @param {number} count - The number of items to request.\n */\n request: (count: number) => {\n backpressure.requested += count\n this.flush(backpressure)\n },\n /**\n * Unsubscribes from the sink, clearing the buffered data.\n */\n unsubscribe: () => {\n backpressure.data = []\n this.subscribers.delete(backpressure)\n }\n }\n }\n\n /**\n * Emits the next value to all subscribers.\n * Validates emission and triggers flushing of buffered data.\n *\n * @param {T} value - The value to emit.\n * @throws {Error} If the sink has already completed.\n */\n public next(value: T): void {\n this.validateEmit()\n for (const subscriber of this.subscribers) {\n subscriber.data.push({emit: 'next', data: value})\n this.flush(subscriber)\n }\n }\n\n /**\n * Emits an error to all subscribers and marks the sink as completed.\n *\n * @param {Error} error - The error to emit.\n * @throws {Error} If the sink has already completed.\n */\n public error(error: Error): void {\n this.validateEmit()\n for (const subscriber of this.subscribers) {\n subscriber.data.push({emit: 'error', data: error})\n this.flush(subscriber)\n }\n }\n\n /**\n * Completes the sink, notifying all subscribers.\n * Prevents further emissions and clears the subscriber set.\n */\n public complete(): void {\n if (this.completed) return\n this.completed = true;\n for (const subscriber of this.subscribers) {\n subscriber.data.push({emit: 'complete'})\n this.flush(subscriber)\n }\n this.subscribers.clear()\n }\n\n /**\n * Emits an action to a specific subscriber.\n * Handles 'next', 'error', and 'complete' emissions.\n *\n * @protected\n * @param {EmitAction<T>} action - The action to be emitted.\n * @param {Subscriber<T>} subscriber - The subscriber to receive the action.\n */\n protected emit(action: EmitAction<T>, subscriber: Subscriber<T>) {\n switch (action.emit) {\n case \"next\": {\n try {\n subscriber.onNext(action.data as T)\n } catch (error) {\n subscriber.onError(error as Error)\n }\n break;\n }\n case \"error\": {\n subscriber.onError(action.data as Error)\n break;\n }\n case \"complete\": {\n subscriber.onComplete()\n }\n }\n }\n\n /**\n * Validates whether the sink can emit new data.\n * Throws an error if the sink has already completed.\n *\n * @protected\n * @throws {Error} If the sink has already completed.\n */\n protected validateEmit() {\n if (this.completed) throw new Error('The completed sink is not accepting new emits.')\n }\n\n /**\n * Flushes buffered data to the subscriber based on the requested count.\n * Ensures that only the requested number of items are sent.\n *\n * @private\n * @param {Backpressure<T>} backpressure - The backpressure object for the subscriber.\n */\n private flush(backpressure: Backpressure<T>) {\n const data = backpressure.data;\n while (backpressure.requested > 0 && data.length > 0) {\n backpressure.requested--\n this.emit(data.shift() as EmitAction<T>, backpressure.subscriber)\n }\n if (data.length > 0 && data[0].emit == \"complete\") {\n backpressure.requested++\n this.flush(backpressure)\n }\n }\n}","import {BackpressureSink} from \"@/sinks/BackpressureSink\";\nimport {Subscriber} from \"@/subscriptions/Subscriber\";\nimport {Subscription} from \"@/subscriptions/Subscription\";\n\n/**\n * A sink that only allows a single subscriber and emits one value before completing.\n * Extends `BackpressureSink` and enforces a unicast pattern.\n * Suitable for scenarios where only one data emission is expected.\n *\n * @template T - The type of data being emitted.\n */\nexport default class OneSink<T> extends BackpressureSink<T> {\n /**\n * Subscribes a single subscriber to the sink.\n * Throws an error if a second subscriber attempts to subscribe.\n *\n * @param {Subscriber<T>} subscriber - The subscriber to add.\n * @returns {Subscription} The subscription object for managing the subscriber's lifecycle.\n * @throws {Error} If more than one subscriber attempts to subscribe.\n */\n public override subscribe(subscriber: Subscriber<T>): Subscription {\n if (this.subscribers.size > 0) {\n throw new Error(\"Only one subscriber is allowed for OneSink.\")\n }\n return super.subscribe(subscriber)\n }\n\n /**\n * Emits a single value and immediately completes the stream.\n * Suitable for cases where only one value is expected.\n *\n * @param {T} value - The value to emit.\n */\n public override next(value: T): void {\n super.next(value)\n this.complete()\n }\n\n /**\n * Emits an error and immediately completes the stream.\n * Ensures that the stream is closed after an error is emitted.\n *\n * @param {Error} error - The error to emit.\n */\n public error(error: Error): void {\n super.error(error)\n this.complete()\n }\n}\n","import {BackpressureSink} from \"@/sinks/BackpressureSink\";\n\n/**\n * A sink that supports multiple subscribers and handles backpressure.\n * Extends `BackpressureSink` to allow broadcasting emitted values to multiple subscribers.\n *\n * @template T - The type of data being emitted.\n */\nexport default class ManySink<T> extends BackpressureSink<T> {\n // Inherits all behavior from BackpressureSink without any modifications.\n}","import ManySink from \"@/sinks/ManySink\";\nimport {EmitAction} from \"@/sinks/BackpressureSink\";\nimport {Subscriber} from \"@/subscriptions/Subscriber\";\nimport {Subscription} from \"@/subscriptions/Subscription\";\n\n/**\n * A sink that stores emitted values and replays them to new subscribers.\n * Extends the `ManySink` to support replaying previously emitted events.\n * Useful in scenarios where late subscribers need to receive historical data.\n *\n * @template T - The type of data being emitted.\n */\nexport abstract class ReplaySink<T> extends ManySink<T> {\n readonly buffer: EmitAction<T>[] = []\n\n /**\n * Emits a value to all current subscribers and stores it in the buffer for future replay.\n * @param {T} value - The value to emit.\n */\n public override next(value: T): void {\n super.next(value)\n this.store('next', value)\n }\n\n /**\n * Emits an error to all current subscribers and stores it in the buffer for future replay.\n * @param {Error} error - The error to emit.\n */\n public override error(error: Error) {\n super.error(error);\n this.store('error', error)\n }\n\n /**\n * Signals the completion of the data stream to all current subscribers.\n * Stores the completion event in the buffer for future replay.\n */\n public override complete() {\n super.complete();\n this.store(\"complete\")\n }\n\n /**\n * Subscribes a subscriber to the sink.\n * Replays all buffered emits to the new subscriber upon subscription.\n * @param {Subscriber<T>} subscriber - The subscriber to add.\n * @returns {Subscription} The subscription object for managing the subscriber's lifecycle.\n */\n public override subscribe(subscriber: Subscriber<T>): Subscription {\n let left = this.buffer.length\n if(left == 0) return super.subscribe(subscriber)\n const replay = new ManySink<T>()\n const i = replay.subscribe(subscriber)\n for (const action of this.buffer) replay[action.emit](action.data as any)\n const o = super.subscribe({\n onNext: value => {\n replay.next(value)\n },\n onError: error => {\n replay.error(error)\n },\n onComplete: () => {\n replay.complete()\n }\n })\n return {\n request(count: number) {\n i.request(count)\n left = left - count\n if(left < 0) {\n o.request(left * -1)\n left = 0\n }\n },\n unsubscribe() {\n i.unsubscribe()\n o.unsubscribe()\n }\n }\n }\n\n /**\n * Stores an emitted action (next, error, complete) in the buffer.\n * @protected\n * @param {'next' | 'error' | 'complete'} emit - The type of emission.\n * @param {T | Error} [data] - The data associated with the emission, if any.\n */\n protected store(emit: 'next' | 'error' | 'complete', data?: T | Error) {\n this.buffer.push({emit, data})\n }\n}","import {ReplaySink} from \"@/sinks/ReplaySink\";\n\n/**\n * A replay sink that retains all emitted events indefinitely.\n * Extends `ReplaySink` to store every event without any limitation.\n *\n * @template T - The type of data being emitted.\n */\nexport class ReplayAllSink<T> extends ReplaySink<T> {\n // Inherits all behavior from ReplaySink without any modifications.\n}\n","import {ReplaySink} from \"@/sinks/ReplaySink\";\n\n/**\n * A replay sink that only retains the latest emitted events up to a specified limit.\n * Extends `ReplaySink` to store a fixed number of the most recent events.\n *\n * @template T - The type of data being emitted.\n */\nexport class ReplayLatestSink<T> extends ReplaySink<T> {\n /**\n * Creates a new `ReplayLatestSink` with a specified limit on the number of stored events.\n * Ensures that only the most recent events are kept, discarding older ones.\n *\n * @param {number} limit - The maximum number of recent events to retain.\n * @throws {Error} If the limit is less than 1.\n */\n public constructor(private readonly limit: number) {\n super()\n if (limit < 1) throw new Error(\"LatestSink: limit must be > 0\")\n }\n\n /**\n * Stores an emitted action (next, error, complete) in the buffer.\n * Keeps only the most recent events, removing older ones when the limit is exceeded.\n *\n * @protected\n * @param {'next' | 'error' | 'complete'} emit - The type of emission.\n * @param {T | Error} [data] - The data associated with the emission, if any.\n */\n protected override store(emit: \"next\" | \"error\" | \"complete\", data?: Error | T) {\n super.store(emit, data)\n if (this.buffer.length > this.limit) this.buffer.shift()\n }\n}","import {ReplaySink} from \"@/sinks/ReplaySink\";\n\n/**\n * A replay sink that limits the number of stored events.\n * Extends `ReplaySink` to restrict the buffer size to a specified limit.\n *\n * @template T - The type of data being emitted.\n */\nexport class ReplayLimitSink<T> extends ReplaySink<T> {\n /**\n * Creates a new `ReplayLimitSink` with a specified limit on the number of stored events.\n * Throws an error if the limit is less than 1.\n *\n * @param {number} limit - The maximum number of events to retain in the buffer.\n * @throws {Error} If the limit is less than 1.\n */\n public constructor(private readonly limit: number) {\n super()\n if (limit < 1) throw new Error(\"LimitSink: limit must be > 0\")\n }\n\n /**\n * Stores an emitted action (next, error, complete) in the buffer.\n * Ensures that the buffer does not exceed the specified limit.\n * @protected\n * @param {'next' | 'error' | 'complete'} emit - The type of emission.\n * @param {T | Error} [data] - The data associated with the emission, if any.\n */\n protected override store(emit: \"next\" | \"error\" | \"complete\", data?: Error | T) {\n if (this.buffer.length < this.limit) super.store(emit, data)\n }\n}","import {Sink} from \"@/sinks/Sink\";\nimport OneSink from \"@/sinks/OneSink\";\nimport ManySink from \"@/sinks/ManySink\";\nimport {ReplayAllSink} from \"@/sinks/ReplayAllSink\";\nimport {ReplayLatestSink} from \"@/sinks/ReplayLatestSink\";\nimport {ReplayLimitSink} from \"@/sinks/ReplayLimitSink\";\n\nexport {type Sink, OneSink, ManySink, ReplayAllSink, ReplayLatestSink, ReplayLimitSink}\n/**\n * A collection of commonly used sinks for data emission and subscription management.\n * Provides factory functions for creating instances of various sink types.\n */\nexport const Sinks = {\n /**\n * Creates a new `OneSink` instance.\n * Suitable for single-value emissions with unicast behavior.\n *\n * @template T - The type of data being emitted.\n * @returns {OneSink<T>} An instance of `OneSink`.\n */\n one: <T>(): OneSink<T> => new OneSink(),\n many: () => ({\n /**\n * Creates a new `ManySink` instance for multicast data emission.\n * Allows broadcasting data to multiple subscribers.\n *\n * @template T - The type of data being emitted.\n * @returns {ManySink<T>} An instance of `ManySink`.\n */\n multicast: <T>(): ManySink<T> => new ManySink(),\n replay: () => ({\n /**\n * Creates a `ReplayAllSink` instance that stores all emitted events.\n * Allows replaying the entire event history to new subscribers.\n *\n * @template T - The type of data being emitted.\n * @returns {ReplayAllSink<T>} An instance of `ReplayAllSink`.\n */\n all: <T>(): ReplayAllSink<T> => new ReplayAllSink(),\n /**\n * Creates a `ReplayLatestSink` instance that stores the most recent N events.\n * Allows replaying the latest events to new subscribers.\n *\n * @template T - The type of data being emitted.\n * @param {number} limit - The maximum number of recent events to retain.\n * @returns {ReplayLatestSink<T>} An instance of `ReplayLatestSink`.\n * @throws {Error} If the limit is less than 1.\n */\n latest: <T>(limit: number): ReplayLatestSink<T> => new ReplayLatestSink(limit),\n /**\n * Creates a `ReplayLimitSink` instance that stores up to a specified number of events.\n * Useful when keeping the entire event history is unnecessary.\n *\n * @template T - The type of data being emitted.\n * @param {number} limit - The maximum number of events to retain.\n * @returns {ReplayLimitSink<T>} An instance of `ReplayLimitSink`.\n * @throws {Error} If the limit is less than 1.\n */\n limit: <T>(limit: number): ReplayLimitSink<T> => new ReplayLimitSink(limit)\n })\n })\n}","import {BackpressurePublisher, Publisher} from \"@/publishers/Publisher\";\nimport {Subscriber} from \"@/subscriptions/Subscriber\";\nimport {Subscription} from \"@/subscriptions/Subscription\";\nimport {Scheduler} from \"@/schedulers/Scheduler\";\nimport {OneSink, Sink} from \"@/sinks\";\n\n/**\n * An interface representing a publisher that supports data transformation and manipulation through pipes.\n * @template T - The type of data being published.\n */\nexport interface PipePublisher<T> extends Publisher<T> {\n /**\n * Pipes the data through custom transformations.\n * @template R - The result type after processing.\n * @param {Function} producer - The function to produce new values.\n * @param {Function} onSubscribe - Callback on subscription.\n * @param {Function} onRequest - Callback on request.\n * @param {Function} onUnsubscribe - Callback on unsubscribe.\n * @returns {PipePublisher<R>} A new pipe publisher with transformed data.\n */\n pipe<R>(producer: (onNext: (value: R) => void, onError: (error: Error) => void, onComplete: () => void) => void, onRequest: (request: number) => void, onUnsubscribe: () => void): PipePublisher<R>\n\n /**\n * Transforms each emitted value using the given function.\n * @template R - The transformed data type.\n * @param {Function} fn - The mapping function.\n * @returns {PipePublisher<R>} A new pipe publisher with mapped data.\n */\n map<R>(fn: (value: T) => R): PipePublisher<R>\n\n /**\n * Transforms each emitted value and filter transform result.\n * @template R - The transformed data type.\n * @param {Function} fn - The mapping function that can return null or undefined.\n * @returns {PipePublisher<R>} A new pipe publisher with mapped non-null data.\n */\n mapNotNull<R>(fn: (value: T) => R | null | undefined): PipePublisher<R>\n\n /**\n * Transforms the value using a function that returns a new publisher.\n * @template R - The resulting data type.\n * @param {Function} fn - The function to transform values into new publishers.\n * @returns {PipePublisher<R>} A pipe publisher that flattens the result.\n */\n flatMap<R>(fn: (value: T) => Publisher<R>): PipePublisher<R>\n\n /**\n * Filters emitted values using a given predicate.\n * @param {Function} predicate - The function to determine whether to emit a value.\n * @returns {PipePublisher<T>} A new pipe publisher with filtered data.\n */\n filter(predicate: (value: T) => boolean): PipePublisher<T>\n\n /**\n * Filters emitted values based on a publisher that returns a boolean.\n * @param {Function} predicate - A function returning a boolean publisher.\n * @returns {PipePublisher<T>} A new pipe publisher with conditional data.\n */\n filterWhen(predicate: (value: T) => Publisher<boolean>): PipePublisher<T>\n\n /**\n * Casts the current publisher to another type.\n * @template R - The target type.\n * @returns {PipePublisher<R>} The casted pipe publisher.\n */\n cast<R>(): PipePublisher<R>\n\n /**\n * Switches to an alternative publisher if the current one is empty.\n * @param {Publisher<T>} alternative - The alternative publisher.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n switchIfEmpty(alternative: Publisher<T>): PipePublisher<T>\n\n /**\n * Continues with a replacement publisher if an error occurs.\n * @param {Publisher<T>} replacement - The publisher to switch to on error.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n onErrorReturn(replacement: Publisher<T>): PipePublisher<T>\n\n /**\n * Continues processing even if an error occurs based on a predicate.\n * @param {Function} predicate - Function to determine whether to continue on error.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n onErrorContinue(predicate: (error: Error) => boolean): PipePublisher<T>\n\n /**\n * Executes a function when the first value is emitted.\n * @param {Function} fn - The function to execute.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n doFirst(fn: () => void): PipePublisher<T>\n\n /**\n * Executes a function when each value is emitted.\n * @param {Function} fn - The function to execute on each value.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n doOnNext(fn: (value: T) => void): PipePublisher<T>\n\n /**\n * Executes a function when each error is emitted.\n * @param {Function} fn - The function to execute on each error.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n doOnError(fn: (value: Error) => void): PipePublisher<T>\n\n /**\n * Executes a function when the stream completes.\n * @param {Function} fn - The function to execute on completion.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n doFinally(fn: () => void): PipePublisher<T>\n\n /**\n * Executes a function when a subscription occurs.\n * @param {Function} fn - The function to execute on subscription.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n doOnSubscribe(fn: (subscription: Subscription) => void): PipePublisher<T>\n\n /**\n * Publishes values on a specified scheduler.\n * @param {Scheduler} scheduler - The scheduler to use.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n publishOn(scheduler: Scheduler): PipePublisher<T>\n\n /**\n * Subscribes to the stream on a specified scheduler.\n * @param {Scheduler} scheduler - The scheduler to use.\n * @returns {PipePublisher<T>} A new pipe publisher.\n */\n subscribeOn(scheduler: Scheduler): PipePublisher<T>\n}\n\n/**\n * Abstract base class for pipe publishers.\n * Implements common functionalities while allowing customization.\n * @template T - The type of data being published.\n */\nexport abstract class AbstractPipePublisher<T> implements PipePublisher<T> {\n private unsubscribeOnComplete = true\n private onSubscribe?: (subscription: Subscription) => void\n\n protected constructor(protected readonly publisher: Publisher<T>) {\n }\n\n public subscribe({\n onNext = (value: T) => {\n },\n onError = (error: Error) => {\n },\n onComplete = () => {\n }\n } = {}): Subscription {\n const subscription = this.publisher.subscribe({\n onNext, onError, onComplete: () => {\n onComplete()\n if (this.unsubscribeOnComplete) subscription.unsubscribe()\n }\n })\n this.onSubscribe?.(subscription)\n return subscription\n }\n\n private canEmitMany() {\n return !(Reflect.construct(Reflect.getPrototypeOf(this)!.constructor, [null]).createSink() instanceof OneSink)\n }\n\n public pipe<R>(producer: (onNext: (value: R) => void, onError: (error: Error) => void, onComplete: () => void) => void, onRequest?: (request: number) => void, onUnsubscribe?: () => void, constructor?: new (publisher: Publisher<R>) => AbstractPipePublisher<R>): PipePublisher<R> {\n // todo сделать так, чтобы конечный пайп мог быть inline, для большего удобства\n const sink = Reflect.construct(constructor || Reflect.getPrototypeOf(this)!.constructor, [null]).createSink();\n const many = !(sink instanceof OneSink)\n const unicast = new class _ extends BackpressurePublisher<R> {\n public override subscribe(subscriber: Subscriber<R>): Subscription {\n try {\n producer(value => {\n if (many && value == null) onRequest?.(1)\n else sink.next(value)\n },\n error => {\n sink.error(error)\n onRequest?.(1)\n },\n () => sink.complete())\n } catch (error) {\n sink.error(error as Error)\n }\n const sub = super.subscribe(subscriber)\n return {\n request(count: number) {\n sub.request(count)\n onRequest?.(count)\n },\n unsubscribe() {\n sub.unsubscribe()\n onUnsubscribe?.()\n }\n };\n }\n }(sink);\n return this.wrap(unicast, constructor)\n }\n\n public map<R>(fn: (value: T) => R): PipePublisher<R> {\n let sub: Subscription;\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext: value => onNext(fn(value)),\n onError,\n onComplete\n }), request => sub?.request(request), () => sub?.unsubscribe())\n }\n\n public mapNotNull<R>(fn: (value: T) => R | null | undefined): PipePublisher<R> {\n return this.map(fn).filter((v): v is R => v != null) as PipePublisher<R>\n }\n\n public flatMap<R>(fn: (value: T) => Publisher<R>): PipePublisher<R> {\n // TODO избавится от Promise и сделать синхронно\n let subscriptions: Subscription[] = [];\n let promises: Promise<void>[] = []\n return this.pipe((onNext, onError, onComplete) => {\n const sub = this.subscribe({\n onNext: (value) => {\n promises.push(new Promise(resolve => {\n let s\n subscriptions.push(s = fn(value).subscribe({\n onNext: value => {\n onNext(value)\n }, onError, onComplete: () => {\n if (this.canEmitMany()) {\n sub.request(1)\n }\n resolve()\n }\n }))\n s.request(Number.MAX_SAFE_INTEGER)\n }\n ))\n }, onError, onComplete: () => {\n Promise.all(promises).then(onComplete)\n }\n })\n subscriptions.push(sub)\n return sub\n }, request => subscriptions[0]?.request(request), () => subscriptions.forEach(sub => sub.unsubscribe()))\n }\n\n public filter(predicate: (value: T) => boolean): PipePublisher<T> {\n let sub: Subscription\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext: (value) => predicate(value) ? onNext(value) : this.canEmitMany() ? onNext(null as T) : onComplete(),\n onError,\n onComplete\n }), request => sub?.request(request + 1), () => sub?.unsubscribe())\n }\n\n public filterWhen(predicate: (value: T) => Publisher<boolean>): PipePublisher<T> {\n let sub: Subscription\n let req = 0\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext: (value) => predicate(value).subscribe({\n onNext: bool => bool ? onNext(value) : this.canEmitMany() ? onNext(null as T) : onComplete(),\n onError,\n onComplete: () => this.canEmitMany() ? () => {\n } : onComplete\n }).request(req), onError, onComplete\n }), request => sub?.request(req = request + 1), () => sub?.unsubscribe())\n }\n\n public cast<R>(): PipePublisher<R> {\n return this as unknown as PipePublisher<R>\n }\n\n public switchIfEmpty(alternative: Publisher<T>): PipePublisher<T> {\n let sub: Subscription\n let req = 0\n return this.pipe((onNext, onError, onComplete) => {\n let emitted = false;\n sub = this.subscribe({\n onNext(value: T) {\n emitted = true\n onNext(value)\n },\n onError(error: Error) {\n emitted = true\n onError(error)\n },\n onComplete: () => {\n return emitted ? onComplete() : alternative.subscribe({onNext, onError, onComplete}).request(req)\n }\n })\n }, request => sub?.request(req = request), () => sub?.unsubscribe())\n }\n\n public onErrorReturn(replacement: Publisher<T>): PipePublisher<T> {\n let sub: Subscription\n let req = 0\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext,\n onError: () => replacement.subscribe({onNext, onError, onComplete}).request(req),\n onComplete\n }), request => sub?.request(req = request), () => sub?.unsubscribe())\n }\n\n public onErrorContinue(predicate: (error: Error) => boolean): PipePublisher<T> {\n let sub: Subscription\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext,\n onError: error => predicate(error) ? this.canEmitMany() ? onNext(null as T) : onComplete() : onError(error),\n onComplete\n }), request => sub?.request(request + 1), () => sub?.unsubscribe())\n }\n\n public doFirst(fn: () => void): PipePublisher<T> {\n let sub: Subscription\n return this.pipe((onNext, onError, onComplete) => {\n fn()\n sub = this.subscribe({onNext, onError, onComplete})\n }, request => sub?.request(request), () => sub?.unsubscribe()\n )\n }\n\n public doOnNext(fn: (value: T) => void): PipePublisher<T> {\n let sub: Subscription\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext: value => {\n fn(value)\n onNext(value)\n }, onError, onComplete\n }), request => sub?.request(request), () => sub?.unsubscribe())\n }\n\n public doOnError(fn: (value: Error) => void): PipePublisher<T> {\n let sub: Subscription\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext, onError: error => {\n fn(error)\n onError(error)\n }, onComplete\n }), request => sub?.request(request), () => sub?.unsubscribe())\n }\n\n public doFinally(fn: () => void): PipePublisher<T> {\n let sub: Subscription\n let called = false;\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext, onError, onComplete\n }), request => sub?.request(request), () => {\n sub?.unsubscribe()\n if (!called) {\n called = true\n fn()\n }\n })\n }\n\n public doOnSubscribe(fn: (subscription: Subscription) => void): PipePublisher<T> {\n let sub: Subscription\n const prev = this.onSubscribe\n this.onSubscribe = (subscription) => {\n prev?.(subscription)\n fn(subscription)\n }\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext,\n onError,\n onComplete\n }), request => sub?.request(request), () => sub?.unsubscribe())\n }\n\n public publishOn(scheduler: Scheduler): PipePublisher<T> {\n let sub: Subscription\n return this.pipe((onNext, onError, onComplete) =>\n sub = this.subscribe({\n onNext: value => scheduler.schedule(() => onNext(value)),\n onError: error => scheduler.schedule(() => onError(error)),\n onComplete: () => scheduler.schedule(() => onComplete())\n }), request => sub?.request(request), () => sub?.unsubscribe())\n }\n\n public subscribeOn(scheduler: Scheduler): PipePublisher<T> {\n let sub: Promise<Subscription>\n return this.pipe((onNext, onError, onComplete) =>\n sub = new Promise(resolve => scheduler.schedule(() => resolve(this.subscribe({\n onNext,\n onError,\n onComplete\n })))), request => sub?.then(value => value.request(request)), () => sub?.then(value => value.unsubscribe())\n )\n }\n\n protected abstract createSink(): Sink<T> & Publisher<T>\n\n private wrap<R>(publisher: Publisher<R>, constructor?: new (publisher: Publisher<R>) => AbstractPipePublisher<R>) {\n this.unsubscribeOnComplete = false\n if (constructor == null) constructor = (Reflect.getPrototypeOf(this) as AbstractPipePublisher<R>).constructor as\n new (publisher: Publisher<R>) => AbstractPipePublisher<R>\n const wrapped: AbstractPipePublisher<R> = Reflect.construct(constructor, [publisher]);\n wrapped.unsubscribeOnComplete = true\n if (this.onSubscribe != undefined) {\n wrapped.onSubscribe = this.onSubscribe\n this.onSubscribe = undefined\n }\n return wrapped\n }\n}\n","import {BackpressurePublisher, Publisher} from \"@/publishers/Publisher\";\nimport {Sink} from \"@/sinks/Sink\";\nimport {Subscriber, Subscription} from \"@/subscriptions\";\nimport {ReplayLatestSink} from \"@/sinks\";\nimport {Flux} from \"@/publishers\";\n\n/**\n * Combines a Sink and a generator function into a single Publisher.\n * Uses the BackpressurePublisher as a base to manage data flow and backpressure.\n *\n * @template T - The type of data being published.\n * @param {Sink<T> & Publisher<T>} sink - The sink that acts as both a data consumer and publisher.\n * @param {Function} generator - A function that generates data and pushes it to the sink.\n * @returns {Publisher<T>} A combined Publisher that handles data emission and backpressure.\n */\nexport function combine<T>(sink: Sink<T> & Publisher<T>, generator: ((sink: Sink<T>) => void)): Publisher<T> {\n return new class CombinedPublisher extends BackpressurePublisher<T> {\n public override subscribe(subscriber: Subscriber<T>): Subscription {\n const gen = generator(sink) as unknown as Subscription\n const sub = super.subscribe(subscriber)\n const req = typeof gen?.request == 'function'\n return {\n request(count: number) {\n sub.request(count)\n !req || gen?.request(count)\n },\n unsubscribe() {\n sub.unsubscribe()\n !req || gen?.unsubscribe()\n }\n };\n }\n }(sink)\n}\n\n/**\n * Creates a reactive subject that holds a single mutable value and supports subscriptions.\n * The subject allows updating the value and notifying subscribers of changes.\n *\n * @template T - The type of the value held by the subject.\n * @param {T} value - The initial value of the subject.\n * @returns {Object} An object with methods to interact with the subject.\n * @property {function(T): void} next - Updates the current value and notifies subscribers.\n * @property {function((current: T) => T): void} update - Updates the current value using a function and notifies subscribers.\n * @property {function(): T} get - Returns the current value of the subject.\n * @property {function(Subscriber<T>): Subscription} subscribe - Subscribes to changes and returns a subscription.\n *\n * @example\n * const count = subject(0);\n * count.next(1); // Update the value to 1\n * count.update(prev => prev + 1); // Increment the value\n * console.log(count.get()); // Output: 2\n * const subscription = count.subscribe({\n * onNext: value => console.log('New value:', value),\n * onComplete: () => console.log('Completed')\n * });\n * subscription.request(1); // Request the next value\n * subscription.unsubscribe(); // Stop receiving updates\n */\nexport function subject<T>(value: T) {\n let subject = value;\n const sink = new ReplayLatestSink<T>(1)\n sink.next(value)\n return {\n next(value: T) {\n sink.next(subject = value)\n },\n update(fn: (current: T) => T) {\n this.next(fn(subject))\n },\n get() {\n return subject\n },\n subscribe({\n onNext = (value: T) => {\n },\n onError = (error: Error) => {\n },\n onComplete = () => {\n }\n } = {}): Subscription {\n return Flux.from(sink).distinctUntilChanged().subscribe({onNext, onError, onComplete})\n }\n }\n}","import {Scheduler} from \"@/schedulers/Scheduler\";\n\n/**\n * A scheduler that executes tasks asynchronously using the microtask queue.\n * Implements the `Scheduler` interface and schedules tasks using `Promise.resolve().then(...)`.\n */\nexport class MicroScheduler implements Scheduler {\n /**\n * Schedules a task to be executed asynchronously as a microtask.\n * Uses `Promise.resolve().then(task)` to place the task in the microtask queue.\n * @param {Function} task - The task function to be executed.\n */\n public schedule(task: () => void): void {\n Promise.resolve().then(task)\n }\n}\n","import {CancellableScheduler} from \"@/schedulers/Scheduler\";\n\n/**\n * A scheduler that executes tasks after a specified delay.\n * Implements the `CancellableScheduler` interface, allowing scheduled tasks to be canceled.\n */\nexport class DelayScheduler implements CancellableScheduler {\n private readonly delay: number\n\n /**\n * Creates a new DelayScheduler.\n * @param {number} delay - The delay duration in milliseconds.\n */\n constructor(delay: number) {\n this.delay = delay\n }\n\n /**\n * Schedules a task to be executed after the specified delay.\n * Returns an object with a cancel method to clear the timeout.\n * @param {Function} task - The task function to be executed.\n * @returns {Object} An object with a `cancel` method to stop the execution.\n */\n public schedule(task: () => void): { cancel: () => void } {\n const id = setTimeout(task, this.delay)\n return {\n /**\n * Cancels the scheduled task.\n */\n cancel: () => clearTimeout(id)\n }\n }\n}\n","import {AbstractPipePublisher} from \"@/publishers/PipePublisher\";\nimport {Publisher} from \"@/publishers/Publisher\";\nimport {Sink} from \"@/sinks/Sink\";\nimport ManySink from \"@/sinks/ManySink\";\nimport {Mono} from \"@/publishers/Mono\";\nimport {Scheduler} from \"@/schedulers/Scheduler\";\nimport {combine} from \"@/utils\";\nimport {MicroScheduler} from \"@/schedulers/MicroScheduler\";\nimport {DelayScheduler} from \"@/schedulers/DelayScheduler\";\nimport {Subscription} from \"@/subscriptions/Subscription\";\nimport {Subscriber} from \"@/subscriptions/Subscriber\";\n\n/**\n * Represents a Flux publisher that can emit multiple values over time.\n * Provides rich reactive programming capabilities such as transformation, filtering, and combination.\n * @template T - The type of data being published.\n */\nexport class Flux<T> extends AbstractPipePublisher<T> {\n protected constructor(publisher: Publisher<T>) {\n super(publisher)\n }\n\n /**\n * Generates a Flux instance using a generator function.\n * @template T - The type of data.\n * @param {Function} generator - The function to generate values.\n * @returns {Flux<T>} A new Flux instance.\n */\n public static generate<T>(generator: ((sink: Sink<T>) => void)): Flux<T> {\n return new Flux(combine(new ManySink<T>(), generator))\n }\n\n /**\n * Creates a Flux instance from another publisher.\n * @template T - The type of data.\n * @param {Publisher<T>} publisher - The source publisher.\n * @returns {Flux<T>} A new Flux instance.\n */\n public static from<T>(publisher: Publisher<T>): Flux<T> {\n return Flux.generate(sink => {\n return publisher.subscribe({\n onNext(value: T) {\n sink.next(value)\n },\n onError(error: Error) {\n sink.error(error)\n },\n onComplete() {\n sink.complete()\n }\n })\n })\n }\n\n /**\n * Creates a Flux from an iterable collection.\n * @template T - The type of data.\n * @param {Iterable<T>} iterable - An iterable to create the Flux from.\n * @returns {Flux<T>} A new Flux instance.\n */\n public static fromIterable<T>(iterable: Iterable<T>): Flux<T> {\n return Flux.generate(sink => {\n for (const value of iterable) {\n sink.next(value);\n }\n sink.complete();\n })\n }\n\n /**\n * Creates a Flux that emits a range of numbers.\n * @param {number} start - The starting number.\n * @param {number} count - The number of elements to emit.\n * @returns {Flux<number>} A new Flux emitting the range of numbers.\n */\n public static range(start: number, count: number): Flux<number> {\n return Flux.generate(sink => {\n let current = start;\n for (let i = 0; i < count; i++) {\n sink.next(current++);\n }\n sink.complete();\n })\n }\n\n /**\n * Creates an empty Flux that immediately completes.\n * @template T - The type of data.\n * @returns {Flux<T>} A new empty Flux instance.\n */\n public static empty<T = never>(): Flux<T> {\n return Flux.generate(sink => {\n sink.complete()\n })\n }\n\n /**\n * Defers the creation of a Flux until it is subscribed to.\n * @template T - The type of data.\n * @param {Function} factory - A function that returns a Flux.\n * @returns {Flux<T>} A new deferred Flux instance.\n */\n public static defer<T>(factory: () => Flux<T>): Flux<T> {\n return Flux.generate(sink => factory().subscribe({\n onNext(value: T) {\n sink.next(value)\n },\n onError(error: Error) {\n sink.error(error)\n },\n onComplete() {\n sink.complete()\n }\n }))\n }\n\n /**\n * Returns a Mono emitting the first element of the Flux.\n * @returns {Mono<T>} A Mono containing the first element.\n */\n public first(): Mono<T> {\n let pipeSub: Subscription\n return this.pipe((onNext, onError, onComplete) => {\n pipeSub = this.subscribe({\n onNext,\n onError,\n onComplete\n })\n }, _ => pipeSub?.request(1), () => pipeSub?.unsubscribe(), Mono as any) as unknown as Mono<T>\n }\n\n /**\n * Returns a Mono emitting the last element of the Flux.\n * @returns {Mono<T>} A Mono containing the last element.\n */\n public last(): Mono<T> {\n let pipeSub: Subscription\n return this.pipe((onNext, onError, onComplete) => {\n let lastValue: T | undefined\n let lastError: Error | undefined\n pipeSub = this.subscribe({\n onNext(value: T): void {\n lastError = undefined\n lastValue = value\n },\n onError(error: Error): void {\n lastValue = undefined\n lastError = error\n },\n onComplete(): void {\n if (lastValue != undefined) onNext(lastValue)\n else if