1 | import {
|
2 | CycleProgram,
|
3 | DisposeFunction,
|
4 | Drivers,
|
5 | Sinks,
|
6 | MatchingDrivers,
|
7 | MatchingMain,
|
8 | Engine,
|
9 | } from './types';
|
10 | import {
|
11 | adaptSources,
|
12 | callDrivers,
|
13 | makeSinkProxies,
|
14 | disposeSources,
|
15 | disposeSinkProxies,
|
16 | isObjectEmpty,
|
17 | replicateMany,
|
18 | } from './internals';
|
19 |
|
20 | export {
|
21 | FantasyObserver,
|
22 | FantasySubscription,
|
23 | FantasyObservable,
|
24 | DevToolEnabledSource,
|
25 | Sources,
|
26 | Sinks,
|
27 | SinkProxies,
|
28 | Driver,
|
29 | Drivers,
|
30 | DisposeFunction,
|
31 | MatchingDrivers,
|
32 | MatchingMain,
|
33 | Main,
|
34 | CycleProgram,
|
35 | Engine,
|
36 | WidenStream,
|
37 | GetValidInputs,
|
38 | } from './types';
|
39 |
|
40 | /**
|
41 | * A function that prepares the Cycle application to be executed. Takes a `main`
|
42 | * function and prepares to circularly connects it to the given collection of
|
43 | * driver functions. As an output, `setup()` returns an object with three
|
44 | * properties: `sources`, `sinks` and `run`. Only when `run()` is called will
|
45 | * the application actually execute. Refer to the documentation of `run()` for
|
46 | * more details.
|
47 | *
|
48 | * **Example:**
|
49 | * ```js
|
50 | * import {setup} from '@cycle/run';
|
51 | * const {sources, sinks, run} = setup(main, drivers);
|
52 | * // ...
|
53 | * const dispose = run(); // Executes the application
|
54 | * // ...
|
55 | * dispose();
|
56 | * ```
|
57 | *
|
58 | * @param {Function} main a function that takes `sources` as input and outputs
|
59 | * `sinks`.
|
60 | * @param {Object} drivers an object where keys are driver names and values
|
61 | * are driver functions.
|
62 | * @return {Object} an object with three properties: `sources`, `sinks` and
|
63 | * `run`. `sources` is the collection of driver sources, `sinks` is the
|
64 | * collection of driver sinks, these can be used for debugging or testing. `run`
|
65 | * is the function that once called will execute the application.
|
66 | * @function setup
|
67 | */
|
68 | export function setup<
|
69 | D extends MatchingDrivers<D, M>,
|
70 | M extends MatchingMain<D, M>
|
71 | >(main: M, drivers: D): CycleProgram<D, M> {
|
72 | if (typeof main !== `function`) {
|
73 | throw new Error(
|
74 | `First argument given to Cycle must be the 'main' ` + `function.`
|
75 | );
|
76 | }
|
77 | if (typeof drivers !== `object` || drivers === null) {
|
78 | throw new Error(
|
79 | `Second argument given to Cycle must be an object ` +
|
80 | `with driver functions as properties.`
|
81 | );
|
82 | }
|
83 | if (isObjectEmpty(drivers)) {
|
84 | throw new Error(
|
85 | `Second argument given to Cycle must be an object ` +
|
86 | `with at least one driver function declared as a property.`
|
87 | );
|
88 | }
|
89 |
|
90 | const engine = setupReusable(drivers);
|
91 | const sinks = main(engine.sources);
|
92 | if (typeof window !== 'undefined') {
|
93 | (window as any).Cyclejs = (window as any).Cyclejs || {};
|
94 | (window as any).Cyclejs.sinks = sinks;
|
95 | }
|
96 | function _run(): DisposeFunction {
|
97 | const disposeRun = engine.run(sinks);
|
98 | return function dispose() {
|
99 | disposeRun();
|
100 | engine.dispose();
|
101 | };
|
102 | }
|
103 | return {sinks, sources: engine.sources, run: _run};
|
104 | }
|
105 |
|
106 | /**
|
107 | * A partially-applied variant of setup() which accepts only the drivers, and
|
108 | * allows many `main` functions to execute and reuse this same set of drivers.
|
109 | *
|
110 | * Takes an object with driver functions as input, and outputs an object which
|
111 | * contains the generated sources (from those drivers) and a `run` function
|
112 | * (which in turn expects sinks as argument). This `run` function can be called
|
113 | * multiple times with different arguments, and it will reuse the drivers that
|
114 | * were passed to `setupReusable`.
|
115 | *
|
116 | * **Example:**
|
117 | * ```js
|
118 | * import {setupReusable} from '@cycle/run';
|
119 | * const {sources, run, dispose} = setupReusable(drivers);
|
120 | * // ...
|
121 | * const sinks = main(sources);
|
122 | * const disposeRun = run(sinks);
|
123 | * // ...
|
124 | * disposeRun();
|
125 | * // ...
|
126 | * dispose(); // ends the reusability of drivers
|
127 | * ```
|
128 | *
|
129 | * @param {Object} drivers an object where keys are driver names and values
|
130 | * are driver functions.
|
131 | * @return {Object} an object with three properties: `sources`, `run` and
|
132 | * `dispose`. `sources` is the collection of driver sources, `run` is the
|
133 | * function that once called with 'sinks' as argument, will execute the
|
134 | * application, tying together sources with sinks. `dispose` terminates the
|
135 | * reusable resources used by the drivers. Note also that `run` returns a
|
136 | * dispose function which terminates resources that are specific (not reusable)
|
137 | * to that run.
|
138 | * @function setupReusable
|
139 | */
|
140 | export function setupReusable<D extends Drivers>(drivers: D): Engine<D> {
|
141 | if (typeof drivers !== `object` || drivers === null) {
|
142 | throw new Error(
|
143 | `Argument given to setupReusable must be an object ` +
|
144 | `with driver functions as properties.`
|
145 | );
|
146 | }
|
147 | if (isObjectEmpty(drivers)) {
|
148 | throw new Error(
|
149 | `Argument given to setupReusable must be an object ` +
|
150 | `with at least one driver function declared as a property.`
|
151 | );
|
152 | }
|
153 |
|
154 | const sinkProxies = makeSinkProxies(drivers);
|
155 | const rawSources = callDrivers(drivers, sinkProxies);
|
156 | const sources = adaptSources(rawSources);
|
157 | function _run<M extends MatchingMain<D, M>>(
|
158 | sinks: Sinks<M>
|
159 | ): DisposeFunction {
|
160 | return replicateMany(sinks, sinkProxies as any);
|
161 | }
|
162 | function disposeEngine() {
|
163 | disposeSources(sources);
|
164 | disposeSinkProxies(sinkProxies);
|
165 | }
|
166 | return {sources, run: _run, dispose: disposeEngine};
|
167 | }
|
168 |
|
169 | /**
|
170 | * Takes a `main` function and circularly connects it to the given collection
|
171 | * of driver functions.
|
172 | *
|
173 | * **Example:**
|
174 | * ```js
|
175 | * import run from '@cycle/run';
|
176 | * const dispose = run(main, drivers);
|
177 | * // ...
|
178 | * dispose();
|
179 | * ```
|
180 | *
|
181 | * The `main` function expects a collection of "source" streams (returned from
|
182 | * drivers) as input, and should return a collection of "sink" streams (to be
|
183 | * given to drivers). A "collection of streams" is a JavaScript object where
|
184 | * keys match the driver names registered by the `drivers` object, and values
|
185 | * are the streams. Refer to the documentation of each driver to see more
|
186 | * details on what types of sources it outputs and sinks it receives.
|
187 | *
|
188 | * @param {Function} main a function that takes `sources` as input and outputs
|
189 | * `sinks`.
|
190 | * @param {Object} drivers an object where keys are driver names and values
|
191 | * are driver functions.
|
192 | * @return {Function} a dispose function, used to terminate the execution of the
|
193 | * Cycle.js program, cleaning up resources used.
|
194 | * @function run
|
195 | */
|
196 | export function run<
|
197 | D extends MatchingDrivers<D, M>,
|
198 | M extends MatchingMain<D, M>
|
199 | >(main: M, drivers: D): DisposeFunction {
|
200 | const program = setup(main, drivers);
|
201 | if (
|
202 | typeof window !== 'undefined' &&
|
203 | (window as any).CyclejsDevTool_startGraphSerializer
|
204 | ) {
|
205 | (window as any).CyclejsDevTool_startGraphSerializer(program.sinks);
|
206 | }
|
207 | return program.run();
|
208 | }
|
209 |
|
210 | export default run;
|