123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- /**
- * ## Thinking in Systems
- * systems are a stateful **data-processing machines** which accept input through **input streams**,
- * init and maintain state in **depots** and, in certain conditions, emit values to subscriptions through **output streams**.
- * Systems can specify other systems as dependencies, and can act as singletons in the resulting dependency tree.
- *
- * ### Depots
- *
- * The first, and probably the most critical part to understand are **the depots**
- * mostly because they are somewhat implicit.
- * Unlike other state management paradigms, the depots are not kept in a single data structure.
- * Insted, depots are defined and maintained as stateful streams, stateful transfomers
- * like [[combineLatest]] or stateful operators like[ []withLatestFrom] or [[scan]].
- *
- * **Depots persist values over time**.
- * If it was not for them, the system had to re-receive its entire input state simultaneously in order to calculate the values for its output stream.
- *
- * Of course, strictly speaking, it is possible to implement a pure, stateless system as a form of a complex map/reduce. urx would not mind that ;).
- *
- * ### Input Streams
- *
- * The system receives updates from the rest of the world through values published in its input streams.
- * The streams used can be either stateless (acting as means to send **signals**) or stateful, where the initial stream state acts as the default value for that system parameter.
- *
- * The effects of the input streams are up to the system data-processing logic. It can change its depots' state, and/or emit values through its output streams.
- *
- * ### Data Processing
- *
- * The actual system behavior is exclusively implemented by **applying transformers and operators** to the input streams, producing the respective output streams.
- * In the final state the system streams are organized in a directed graph, where incoming data is routed through certain edges and nodes.
- * Simple systems like the one in [urx by example](https://urx.virtuoso.dev/docs/urx-by-example) can use a straightforward single-step transformation (in this case, `combineLatest` and `map`),
- * while complex ones can introduce multiple intermediate streams to model their logic.
- *
- * ### Output Streams
- *
- * The system publishes updates to its clients (other systems or an UI bound to it) by publishing data in its output streams.
- * State-reflecting output streams, like `sum` in the [urx by example](https://urx.virtuoso.dev/docs/urx-by-example) should use stateful streams as output streams.
- * Signal-like output should use regular, stateless streams. In general, stateless input streams tend to have a symmetrical stateless streams, and the opposite.
- *
- * @packageDocumentation
- */
- import { Emitter } from './actions';
- /**
- * Systems are a dictionaries of streams. a [[SystemConstructor]] should return a System.
- */
- export interface System {
- [key: string]: Emitter<any>;
- }
- /**
- * a SystemSpec is the result from a [[system]] call. To obtain the [[System]], pass the spec to [[init]].
- */
- export interface SystemSpec<SS extends SystemSpecs, C extends SystemConstructor<SS>> {
- id: string;
- constructor: C;
- dependencies: SS;
- singleton: boolean;
- }
- /** @internal **/
- export declare type AnySystemSpec = SystemSpec<any, any>;
- /** @internal **/
- export declare type SystemSpecs = AnySystemSpec[];
- /** @internal **/
- export declare type SR<E extends AnySystemSpec, R extends System = ReturnType<E['constructor']>> = R;
- /** @internal **/
- export declare type SpecResults<SS extends SystemSpecs, L = SS['length']> = L extends 0 ? [] : L extends 1 ? [SR<SS[0]>] : L extends 2 ? [SR<SS[0]>, SR<SS[1]>] : L extends 3 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>] : L extends 4 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>] : L extends 5 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>] : L extends 6 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>, SR<SS[5]>] : L extends 7 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>, SR<SS[5]>, SR<SS[6]>] : L extends 8 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>, SR<SS[5]>, SR<SS[6]>, SR<SS[7]>] : L extends 9 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>, SR<SS[5]>, SR<SS[6]>, SR<SS[7]>, SR<SS[8]>] : L extends 10 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>, SR<SS[5]>, SR<SS[6]>, SR<SS[7]>, SR<SS[8]>, SR<SS[9]>] : L extends 11 ? [SR<SS[0]>, SR<SS[1]>, SR<SS[2]>, SR<SS[3]>, SR<SS[4]>, SR<SS[5]>, SR<SS[6]>, SR<SS[7]>, SR<SS[8]>, SR<SS[9]>, SR<SS[10]>] : never;
- /**
- * The system constructor is a function which initializes and connects streams and returns them as a [[System]].
- * If the [[system]] call specifies system dependencies, the constructor receives the dependencies as an array argument.
- */
- export declare type SystemConstructor<D extends SystemSpecs> = (dependencies: SpecResults<D>) => System;
- /**
- * `system` defines a specification of a system - its constructor, dependencies and if it should act as a singleton in a system dependency tree.
- * When called, system returns a [[SystemSpec]], which is then initialized along with its dependencies by passing it to [[init]].
- *
- * ```ts
- * @import { subscribe, publish, system, init, tup, connect, map, pipe } from 'urx'
- *
- * // a simple system with two streams
- * const sys1 = system(() => {
- * const a = stream<number>()
- * const b = stream<number>()
- *
- * connect(pipe(a, map(value => value * 2)), b)
- * return { a, b }
- * })
- *
- * // a second system which depends on the streams from the first one
- * const sys2 = system(([ {a, b} ]) => {
- * const c = stream<number>()
- * connect(pipe(b, map(value => value * 2)), c)
- * // re-export the `a` stream, keep `b` internal
- * return { a, c }
- * }, tup(sys1))
- *
- * // init will recursively initialize sys2 dependencies, in this case sys1
- * const { a, c } = init(sys2)
- * subscribe(c, c => console.log(`Value multiplied by 4`, c))
- * publish(a, 2)
- * ```
- *
- * #### Singletons in Dependency Tree
- *
- * By default, systems will be initialized only once if encountered multiple times in the dependency tree.
- * In the below dependency system tree, systems `b` and `c` will receive the same stream instances from system `a` when system `d` is initialized.
- * ```txt
- * a
- * / \
- * b c
- * \ /
- * d
- * ```
- * If `a` gets `{singleton: false}` as a last argument, `init` creates two separate instances - one for `b` and one for `c`.
- *
- * @param constructor the system constructor function. Initialize and connect the streams in its body.
- *
- * @param dependencies the system dependencies, which the constructor will receive as arguments.
- * Use the [[tup]] utility **For TypeScript type inference to work correctly**.
- * ```ts
- * const sys3 = system(() => { ... }, tup(sys2, sys1))
- * ```
- * @param __namedParameters Options
- * @param singleton determines if the system will act as a singleton in a system dependency tree. `true` by default.
- */
- export declare function system<F extends SystemConstructor<D>, D extends SystemSpecs>(constructor: F, dependencies?: D, { singleton }?: {
- singleton: boolean;
- }): SystemSpec<D, F>;
- /**
- * Initializes a [[SystemSpec]] by recursively initializing its dependencies.
- *
- * ```ts
- * // a simple system with two streams
- * const sys1 = system(() => {
- * const a = stream<number>()
- * const b = stream<number>()
- *
- * connect(pipe(a, map(value => value * 2)), b)
- * return { a, b }
- * })
- *
- * const { a, b } = init(sys1)
- * subscribe(b, b => console.log(b))
- * publish(a, 2)
- * ```
- *
- * @returns the [[System]] constructed by the spec constructor.
- * @param systemSpec the system spec to initialize.
- */
- export declare function init<SS extends AnySystemSpec>(systemSpec: SS): SR<SS>;
|