├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── dist
├── cjs
│ ├── channel.js
│ └── data-structures.js
└── umd
│ ├── async-csp.js
│ ├── async-csp.min.js
│ └── async-csp.min.js.map
├── examples
├── browser
│ └── index.html
├── char-counter
│ ├── char-counter.js
│ ├── index.js
│ └── text.js
├── file-streams
│ ├── file-streams.js
│ ├── index.js
│ ├── read.csv
│ └── write.sql
└── ping-pong
│ ├── index.js
│ └── ping-pong.js
├── gulpfile.js
├── package.json
├── src
├── channel.js
└── data-structures.js
├── test
└── channel.spec.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "stage-0"
5 | ],
6 | "plugins": [
7 | "transform-runtime"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true,
5 | "mocha": true
6 | },
7 | "parserOptions": {
8 | "ecmaVersion": 2017,
9 | "sourceType": "module"
10 | },
11 | "extends": [
12 | "eslint:recommended",
13 | "prettier"
14 | ],
15 | "rules": {
16 | "prefer-const": "error"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | test
3 | examples
4 | .babelrc
5 | .eslintrc
6 | .npmignore
7 | gulpfile.js
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": true,
3 | "printWidth": 80,
4 | "semi": false,
5 | "singleQuote": true,
6 | "tabWidth": 2,
7 | "trailingComma": "all",
8 | "useTabs": false
9 | }
10 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 0.5.0 (2016-04-17)
4 |
5 | ### Added
6 |
7 | - `Channel.pipeline()` can accept a `Channel` directly. ([efbb0a5](https://github.com/dvlsg/async-csp/commit/efbb0a5cd0d2033dca530c999bf389da2704aded))
8 |
9 | - `Channel#pipe()` can accept a `Function` directly. ([afa1e70](https://github.com/dvlsg/async-csp/commit/afa1e70deff3fd2de0f5d90632d146d6969893fe))
10 |
11 | - `Channel` is now exposed as both named and default. ([fc2d063](https://github.com/dvlsg/async-csp/commit/fc2d063a803dea11cbe571191bd775d78fca6310))
12 |
13 | - Added umd output in `dist`. ([d7640c1](https://github.com/dvlsg/async-csp/commit/d7640c156fdb83432772cda2729061c0bbf44dbd), [4728a52](https://github.com/dvlsg/async-csp/commit/4728a522e6a21118b0e94dad575947c3eb563bab))
14 |
15 | ## 0.4.0 (2016-03-27)
16 |
17 | ### Changed
18 |
19 | - Using Babel v6 to build the dist files ([1b9ca7d])(https://github.com/dvlsg/async-csp/commit/1b9ca7d7d4b740544e0d812a163087d3d6b2b321)
20 |
21 | ## 0.3.0 (2015-11-21)
22 |
23 | ### Changed
24 |
25 | - **(BREAKING)** `Channel` is no longer buffered by default. To make a buffered `Channel`, you must pass a size into the constructor. ([c17ddb8](https://github.com/dvlsg/async-csp/commit/c17ddb8954090d655c2b20790fb4dc07d4d985f5))
26 |
27 | ### Added
28 |
29 | - `Channel.pipeline()` can now accept an array of functions to make a set of piped `Channel`s. ([45656df](https://github.com/dvlsg/async-csp/commit/45656df827f8df589f947bd7f67054674dc19d6c))
30 |
31 | ## 0.2.2 (2015-09-28)
32 |
33 | ### Added
34 |
35 | - Added `Channel#tail()`. ([6058027](https://github.com/dvlsg/async-csp/commit/605802764bd7f2498a7ef97dce27f8f4041ec846))
36 |
37 | ## 0.2.1 (2015-07-25)
38 |
39 | ### Changed
40 |
41 | - Performance improvements with `Channel#consume()`. ([5d2f9e9](https://github.com/dvlsg/async-csp/commit/5d2f9e9b1d759d84f3e9f3fe57ced5c37a519def))
42 |
43 | ## 0.2.0 (2015-07-25)
44 |
45 | ### Changed
46 |
47 | - `Channel.from()` will now set the size of the returned `Channel` to the size of the original iterable. ([4a712bf](https://github.com/dvlsg/async-csp/commit/4a712bf8c8dd66aeb0ae41afe083633dbe598c34))
48 |
49 | ### Added
50 |
51 | - Added support for asynchronous `Channel` transforms. ([15b0eb0](https://github.com/dvlsg/async-csp/commit/15b0eb09e84b26cd8a73b96eb1fd4638b4287dc3), [4a712bf](https://github.com/dvlsg/async-csp/commit/4a712bf8c8dd66aeb0ae41afe083633dbe598c34))
52 |
53 | ### Fixed
54 |
55 | - `Channel#consume()` will no longer trigger a `Channel#close()` until the final consume callback has been settled. This means the general flow should still be the same, but any instances of `Channel#done()` should wait to resolve until `Channel#consume()` is done working. ([da66c61](https://github.com/dvlsg/async-csp/commit/da66c61e954caad58bbd62a68c11b43216859db7))
56 |
57 | ## 0.1.0 (2015-06-27)
58 |
59 | ### Added
60 |
61 | - Initial commit for all `Channel` code.
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 dvlsg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # async-csp
2 | [Communicating sequential processes](https://wikipedia.org/wiki/Communicating_sequential_processes) for use with ES2016's async/await syntax.
3 |
4 | Here's [GoLang's ping/pong example](https://talks.golang.org/2013/advconc.slide#6) in `async-csp` flavor:
5 |
6 | ```js
7 | import Channel from 'async-csp'
8 |
9 | async function sleep(duration) {
10 | return new Promise(resolve => setTimeout(resolve, duration))
11 | }
12 |
13 | async function player(name, table) {
14 | while (true) {
15 | let ball = await table.take();
16 | if (ball === Channel.DONE) {
17 | console.log(`${name}: table's gone!`);
18 | break;
19 | }
20 | ball.hits++;
21 | console.log(`${name}! Hits: ${ball.hits}`);
22 | await sleep(100);
23 | await table.put(ball);
24 | }
25 | }
26 |
27 | async function pingPong() {
28 | console.log('Opening ping-pong channel!');
29 | let table = new Channel();
30 |
31 | player('ping', table);
32 | player('pong', table);
33 |
34 | console.log('Serving ball...');
35 | let ball = {hits: 0};
36 | await table.put(ball);
37 | await sleep(1000);
38 |
39 | console.log('Closing ping-pong channel...');
40 | table.close();
41 |
42 | await table.done();
43 | console.log('Channel is fully closed!');
44 | console.log(`Ball was hit ${ball.hits} times!`);
45 | }
46 |
47 | pingPong()
48 | ```
49 |
50 | Sometimes the output of this example is
51 |
52 | ```
53 | Opening ping-pong channel!
54 | Serving ball...
55 | ping! Hits: 1
56 | pong! Hits: 2
57 | ping! Hits: 3
58 | pong! Hits: 4
59 | ping! Hits: 5
60 | pong! Hits: 6
61 | ping! Hits: 7
62 | pong! Hits: 8
63 | ping! Hits: 9
64 | Closing ping-pong channel...
65 | pong: table's gone!
66 | Channel is fully closed!
67 | Ball was hit 9 times!
68 | ping: table's gone!
69 | ```
70 |
71 | and sometimes it's
72 |
73 | ```
74 | Opening ping-pong channel!
75 | Serving ball...
76 | ping! Hits: 1
77 | pong! Hits: 2
78 | ping! Hits: 3
79 | pong! Hits: 4
80 | ping! Hits: 5
81 | pong! Hits: 6
82 | ping! Hits: 7
83 | pong! Hits: 8
84 | ping! Hits: 9
85 | pong! Hits: 10
86 | Closing ping-pong channel...
87 | ping: table's gone!
88 | Channel is fully closed!
89 | Ball was hit 10 times!
90 | pong: table's gone!
91 | ```
92 |
93 | Sometimes the ball is hit 9 times, and sometimes 10! This is due to the nature of asynchronicity which is nicely depicted in this example.
94 |
95 | ## Installation
96 |
97 | ```
98 | npm install async-csp
99 | ```
100 |
101 | ## Default Task
102 |
103 | * Install node.js
104 | * Clone the async-csp project
105 | * Run `npm install`
106 | * Run `gulp`
107 | * Executes tests
108 | * Cleans dist
109 | * Lints source
110 | * Builds source
111 | * Watches source and tests
112 |
113 | ## Examples
114 |
115 | Examples can be found [here](examples/).
116 | To run any example, make sure the default task has been successfully run once (or at least `npm install`), then run `node index.js` from the root folder of the example.
117 |
118 | ## Usage
119 |
120 | *Note: All of the code pieces below are assumed to be executed from an `async` context, so `await` is available at the base level. To read more about these methods, see [this proposal](https://github.com/lukehoban/ecmascript-asyncawait) for async/await in ES7.*
121 |
122 | ### Data Flow
123 |
124 | A `Channel` is a container which makes use of `Promises` to handle the incoming and outgoing flow of data.
125 |
126 | To put a value on a `Channel` use `Channel#put()`, and to take a value from the channel use `Channel#take()`.
127 |
128 | By default, the promise returned from `Channel#put()` will not resolve until its value is taken from the channel, and the promise returned from `Channel#take()` will not resolve until a value can be taken from the channel.
129 |
130 | ```js
131 | import Channel from 'async-csp';
132 |
133 | let channel = new Channel();
134 |
135 | async function puts(ch) {
136 | await ch.put(1); // resolves when the first ch.take() is executed
137 | await ch.put(2); // resolves when the second ch.take() is executed
138 | await ch.put(3); // resolves when the third ch.take() is executed
139 | }
140 |
141 | async function takes(ch) {
142 | console.log(await ch.take()); // resolves to 1, from the first ch.put()
143 | console.log(await ch.take()); // resolves to 2, from the second ch.put()
144 | console.log(await ch.take()); // resolves to 3, from the third ch.put()
145 | }
146 |
147 | puts(channel);
148 | takes(channel);
149 | ```
150 |
151 | ### Buffering
152 |
153 | A `Channel` can be created with a buffer for receiving puts.
154 | Essentially, this means a put can resolve while space is available on the buffer, even if no take is waiting to receive a value.
155 | As soon as the buffer becomes full, put will begin blocking again until a take clears a space from the buffer.
156 |
157 | To create a `Channel` with a buffer, pass in a `Number` as the first argument to the constructor.
158 |
159 | ```js
160 | import Channel, { timeout } from 'async-csp';
161 |
162 | let channel = new Channel(2); // buffer size of 2
163 |
164 | async function puts(ch) {
165 | await ch.put(1); //=> this can resolve immediately, taking one space on the buffer
166 | console.log('after put 1'); // fires immediately
167 | await ch.put(2); //=> this can also resolve immediately, taking the second space on the buffer
168 | console.log('after put 2'); // also fires immediately
169 | await ch.put(3); //=> buffer is full! this will block until another process takes a value from the Channel
170 | console.log('after put 3'); // fires after the unblock!
171 | }
172 |
173 | async function takes(ch) {
174 | console.log(await ch.take()); //=> resolves to 1, clears a space on the buffer and allows the blocked ch.put(3) to also resolve
175 | console.log(await ch.take()); //=> resolves to 2
176 | console.log(await ch.take()); //=> resolves to 3
177 | }
178 |
179 | // execute the puts right away
180 | puts(channel);
181 |
182 | // use a helper method to wait for 1 second
183 | // to help show the effects of blocking
184 | await timeout(1000);
185 |
186 | // after 1 second, start executing takes
187 | takes(channel);
188 | ```
189 |
190 | ### Non-blocking puts
191 |
192 | A common use for a `Channel` requires data to be input from a non async context, or without waiting for the put to resolve.
193 |
194 | In this scenario, do not await the result of `Channel#put()`.
195 |
196 | ```js
197 | let ch = new Channel();
198 |
199 | // non-blocking puts, don't use `await`
200 | ch.put(1);
201 | ch.put(2);
202 | ch.put(3);
203 |
204 | console.log(await ch.take()); //=> 1
205 | console.log(await ch.take()); //=> 2
206 | console.log(await ch.take()); //=> 3
207 | ```
208 |
209 | ### Transforming
210 |
211 | When constructing a `Channel`, you can pass in a callback to transform values as they are taken.
212 |
213 | ```js
214 | let ch = new Channel(x => x * 2);
215 |
216 | ch.put(1);
217 | ch.put(2);
218 | ch.put(3);
219 |
220 | console.log(await ch.take()); //=> 2
221 | console.log(await ch.take()); //=> 4
222 | console.log(await ch.take()); //=> 6
223 | ```
224 |
225 | If values should be dropped from the `Channel`, simply return `undefined` from the transform callback.
226 |
227 | ```js
228 | let ch = new Channel(x => {
229 | if (x > 2)
230 | return x;
231 | });
232 |
233 | ch.put(1);
234 | ch.put(1);
235 | ch.put(3);
236 | ch.put(4);
237 |
238 | console.log(await ch.take()); //=> 3
239 | console.log(await ch.take()); //=> 4
240 | ```
241 |
242 | If a transform needs to expand a single value into multiple values, use the `push` parameter with the transform.
243 |
244 | Note that when using this callback style, all values must be sent through `push`.
245 | Any value returned from the transform callback will be ignored
246 | when the provided transformer has more than one parameter defined.
247 |
248 | ```js
249 | let ch = new Channel((x, push) => {
250 | push(x);
251 | push(x + 1);
252 | });
253 |
254 | ch.put(1);
255 | ch.put(3);
256 |
257 | console.log(await ch.take()); //=> 1
258 | console.log(await ch.take()); //=> 2
259 | console.log(await ch.take()); //=> 3
260 | console.log(await ch.take()); //=> 4
261 | ```
262 |
263 | If the transform needs to work asynchronously, there are a few ways to accomplish this.
264 |
265 | The first is to use an async callback.
266 |
267 | ```js
268 | let ch = new Channel(async x => {
269 | await timeout(100);
270 | return x;
271 | });
272 |
273 | ch.put(1);
274 | ch.put(2);
275 |
276 | console.log(await ch.take()); //=> 1
277 | console.log(await ch.take()); //=> 2
278 | ```
279 |
280 | The second way to use an asynchronous transform is by passing in an async callback with a parameter length of 2.
281 | Similar to the non-async callback with a parameter length of 2, all values must be sent through `push`,
282 | and returned values will be ignored.
283 |
284 | ```js
285 | let ch = new Channel(async(x, push) => {
286 | await timeout(100);
287 | push(x);
288 | await timeout(100);
289 | push(x + 1);
290 | });
291 |
292 | ch.put(1);
293 | ch.put(3);
294 |
295 | console.log(await ch.take()); //=> 1
296 | console.log(await ch.take()); //=> 2
297 | console.log(await ch.take()); //=> 3
298 | console.log(await ch.take()); //=> 4
299 | ```
300 |
301 | The final way to use an asynchronous transform is with a three-parameter callback.
302 | To signify that the transform has completed, execute the third argument.
303 |
304 | ```js
305 | let ch = new Channel((x, push, done) => {
306 | push(x);
307 | setTimeout(() => {
308 | push(x + 1);
309 | done();
310 | }, 100);
311 | });
312 |
313 | ch.put(1);
314 | ch.put(3);
315 |
316 | console.log(await ch.take()); //=> 1
317 | console.log(await ch.take()); //=> 2
318 | console.log(await ch.take()); //=> 3
319 | console.log(await ch.take()); //=> 4
320 | ```
321 |
322 | One final note: Using a transform does not prevent you from simultaneously using a buffer.
323 | To use a transform with a buffered `Channel`, pass in the buffer size as the first argument, and the transform as the second.
324 |
325 | ```js
326 | let ch = new Channel(2, x => x + 1);
327 |
328 | // note that puts will be resolved immediately, since we have space on the buffer
329 | await ch.put(1);
330 | await ch.put(3);
331 |
332 | console.log(await ch.take()); //=> 2
333 | console.log(await ch.take()); //=> 4
334 | ```
335 |
336 | ### Channel#pipe()
337 |
338 | Similarly to `Streams`, `Channels` can be piped from one to another.
339 |
340 | ```js
341 | let ch1 = new Channel();
342 | let ch2 = new Channel();
343 | let ch3 = new Channel();
344 |
345 | ch1.pipe(ch2).pipe(ch3);
346 | /*
347 | +---+
348 | |ch1|
349 | +---+
350 | |
351 | V
352 | +---+
353 | |ch2|
354 | +---+
355 | |
356 | V
357 | +---+
358 | |ch3|
359 | +---+
360 | */
361 |
362 | ch1.put(1);
363 | ch1.put(2);
364 | ch1.put(3);
365 |
366 | console.log(await ch3.take()); //=> 1
367 | console.log(await ch3.take()); //=> 2
368 | console.log(await ch3.take()); //=> 3
369 | ```
370 |
371 | A `Channel` can be piped to multiple destinations.
372 | In this case, downstream `Channels` will receive every value from upstream.
373 |
374 | ```js
375 | let ch1 = new Channel();
376 | let ch2 = new Channel();
377 | let ch3 = new Channel();
378 |
379 | ch1.pipe(ch2, ch3); // or `ch1.pipe(ch2); ch1.pipe(ch3);`
380 | /*
381 | +---+
382 | +-|ch1|-+
383 | | +---+ |
384 | | |
385 | V V
386 | +---+ +---+
387 | |ch2| |ch3|
388 | +---+ +---+
389 | */
390 |
391 | ch1.put(1);
392 | ch1.put(2);
393 | ch1.put(3);
394 |
395 | // 1 is taken from ch1 and put on ch2 and ch2
396 |
397 | console.log(await ch2.take()); //=> 1
398 | console.log(await ch3.take()); //=> 1
399 |
400 | // 2 is taken from ch1 and put on ch2 and ch3
401 |
402 | console.log(await ch2.take()); //=> 2
403 | console.log(await ch3.take()); //=> 2
404 |
405 | // 3 is taken from ch1 and put on ch2 and ch3
406 |
407 | console.log(await ch2.take()); //=> 3
408 | console.log(await ch3.take()); //=> 3
409 | ```
410 |
411 | Typically, it is recommended to pass a `Channel` into pipe, but if you need a shortcut for creating a `Channel` from a transform callback, you may also pass in a callback directly.
412 |
413 | ```js
414 | let ch1 = new Channel(x => x + 1);
415 |
416 | // note that this returns a reference to the last `Channel` from `#pipe()`
417 | let ch3 = ch1.pipe(x => x + 2).pipe(x => x + 3);
418 |
419 | ch1.put(1);
420 | ch1.put(2);
421 | ch1.put(3);
422 |
423 | console.log(await ch3.take()); //=> 7
424 | console.log(await ch3.take()); //=> 8
425 | console.log(await ch3.take()); //=> 9
426 | ```
427 |
428 | Also take note that if one downstream `Channel` is blocked from a currently unresolved `Channel#put()` (buffered or non-buffered), then the *entire* pipe will be blocked.
429 | In the example above, an attempt to take all 3 values from `ch2` before taking any values from `ch3` would have resulted in deadlock.
430 |
431 | Finally, any piped `Channel` will also execute transforms.
432 |
433 | ```js
434 | let ch1 = new Channel(x => x + 2);
435 | let ch2 = new Channel(x => x.toString());
436 | let ch3 = new Channel(x => ({ x: x }));
437 |
438 | ch1.pipe(ch2).pipe(ch3);
439 |
440 | ch1.put(1);
441 | ch1.put(2);
442 | ch1.put(3);
443 |
444 | console.log(await ch3.take()); //=> { x: '3' }
445 | console.log(await ch3.take()); //=> { x: '4' }
446 | console.log(await ch3.take()); //=> { x: '5' }
447 | ```
448 |
449 | ### Channel.pipeline()
450 |
451 | `Channel.pipeline()` is a helper method for creating piped channels from any number of callbacks or `Channels`.
452 | Inputs can be provided either as separate arguments, or contained in an array as the first argument. If an input is provided as a callback, it will be turned into a `Channel` using that callback as the transform.
453 |
454 | `Channel.pipeline()` will return an array containing the first and the last `Channel` in the pipeline.
455 |
456 | ```js
457 | let [ ch1, ch3 ] = Channel.pipeline(
458 | x => x + 2,
459 | new Channel(x => x.toString()),
460 | async x => ({ x })
461 | );
462 |
463 | ch1.put(1);
464 | ch1.put(2);
465 | ch1.put(3);
466 |
467 | console.log(await ch3.take()); //=> { x: '3' }
468 | console.log(await ch3.take()); //=> { x: '4' }
469 | console.log(await ch3.take()); //=> { x: '5' }
470 | ```
471 |
472 | ### Channel#unpipe()
473 |
474 | If a `Channel` should be taken out of an existing pipe, use `Channel#unpipe()`.
475 |
476 | ```js
477 | let ch1 = new Channel();
478 | let ch2 = new Channel();
479 | let ch3 = new Channel();
480 |
481 | ch1.pipe(ch2).pipe(ch3);
482 |
483 | ch1.put(1);
484 | console.log(await ch3.take()); //=> 1
485 |
486 | // now take ch2 out of the pipe
487 | ch1.unpipe(ch2);
488 |
489 | ch1.put(2);
490 | console.log(await ch1.take()); //=> 2, note that we took from ch1
491 |
492 | // note that ch2 is still piping to ch3
493 | ch2.put(3);
494 | console.log(await ch3.take()); //=> 3
495 | ```
496 |
497 | ### Channel#merge()
498 |
499 | `Channel.merge()` is a helper method for piping multiple `Channels` into a single, new `Channel`.
500 |
501 | ```js
502 | let ch1 = new Channel();
503 | let ch2 = new Channel();
504 | let ch3 = ch1.merge(ch2); // or, `ch3 = Channel.merge(ch1, ch2)`
505 |
506 | ch1.put(1);
507 | ch2.put(2);
508 |
509 | console.log(await ch3.take()); //=> 1
510 | console.log(await ch3.take()); //=> 2
511 | ```
512 |
513 | ### Channel#close()
514 |
515 | A `Channel` has 3 states: open, closed, and ended. An open `Channel` can be written to, a closed `Channel` will not accept any new values but may be non-empty, and an ended `Channel` is both closed and empty.
516 |
517 | To signify that a `Channel` should be done accepting new values, execute `Channel#close()`. Data can still be taken from the channel after that point, but no more values can be added.
518 |
519 | ```js
520 | let ch1 = new Channel();
521 |
522 | ch1.put(1);
523 | ch1.put(2);
524 |
525 | ch1.close();
526 |
527 | ch1.put(3); // resolves immediately with value of Channel.DONE
528 |
529 | console.log(await ch1.take()); //=> 1
530 | console.log(await ch1.take()); //=> 2
531 | console.log(await ch1.take()); //=> Channel.DONE
532 | ```
533 |
534 | If `Channels` are piped together, and you want the entire pipeline to close when possible, simply pass `true` as an argument to `Channel#close()`.
535 |
536 | ```js
537 | let ch1 = new Channel();
538 | let ch2 = new Channel();
539 | ch1.pipe(ch2);
540 |
541 | ch1.put(1);
542 | ch1.put(2);
543 |
544 | ch1.close(true);
545 |
546 | console.log(await ch2.take()); //=> 1
547 | console.log(await ch2.take()); //=> 2
548 | console.log(await ch2.take()); //=> Channel.DONE
549 | ```
550 |
551 | ### Channel#done()
552 |
553 | In order to wait for a channel to be ended (closed and empty), await the resolution of `done`.
554 |
555 | ```js
556 | let ch = new Channel();
557 |
558 | ch.put(1);
559 | ch.put(2);
560 | ch.close();
561 |
562 | let arr = [];
563 | (async() => {
564 | await timeout(1000);
565 | arr.push(await ch.take());
566 | await timeout(1000);
567 | arr.push(await ch.take());
568 | })();
569 |
570 | await ch.done(); // will not resolve until the async IIFE takes both values from the channel
571 | console.log(arr); //=> [ 1, 2 ]
572 | ```
573 |
574 | ### Channel#tail()
575 |
576 | While manually appending values to a `Channel` can be accomplished,
577 | it often becomes significantly more difficult
578 | when items such as pipes and asynchronous transforms are in play.
579 |
580 | For simplicity, `Channel#tail()` is provided as an alternative method for
581 | providing values to `Channel#take()` only after the `Channel` is closed
582 | and all existing `Channel#put()`s have been resolved.
583 |
584 | ```js
585 | let ch = new Channel();
586 |
587 | ch.put(1);
588 | ch.tail(4);
589 | ch.put(2);
590 | ch.put(3);
591 | ch.close();
592 |
593 | console.log(await ch.take()); //=> 1
594 | console.log(await ch.take()); //=> 2
595 | console.log(await ch.take()); //=> 3
596 | console.log(await ch.take()); //=> 4
597 | ```
598 |
599 | Note that when a `Channel` has a transform, any values provided through `Channel#tail()`
600 | will also use that transform.
601 |
602 | ```js
603 | let ch = new Channel(x => x + 2);
604 |
605 | ch.put(1);
606 | ch.tail(4);
607 | ch.put(2);
608 | ch.put(3);
609 | ch.close();
610 |
611 | console.log(await ch.take()); //=> 3
612 | console.log(await ch.take()); //=> 4
613 | console.log(await ch.take()); //=> 5
614 | console.log(await ch.take()); //=> 6
615 | ```
616 |
617 | ### Channel#consume()
618 |
619 | If you would like to execute a callback as soon as values can be taken from the `Channel`,
620 | you may add a consumer by using `Channel#consume()`.
621 |
622 | ```js
623 | let ch = new Channel();
624 | ch.consume(x => {
625 | console.log(x);
626 | });
627 |
628 | await ch.put(1);
629 | await ch.put(2);
630 | await ch.put(3);
631 | await ch.put(4);
632 |
633 | // console logs
634 | //=> 1
635 | //=> 2
636 | //=> 3
637 | //=> 4
638 | ```
639 |
640 | `Channel#consume()` can also be handled asynchronously, and will not attempt to queue up another `Channel#take()`
641 | until the consumer callback has completed running.
642 |
643 | ```js
644 | let ch = new Channel();
645 | let arr = [];
646 | ch.consume(async x => {
647 | await timeout(1000);
648 | arr.push(x);
649 | console.log(x);
650 | });
651 |
652 | await ch.put(1);
653 | await ch.put(2);
654 | await ch.put(3);
655 | await ch.put(4);
656 |
657 | // console logs, once a second
658 | //=> 1
659 | //=> 2
660 | //=> 3
661 | //=> 4
662 | ```
663 |
664 | ### Channel#produce()
665 |
666 | Similar to `Channel#consume()`, `Channel#produce()` will put returned values
667 | onto the `Channel` as soon as space becomes available.
668 |
669 | ```js
670 | let ch = new Channel();
671 | let counter = 0;
672 | ch.produce(() => ++counter);
673 |
674 | console.log(await ch.take()); //=> 1
675 | console.log(await ch.take()); //=> 2
676 | console.log(await ch.take()); //=> 3
677 | console.log(await ch.take()); //=> 4
678 | ```
679 |
680 | As with `Channel#consume()`, `Channel#produce()` can also work asynchronously.
681 |
682 | ```js
683 | let ch = new Channel();
684 | let counter = 0;
685 | ch.produce(async() => {
686 | await timeout(1000);
687 | return ++counter;
688 | });
689 |
690 | console.log(await ch.take()); //=> 1, after 1 second
691 | console.log(await ch.take()); //=> 2, after 2 seconds
692 | console.log(await ch.take()); //=> 3, after 3 seconds
693 | console.log(await ch.take()); //=> 4, after 4 seconds
694 | ```
695 |
696 | ### Channel.from()
697 |
698 | If you have an iterable item which you would like to convert into a `Channel`,
699 | use `Channel.from()` to construct a `Channel` from that iterable.
700 |
701 | ```js
702 | let arr = [ 1, 2, 3 ];
703 | let ch = Channel.from(arr);
704 |
705 | console.log(await ch.take()); //=> 1
706 | console.log(await ch.take()); //=> 2
707 | console.log(await ch.take()); //=> 3
708 | ```
709 |
710 | Note that in this case, a buffer is created with the size of the iterable,
711 | all values are placed directly onto the buffer, and the `Channel` is marked as closed,
712 | which will include any attached downstream pipes.
713 |
714 | If the channel or any downstream pipes should remain open to continue receiving puts,
715 | pass in a `true` as the second argument.
716 |
717 | ```js
718 | let arr = [ 1, 2, 3 ];
719 | let ch = Channel.from(arr, true);
720 | ch.put(4);
721 | ch.close();
722 |
723 | console.log(await ch.take()); //=> 1
724 | console.log(await ch.take()); //=> 2
725 | console.log(await ch.take()); //=> 3
726 | console.log(await ch.take()); //=> 4
727 | ```
728 |
729 | ## License
730 |
731 | All code released under the [MIT](https://github.com/dvlsg/async-csp/blob/master/LICENSE) license.
732 |
--------------------------------------------------------------------------------
/dist/cjs/data-structures.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.SlidingBuffer = exports.DroppingBuffer = exports.FixedQueue = exports.List = exports.Queue = exports.FixedStack = exports.Stack = undefined;
7 |
8 | var _get2 = require('babel-runtime/helpers/get');
9 |
10 | var _get3 = _interopRequireDefault(_get2);
11 |
12 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
13 |
14 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
15 |
16 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
17 |
18 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
19 |
20 | var _inherits2 = require('babel-runtime/helpers/inherits');
21 |
22 | var _inherits3 = _interopRequireDefault(_inherits2);
23 |
24 | var _create = require('babel-runtime/core-js/object/create');
25 |
26 | var _create2 = _interopRequireDefault(_create);
27 |
28 | var _toStringTag = require('babel-runtime/core-js/symbol/to-string-tag');
29 |
30 | var _toStringTag2 = _interopRequireDefault(_toStringTag);
31 |
32 | var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
33 |
34 | var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
35 |
36 | var _getIterator2 = require('babel-runtime/core-js/get-iterator');
37 |
38 | var _getIterator3 = _interopRequireDefault(_getIterator2);
39 |
40 | var _iterator = require('babel-runtime/core-js/symbol/iterator');
41 |
42 | var _iterator2 = _interopRequireDefault(_iterator);
43 |
44 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
45 |
46 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
47 |
48 | var _createClass2 = require('babel-runtime/helpers/createClass');
49 |
50 | var _createClass3 = _interopRequireDefault(_createClass2);
51 |
52 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
53 |
54 | var MAX_SIZE = 4096;
55 |
56 | // can be swapped to symbols to make more 'private'
57 | // makes it more difficult to debug, though.
58 | var ARR = '_arr';
59 | var SIZE = '_size';
60 |
61 | // internal to be inherited
62 |
63 | var Data = function () {
64 | function Data() {
65 | (0, _classCallCheck3.default)(this, Data);
66 |
67 | this[ARR] = [];
68 | }
69 |
70 | (0, _createClass3.default)(Data, [{
71 | key: _iterator2.default,
72 | value: function value() {
73 | return (0, _getIterator3.default)(this[ARR]); // should be overridden for stacks, so we iterate from back to front
74 | }
75 | }, {
76 | key: 'flush',
77 | value: function flush() {
78 | this[ARR].length = 0;
79 | }
80 | }, {
81 | key: 'empty',
82 | value: function empty() {
83 | return this[ARR].length === 0;
84 | }
85 | }, {
86 | key: 'toString',
87 | value: function toString() {
88 | return this[ARR].join(', ');
89 | }
90 | }, {
91 | key: 'values',
92 | value: function values() {
93 | return [].concat((0, _toConsumableArray3.default)(this[ARR]));
94 | }
95 | }, {
96 | key: _toStringTag2.default,
97 | get: function get() {
98 | return 'Data';
99 | }
100 | }, {
101 | key: 'length',
102 | get: function get() {
103 | return this[ARR].length;
104 | }
105 | }], [{
106 | key: 'construct',
107 | value: function construct() {
108 | return (0, _create2.default)(this.constructor);
109 | }
110 | }]);
111 | return Data;
112 | }();
113 | // Data[Symbol.toStringTag] = 'Data';
114 |
115 | var Stack = exports.Stack = function (_Data) {
116 | (0, _inherits3.default)(Stack, _Data);
117 |
118 | function Stack() {
119 | (0, _classCallCheck3.default)(this, Stack);
120 | return (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(Stack).call(this));
121 | }
122 |
123 | (0, _createClass3.default)(Stack, [{
124 | key: 'push',
125 | value: function push(val) {
126 | this[ARR].push(val);
127 | }
128 | }, {
129 | key: 'pop',
130 | value: function pop() {
131 | return this[ARR].pop();
132 | }
133 | }, {
134 | key: 'peek',
135 | value: function peek() {
136 | return this[ARR][this.length - 1];
137 | }
138 | }, {
139 | key: _toStringTag2.default,
140 | get: function get() {
141 | return 'Stack';
142 | }
143 | }]);
144 | return Stack;
145 | }(Data);
146 |
147 | var FixedStack = exports.FixedStack = function (_Stack) {
148 | (0, _inherits3.default)(FixedStack, _Stack);
149 |
150 | function FixedStack() {
151 | var size = arguments.length <= 0 || arguments[0] === undefined ? MAX_SIZE : arguments[0];
152 | (0, _classCallCheck3.default)(this, FixedStack);
153 |
154 | var _this2 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(FixedStack).call(this));
155 |
156 | _this2[SIZE] = size;
157 | return _this2;
158 | }
159 |
160 | (0, _createClass3.default)(FixedStack, [{
161 | key: 'push',
162 | value: function push(val) {
163 | if (!this.full()) return (0, _get3.default)((0, _getPrototypeOf2.default)(FixedStack.prototype), 'push', this).call(this, val);
164 | }
165 | }, {
166 | key: 'full',
167 | value: function full() {
168 | return this.length >= this[SIZE];
169 | }
170 | }, {
171 | key: _toStringTag2.default,
172 | get: function get() {
173 | return 'FixedStack';
174 | }
175 | }, {
176 | key: 'size',
177 | get: function get() {
178 | return this[SIZE];
179 | }
180 | }]);
181 | return FixedStack;
182 | }(Stack);
183 |
184 | var Queue = exports.Queue = function (_Data2) {
185 | (0, _inherits3.default)(Queue, _Data2);
186 |
187 | function Queue() {
188 | (0, _classCallCheck3.default)(this, Queue);
189 | return (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(Queue).call(this));
190 | }
191 |
192 | (0, _createClass3.default)(Queue, [{
193 | key: 'push',
194 | value: function push(val) {
195 | this[ARR].push(val);
196 | }
197 | }, {
198 | key: 'shift',
199 | value: function shift() {
200 | return this[ARR].shift();
201 | }
202 | }, {
203 | key: 'peek',
204 | value: function peek() {
205 | return this[ARR][0];
206 | }
207 | }, {
208 | key: _toStringTag2.default,
209 | get: function get() {
210 | return 'Queue';
211 | }
212 | }]);
213 | return Queue;
214 | }(Data);
215 |
216 | var List = exports.List = function (_Queue) {
217 | (0, _inherits3.default)(List, _Queue);
218 |
219 | function List() {
220 | (0, _classCallCheck3.default)(this, List);
221 | return (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(List).call(this));
222 | }
223 |
224 | (0, _createClass3.default)(List, [{
225 | key: 'unshift',
226 | value: function unshift() {
227 | var _ARR;
228 |
229 | return (_ARR = this[ARR]).unshift.apply(_ARR, arguments);
230 | }
231 | }, {
232 | key: _toStringTag2.default,
233 | get: function get() {
234 | return 'List';
235 | }
236 | }]);
237 | return List;
238 | }(Queue);
239 |
240 | var FixedQueue = exports.FixedQueue = function (_Queue2) {
241 | (0, _inherits3.default)(FixedQueue, _Queue2);
242 |
243 | function FixedQueue() {
244 | var size = arguments.length <= 0 || arguments[0] === undefined ? MAX_SIZE : arguments[0];
245 | (0, _classCallCheck3.default)(this, FixedQueue);
246 |
247 | var _this5 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(FixedQueue).call(this));
248 |
249 | _this5[SIZE] = size;
250 | return _this5;
251 | }
252 |
253 | (0, _createClass3.default)(FixedQueue, [{
254 | key: 'push',
255 | value: function push(val) {
256 | if (!this.full()) // throw overflow? drop overflow? allow overflow?
257 | return (0, _get3.default)((0, _getPrototypeOf2.default)(FixedQueue.prototype), 'push', this).call(this, val);
258 | }
259 | }, {
260 | key: 'full',
261 | value: function full() {
262 | return this.length >= this[SIZE];
263 | }
264 | }, {
265 | key: 'unshift',
266 | value: function unshift() {
267 | var _ARR2;
268 |
269 | // this isn't really a queue anymore. maybe FixedList instead?
270 | return (_ARR2 = this[ARR]).unshift.apply(_ARR2, arguments);
271 | }
272 | }, {
273 | key: _toStringTag2.default,
274 | get: function get() {
275 | return 'FixedQueue';
276 | }
277 | }, {
278 | key: 'size',
279 | get: function get() {
280 | return this[SIZE];
281 | }
282 | }]);
283 | return FixedQueue;
284 | }(Queue);
285 |
286 | var DroppingBuffer = exports.DroppingBuffer = function (_Queue3) {
287 | (0, _inherits3.default)(DroppingBuffer, _Queue3);
288 |
289 | function DroppingBuffer() {
290 | var size = arguments.length <= 0 || arguments[0] === undefined ? MAX_SIZE : arguments[0];
291 | (0, _classCallCheck3.default)(this, DroppingBuffer);
292 |
293 | var _this6 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(DroppingBuffer).call(this));
294 |
295 | _this6[SIZE] = size;
296 | return _this6;
297 | }
298 |
299 | (0, _createClass3.default)(DroppingBuffer, [{
300 | key: 'unshift',
301 | value: function unshift() {
302 | // we only need to grab the first item
303 | if (this[ARR].length === 0 && (arguments.length <= 0 ? undefined : arguments[0])) this[ARR][0] = arguments.length <= 0 ? undefined : arguments[0];
304 | }
305 | }, {
306 | key: 'push',
307 | value: function push(val) {
308 | if (this[ARR].length === 0) this[ARR][0] = val;
309 | }
310 | }, {
311 | key: 'full',
312 | value: function full() {
313 | return false;
314 | }
315 | }, {
316 | key: _toStringTag2.default,
317 | get: function get() {
318 | return 'DroppingBuffer';
319 | }
320 | }]);
321 | return DroppingBuffer;
322 | }(Queue);
323 |
324 | var SlidingBuffer = exports.SlidingBuffer = function (_Queue4) {
325 | (0, _inherits3.default)(SlidingBuffer, _Queue4);
326 |
327 | function SlidingBuffer() {
328 | var size = arguments.length <= 0 || arguments[0] === undefined ? MAX_SIZE : arguments[0];
329 | (0, _classCallCheck3.default)(this, SlidingBuffer);
330 |
331 | var _this7 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(SlidingBuffer).call(this));
332 |
333 | _this7[SIZE] = size; // need to make sure size is a positive integer.
334 | _this7.head = 0; // pointer to oldest value
335 | _this7.tail = 0; // pointer to newest value
336 | _this7.count = 0;
337 | return _this7;
338 | }
339 |
340 | (0, _createClass3.default)(SlidingBuffer, [{
341 | key: 'empty',
342 | value: function empty() {
343 | return this.count === 0;
344 | }
345 | }, {
346 | key: 'full',
347 | value: function full() {
348 | return false;
349 | }
350 | }, {
351 | key: 'push',
352 | value: function push(val) {
353 | if (this.count === 0) {
354 | this[ARR][this.tail] = val;
355 | this.head = this.tail;
356 | this.count = 1;
357 | return;
358 | }
359 | var _size = this[SIZE];
360 | this.tail = (this.tail + 1) % _size;
361 | this[ARR][this.tail] = val;
362 | var overwrite = this.tail === this.head;
363 | if (overwrite) this.head = (this.head + 1) % _size;
364 | if (!overwrite) this.count += 1;
365 | }
366 | }, {
367 | key: 'shift',
368 | value: function shift() {
369 | var val = this[ARR][this.head];
370 | delete this[ARR][this.head];
371 | this.head = (this.head + 1) % this[SIZE];
372 | this.count -= 1;
373 | return val;
374 | }
375 | }, {
376 | key: _toStringTag2.default,
377 | get: function get() {
378 | return 'SlidingBuffer';
379 | }
380 | }]);
381 | return SlidingBuffer;
382 | }(Queue);
--------------------------------------------------------------------------------
/dist/umd/async-csp.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports["async-csp"]=e():t["async-csp"]=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var u=n[r]={exports:{},id:r,loaded:!1};return t[r].call(u.exports,u,u.exports,e),u.loaded=!0,u.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function u(t){setTimeout(function(){throw t})}function o(t){t[B]=I.ENDED;for(var e=null;e=t.waiting.shift();)e()}function i(t,e,n){var r=this,u=null;return e instanceof Function?1===e.length?u=function(){var n=(0,M["default"])(E["default"].mark(function u(){var n,o;return E["default"].wrap(function(r){for(;;)switch(r.prev=r.next){case 0:if(n=e(t),!(n instanceof S["default"])){r.next=6;break}return r.next=4,n;case 4:return o=r.sent,r.abrupt("return",o);case 6:return r.abrupt("return",n);case 7:case"end":return r.stop()}},u,r)}));return function(){return n.apply(this,arguments)}}():!function(){var n=new C.List;u=2===e.length?function(){var u=(0,M["default"])(E["default"].mark(function o(){return E["default"].wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,e(t,function(t){"undefined"!=typeof t&&n.push(t)});case 2:return r.abrupt("return",n);case 3:case"end":return r.stop()}},o,r)}));return function(){return u.apply(this,arguments)}}():function(){return new S["default"](function(r){e(t,function(t){"undefined"!=typeof t&&n.push(t)},function(){r(n)})})}}():u=function(){var e=(0,M["default"])(E["default"].mark(function n(){return E["default"].wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",t);case 1:case"end":return e.stop()}},n,r)}));return function(){return e.apply(this,arguments)}}(),{wrapped:u,resolve:n,transform:e,val:t}}function a(t){return t.buf?!t.buf.full()&&!t.puts.empty()||!t.takes.empty()&&!t.buf.empty():!t.takes.empty()&&!t.puts.empty()}function f(){var t=arguments.length<=0||void 0===arguments[0]?0:arguments[0];return new S["default"](function(e){setTimeout(e,t)})}Object.defineProperty(e,"__esModule",{value:!0}),e.Channel=e.ACTIONS=e.STATES=void 0;var c,s=n(1),l=r(s),p=n(58),h=r(p),d=n(54),v=r(d),y=n(60),g=r(y),x=n(76),m=r(x),b=n(77),w=r(b),_=n(81),k=r(_),O=n(88),E=r(O),j=n(92),S=r(j),L=n(110),M=r(L),P=n(63),A=r(P),N=function(){var t=(0,M["default"])(E["default"].mark(function e(t){var n,r;return E["default"].wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(t.empty()){e.next=2;break}throw new Error("Attempted to execute flush(Channel) on a non-empty channel!");case 2:if(!t[z]){e.next=4;break}return e.abrupt("return");case 4:for(t[z]=!0,n=null,r=[];n=t.takes.shift();)r.push(n(R.DONE));return e.next=9,S["default"].all(r);case 9:t[W]||o(t),t[z]=!1;case 11:case"end":return e.stop()}},e,this)}));return function(e){return t.apply(this,arguments)}}(),T=function(){var t=(0,M["default"])(E["default"].mark(function e(t){var n,r,u=this;return E["default"].wrap(function(e){for(;;)switch(e.prev=e.next){case 0:n=E["default"].mark(function o(){var e,n,r;return E["default"].wrap(function(u){for(;;)switch(u.prev=u.next){case 0:if(e=t.buf.shift(),n=null,!e||!e.wrapped){u.next=8;break}return u.next=5,e.wrapped();case 5:n=u.sent,u.next=9;break;case 8:n=e;case 9:"undefined"!=typeof n&&(n instanceof C.List?!function(){var r=[].concat((0,k["default"])(n));if(0===r.length)e.resolve();else if(1===r.length){e.resolve();var u=t.takes.shift();u(r[0])}else!function(){var n,u=0,o=function(){u++,u===r.length&&e.resolve()},a=r.map(function(t){return i(t,function(t){return t},o)});(n=t.buf).unshift.apply(n,(0,k["default"])(a))}()}():(r=t.takes.shift())(n));case 10:case"end":return u.stop()}},o,u)});case 1:if(t.buf.empty()||t.takes.empty()){e.next=5;break}return e.delegateYield(n(),"t0",3);case 3:e.next=1;break;case 5:for(;!t.puts.empty()&&!t.buf.full();)r=t.puts.shift(),t.buf.push(r),r.resolve();case 6:case"end":return e.stop()}},e,this)}));return function(e){return t.apply(this,arguments)}}(),F=function(){var t=(0,M["default"])(E["default"].mark(function e(t){var n,r=this;return E["default"].wrap(function(e){for(;;)switch(e.prev=e.next){case 0:n=E["default"].mark(function u(){var e,n,o;return E["default"].wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return e=t.puts.shift(),r.next=3,e.wrapped();case 3:n=r.sent,"undefined"!=typeof n?n instanceof C.List?!function(){var r=[].concat((0,k["default"])(n));if(0===r.length)e.resolve();else if(1===r.length){e.resolve();var u=t.takes.shift();u(r[0])}else!function(){var n,u=0,o=function(){u++,u===r.length&&e.resolve()},a=r.map(function(t){return i(t,function(t){return t},o)});(n=t.puts).unshift.apply(n,(0,k["default"])(a))}()}():(e.resolve(),(o=t.takes.shift())(n)):e.resolve();case 5:case"end":return r.stop()}},u,r)});case 1:if(t.takes.empty()||t.puts.empty()){e.next=5;break}return e.delegateYield(n(),"t0",3);case 3:e.next=1;break;case 5:case"end":return e.stop()}},e,this)}));return function(e){return t.apply(this,arguments)}}(),D=function(){var t=(0,M["default"])(E["default"].mark(function e(t){var n;return E["default"].wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!t[Y]){e.next=2;break}return e.abrupt("return");case 2:t[Y]=!0;case 3:if(!a(t)){e.next=8;break}return e.next=6,t[G](t);case 6:e.next=3;break;case 8:if(t[B]!==I.CLOSED||t.tails.empty()||(t.buf?!t.buf.empty():0)||!t.puts.empty()){e.next=16;break}(n=t.puts).unshift.apply(n,(0,k["default"])(t.tails)),t.tails=new C.List;case 11:if(!a(t)){e.next=16;break}return e.next=14,t[G](t);case 14:e.next=11;break;case 16:(t[B]===I.CLOSED||t[B]===I.ENDED)&&(t.buf?t.buf.empty():!0)&&t.puts.empty()&&t.tails.empty()&&N(t),t[Y]=!1;case 18:case"end":return e.stop()}},e,this)}));return function(e){return t.apply(this,arguments)}}();e.timeout=f;var C=n(111),I=((c=console).log.bind(c),e.STATES={OPEN:(0,A["default"])("channel_open"),CLOSED:(0,A["default"])("channel_closed"),ENDED:(0,A["default"])("channel_ended")}),R=e.ACTIONS={DONE:(0,A["default"])("channel_done"),CANCEL:(0,A["default"])("channel_cancel")},G=(0,A["default"])("channel_slider"),B=(0,A["default"])("channel_state"),Q=(0,A["default"])("channel_should_close"),W=(0,A["default"])("channel_consuming"),z=(0,A["default"])("channel_flushing"),Y=(0,A["default"])("channel_sliding"),J=e.Channel=function(){function t(){(0,m["default"])(this,t),this.puts=new C.List,this.tails=new C.List,this.takes=new C.List,this.buf=null,this.transform=null,this.pipeline=[],this.waiting=[];var e=null,n=null,r=null;"function"==typeof(arguments.length<=0?void 0:arguments[0])&&(n=arguments.length<=0?void 0:arguments[0]),"number"==typeof(arguments.length<=0?void 0:arguments[0])&&(e=arguments.length<=0?void 0:arguments[0],(arguments.length<=1?void 0:arguments[1])&&"function"==typeof(arguments.length<=1?void 0:arguments[1])&&(n=arguments.length<=1?void 0:arguments[1])),"object"===(0,g["default"])(arguments.length<=0?void 0:arguments[0])&&(r=arguments.length<=0?void 0:arguments[0],(arguments.length<=1?void 0:arguments[1])&&"function"==typeof(arguments.length<=1?void 0:arguments[1])&&(n=arguments.length<=1?void 0:arguments[1])),this.transform=n,this[B]=I.OPEN,e?(this.buf=new C.FixedQueue(e),this[G]=T):r?(this.buf=r,this[G]=T):this[G]=F}return(0,w["default"])(t,[{key:"close",value:function(){var e=arguments.length<=0||void 0===arguments[0]?!1:arguments[0];return t.close(this,e)}},{key:"empty",value:function(){return t.empty(this)}},{key:"put",value:function(e){return t.put(this,e)}},{key:"take",value:function(){return t.take(this)}},{key:"tail",value:function(e){return t.tail(this,e)}},{key:"produce",value:function(e){return t.produce(this,e)}},{key:"consume",value:function(){var e=arguments.length<=0||void 0===arguments[0]?function(){}:arguments[0];return t.consume(this,e)}},{key:"done",value:function(){return t.done(this)}},{key:"pipe",value:function(){for(var e=arguments.length,n=Array(e),r=0;e>r;r++)n[r]=arguments[r];return t.pipe.apply(t,[this].concat(n))}},{key:"merge",value:function(){for(var e=arguments.length,n=Array(e),r=0;e>r;r++)n[r]=arguments[r];return t.merge.apply(t,[this].concat(n))}},{key:"unpipe",value:function(){for(var e=arguments.length,n=Array(e),r=0;e>r;r++)n[r]=arguments[r];return t.unpipe.apply(t,[this].concat(n))}},{key:"state",set:function(t){this[B]=t},get:function(){return this[B]}},{key:"length",get:function(){return this.buf?this.buf.length+this.puts.length:this.puts.length}},{key:"size",get:function(){return this.buf?this.buf.size:void 0}}],[{key:"from",value:function(e){var n=arguments.length<=1||void 0===arguments[1]?!1:arguments[1],r=[].concat((0,k["default"])(e)),u=new t(r.length),o=!0,i=!1,a=void 0;try{for(var f,c=(0,v["default"])(r);!(o=(f=c.next()).done);o=!0){var s=f.value;u.buf.push(s)}}catch(l){i=!0,a=l}finally{try{!o&&c["return"]&&c["return"]()}finally{if(i)throw a}}return n||u.close(!0),u}},{key:"close",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?!1:arguments[1];t.state=I.CLOSED,e&&(t[Q]=!0),setTimeout(function(){return D(t)})}},{key:"empty",value:function(t){return t.buf?t.buf.empty()&&t.puts.empty():t.puts.empty()}},{key:"put",value:function(t,e){return new S["default"](function(n){if(t.state!==I.OPEN)return n(R.DONE);var r=i(e,t.transform,n);t.puts.push(r),D(t)})}},{key:"take",value:function(t){return new S["default"](function(e){return t.state===I.ENDED?e(R.DONE):(t.takes.push(e),void D(t))})}},{key:"tail",value:function(t,e){return new S["default"](function(n){if(t.state!==I.OPEN)return n(R.DONE);var r=i(e,t.transform,n);t.tails.push(r),D(t)})}},{key:"produce",value:function(){function e(t,e){return n.apply(this,arguments)}var n=(0,M["default"])(E["default"].mark(function r(e,n){var o,i=this;return E["default"].wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return o=!0,(0,M["default"])(E["default"].mark(function a(){var r,c;return E["default"].wrap(function(i){for(;;)switch(i.prev=i.next){case 0:i.prev=0;case 1:if(!o){i.next=18;break}if(r=n(),!(r instanceof S["default"])){i.next=9;break}return i.next=6,r;case 6:r=i.sent,i.next=11;break;case 9:return i.next=11,f();case 11:return i.next=13,t.put(e,r);case 13:if(c=i.sent,c!==R.DONE){i.next=16;break}return i.abrupt("break",18);case 16:i.next=1;break;case 18:i.next=23;break;case 20:i.prev=20,i.t0=i["catch"](0),u(i.t0);case 23:case"end":return i.stop()}},a,i,[[0,20]])}))(),r.abrupt("return",function(){o=!1});case 3:case"end":return r.stop()}},r,this)}));return e}()},{key:"consume",value:function(){function e(t,e){return n.apply(this,arguments)}var n=(0,M["default"])(E["default"].mark(function r(e){var n=this,u=arguments.length<=1||void 0===arguments[1]?function(){}:arguments[1];return E["default"].wrap(function(r){for(;;)switch(r.prev=r.next){case 0:e[W]=!0,(0,M["default"])(E["default"].mark(function i(){var r,a,f;return E["default"].wrap(function(n){for(;;)switch(n.prev=n.next){case 0:r=t.take(e);case 1:if(!e[W]){n.next=13;break}return n.next=4,r;case 4:if(a=n.sent,a!==R.DONE){n.next=7;break}return n.abrupt("break",13);case 7:return f=u(a),r=t.take(e),n.next=11,f;case 11:n.next=1;break;case 13:if(e[W]=!1,!e[z]){n.next=19;break}return n.next=17,e[z];case 17:n.next=20;break;case 19:o(e);case 20:case"end":return n.stop()}},i,n)}))();case 2:case"end":return r.stop()}},r,this)}));return e}()},{key:"done",value:function(t){return new S["default"](function(e){return t.state===I.ENDED?e():void t.waiting.push(e)})}},{key:"pipeline",value:function(){for(var e=arguments.length,n=Array(e),r=0;e>r;r++)n[r]=arguments[r];var u=null,o=null;if(0===n.length)u=new t,o=u;else{Array.isArray(n[0])&&(n=[].concat((0,k["default"])(n[0])));var i=n.filter(function(e){return e instanceof Function||e instanceof t}).map(function(e){return e instanceof t?e:new t(e)});u=i[0],o=i.reduce(function(t,e){return t.pipe(e)})}return[u,o]}},{key:"pipe",value:function(e){for(var n,r=this,u=arguments.length,o=Array(u>1?u-1:0),i=1;u>i;i++)o[i-1]=arguments[i];return o=o.map(function(e){return e instanceof Function?new t(e):e}),(n=e.pipeline).push.apply(n,(0,k["default"])(o)),e[R.CANCEL]||!function(){var t=!0;(0,M["default"])(E["default"].mark(function n(){var u,o;return E["default"].wrap(function(n){for(;;)switch(n.prev=n.next){case 0:u=E["default"].mark(function i(){var t,n,u,o,a,f,c;return E["default"].wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,e.take();case 2:if(t=r.sent,t!==R.DONE){r.next=25;break}if(!e[Q]){r.next=24;break}for(n=!0,u=!1,o=void 0,r.prev=8,a=(0,v["default"])(e.pipeline);!(n=(f=a.next()).done);n=!0)c=f.value,c.close(!0);r.next=16;break;case 12:r.prev=12,r.t0=r["catch"](8),u=!0,o=r.t0;case 16:r.prev=16,r.prev=17,!n&&a["return"]&&a["return"]();case 19:if(r.prev=19,!u){r.next=22;break}throw o;case 22:return r.finish(19);case 23:return r.finish(16);case 24:return r.abrupt("return","break");case 25:return r.next=27,S["default"].all(e.pipeline.map(function(e){return e.put(t)}));case 27:case"end":return r.stop()}},i,r,[[8,12,16,24],[17,,19,23]])});case 1:if(!t){n.next=8;break}return n.delegateYield(u(),"t0",3);case 3:if(o=n.t0,"break"!==o){n.next=6;break}return n.abrupt("break",8);case 6:n.next=1;break;case 8:case"end":return n.stop()}},n,r)}))(),e[R.CANCEL]=function(){t=!1}}(),o[o.length-1]}},{key:"merge",value:function(){for(var e=new t,n=arguments.length,r=Array(n),u=0;n>u;u++)r[u]=arguments[u];var o=!0,i=!1,a=void 0;try{for(var f,c=(0,v["default"])(r);!(o=(f=c.next()).done);o=!0){var s=f.value;s.pipe(e)}}catch(l){i=!0,a=l}finally{try{!o&&c["return"]&&c["return"]()}finally{if(i)throw a}}return e}},{key:"unpipe",value:function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),r=1;e>r;r++)n[r-1]=arguments[r];var u=!0,o=!1,i=void 0;try{for(var a,f=(0,v["default"])((0,h["default"])(t.pipeline));!(u=(a=f.next()).done);u=!0){var c=(0,l["default"])(a.value,2),s=c[0],p=c[1],d=!0,y=!1,g=void 0;try{for(var x,m=(0,v["default"])(n);!(d=(x=m.next()).done);d=!0){var b=x.value;p===b&&t.pipeline.splice(s,1)}}catch(w){y=!0,g=w}finally{try{!d&&m["return"]&&m["return"]()}finally{if(y)throw g}}}}catch(w){o=!0,i=w}finally{try{!u&&f["return"]&&f["return"]()}finally{if(o)throw i}}return 0===t.pipeline.length&&t[R.CANCEL]&&t[R.CANCEL](),t}}]),t}();J.DONE=R.DONE,e["default"]=J},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(2),o=r(u),i=n(54),a=r(i);e["default"]=function(){function t(t,e){var n=[],r=!0,u=!1,o=void 0;try{for(var i,f=(0,a["default"])(t);!(r=(i=f.next()).done)&&(n.push(i.value),!e||n.length!==e);r=!0);}catch(c){u=!0,o=c}finally{try{!r&&f["return"]&&f["return"]()}finally{if(u)throw o}}return n}return function(e,n){if(Array.isArray(e))return e;if((0,o["default"])(Object(e)))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}()},function(t,e,n){t.exports={"default":n(3),__esModule:!0}},function(t,e,n){n(4),n(50),t.exports=n(52)},function(t,e,n){n(5);for(var r=n(16),u=n(20),o=n(8),i=n(47)("toStringTag"),a=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],f=0;5>f;f++){var c=a[f],s=r[c],l=s&&s.prototype;l&&!l[i]&&u(l,i,c),o[c]=o.Array}},function(t,e,n){"use strict";var r=n(6),u=n(7),o=n(8),i=n(9);t.exports=n(13)(Array,"Array",function(t,e){this._t=i(t),this._i=0,this._k=e},function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,u(1)):"keys"==e?u(0,n):"values"==e?u(0,t[n]):u(0,[n,t[n]])},"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},function(t,e){t.exports=function(){}},function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},function(t,e){t.exports={}},function(t,e,n){var r=n(10),u=n(12);t.exports=function(t){return r(u(t))}},function(t,e,n){var r=n(11);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){"use strict";var r=n(14),u=n(15),o=n(30),i=n(20),a=n(31),f=n(8),c=n(32),s=n(46),l=n(48),p=n(47)("iterator"),h=!([].keys&&"next"in[].keys()),d="@@iterator",v="keys",y="values",g=function(){return this};t.exports=function(t,e,n,x,m,b,w){c(n,e,x);var _,k,O,E=function(t){if(!h&&t in M)return M[t];switch(t){case v:return function(){return new n(this,t)};case y:return function(){return new n(this,t)}}return function(){return new n(this,t)}},j=e+" Iterator",S=m==y,L=!1,M=t.prototype,P=M[p]||M[d]||m&&M[m],A=P||E(m),N=m?S?E("entries"):A:void 0,T="Array"==e?M.entries||P:P;if(T&&(O=l(T.call(new t)),O!==Object.prototype&&(s(O,j,!0),r||a(O,p)||i(O,p,g))),S&&P&&P.name!==y&&(L=!0,A=function(){return P.call(this)}),r&&!w||!h&&!L&&M[p]||i(M,p,A),f[e]=A,f[j]=g,m)if(_={values:S?A:E(y),keys:b?A:E(v),entries:N},w)for(k in _)k in M||o(M,k,_[k]);else u(u.P+u.F*(h||L),e,_);return _}},function(t,e){t.exports=!0},function(t,e,n){var r=n(16),u=n(17),o=n(18),i=n(20),a="prototype",f=function(t,e,n){var c,s,l,p=t&f.F,h=t&f.G,d=t&f.S,v=t&f.P,y=t&f.B,g=t&f.W,x=h?u:u[e]||(u[e]={}),m=x[a],b=h?r:d?r[e]:(r[e]||{})[a];h&&(n=e);for(c in n)s=!p&&b&&void 0!==b[c],s&&c in x||(l=s?b[c]:n[c],x[c]=h&&"function"!=typeof b[c]?n[c]:y&&s?o(l,r):g&&b[c]==l?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e[a]=t[a],e}(l):v&&"function"==typeof l?o(Function.call,l):l,v&&((x.virtual||(x.virtual={}))[c]=l,t&f.R&&m&&!m[c]&&i(m,c,l)))};f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e){var n=t.exports={version:"2.2.1"};"number"==typeof __e&&(__e=n)},function(t,e,n){var r=n(19);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,u){return t.call(e,n,r,u)}}return function(){return t.apply(e,arguments)}}},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e,n){var r=n(21),u=n(29);t.exports=n(25)?function(t,e,n){return r.f(t,e,u(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(22),u=n(24),o=n(28),i=Object.defineProperty;e.f=n(25)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),u)try{return i(t,e,n)}catch(a){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(23);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){t.exports=!n(25)&&!n(26)(function(){return 7!=Object.defineProperty(n(27)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){t.exports=!n(26)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e){t.exports=function(t){try{return!!t()}catch(e){return!0}}},function(t,e,n){var r=n(23),u=n(16).document,o=r(u)&&r(u.createElement);t.exports=function(t){return o?u.createElement(t):{}}},function(t,e,n){var r=n(23);t.exports=function(t,e){if(!r(t))return t;var n,u;if(e&&"function"==typeof(n=t.toString)&&!r(u=n.call(t)))return u;if("function"==typeof(n=t.valueOf)&&!r(u=n.call(t)))return u;if(!e&&"function"==typeof(n=t.toString)&&!r(u=n.call(t)))return u;throw TypeError("Can't convert object to primitive value")}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){t.exports=n(20)},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){"use strict";var r=n(33),u=n(29),o=n(46),i={};n(20)(i,n(47)("iterator"),function(){return this}),t.exports=function(t,e,n){t.prototype=r(i,{next:u(1,n)}),o(t,e+" Iterator")}},function(t,e,n){var r=n(22),u=n(34),o=n(44),i=n(41)("IE_PROTO"),a=function(){},f="prototype",c=function(){var t,e=n(27)("iframe"),r=o.length,u=">";for(e.style.display="none",n(45).appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write("f;)r.f(t,n=i[f++],e[n]);return t}},function(t,e,n){var r=n(36),u=n(44);t.exports=Object.keys||function(t){return r(t,u)}},function(t,e,n){var r=n(31),u=n(9),o=n(37)(!1),i=n(41)("IE_PROTO");t.exports=function(t,e){var n,a=u(t),f=0,c=[];for(n in a)n!=i&&r(a,n)&&c.push(n);for(;e.length>f;)r(a,n=e[f++])&&(~o(c,n)||c.push(n));return c}},function(t,e,n){var r=n(9),u=n(38),o=n(40);t.exports=function(t){return function(e,n,i){var a,f=r(e),c=u(f.length),s=o(i,c);if(t&&n!=n){for(;c>s;)if(a=f[s++],a!=a)return!0}else for(;c>s;s++)if((t||s in f)&&f[s]===n)return t||s;return!t&&-1}}},function(t,e,n){var r=n(39),u=Math.min;t.exports=function(t){return t>0?u(r(t),9007199254740991):0}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(39),u=Math.max,o=Math.min;t.exports=function(t,e){return t=r(t),0>t?u(t+e,0):o(t,e)}},function(t,e,n){var r=n(42)("keys"),u=n(43);t.exports=function(t){return r[t]||(r[t]=u(t))}},function(t,e,n){var r=n(16),u="__core-js_shared__",o=r[u]||(r[u]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){t.exports=n(16).document&&document.documentElement},function(t,e,n){var r=n(21).f,u=n(31),o=n(47)("toStringTag");t.exports=function(t,e,n){t&&!u(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(42)("wks"),u=n(43),o=n(16).Symbol,i="function"==typeof o;t.exports=function(t){return r[t]||(r[t]=i&&o[t]||(i?o:u)("Symbol."+t))}},function(t,e,n){var r=n(31),u=n(49),o=n(41)("IE_PROTO"),i=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=u(t),r(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?i:null}},function(t,e,n){var r=n(12);t.exports=function(t){return Object(r(t))}},function(t,e,n){"use strict";var r=n(51)(!0);n(13)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,e=this._t,n=this._i;return n>=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})})},function(t,e,n){var r=n(39),u=n(12);t.exports=function(t){return function(e,n){var o,i,a=String(u(e)),f=r(n),c=a.length;return 0>f||f>=c?t?"":void 0:(o=a.charCodeAt(f),55296>o||o>56319||f+1===c||(i=a.charCodeAt(f+1))<56320||i>57343?t?a.charAt(f):o:t?a.slice(f,f+2):(o-55296<<10)+(i-56320)+65536)}}},function(t,e,n){var r=n(53),u=n(47)("iterator"),o=n(8);t.exports=n(17).isIterable=function(t){var e=Object(t);return void 0!==e[u]||"@@iterator"in e||o.hasOwnProperty(r(e))}},function(t,e,n){var r=n(11),u=n(47)("toStringTag"),o="Arguments"==r(function(){return arguments}()),i=function(t,e){try{return t[e]}catch(n){}};t.exports=function(t){var e,n,a;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=i(e=Object(t),u))?n:o?r(e):"Object"==(a=r(e))&&"function"==typeof e.callee?"Arguments":a}},function(t,e,n){t.exports={"default":n(55),__esModule:!0}},function(t,e,n){n(4),n(50),t.exports=n(56)},function(t,e,n){var r=n(22),u=n(57);t.exports=n(17).getIterator=function(t){var e=u(t);if("function"!=typeof e)throw TypeError(t+" is not iterable!");return r(e.call(t))}},function(t,e,n){var r=n(53),u=n(47)("iterator"),o=n(8);t.exports=n(17).getIteratorMethod=function(t){return void 0!=t?t[u]||t["@@iterator"]||o[r(t)]:void 0}},function(t,e,n){t.exports={"default":n(59),__esModule:!0}},function(t,e,n){n(5),t.exports=n(17).Array.entries},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(61),o=r(u),i=n(63),a=r(i),f="function"==typeof a["default"]&&"symbol"==typeof o["default"]?function(t){return typeof t}:function(t){return t&&"function"==typeof a["default"]&&t.constructor===a["default"]?"symbol":typeof t};e["default"]="function"==typeof a["default"]&&"symbol"===f(o["default"])?function(t){return"undefined"==typeof t?"undefined":f(t)}:function(t){return t&&"function"==typeof a["default"]&&t.constructor===a["default"]?"symbol":"undefined"==typeof t?"undefined":f(t)}},function(t,e,n){t.exports={"default":n(62),__esModule:!0}},function(t,e,n){n(50),n(4),t.exports=n(47)("iterator")},function(t,e,n){t.exports={"default":n(64),__esModule:!0}},function(t,e,n){n(65),n(75),t.exports=n(17).Symbol},function(t,e,n){"use strict";var r=n(16),u=n(17),o=n(31),i=n(25),a=n(15),f=n(30),c=n(66).KEY,s=n(26),l=n(42),p=n(46),h=n(43),d=n(47),v=n(67),y=n(68),g=n(71),x=n(22),m=n(9),b=n(28),w=n(29),_=n(33),k=n(72),O=n(74),E=n(21),j=O.f,S=E.f,L=k.f,M=r.Symbol,P=r.JSON,A=P&&P.stringify,N=!1,T="prototype",F=d("_hidden"),D=d("toPrimitive"),C={}.propertyIsEnumerable,I=l("symbol-registry"),R=l("symbols"),G=Object[T],B="function"==typeof M,Q=r.QObject,W=i&&s(function(){return 7!=_(S({},"a",{get:function(){return S(this,"a",{value:7}).a}})).a})?function(t,e,n){var r=j(G,e);r&&delete G[e],S(t,e,n),r&&t!==G&&S(G,e,r)}:S,z=function(t){var e=R[t]=_(M[T]);return e._k=t,i&&N&&W(G,t,{configurable:!0,set:function(e){o(this,F)&&o(this[F],t)&&(this[F][t]=!1),W(this,t,w(1,e))}}),e},Y=B&&"symbol"==typeof M.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof M},J=function(t,e,n){return x(t),e=b(e,!0),x(n),o(R,e)?(n.enumerable?(o(t,F)&&t[F][e]&&(t[F][e]=!1),n=_(n,{enumerable:w(0,!1)})):(o(t,F)||S(t,F,w(1,{})),t[F][e]=!0),W(t,e,n)):S(t,e,n)},K=function(t,e){x(t);for(var n,r=y(e=m(e)),u=0,o=r.length;o>u;)J(t,n=r[u++],e[n]);return t},U=function(t,e){return void 0===e?_(t):K(_(t),e)},H=function(t){var e=C.call(this,t=b(t,!0));return e||!o(this,t)||!o(R,t)||o(this,F)&&this[F][t]?e:!0},q=function(t,e){var n=j(t=m(t),e=b(e,!0));return!n||!o(R,e)||o(t,F)&&t[F][e]||(n.enumerable=!0),n},V=function(t){for(var e,n=L(m(t)),r=[],u=0;n.length>u;)o(R,e=n[u++])||e==F||e==c||r.push(e);return r},X=function(t){for(var e,n=L(m(t)),r=[],u=0;n.length>u;)o(R,e=n[u++])&&r.push(R[e]);return r},Z=function(t){if(void 0!==t&&!Y(t)){for(var e,n,r=[t],u=1;arguments.length>u;)r.push(arguments[u++]);return e=r[1],"function"==typeof e&&(n=e),!n&&g(e)||(e=function(t,e){return n&&(e=n.call(this,t,e)),Y(e)?void 0:e}),r[1]=e,A.apply(P,r)}},$=s(function(){var t=M();return"[null]"!=A([t])||"{}"!=A({a:t})||"{}"!=A(Object(t))});B||(M=function(){if(this instanceof M)throw TypeError("Symbol is not a constructor!");return z(h(arguments.length>0?arguments[0]:void 0))},f(M[T],"toString",function(){return this._k}),O.f=q,E.f=J,n(73).f=k.f=V,n(70).f=H,n(69).f=X,i&&!n(14)&&f(G,"propertyIsEnumerable",H,!0)),a(a.G+a.W+a.F*!B,{Symbol:M});for(var tt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),et=0;tt.length>et;){var nt=tt[et++],rt=u.Symbol,ut=d(nt);nt in rt||S(rt,nt,{value:B?ut:z(ut)})}Q&&Q[T]&&Q[T].findChild||(N=!0),a(a.S+a.F*!B,"Symbol",{"for":function(t){return o(I,t+="")?I[t]:I[t]=M(t)},keyFor:function(t){if(Y(t))return v(I,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){N=!0},useSimple:function(){N=!1}}),a(a.S+a.F*!B,"Object",{create:U,defineProperty:J,defineProperties:K,getOwnPropertyDescriptor:q,getOwnPropertyNames:V,getOwnPropertySymbols:X}),P&&a(a.S+a.F*(!B||$),"JSON",{stringify:Z}),M[T][D]||n(20)(M[T],D,M[T].valueOf),p(M,"Symbol"),p(Math,"Math",!0),p(r.JSON,"JSON",!0)},function(t,e,n){var r=n(43)("meta"),u=n(23),o=n(31),i=n(21).f,a=0,f=Object.isExtensible||function(){return!0},c=!n(26)(function(){return f(Object.preventExtensions({}))}),s=function(t){i(t,r,{value:{i:"O"+ ++a,w:{}}})},l=function(t,e){if(!u(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,r)){if(!f(t))return"F";if(!e)return"E";s(t)}return t[r].i},p=function(t,e){if(!o(t,r)){if(!f(t))return!0;if(!e)return!1;s(t)}return t[r].w},h=function(t){return c&&d.NEED&&f(t)&&!o(t,r)&&s(t),t},d=t.exports={KEY:r,NEED:!1,fastKey:l,getWeak:p,onFreeze:h}},function(t,e,n){var r=n(35),u=n(9);t.exports=function(t,e){for(var n,o=u(t),i=r(o),a=i.length,f=0;a>f;)if(o[n=i[f++]]===e)return n}},function(t,e,n){var r=n(35),u=n(69),o=n(70);t.exports=function(t){var e=r(t),n=u.f;if(n)for(var i,a=n(t),f=o.f,c=0;a.length>c;)f.call(t,i=a[c++])&&e.push(i);return e}},function(t,e){e.f=Object.getOwnPropertySymbols},function(t,e){e.f={}.propertyIsEnumerable},function(t,e,n){var r=n(11);t.exports=Array.isArray||function(t){return"Array"==r(t)}},function(t,e,n){var r=n(9),u=n(73).f,o={}.toString,i="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],a=function(t){try{return u(t)}catch(e){return i.slice()}};t.exports.f=function(t){return i&&"[object Window]"==o.call(t)?a(t):u(r(t))}},function(t,e,n){var r=n(36),u=n(44).concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return r(t,u)}},function(t,e,n){var r=n(70),u=n(29),o=n(9),i=n(28),a=n(31),f=n(24),c=Object.getOwnPropertyDescriptor;e.f=n(25)?c:function(t,e){if(t=o(t),e=i(e,!0),f)try{return c(t,e)}catch(n){}return a(t,e)?u(!r.f.call(t,e),t[e]):void 0}},function(t,e){},function(t,e){"use strict";e.__esModule=!0,e["default"]=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(78),o=r(u);e["default"]=function(){function t(t,e){for(var n=0;n1?arguments[1]:void 0,v=void 0!==d,y=0,g=c(l);if(v&&(d=r(d,h>2?arguments[2]:void 0,2)),void 0==g||p==Array&&a(g))for(e=f(l.length),n=new p(e);e>y;y++)n[y]=v?d(l[y],y):l[y];else for(s=g.call(l),n=new p;!(u=s.next()).done;y++)n[y]=v?i(s,d,[u.value,y],!0):u.value;return n.length=y,n}})},function(t,e,n){var r=n(22);t.exports=function(t,e,n,u){try{return u?e(r(n)[0],n[1]):e(n)}catch(o){var i=t["return"];throw void 0!==i&&r(i.call(t)),o}}},function(t,e,n){var r=n(8),u=n(47)("iterator"),o=Array.prototype;t.exports=function(t){return void 0!==t&&(r.Array===t||o[u]===t)}},function(t,e,n){var r=n(47)("iterator"),u=!1;try{var o=[7][r]();o["return"]=function(){u=!0},Array.from(o,function(){throw 2;
2 | })}catch(i){}t.exports=function(t,e){if(!e&&!u)return!1;var n=!1;try{var o=[7],i=o[r]();i.next=function(){n=!0},o[r]=function(){return i},t(o)}catch(a){}return n}},function(t,e,n){(function(e){var r="object"==typeof e?e:"object"==typeof window?window:"object"==typeof self?self:this,u=r.regeneratorRuntime&&Object.getOwnPropertyNames(r).indexOf("regeneratorRuntime")>=0,o=u&&r.regeneratorRuntime;if(r.regeneratorRuntime=void 0,t.exports=n(89),u)r.regeneratorRuntime=o;else try{delete r.regeneratorRuntime}catch(i){r.regeneratorRuntime=void 0}t.exports={"default":t.exports,__esModule:!0}}).call(e,function(){return this}())},function(t,e,n){(function(t,e,r){"use strict";function u(t){return t&&t.__esModule?t:{"default":t}}var o=n(92),i=u(o),a=n(104),f=u(a),c=n(107),s=u(c),l=n(60),p=u(l),h=n(61),d=u(h),v=n(63),y=u(v);!function(t){function n(t,e,n,r){var u=(0,s["default"])((e||o).prototype),i=new b(r||[]);return u._invoke=g(t,n,i),u}function u(t,e,n){try{return{type:"normal",arg:t.call(e,n)}}catch(r){return{type:"throw",arg:r}}}function o(){}function a(){}function c(){}function l(t){["next","throw","return"].forEach(function(e){t[e]=function(t){return this._invoke(e,t)}})}function h(t){this.arg=t}function v(t){function e(e,n){var r=t[e](n),u=r.value;return u instanceof h?i["default"].resolve(u.arg).then(o,a):i["default"].resolve(u).then(function(t){return r.value=t,r})}function n(t,n){function r(){return e(t,n)}return u=u?u.then(r,r):new i["default"](function(t){t(r())})}"object"===("undefined"==typeof r?"undefined":(0,p["default"])(r))&&r.domain&&(e=r.domain.bind(e));var u,o=e.bind(t,"next"),a=e.bind(t,"throw");e.bind(t,"return");this._invoke=n}function g(t,e,n){var r=L;return function(o,i){if(r===P)throw new Error("Generator is already running");if(r===A){if("throw"===o)throw i;return _()}for(;;){var a=n.delegate;if(a){if("return"===o||"throw"===o&&a.iterator[o]===k){n.delegate=null;var f=a.iterator["return"];if(f){var c=u(f,a.iterator,i);if("throw"===c.type){o="throw",i=c.arg;continue}}if("return"===o)continue}var c=u(a.iterator[o],a.iterator,i);if("throw"===c.type){n.delegate=null,o="throw",i=c.arg;continue}o="next",i=k;var s=c.arg;if(!s.done)return r=M,s;n[a.resultName]=s.value,n.next=a.nextLoc,n.delegate=null}if("next"===o)n._sent=i,r===M?n.sent=i:n.sent=k;else if("throw"===o){if(r===L)throw r=A,i;n.dispatchException(i)&&(o="next",i=k)}else"return"===o&&n.abrupt("return",i);r=P;var c=u(t,e,n);if("normal"===c.type){r=n.done?A:M;var s={value:c.arg,done:n.done};if(c.arg!==N)return s;n.delegate&&"next"===o&&(i=k)}else"throw"===c.type&&(r=A,o="throw",i=c.arg)}}}function x(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function m(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function b(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(x,this),this.reset(!0)}function w(t){if(t){var e=t[E];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,r=function u(){for(;++n=0;--r){var u=this.tryEntries[r],o=u.completion;if("root"===u.tryLoc)return e("end");if(u.tryLoc<=this.prev){var i=O.call(u,"catchLoc"),a=O.call(u,"finallyLoc");if(i&&a){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&O.call(r,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),m(n),N}},"catch":function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var u=r.arg;m(n)}return u}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:w(t),resultName:e,nextLoc:n},N}}}("object"===("undefined"==typeof t?"undefined":(0,p["default"])(t))?t:"object"===("undefined"==typeof window?"undefined":(0,p["default"])(window))?window:"object"===("undefined"==typeof self?"undefined":(0,p["default"])(self))?self:void 0)}).call(e,function(){return this}(),n(90)(t),n(91))},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){function n(){c=!1,i.length?f=i.concat(f):s=-1,f.length&&r()}function r(){if(!c){var t=setTimeout(n);c=!0;for(var e=f.length;e;){for(i=f,f=[];++s1)for(var n=1;no;)i(n[o++]);t._c=[],t._n=!1,e&&!t._h&&A(t)})}},A=function(t){y.call(a,function(){var e,n,r,u=t._v;if(N(t)&&(e=M(function(){_?b.emit("unhandledRejection",u,t):(n=a.onunhandledrejection)?n({promise:t,reason:u}):(r=a.console)&&r.error&&r.error("Unhandled promise rejection",u)}),t._h=_||N(t)?2:1),t._a=void 0,e)throw e.error})},N=function(t){if(1==t._h)return!1;for(var e,n=t._a||t._c,r=0;n.length>r;)if(e=n[r++],e.fail||!N(e.promise))return!1;return!0},T=function(t){y.call(a,function(){var e;_?b.emit("rejectionHandled",t):(e=a.onrejectionhandled)&&e({promise:t,reason:t._v})})},F=function(t){var e=this;e._d||(e._d=!0,e=e._w||e,e._v=t,e._s=2,e._a||(e._a=e._c.slice()),P(e,!0))},D=function(t){var e,n=this;if(!n._d){n._d=!0,n=n._w||n;try{if(n===t)throw m("Promise can't be resolved itself");(e=j(t))?g(function(){var r={_w:n,_d:!1};try{e.call(t,f(D,r,1),f(F,r,1))}catch(u){F.call(r,u)}}):(n._v=t,n._s=1,P(n,!1))}catch(r){F.call({_w:n,_d:!1},r)}}};O||(w=function(t){h(this,w,x,"_h"),p(t),r.call(this);try{t(f(D,this,1),f(F,this,1))}catch(e){F.call(this,e)}},r=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1},r.prototype=n(102)(w.prototype,{then:function(t,e){var n=S(v(this,w));return n.ok="function"==typeof t?t:!0,n.fail="function"==typeof e&&e,n.domain=_?b.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&P(this,!1),n.promise},"catch":function(t){return this.then(void 0,t)}}),L=function(){var t=new r;this.promise=t,this.resolve=f(D,t,1),this.reject=f(F,t,1)}),s(s.G+s.W+s.F*!O,{Promise:w}),n(46)(w,x),n(103)(x),o=n(17)[x],s(s.S+s.F*!O,x,{reject:function(t){var e=S(this),n=e.reject;return n(t),e.promise}}),s(s.S+s.F*(i||!O),x,{resolve:function(t){if(t instanceof w&&E(t.constructor,this))return t;var e=S(this),n=e.resolve;return n(t),e.promise}}),s(s.S+s.F*!(O&&n(87)(function(t){w.all(t)["catch"](k)})),x,{all:function(t){var e=this,n=S(e),r=n.resolve,u=n.reject,o=M(function(){var n=[],o=0,i=1;d(t,!1,function(t){var a=o++,f=!1;n.push(void 0),i++,e.resolve(t).then(function(t){f||(f=!0,n[a]=t,--i||r(n))},u)}),--i||r(n)});return o&&u(o.error),n.promise},race:function(t){var e=this,n=S(e),r=n.reject,u=M(function(){d(t,!1,function(t){e.resolve(t).then(n.resolve,r)})});return u&&r(u.error),n.promise}})},function(t,e){t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},function(t,e,n){var r=n(18),u=n(85),o=n(86),i=n(22),a=n(38),f=n(57);t.exports=function(t,e,n,c,s){var l,p,h,d=s?function(){return t}:f(t),v=r(n,c,e?2:1),y=0;if("function"!=typeof d)throw TypeError(t+" is not iterable!");if(o(d))for(l=a(t.length);l>y;y++)e?v(i(p=t[y])[0],p[1]):v(t[y]);else for(h=d.call(t);!(p=h.next()).done;)u(h,v,p.value,e)}},function(t,e,n){var r=n(23),u=n(22),o=function(t,e){if(u(t),!r(e)&&null!==e)throw TypeError(e+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,e,r){try{r=n(18)(Function.call,n(74).f(Object.prototype,"__proto__").set,2),r(t,[]),e=!(t instanceof Array)}catch(u){e=!0}return function(t,n){return o(t,n),e?t.__proto__=n:r(t,n),t}}({},!1):void 0),check:o}},function(t,e,n){var r=n(22),u=n(19),o=n(47)("species");t.exports=function(t,e){var n,i=r(t).constructor;return void 0===i||void 0==(n=r(i)[o])?e:u(n)}},function(t,e,n){var r,u,o,i=n(18),a=n(100),f=n(45),c=n(27),s=n(16),l=s.process,p=s.setImmediate,h=s.clearImmediate,d=s.MessageChannel,v=0,y={},g="onreadystatechange",x=function(){var t=+this;if(y.hasOwnProperty(t)){var e=y[t];delete y[t],e()}},m=function(t){x.call(t.data)};p&&h||(p=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return y[++v]=function(){a("function"==typeof t?t:Function(t),e)},r(v),v},h=function(t){delete y[t]},"process"==n(11)(l)?r=function(t){l.nextTick(i(x,t,1))}:d?(u=new d,o=u.port2,u.port1.onmessage=m,r=i(o.postMessage,o,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts?(r=function(t){s.postMessage(t+"","*")},s.addEventListener("message",m,!1)):r=g in c("script")?function(t){f.appendChild(c("script"))[g]=function(){f.removeChild(this),x.call(t)}}:function(t){setTimeout(i(x,t,1),0)}),t.exports={set:p,clear:h}},function(t,e){t.exports=function(t,e,n){var r=void 0===n;switch(e.length){case 0:return r?t():t.call(n);case 1:return r?t(e[0]):t.call(n,e[0]);case 2:return r?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return r?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return r?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3])}return t.apply(n,e)}},function(t,e,n){var r,u,o,i=n(16),a=n(99).set,f=i.MutationObserver||i.WebKitMutationObserver,c=i.process,s=i.Promise,l="process"==n(11)(c),p=function(){var t,e;for(l&&(t=c.domain)&&t.exit();r;)e=r.fn,e(),r=r.next;u=void 0,t&&t.enter()};if(l)o=function(){c.nextTick(p)};else if(f){var h=!0,d=document.createTextNode("");new f(p).observe(d,{characterData:!0}),o=function(){d.data=h=!h}}else o=s&&s.resolve?function(){s.resolve().then(p)}:function(){a.call(i,p)};t.exports=function(t){var e={fn:t,next:void 0};u&&(u.next=e),r||(r=e,o()),u=e}},function(t,e,n){var r=n(20);t.exports=function(t,e,n){for(var u in e)n&&t[u]?t[u]=e[u]:r(t,u,e[u]);return t}},function(t,e,n){"use strict";var r=n(16),u=n(17),o=n(21),i=n(25),a=n(47)("species");t.exports=function(t){var e="function"==typeof u[t]?u[t]:r[t];i&&e&&!e[a]&&o.f(e,a,{configurable:!0,get:function(){return this}})}},function(t,e,n){t.exports={"default":n(105),__esModule:!0}},function(t,e,n){n(106),t.exports=n(17).Object.setPrototypeOf},function(t,e,n){var r=n(15);r(r.S,"Object",{setPrototypeOf:n(97).set})},function(t,e,n){t.exports={"default":n(108),__esModule:!0}},function(t,e,n){n(109);var r=n(17).Object;t.exports=function(t,e){return r.create(t,e)}},function(t,e,n){var r=n(15);r(r.S,"Object",{create:n(33)})},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(92),o=r(u);e["default"]=function(t){return function(){var e=t.apply(this,arguments);return new o["default"](function(t,n){function r(u,i){try{var a=e[u](i),f=a.value}catch(c){return void n(c)}return a.done?void t(f):o["default"].resolve(f).then(function(t){return r("next",t)},function(t){return r("throw",t)})}return r("next")})}}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0}),e.SlidingBuffer=e.DroppingBuffer=e.FixedQueue=e.List=e.Queue=e.FixedStack=e.Stack=void 0;var u=n(112),o=r(u),i=n(113),a=r(i),f=n(120),c=r(f),s=n(121),l=r(s),p=n(107),h=r(p),d=n(122),v=r(d),y=n(81),g=r(y),x=n(54),m=r(x),b=n(61),w=r(b),_=n(76),k=r(_),O=n(77),E=r(O),j=4096,S="_arr",L="_size",M=function(){function t(){(0,k["default"])(this,t),this[S]=[]}return(0,E["default"])(t,[{key:w["default"],value:function(){return(0,m["default"])(this[S])}},{key:"flush",value:function(){this[S].length=0}},{key:"empty",value:function(){return 0===this[S].length}},{key:"toString",value:function(){return this[S].join(", ")}},{key:"values",value:function(){return[].concat((0,g["default"])(this[S]))}},{key:v["default"],get:function(){return"Data"}},{key:"length",get:function(){return this[S].length}}],[{key:"construct",value:function(){return(0,h["default"])(this.constructor)}}]),t}(),P=e.Stack=function(t){function e(){return(0,k["default"])(this,e),(0,c["default"])(this,(0,a["default"])(e).call(this))}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"push",value:function(t){this[S].push(t)}},{key:"pop",value:function(){return this[S].pop()}},{key:"peek",value:function(){return this[S][this.length-1]}},{key:v["default"],get:function(){return"Stack"}}]),e}(M),A=(e.FixedStack=function(t){function e(){var t=arguments.length<=0||void 0===arguments[0]?j:arguments[0];(0,k["default"])(this,e);var n=(0,c["default"])(this,(0,a["default"])(e).call(this));return n[L]=t,n}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"push",value:function(t){return this.full()?void 0:(0,o["default"])((0,a["default"])(e.prototype),"push",this).call(this,t)}},{key:"full",value:function(){return this.length>=this[L]}},{key:v["default"],get:function(){return"FixedStack"}},{key:"size",get:function(){return this[L]}}]),e}(P),e.Queue=function(t){function e(){return(0,k["default"])(this,e),(0,c["default"])(this,(0,a["default"])(e).call(this))}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"push",value:function(t){this[S].push(t)}},{key:"shift",value:function(){return this[S].shift()}},{key:"peek",value:function(){return this[S][0]}},{key:v["default"],get:function(){return"Queue"}}]),e}(M));e.List=function(t){function e(){return(0,k["default"])(this,e),(0,c["default"])(this,(0,a["default"])(e).call(this))}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"unshift",value:function(){var t;return(t=this[S]).unshift.apply(t,arguments)}},{key:v["default"],get:function(){return"List"}}]),e}(A),e.FixedQueue=function(t){function e(){var t=arguments.length<=0||void 0===arguments[0]?j:arguments[0];(0,k["default"])(this,e);var n=(0,c["default"])(this,(0,a["default"])(e).call(this));return n[L]=t,n}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"push",value:function(t){return this.full()?void 0:(0,o["default"])((0,a["default"])(e.prototype),"push",this).call(this,t)}},{key:"full",value:function(){return this.length>=this[L]}},{key:"unshift",value:function(){var t;return(t=this[S]).unshift.apply(t,arguments)}},{key:v["default"],get:function(){return"FixedQueue"}},{key:"size",get:function(){return this[L]}}]),e}(A),e.DroppingBuffer=function(t){function e(){var t=arguments.length<=0||void 0===arguments[0]?j:arguments[0];(0,k["default"])(this,e);var n=(0,c["default"])(this,(0,a["default"])(e).call(this));return n[L]=t,n}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"unshift",value:function(){0===this[S].length&&(arguments.length<=0?void 0:arguments[0])&&(this[S][0]=arguments.length<=0?void 0:arguments[0])}},{key:"push",value:function(t){0===this[S].length&&(this[S][0]=t)}},{key:"full",value:function(){return!1}},{key:v["default"],get:function(){return"DroppingBuffer"}}]),e}(A),e.SlidingBuffer=function(t){function e(){var t=arguments.length<=0||void 0===arguments[0]?j:arguments[0];(0,k["default"])(this,e);var n=(0,c["default"])(this,(0,a["default"])(e).call(this));return n[L]=t,n.head=0,n.tail=0,n.count=0,n}return(0,l["default"])(e,t),(0,E["default"])(e,[{key:"empty",value:function(){return 0===this.count}},{key:"full",value:function(){return!1}},{key:"push",value:function(t){if(0===this.count)return this[S][this.tail]=t,this.head=this.tail,void(this.count=1);var e=this[L];this.tail=(this.tail+1)%e,this[S][this.tail]=t;var n=this.tail===this.head;n&&(this.head=(this.head+1)%e),n||(this.count+=1)}},{key:"shift",value:function(){var t=this[S][this.head];return delete this[S][this.head],this.head=(this.head+1)%this[L],this.count-=1,t}},{key:v["default"],get:function(){return"SlidingBuffer"}}]),e}(A)},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(113),o=r(u),i=n(117),a=r(i);e["default"]=function f(t,e,n){null===t&&(t=Function.prototype);var r=(0,a["default"])(t,e);if(void 0===r){var u=(0,o["default"])(t);return null===u?void 0:f(u,e,n)}if("value"in r)return r.value;var i=r.get;if(void 0!==i)return i.call(n)}},function(t,e,n){t.exports={"default":n(114),__esModule:!0}},function(t,e,n){n(115),t.exports=n(17).Object.getPrototypeOf},function(t,e,n){var r=n(49),u=n(48);n(116)("getPrototypeOf",function(){return function(t){return u(r(t))}})},function(t,e,n){var r=n(15),u=n(17),o=n(26);t.exports=function(t,e){var n=(u.Object||{})[t]||Object[t],i={};i[t]=e(n),r(r.S+r.F*o(function(){n(1)}),"Object",i)}},function(t,e,n){t.exports={"default":n(118),__esModule:!0}},function(t,e,n){n(119);var r=n(17).Object;t.exports=function(t,e){return r.getOwnPropertyDescriptor(t,e)}},function(t,e,n){var r=n(9),u=n(74).f;n(116)("getOwnPropertyDescriptor",function(){return function(t,e){return u(r(t),e)}})},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(60),o=r(u);e["default"]=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!==("undefined"==typeof e?"undefined":(0,o["default"])(e))&&"function"!=typeof e?t:e}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0;var u=n(104),o=r(u),i=n(107),a=r(i),f=n(60),c=r(f);e["default"]=function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+("undefined"==typeof e?"undefined":(0,c["default"])(e)));t.prototype=(0,a["default"])(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(o["default"]?(0,o["default"])(t,e):t.__proto__=e)}},function(t,e,n){t.exports={"default":n(123),__esModule:!0}},function(t,e,n){n(75),t.exports=n(47)("toStringTag")}])});
3 | //# sourceMappingURL=async-csp.min.js.map
4 |
--------------------------------------------------------------------------------
/examples/browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Async-CSP example
7 |
8 |
9 | Async-CSP browser UMD example
10 | See console for output
11 |
12 |
13 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/char-counter/char-counter.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import Channel from '../../dist/channel.js';
4 | let log = console.log.bind(console);
5 |
6 | import text from './text.js';
7 |
8 | function isAlphanumeric(char) {
9 | if (!(char instanceof Number))
10 | char = char.charCodeAt(0);
11 | if (char > 47 && char < 58)
12 | return true;
13 | if (char > 64 && char < 91)
14 | return true;
15 | if (char > 96 && char < 123)
16 | return true;
17 | return false;
18 | }
19 |
20 | export async function run() {
21 | try {
22 | let start = new Date().getTime();
23 | let lines = new Channel((original, accept) => {
24 | for (let line of original.split('\n'))
25 | accept(line);
26 | });
27 |
28 | let sentences = new Channel((line, accept) => {
29 | for (let sentence of line.split('.'))
30 | accept(sentence);
31 | });
32 |
33 | let words = new Channel((sentence, accept) => {
34 | for (let word of sentence.split(' '))
35 | accept(word);
36 | });
37 |
38 | let chars = new Channel((word, accept) => {
39 | for (let char of word.split('').filter(isAlphanumeric).map(x => x.toLowerCase()))
40 | accept(char);
41 | });
42 |
43 | let charset = {};
44 | chars.consume(char => {
45 | if (!charset[char])
46 | charset[char] = 0;
47 | charset[char]++;
48 | });
49 |
50 | lines.pipe(sentences)
51 | .pipe(words)
52 | .pipe(chars);
53 |
54 | log('Parsing text from ./text.js and counting alphanumeric characters...');
55 | await lines.put(text);
56 | lines.close(true);
57 | await chars.done();
58 |
59 | let sorted = Object.entries(charset).sort((a, b) => {
60 | return a[1] > b[1] ? -1 : a[1] < b[1] ? 1 : 0;
61 | });
62 | log('Character counts were:');
63 | log(sorted);
64 | let end = new Date().getTime();
65 | log(`Counting characters took ${end - start}ms`);
66 | }
67 | catch(e) {
68 | log('error!', e);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/examples/char-counter/index.js:
--------------------------------------------------------------------------------
1 | require('babel-core/register'); // so we can keep the interesting code in ES6/7
2 | var app = require('./char-counter.js');
3 | app.run();
4 |
--------------------------------------------------------------------------------
/examples/char-counter/text.js:
--------------------------------------------------------------------------------
1 | let text = `Imperdiet quisque venenatis gravida adipiscing integer molestie rutrum Ornare, eget pretium aptent taciti ante primis vestibulum nunc natoque eros semper quisque turpis odio iaculis sem pretium leo laoreet dis placerat faucibus natoque diam nullam nibh hymenaeos quam eu ridiculus Inceptos, elit tempus integer. Ridiculus ac quisque. Varius auctor. Posuere. Aliquam fames blandit taciti, velit consectetuer bibendum vivamus at Orci donec quis inceptos eget metus leo donec feugiat curae; cubilia eleifend sit vehicula. Fames potenti congue pulvinar metus nascetur consequat dis a. Hendrerit eu Iaculis augue amet sapien sed aliquet eu hymenaeos litora dignissim aliquet bibendum donec convallis sit placerat nibh libero Montes justo varius. Vivamus ut egestas scelerisque cursus ipsum leo suscipit, tortor tristique ut convallis nostra cras elit, pharetra integer est Montes lacus consequat dis. Ullamcorper quam non ornare amet ad ipsum et magna nam suspendisse lacinia sit sollicitudin adipiscing vehicula nisi. Ullamcorper nonummy metus interdum.
2 | Lacus tincidunt ultrices pellentesque conubia mattis risus elementum tellus dapibus. Class a platea eget metus accumsan dis sagittis per justo quisque vulputate sollicitudin auctor tempor aenean feugiat parturient justo pharetra sapien neque sagittis nullam magnis porta sociis dignissim nascetur eleifend pretium varius class facilisi consequat, facilisi. Sociosqu. Tincidunt aliquam Condimentum pede vel lorem vivamus nullam cubilia Dictum ipsum libero faucibus, enim sem. Turpis lacinia varius vivamus sodales. Sapien id iaculis lorem condimentum duis interdum fusce arcu. Amet semper ridiculus vel pretium. Molestie in eu mattis mattis tortor dictumst, et accumsan curae; luctus dapibus tortor. Aliquam. Egestas, posuere ad praesent eu mi. Fermentum orci iaculis nullam. Ridiculus vivamus ligula ridiculus aenean tellus hac ut nostra feugiat Magna eros neque ligula maecenas mauris leo aliquet fusce. Platea eros habitant massa parturient vivamus eu. Etiam dictumst per ad cursus ac aliquam enim suspendisse sollicitudin congue cum Accumsan dignissim bibendum vulputate. Tristique dictumst pharetra pharetra habitant. Amet rhoncus dapibus tristique dictum. Quisque mauris habitant praesent rhoncus cras. Eu sollicitudin rutrum libero tristique porta at sollicitudin ullamcorper phasellus aliquet nisl urna hac, metus libero fermentum mus aenean vehicula magnis volutpat justo dolor sapien ac nisl posuere orci taciti vitae habitant tempor erat, sagittis dapibus aenean sed varius posuere iaculis fringilla fringilla suspendisse nulla donec sapien tempus luctus commodo blandit per tristique leo. Blandit sed, accumsan a taciti iaculis ad egestas volutpat dolor venenatis ullamcorper mauris bibendum nisi congue. Quisque netus, semper taciti convallis congue ornare mus sociosqu urna hymenaeos. Habitant semper leo inceptos amet. Eget fames lectus consectetuer justo congue. Convallis parturient facilisis habitant suspendisse mi semper iaculis sit vehicula rutrum dignissim diam nam, magnis. Tellus sed potenti quisque arcu cras porta. Vestibulum natoque rutrum.
3 | Pede. Quam luctus erat tempus molestie aenean mi. Vitae potenti consectetuer est senectus metus sodales elit vel egestas, dolor. Aptent nonummy odio enim. Litora a. Auctor conubia. Torquent eros iaculis per congue neque ridiculus lobortis. Curabitur accumsan habitant nonummy non erat Tortor blandit vulputate interdum neque hymenaeos aliquet turpis euismod augue. Adipiscing vel, lacinia. Eleifend nibh, eu facilisis consequat vulputate mollis mauris mauris malesuada facilisis dolor mollis pharetra. Maecenas dictumst. Mi felis aptent dignissim integer, laoreet parturient porta bibendum quisque mi suscipit accumsan ut amet nisi massa consequat sagittis cras. Congue hymenaeos tincidunt commodo gravida sem leo lorem turpis pede lacinia pede a elit mus fermentum risus quisque dolor etiam euismod bibendum augue molestie odio laoreet accumsan dolor proin orci aenean aliquam amet lorem nulla leo leo etiam duis non vel. Magnis consequat at praesent elementum. Primis elementum. Rhoncus vulputate dictum eleifend nullam pharetra diam condimentum, accumsan quam malesuada. Torquent ullamcorper sociis neque hymenaeos ut parturient eleifend nisi sociosqu porttitor nam habitasse luctus est pulvinar.
4 | Auctor. Nisi nullam auctor placerat cubilia lorem convallis venenatis semper duis odio posuere lacinia eleifend et tempor. Consequat diam lobortis aptent nonummy nibh. Quis phasellus placerat nostra praesent. Nec. Nulla eros quis. Nisi hendrerit bibendum mollis dictum suspendisse risus aliquet nam cursus nibh auctor erat nonummy sociis ad hymenaeos porttitor rutrum eleifend magnis lobortis iaculis habitasse congue. Primis justo. Molestie convallis lorem aptent convallis potenti aptent morbi feugiat libero praesent eros rutrum Nam erat sit. Viverra class tortor at hendrerit. Mollis cubilia est ornare etiam taciti tempus. Mi duis proin gravida curabitur in Ultricies ligula dapibus Imperdiet luctus primis et sollicitudin sapien fusce in. Taciti sem litora fringilla curae;. Mauris. Gravida bibendum orci blandit rutrum eget sollicitudin class augue praesent. Diam vivamus tincidunt accumsan volutpat. Nibh fusce ultricies proin egestas ridiculus odio ad ultricies praesent pede, fringilla tristique. Pellentesque platea natoque. Dictumst curabitur habitant molestie. Fusce scelerisque, erat rhoncus. Lorem magnis nunc et Curabitur mus Inceptos ut aliquet laoreet augue sociis iaculis mauris. Ridiculus. Dui dapibus. Taciti dictum auctor interdum tincidunt nonummy vestibulum egestas metus orci. Adipiscing erat Platea sem nisi fames phasellus adipiscing congue placerat laoreet convallis in. Nulla, eros. Etiam, ultrices ante dapibus leo magnis ipsum platea venenatis rhoncus luctus ornare cum sodales elit nisi elit metus feugiat nec urna laoreet cursus dolor. Morbi pede potenti Curae; tincidunt rhoncus praesent erat class et habitant parturient vulputate tempor ornare nec neque hendrerit mi. Porttitor libero nonummy iaculis in blandit pretium Ultrices elit maecenas. Conubia curabitur habitasse. Aliquet. Aliquam. Bibendum. Sit duis eget aenean, leo Libero, rhoncus Magnis hendrerit a tristique pretium justo non pretium magna potenti Penatibus conubia sed morbi blandit risus sociosqu diam ullamcorper. Tellus interdum a gravida auctor, eros. Primis tempus. Vulputate eu libero praesent habitasse habitant.
5 | Pharetra quam, vulputate hendrerit mauris nisi vivamus varius laoreet tristique nonummy morbi parturient erat lectus mauris molestie lorem facilisis faucibus. Iaculis nullam quisque. Eu per hac augue, pede conubia pulvinar habitasse sociosqu laoreet ullamcorper phasellus torquent molestie dictumst tincidunt luctus montes commodo nibh feugiat imperdiet ante scelerisque sociis, mus odio praesent ipsum pulvinar suscipit id nullam vehicula semper sagittis nullam lobortis taciti aliquet ultricies torquent malesuada posuere potenti ullamcorper. Dis. Lorem augue ultrices litora dui lacus ligula dapibus nunc. Sollicitudin erat mauris commodo dis cras sagittis ac tempor gravida fringilla cras, non Vel erat sem commodo tempor senectus viverra sociis risus et consectetuer luctus faucibus fermentum iaculis dictum faucibus mollis consequat scelerisque potenti Tempor vulputate maecenas. Egestas conubia.`;
6 |
7 | export default text;
8 |
--------------------------------------------------------------------------------
/examples/file-streams/file-streams.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 |
3 | "use strict";
4 |
5 | import fs from 'fs';
6 | import Channel from '../../dist/channel.js';
7 | let log = console.log.bind(console);
8 |
9 | let input = './read.csv';
10 | let output = './write.sql';
11 |
12 | function now() {
13 | let time = process.hrtime();
14 | return (time[0] * 1e9 + time[1]) / 1e6;
15 | }
16 |
17 | export async function run() {
18 |
19 | let start = now();
20 |
21 | let makeArrays = new Channel(line => {
22 | line = line.trim();
23 | if (line) // drop empty lines from the channels
24 | return line.split(',').map(x => x.trim());
25 | });
26 |
27 | let makeObjects = new Channel(row => ({
28 | id : row[0],
29 | first_name : row[1],
30 | last_name : row[2],
31 | email : row[3],
32 | password : row[4],
33 | country : row[5],
34 | city : row[6],
35 | state : row[7],
36 | address : row[8],
37 | post_code : row[9]
38 | }));
39 |
40 | let prepend = 'INSERT INTO people (import_id, first_name, last_name, email, password) VALUES (';
41 | let append = ')\n';
42 | let makeStatements = new Channel(obj => {
43 | let out = [
44 | obj.id,
45 | `'${obj.first_name}'`,
46 | `'${obj.last_name}'`,
47 | `'${obj.email}'`,
48 | `'${obj.password}'`
49 | ];
50 | return prepend + out.join(', ') + append;
51 | });
52 |
53 | log(`Reading from ${input}...`);
54 | let fin = fs.createReadStream(input);
55 |
56 | let carry = null;
57 | fin.on('data', data => {
58 | // split input pipe on newlines
59 | let str = data.toString();
60 | let lines = str.split('\n');
61 | if (carry)
62 | lines[0] = carry + lines[0];
63 | for (let i = 0; i < lines.length - 1; i++) {
64 | let line = lines[i];
65 | makeArrays.put(line); // put each line on the makeArrays channel
66 | }
67 | carry = lines[lines.length - 1];
68 | });
69 | fin.on('end', () => {
70 | makeArrays.close(true); // trigger a full pipe close when read stream ends
71 | });
72 |
73 | let fout = fs.createWriteStream(output);
74 | makeStatements.consume(async sql => {
75 | if (!fout.write(sql)) {
76 | await new Promise(resolve => {
77 | fout.once('drain', resolve);
78 | });
79 | }
80 | });
81 |
82 | makeArrays
83 | .pipe(makeObjects)
84 | .pipe(makeStatements);
85 | await makeStatements.done();
86 | log(`Wrote statements to ${output}!`);
87 | let end = now();
88 | log(`Output took ${end - start}ms`);
89 | }
90 |
--------------------------------------------------------------------------------
/examples/file-streams/index.js:
--------------------------------------------------------------------------------
1 | require('babel-core/register'); // so we can keep the interesting code in ES6/7
2 | var app = require('./file-streams.js');
3 | app.run();
4 |
--------------------------------------------------------------------------------
/examples/ping-pong/index.js:
--------------------------------------------------------------------------------
1 | require('babel-core/register'); // so we can keep the interesting code in ES6/7
2 | var app = require('./ping-pong.js');
3 | app.run();
4 |
--------------------------------------------------------------------------------
/examples/ping-pong/ping-pong.js:
--------------------------------------------------------------------------------
1 | import Channel, { timeout } from '../../dist/channel.js';
2 |
3 | async function player(name, table) {
4 | while (true) {
5 | let ball = await table.take();
6 | if (ball === Channel.DONE) {
7 | console.log(`${name}: table's gone!`);
8 | break;
9 | }
10 | ball.hits++;
11 | console.log(`${name}! Hits: ${ball.hits}`);
12 | await timeout(100);
13 | await table.put(ball);
14 | }
15 | }
16 |
17 | export async function run() {
18 | console.log('Opening ping-pong channel!');
19 | let table = new Channel();
20 |
21 | player('ping', table);
22 | player('pong', table);
23 |
24 | console.log('Serving ball...');
25 | let ball = { hits: 0 };
26 | await table.put(ball);
27 | await timeout(1000);
28 |
29 | console.log('Closing ping-pong channel...');
30 | table.close();
31 |
32 | await table.done();
33 | console.log('Channel is fully closed!');
34 | console.log(`Ball was hit ${ball.hits} times!`);
35 | }
36 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const babel = require('gulp-babel')
2 | const del = require('del')
3 | const gulp = require('gulp')
4 | const plumber = require('gulp-plumber')
5 | const rename = require('gulp-rename')
6 | const sequence = require('run-sequence')
7 | const uglify = require('gulp-uglify')
8 | const webpack = require('webpack-stream')
9 | const sourcemaps = require('gulp-sourcemaps')
10 |
11 | const srcDir = './src'
12 | const srcGlob = `${srcDir}/*.js`
13 | const srcEntry = `channel.js`
14 | const distDir = './dist'
15 | const distUmdDir = `${distDir}/umd`
16 | const distCjsDir = `${distDir}/cjs`
17 | const singleFilename = 'async-csp.js'
18 | const libraryName = 'async-csp'
19 |
20 | function transpileUmd(config) {
21 | if (config === undefined) {
22 | config = {
23 | output: {
24 | filename: singleFilename,
25 | library: libraryName,
26 | libraryTarget: 'umd',
27 | },
28 | module: {
29 | loaders: [
30 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
31 | ],
32 | },
33 | }
34 | }
35 | return webpack(config)
36 | }
37 |
38 | gulp.task('clean', done => {
39 | return del([distDir], done)
40 | })
41 |
42 | gulp.task('build:umd', () => {
43 | return gulp
44 | .src(`${srcDir}/${srcEntry}`)
45 | .pipe(transpileUmd())
46 | .pipe(sourcemaps.init())
47 | .pipe(gulp.dest(distUmdDir))
48 | .pipe(uglify())
49 | .pipe(rename({ suffix: '.min' }))
50 | .pipe(sourcemaps.write('.'))
51 | .pipe(gulp.dest(distUmdDir))
52 | })
53 |
54 | gulp.task('build:cjs', () => {
55 | return gulp
56 | .src(srcGlob)
57 | .pipe(plumber())
58 | .pipe(babel()) // config in .babelrc
59 | .pipe(plumber.stop())
60 | .pipe(gulp.dest(distCjsDir))
61 | })
62 |
63 | gulp.task('build', done => {
64 | sequence(['build:cjs', 'build:umd'], done)
65 | })
66 |
67 | gulp.task('default', done => {
68 | sequence('test', 'clean', 'lint', 'build', 'watch', done)
69 | })
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "async-csp",
3 | "version": "0.5.0",
4 | "description": "CSP style channels using ES7 async/await",
5 | "keywords": [
6 | "CSP",
7 | "async",
8 | "await",
9 | "channel",
10 | "communicating",
11 | "pipe",
12 | "pipeline",
13 | "processes",
14 | "sequential"
15 | ],
16 | "main": "dist/cjs/channel.js",
17 | "scripts": {
18 | "build": "gulp build",
19 | "lint": "eslint src/**/*.js test/**/*.js *.js",
20 | "test": "mocha \"./{,!(node_modules)/**/}*.spec.js\"",
21 | "test:watch": "npm run test -- --watch",
22 | "watch": "gulp watch"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/dvlsg/async-csp.git"
27 | },
28 | "author": "Dave Lesage",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/dvlsg/async-csp/issues"
32 | },
33 | "homepage": "https://github.com/dvlsg/async-csp",
34 | "devDependencies": {
35 | "babel-core": "6.7.5",
36 | "babel-eslint": "^4.1.8",
37 | "babel-loader": "6.2.4",
38 | "babel-plugin-transform-runtime": "^6.6.0",
39 | "babel-preset-es2015": "^6.6.0",
40 | "babel-preset-stage-0": "^6.5.0",
41 | "babel-register": "^6.7.2",
42 | "chai": "^4.1.2",
43 | "del": "^1.2.0",
44 | "eslint": "^4.10.0",
45 | "eslint-config-prettier": "^2.7.0",
46 | "gulp": "^3.9.0",
47 | "gulp-babel": "^6.1.2",
48 | "gulp-plumber": "^1.1.0",
49 | "gulp-rename": "^1.2.2",
50 | "gulp-sourcemaps": "^1.6.0",
51 | "gulp-uglify": "^1.5.3",
52 | "in-publish": "2.0.0",
53 | "mocha": "^4.0.1",
54 | "prettier": "^1.7.4",
55 | "prettier-eslint-cli": "^4.4.0",
56 | "run-sequence": "^1.1.5",
57 | "webpack-stream": "^3.1.0",
58 | "zana-assert": "^0.1.4"
59 | },
60 | "dependencies": {
61 | "babel-runtime": "^6.6.1"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/channel.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { List, FixedQueue } = require('./data-structures')
4 |
5 | function arrayEntries(array) {
6 | let i = -1
7 | const length = array.length
8 | const entries = []
9 | while (++i < length) {
10 | const val = array[i]
11 | entries[entries.length] = [i, val]
12 | }
13 | return entries
14 | }
15 |
16 | /*
17 | Three possible states:
18 |
19 | OPEN : The Channel can be written to and taken from freely.
20 | CLOSED : The Channel can no longer be written to, but still has values to be taken.
21 | ENDED : The Channel is closed, and no longer has values to be taken.
22 | */
23 | const STATES = {
24 | OPEN: Symbol('channel_open'),
25 | CLOSED: Symbol('channel_closed'),
26 | ENDED: Symbol('channel_ended'),
27 | }
28 |
29 | const ACTIONS = {
30 | // channel has just been closed, and has no more values to take
31 | DONE: Symbol('channel_done'),
32 | CANCEL: Symbol('channel_cancel'),
33 | }
34 |
35 | const SLIDER = Symbol('channel_slider')
36 | const STATE = Symbol('channel_state')
37 | const SHOULD_CLOSE = Symbol('channel_should_close')
38 | const IS_CONSUMING = Symbol('channel_consuming')
39 | const IS_FLUSHING = Symbol('channel_flushing')
40 | const IS_SLIDING = Symbol('channel_sliding')
41 |
42 | /*
43 | Error expose method to assist with ensuring
44 | that error messages are properly thrown instead of swallowed.
45 |
46 | setTimeout is used to ensure that the error is thrown
47 | from a location that will not be eaten by an async throw.
48 | */
49 | function expose(e) {
50 | setTimeout(() => {
51 | throw e
52 | })
53 | }
54 |
55 | /*
56 | Marks a channel as ended, and signals any promises
57 | which are waiting for the end of the channel.
58 | */
59 | function finish(ch) {
60 | ch[STATE] = STATES.ENDED
61 | let waiting = null
62 | while (
63 | (waiting = ch.waiting.shift()) // eslint-disable-line no-cond-assign
64 | ) {
65 | waiting()
66 | }
67 | }
68 |
69 | /*
70 | Flushes out any remaining takes from the channel
71 | by sending them the value of `ACTIONS.DONE`.
72 | */
73 | async function flush(ch) {
74 | if (!ch.empty())
75 | // this error is never expected to be thrown
76 | // just a sanity check during development
77 | throw new Error(
78 | 'Attempted to execute flush(Channel) on a non-empty channel!',
79 | )
80 | if (ch[IS_FLUSHING]) return
81 | ch[IS_FLUSHING] = true
82 | let take = null
83 | const takes = []
84 | while (
85 | (take = ch.takes.shift()) // eslint-disable-line no-cond-assign
86 | ) {
87 | takes.push(take(ACTIONS.DONE))
88 | }
89 | await Promise.all(takes)
90 | if (!ch[IS_CONSUMING]) finish(ch)
91 | ch[IS_FLUSHING] = false
92 | }
93 |
94 | function wrap(val, transform, resolve) {
95 | let wrapped = null
96 | if (transform instanceof Function) {
97 | if (transform.length === 1) {
98 | wrapped = async () => {
99 | const transformed = transform(val)
100 | if (transformed instanceof Promise) {
101 | const actual = await transformed
102 | return actual
103 | }
104 | return transformed
105 | }
106 | } else {
107 | const accepted = new List()
108 | if (transform.length === 2) {
109 | wrapped = async () => {
110 | await transform(val, acc => {
111 | if (typeof acc !== 'undefined') accepted.push(acc)
112 | })
113 | return accepted
114 | }
115 | } else {
116 | /* transform.length === 3 */ wrapped = () => {
117 | return new Promise(res => {
118 | transform(
119 | val,
120 | acc => {
121 | if (typeof acc !== 'undefined') accepted.push(acc)
122 | },
123 | () => {
124 | res(accepted)
125 | },
126 | )
127 | })
128 | }
129 | }
130 | }
131 | } else {
132 | wrapped = async () => val
133 | }
134 | return {
135 | wrapped,
136 | resolve,
137 | transform,
138 | val,
139 | }
140 | }
141 |
142 | async function _bufferedSlide(ch) {
143 | while (!ch.buf.empty() && !ch.takes.empty()) {
144 | const buf = ch.buf.shift()
145 | let val = null
146 | if (buf && buf.wrapped) val = await buf.wrapped()
147 | else val = buf // this is a special case caused by `from`. can we get rid of the need for this?
148 | if (typeof val !== 'undefined') {
149 | if (val instanceof List) {
150 | // need a way to distinguish this as a "special" array return
151 | const accepted = [...val]
152 | if (accepted.length === 0) buf.resolve()
153 | else if (accepted.length === 1) {
154 | buf.resolve()
155 | const take = ch.takes.shift()
156 | take(accepted[0])
157 | } else {
158 | /* accepted.length > 1 */ let count = 0
159 | const counter = () => {
160 | count++
161 | if (count === accepted.length) buf.resolve()
162 | }
163 | const wrappers = accepted.map(acc => wrap(acc, x => x, counter))
164 | // when we use counter as the resolve, it makes us need
165 | // to call buf.resolve(), whereas we wouldn't normally,
166 | // since resolve() should have been called when moving
167 | // from put -> buf.
168 |
169 | // the problem is that when we use these expanded wrappers,
170 | // we need to execute the resolution. if we place on the buffer
171 | // directly, we can be sure we maintain the correct order.
172 | // if we place back on puts instead of the buffer,
173 | // we may or may not have the right order anymore.
174 |
175 | // another issue is what if we accept more than the buffer has space for?
176 | // what if there were already items on the buffer? do we kick them out,
177 | // and put them back in puts? that gives us essentially the same problem --
178 | // then we would have puts which don't need put.resolve() to be called,
179 | // which doesn't follow the usual pattern.
180 |
181 | // what to do, what to do... try to hammer out the inconsistency at some point.
182 |
183 | ch.buf.unshift(...wrappers) // this can expand beyond the actual buffer size. unintuitive?
184 | }
185 | } else {
186 | const take = ch.takes.shift()
187 | take(val)
188 | }
189 | }
190 | }
191 | while (!ch.puts.empty() && !ch.buf.full()) {
192 | const put = ch.puts.shift()
193 | ch.buf.push(put)
194 | put.resolve()
195 | }
196 | }
197 |
198 | async function _slide(ch) {
199 | while (!ch.takes.empty() && !ch.puts.empty()) {
200 | const put = ch.puts.shift()
201 | const val = await put.wrapped()
202 | if (typeof val !== 'undefined') {
203 | if (val instanceof List) {
204 | // need a way to distinguish this as a "special" array return
205 | const accepted = [...val]
206 | if (accepted.length === 0) put.resolve()
207 | else if (accepted.length === 1) {
208 | put.resolve()
209 | const take = ch.takes.shift()
210 | take(accepted[0])
211 | } else {
212 | /* val.length > 1 */ let count = 0
213 | const counter = () => {
214 | count++
215 | if (count === accepted.length) put.resolve()
216 | }
217 | const wrappers = accepted.map(acc => wrap(acc, x => x, counter))
218 | ch.puts.unshift(...wrappers)
219 | }
220 | } else {
221 | put.resolve()
222 | const take = ch.takes.shift()
223 | take(val)
224 | }
225 | } else {
226 | put.resolve()
227 | }
228 | }
229 | }
230 |
231 | function canSlide(ch) {
232 | return ch.buf
233 | ? (!ch.buf.full() && !ch.puts.empty()) ||
234 | (!ch.takes.empty() && !ch.buf.empty())
235 | : !ch.takes.empty() && !ch.puts.empty()
236 | }
237 |
238 | async function slide(ch) {
239 | if (ch[IS_SLIDING]) return
240 | ch[IS_SLIDING] = true
241 |
242 | while (canSlide(ch)) await ch[SLIDER](ch)
243 |
244 | if (
245 | ch[STATE] === STATES.CLOSED &&
246 | !ch.tails.empty() &&
247 | (ch.buf ? ch.buf.empty() : true) &&
248 | ch.puts.empty()
249 | ) {
250 | ch.puts.unshift(...ch.tails)
251 | ch.tails = new List() // need a way to empty out the list
252 | while (canSlide(ch)) await ch[SLIDER](ch)
253 | }
254 |
255 | if (
256 | (ch[STATE] === STATES.CLOSED || ch[STATE] === STATES.ENDED) &&
257 | (ch.buf ? ch.buf.empty() : true) &&
258 | ch.puts.empty() &&
259 | ch.tails.empty()
260 | )
261 | flush(ch)
262 |
263 | ch[IS_SLIDING] = false
264 | }
265 |
266 | function timeout(delay = 0) {
267 | return new Promise(resolve => {
268 | setTimeout(resolve, delay)
269 | })
270 | }
271 |
272 | class Channel {
273 | /*
274 | Default constructor for a Channel.
275 |
276 | Accepts an optional size for the internal buffer,
277 | and an optional transform function to be used by the Channel.
278 |
279 | Examples:
280 | new Channel() -> Non buffered channel, no transform
281 | new Channel(x => x * 2) -> Non buffered channel, with transform
282 | new Channel(8) -> Buffered channel, no transform
283 | new Channel(8, x => x * 2) -> Buffered channel, with transform
284 | */
285 | constructor(...argv) {
286 | // A List containing any puts which could not be placed directly onto the buffer
287 | this.puts = new List()
288 |
289 | // A List containing any puts to be appended to the end of the channel
290 | this.tails = new List()
291 |
292 | // A List containing any takes waiting for values to be provided
293 | this.takes = new List()
294 |
295 | // A FixedQueue containing values ready to be taken.
296 | this.buf = null
297 |
298 | // An optional function to used to transform values passing through the channel.
299 | this.transform = null
300 |
301 | // An optional pipeline of channels, to be used to pipe values
302 | // from one channel to multiple others.
303 | this.pipeline = []
304 |
305 | // An optional array of promises, to be resolved when the channel is marked as finished.
306 | this.waiting = []
307 |
308 | let size = null
309 | let transform = null
310 | let buffer = null
311 | if (typeof argv[0] === 'function') transform = argv[0]
312 | if (typeof argv[0] === 'number') {
313 | size = argv[0]
314 | if (argv[1] && typeof argv[1] === 'function') transform = argv[1]
315 | }
316 | if (typeof argv[0] === 'object') {
317 | // assume first arg is buffer type
318 | // consider adding some duck-type or instanceof safety
319 | buffer = argv[0]
320 | if (argv[1] && typeof argv[1] === 'function') transform = argv[1]
321 | }
322 | this.transform = transform
323 | this[STATE] = STATES.OPEN
324 |
325 | if (size) {
326 | this.buf = new FixedQueue(size)
327 | this[SLIDER] = _bufferedSlide
328 | } else if (buffer) {
329 | this.buf = buffer
330 | this[SLIDER] = _bufferedSlide
331 | } else this[SLIDER] = _slide
332 | }
333 |
334 | /*
335 | A helper constructor which will convert any iterable into a channel,
336 | placing all of the iterable's values onto that channel.
337 | */
338 | static from(iterable, keepOpen = false) {
339 | const arr = [...iterable]
340 | const ch = new Channel(arr.length)
341 | for (const val of arr) ch.buf.push(val)
342 | if (!keepOpen) ch.close(true)
343 | return ch
344 | }
345 |
346 | /*
347 | Sets the state of the channel.
348 | */
349 | set state(val) {
350 | this[STATE] = val
351 | }
352 |
353 | /*
354 | Gets the state of the channel.
355 | */
356 | get state() {
357 | return this[STATE]
358 | }
359 |
360 | /*
361 | Gets the length of the channel,
362 | which is interpreted as the current length of the buffer
363 | added to any puts which are waiting for space in the buffer.
364 | */
365 | get length() {
366 | if (this.buf) return this.buf.length + this.puts.length
367 | return this.puts.length
368 | }
369 |
370 | /*
371 | Gets the size of the channel,
372 | which is interpreted as the size of the buffer.
373 | */
374 | get size() {
375 | return this.buf ? this.buf.size : undefined
376 | }
377 |
378 | /*
379 | Marks a channel to no longer be writable.
380 |
381 | Accepts an optional boolean `all`, to signify
382 | whether or not to close the entire pipeline.
383 | */
384 | static close(ch, all = false) {
385 | ch.state = STATES.CLOSED
386 | if (all) ch[SHOULD_CLOSE] = true
387 | setTimeout(() => slide(ch)) // we have a timing problem with pipes.. this resolves it, but is hacky.
388 | }
389 |
390 | /*
391 | Calls Channel.close for `this`, `all`.
392 | */
393 | close(all = false) {
394 | return Channel.close(this, all)
395 | }
396 |
397 | /*
398 | Determines if a channel
399 | has any values left for `take` to use.
400 | */
401 | static empty(ch) {
402 | if (ch.buf) return ch.buf.empty() && ch.puts.empty()
403 | return ch.puts.empty()
404 | }
405 |
406 | /*
407 | Returns Channel.empty for `this`.
408 | */
409 | empty() {
410 | return Channel.empty(this)
411 | }
412 |
413 | /*
414 | Places a new value onto the provided channel.
415 |
416 | If the buffer is full, the promise will be pushed
417 | onto Channel.puts to be resolved when buffer space is available.
418 | */
419 | static put(ch, val) {
420 | return new Promise(resolve => {
421 | if (ch.state !== STATES.OPEN) return resolve(ACTIONS.DONE)
422 | const put = wrap(val, ch.transform, resolve)
423 | ch.puts.push(put)
424 | slide(ch)
425 | })
426 | }
427 |
428 | /*
429 | Returns Channel.put for `this`, `val`.
430 | */
431 | put(val) {
432 | return Channel.put(this, val)
433 | }
434 |
435 | /*
436 | Takes the first value from the provided channel.
437 |
438 | If no value is provided, the promise will be pushed
439 | onto Channel.takes to be resolved when a value is available.
440 | */
441 | static take(ch) {
442 | return new Promise(resolve => {
443 | if (ch.state === STATES.ENDED) return resolve(ACTIONS.DONE)
444 | ch.takes.push(resolve)
445 | slide(ch)
446 | })
447 | }
448 |
449 | /*
450 | Returns Channel.take for `this`.
451 | */
452 | take() {
453 | return Channel.take(this)
454 | }
455 |
456 | static tail(ch, val) {
457 | return new Promise(resolve => {
458 | if (ch.state !== STATES.OPEN) return resolve(ACTIONS.DONE)
459 | const tail = wrap(val, ch.transform, resolve)
460 | ch.tails.push(tail)
461 | slide(ch)
462 | })
463 | }
464 |
465 | /*
466 | Returns Channel.tail for `this`.
467 | */
468 | tail(val) {
469 | return Channel.tail(this, val)
470 | }
471 |
472 | /*
473 | Helper method for putting values onto a channel
474 | from a provided producer whenever there is space.
475 | */
476 | static async produce(ch, producer) {
477 | let spin = true
478 | ;(async () => {
479 | try {
480 | while (spin) {
481 | let val = producer()
482 | if (val instanceof Promise) val = await val
483 | else await timeout()
484 | const r = await Channel.put(ch, val)
485 | if (r === ACTIONS.DONE) break
486 | }
487 | } catch (e) {
488 | expose(e)
489 | }
490 | })()
491 | return () => {
492 | spin = false
493 | }
494 | }
495 |
496 | /*
497 | Calls Channel.produce for `this`, `producer`.
498 | */
499 | produce(producer) {
500 | return Channel.produce(this, producer)
501 | }
502 |
503 | /*
504 | Helper method for executing a provided consumer
505 | each time a channel value is available.
506 | */
507 | static async consume(ch, consumer = () => {}) {
508 | ch[IS_CONSUMING] = true
509 | ;(async () => {
510 | let taking = Channel.take(ch)
511 | while (ch[IS_CONSUMING]) {
512 | const val = await taking
513 | if (val === ACTIONS.DONE) break
514 | const consuming = consumer(val)
515 | taking = Channel.take(ch)
516 | await consuming
517 | }
518 | ch[IS_CONSUMING] = false
519 | if (ch[IS_FLUSHING]) await ch[IS_FLUSHING]
520 | else finish(ch)
521 | })()
522 | }
523 |
524 | /*
525 | Calls Channel.consume for `this`, `consumer`.
526 | */
527 | consume(consumer = () => {}) {
528 | return Channel.consume(this, consumer)
529 | }
530 |
531 | /*
532 | Registers a promise to be resolved
533 | when the channel has fully ended.
534 | */
535 | static done(ch) {
536 | return new Promise(resolve => {
537 | if (ch.state === STATES.ENDED) return resolve()
538 | ch.waiting.push(resolve)
539 | })
540 | }
541 |
542 | /*
543 | Returns Channel.done for `this`.
544 | */
545 | done() {
546 | return Channel.done(this)
547 | }
548 |
549 | /*
550 | Automatically builds a set of channels
551 | for the provided function arguments,
552 | setting up a pipe from the first channel
553 | all the way down to the last channel.
554 |
555 | Returns references to both
556 | the first and the last channel.
557 | */
558 | static pipeline(...args) {
559 | let first = null
560 | let last = null
561 | if (args.length === 0) {
562 | first = new Channel()
563 | last = first
564 | } else {
565 | if (Array.isArray(args[0])) args = [...args[0]]
566 | const channels = args
567 | .filter(x => x instanceof Function || x instanceof Channel)
568 | .map(x => (x instanceof Channel ? x : new Channel(x)))
569 | first = channels[0]
570 | last = channels.reduce((x, y) => x.pipe(y))
571 | }
572 | return [first, last]
573 | }
574 |
575 | /*
576 | Builds a pipeline from a parent channel
577 | to one or more children.
578 |
579 | This will automatically pipe values from
580 | the parent onto each of the children.
581 |
582 | (dev note: careful, errors which are thrown from here
583 | do NOT bubble up to the user yet in nodejs.
584 | will be fixed in the future, supposedly).
585 | */
586 | static pipe(parent, ...channels) {
587 | channels = channels.map(x => (x instanceof Function ? new Channel(x) : x))
588 | parent.pipeline.push(...channels)
589 | if (!parent[ACTIONS.CANCEL]) {
590 | let running = true
591 | ;(async () => {
592 | while (running) {
593 | const val = await parent.take()
594 | if (val === ACTIONS.DONE) {
595 | if (parent[SHOULD_CLOSE]) {
596 | for (const channel of parent.pipeline) channel.close(true)
597 | }
598 | break
599 | }
600 | await Promise.all(parent.pipeline.map(x => x.put(val))) // eslint-disable-line no-loop-func
601 | }
602 | })()
603 | parent[ACTIONS.CANCEL] = () => {
604 | running = false
605 | }
606 | }
607 | return channels[channels.length - 1]
608 | }
609 |
610 | /*
611 | Returns Channel.pipe for `this`, `...channels`.
612 | */
613 | pipe(...channels) {
614 | return Channel.pipe(this, ...channels)
615 | }
616 |
617 | /*
618 | Pipes all provided channels into a new, single destination.
619 | */
620 | static merge(...channels) {
621 | const child = new Channel()
622 | for (const parent of channels) parent.pipe(child)
623 | return child
624 | }
625 |
626 | /*
627 | Returns Channel.merge for `this`, `...channels`.
628 | */
629 | merge(...channels) {
630 | return Channel.merge(this, ...channels)
631 | }
632 |
633 | static unpipe(parent, ...channels) {
634 | for (const [index, pipe] of arrayEntries(parent.pipeline)) {
635 | for (const ch2 of channels) {
636 | if (pipe === ch2) parent.pipeline.splice(index, 1)
637 | }
638 | }
639 | if (parent.pipeline.length === 0 && parent[ACTIONS.CANCEL])
640 | parent[ACTIONS.CANCEL]() // don't spin the automatic pipe method when no pipeline is attached
641 | return parent
642 | }
643 |
644 | unpipe(...channels) {
645 | return Channel.unpipe(this, ...channels)
646 | }
647 | }
648 |
649 | Channel.DONE = ACTIONS.DONE // expose this so loops can listen for it
650 |
651 | module.exports = {
652 | default: Channel,
653 | Channel,
654 | timeout,
655 | STATES,
656 | ACTIONS,
657 | }
658 |
--------------------------------------------------------------------------------
/src/data-structures.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const MAX_SIZE = 4096
4 |
5 | // can be swapped to symbols to make more 'private'
6 | // makes it more difficult to debug, though.
7 | const ARR = '_arr'
8 | const SIZE = '_size'
9 |
10 | // internal to be inherited
11 | class Data {
12 | constructor() {
13 | this[ARR] = []
14 | }
15 |
16 | static construct() {
17 | return Object.create(this.constructor)
18 | }
19 |
20 | get [Symbol.toStringTag]() {
21 | return 'Data'
22 | }
23 |
24 | [Symbol.iterator]() {
25 | return this[ARR][Symbol.iterator]() // should be overridden for stacks, so we iterate from back to front
26 | }
27 |
28 | flush() {
29 | this[ARR].length = 0
30 | }
31 |
32 | empty() {
33 | return this[ARR].length === 0
34 | }
35 |
36 | get length() {
37 | return this[ARR].length
38 | }
39 |
40 | toString() {
41 | return this[ARR].join(', ')
42 | }
43 |
44 | values() {
45 | return [...this[ARR]]
46 | }
47 | }
48 | // Data[Symbol.toStringTag] = 'Data';
49 |
50 | class Stack extends Data {
51 | constructor() {
52 | super()
53 | }
54 |
55 | get [Symbol.toStringTag]() {
56 | return 'Stack'
57 | }
58 |
59 | push(val) {
60 | this[ARR].push(val)
61 | }
62 |
63 | pop() {
64 | return this[ARR].pop()
65 | }
66 |
67 | peek() {
68 | return this[ARR][this.length - 1]
69 | }
70 | }
71 |
72 | class FixedStack extends Stack {
73 | constructor(size = MAX_SIZE) {
74 | super()
75 | this[SIZE] = size
76 | }
77 |
78 | get [Symbol.toStringTag]() {
79 | return 'FixedStack'
80 | }
81 |
82 | get size() {
83 | return this[SIZE]
84 | }
85 |
86 | push(val) {
87 | if (!this.full()) return super.push(val)
88 | }
89 |
90 | full() {
91 | return this.length >= this[SIZE]
92 | }
93 | }
94 |
95 | class Queue extends Data {
96 | constructor() {
97 | super()
98 | }
99 |
100 | get [Symbol.toStringTag]() {
101 | return 'Queue'
102 | }
103 |
104 | push(val) {
105 | this[ARR].push(val)
106 | }
107 |
108 | shift() {
109 | return this[ARR].shift()
110 | }
111 |
112 | peek() {
113 | return this[ARR][0]
114 | }
115 | }
116 |
117 | class List extends Queue {
118 | constructor() {
119 | super()
120 | }
121 |
122 | get [Symbol.toStringTag]() {
123 | return 'List'
124 | }
125 |
126 | unshift(...vals) {
127 | return this[ARR].unshift(...vals)
128 | }
129 | }
130 |
131 | class FixedQueue extends Queue {
132 | constructor(size = MAX_SIZE) {
133 | super()
134 | this[SIZE] = size
135 | }
136 |
137 | get [Symbol.toStringTag]() {
138 | return 'FixedQueue'
139 | }
140 |
141 | get size() {
142 | return this[SIZE]
143 | }
144 |
145 | push(val) {
146 | if (!this.full())
147 | // throw overflow? drop overflow? allow overflow?
148 | return super.push(val)
149 | }
150 |
151 | full() {
152 | return this.length >= this[SIZE]
153 | }
154 |
155 | unshift(...vals) {
156 | // this isn't really a queue anymore. maybe FixedList instead?
157 | return this[ARR].unshift(...vals)
158 | }
159 | }
160 |
161 | class DroppingBuffer extends Queue {
162 | constructor(size = MAX_SIZE) {
163 | super()
164 | this[SIZE] = size
165 | }
166 |
167 | get [Symbol.toStringTag]() {
168 | return 'DroppingBuffer'
169 | }
170 |
171 | unshift(...vals) {
172 | // we only need to grab the first item
173 | if (this[ARR].length === 0 && vals[0]) this[ARR][0] = vals[0]
174 | }
175 |
176 | push(val) {
177 | if (this[ARR].length === 0) this[ARR][0] = val
178 | }
179 |
180 | full() {
181 | return false
182 | }
183 | }
184 |
185 | class SlidingBuffer extends Queue {
186 | constructor(size = MAX_SIZE) {
187 | super()
188 | this[SIZE] = size // need to make sure size is a positive integer.
189 | this.head = 0 // pointer to oldest value
190 | this.tail = 0 // pointer to newest value
191 | this.count = 0
192 | }
193 |
194 | get [Symbol.toStringTag]() {
195 | return 'SlidingBuffer'
196 | }
197 |
198 | empty() {
199 | return this.count === 0
200 | }
201 |
202 | full() {
203 | return false
204 | }
205 |
206 | push(val) {
207 | if (this.count === 0) {
208 | this[ARR][this.tail] = val
209 | this.head = this.tail
210 | this.count = 1
211 | return
212 | }
213 | const _size = this[SIZE]
214 | this.tail = (this.tail + 1) % _size
215 | this[ARR][this.tail] = val
216 | const overwrite = this.tail === this.head
217 | if (overwrite) this.head = (this.head + 1) % _size
218 | if (!overwrite) this.count += 1
219 | }
220 |
221 | shift() {
222 | const val = this[ARR][this.head]
223 | delete this[ARR][this.head]
224 | this.head = (this.head + 1) % this[SIZE]
225 | this.count -= 1
226 | return val
227 | }
228 | }
229 |
230 | module.exports = {
231 | Stack,
232 | FixedStack,
233 | Queue,
234 | List,
235 | FixedQueue,
236 | DroppingBuffer,
237 | SlidingBuffer,
238 | }
239 |
--------------------------------------------------------------------------------
/test/channel.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { STATES, timeout, default: Channel } = require('../src/channel')
4 | const {
5 | List,
6 | FixedQueue,
7 | DroppingBuffer,
8 | SlidingBuffer,
9 | } = require('../src/data-structures')
10 | const { assert } = require('chai')
11 | const log = console.log.bind(console) // eslint-disable-line
12 |
13 | describe('Channel', function() {
14 | this.timeout(100)
15 |
16 | describe('constructor', () => {
17 | it('should initialize properly', async () => {
18 | const ch = new Channel()
19 |
20 | assert.isOk(ch.empty())
21 | assert.instanceOf(ch, Channel)
22 | assert.deepEqual(ch.state, STATES.OPEN)
23 |
24 | assert.isOk(ch.puts)
25 | assert.instanceOf(ch.puts, List)
26 | assert.isOk(ch.puts.empty())
27 |
28 | assert.isOk(ch.takes)
29 | assert.instanceOf(ch.takes, List)
30 | assert.isOk(ch.takes.empty())
31 |
32 | assert.isOk(ch.pipeline)
33 | assert.strictEqual(ch.pipeline.length, 0)
34 | assert.instanceOf(ch.pipeline, Array)
35 |
36 | assert.isOk(ch.waiting)
37 | assert.strictEqual(ch.waiting.length, 0)
38 | assert.instanceOf(ch.waiting, Array)
39 | })
40 |
41 | it('should initialize with a buffer size', async () => {
42 | const ch = new Channel(92)
43 | assert.isOk(ch.empty())
44 | assert.isOk(ch.buf)
45 | assert.instanceOf(ch.buf, FixedQueue)
46 | assert.isOk(ch.buf.empty())
47 | assert.deepEqual(ch.buf.size, 92)
48 | })
49 |
50 | it('should initialize with a transform', async () => {
51 | const fn = x => x ** 2
52 | const ch = new Channel(fn)
53 | assert.deepEqual(ch.transform, fn)
54 | })
55 |
56 | it('should initialize with a buffer size and a transform', async () => {
57 | const fn = x => ({ x })
58 | const size = 3
59 | const ch = new Channel(size, fn)
60 | assert.isOk(ch.buf)
61 | assert.isOk(ch.buf.empty())
62 | assert.isNotOk(ch.buf.full())
63 | assert.deepEqual(ch.buf.size, size)
64 | assert.deepEqual(ch.transform, fn)
65 | })
66 |
67 | it('should accept data structures as first parameter', async () => {
68 | const size = 8
69 | const buf = new FixedQueue(size)
70 | const ch = new Channel(buf)
71 | assert.isOk(ch.buf)
72 | assert.isOk(ch.buf.empty())
73 | assert.isNotOk(ch.buf.full())
74 | assert.deepEqual(ch.buf.size, size)
75 | })
76 | })
77 |
78 | describe('.from()', () => {
79 | it('can initialize a channel from an array', async () => {
80 | const arr = [1, 2, 3, 4, 5]
81 | const ch = Channel.from(arr)
82 | assert.deepEqual(arr.length, ch.buf.length)
83 | for (const val of arr) assert.deepEqual(await ch.take(), val)
84 | assert.isOk(ch.empty())
85 | await ch.done()
86 | assert.deepEqual(ch.state, STATES.ENDED)
87 | })
88 |
89 | it('can initialize a channel from a generator', async () => {
90 | const gen = function*() {
91 | yield 1
92 | yield 2
93 | yield 3
94 | }
95 | const ch = Channel.from(gen())
96 | assert.deepEqual(ch.buf.length, 3)
97 | let val = null
98 | let i = 1
99 | while ((val = await ch.take()) !== Channel.DONE)
100 | assert.deepEqual(val, i++)
101 | })
102 |
103 | it('can initialize a channel from any iterable', async () => {
104 | const obj = {
105 | *[Symbol.iterator]() {
106 | yield 4
107 | yield 5
108 | yield 6
109 | },
110 | }
111 | const ch = Channel.from(obj)
112 | assert.deepEqual(ch.buf.length, 3)
113 | let val = null
114 | let i = 4
115 | while ((val = await ch.take()) !== Channel.DONE)
116 | assert.deepEqual(val, i++)
117 | })
118 |
119 | it('should initialize the channel as closed', async () => {
120 | const ch = Channel.from([1, 2, 3])
121 | assert.deepEqual(ch.state, STATES.CLOSED)
122 | })
123 |
124 | it('can leave the channel open by flag', async () => {
125 | const ch = Channel.from([1, 2, 3], true)
126 | assert.deepEqual(ch.state, STATES.OPEN)
127 | })
128 | })
129 |
130 | describe('#size', () => {
131 | it('should be undefined when no buffer is used', () => {
132 | const ch = new Channel()
133 | assert.deepEqual(ch.size, undefined)
134 | })
135 |
136 | it('should be the size of the buffer when a buffer is used', () => {
137 | const ch = new Channel(96)
138 | assert.deepEqual(ch.size, 96)
139 | })
140 | })
141 |
142 | describe('#length', () => {
143 | it('should equal puts length', async () => {
144 | const ch = new Channel()
145 | assert.deepEqual(ch.length, 0)
146 | assert.deepEqual(ch.length, ch.puts.length)
147 | ch.put(1)
148 | assert.deepEqual(ch.length, 1)
149 | assert.deepEqual(ch.length, ch.puts.length)
150 | ch.put(2)
151 | assert.deepEqual(ch.length, 2)
152 | assert.deepEqual(ch.length, ch.puts.length)
153 | await ch.take()
154 | assert.deepEqual(ch.length, 1)
155 | assert.deepEqual(ch.length, ch.puts.length)
156 | })
157 |
158 | it('should equal puts length plus buffer length', async () => {
159 | const ch = new Channel(2)
160 | assert.deepEqual(ch.length, 0)
161 | assert.deepEqual(ch.buf.length, 0)
162 | assert.deepEqual(ch.puts.length, 0)
163 | await ch.put(1)
164 | assert.deepEqual(ch.length, 1)
165 | assert.deepEqual(ch.buf.length, 1)
166 | assert.deepEqual(ch.puts.length, 0)
167 | await ch.put(2)
168 | assert.deepEqual(ch.length, 2)
169 | assert.deepEqual(ch.buf.length, 2)
170 | assert.deepEqual(ch.puts.length, 0)
171 | ch.put(3)
172 | assert.deepEqual(ch.length, 3)
173 | assert.deepEqual(ch.buf.length, 2)
174 | assert.deepEqual(ch.puts.length, 1)
175 | })
176 | })
177 |
178 | describe('#empty()', () => {
179 | it('should be true when puts queue is empty', async () => {
180 | const ch = new Channel()
181 | assert.isOk(ch.puts.empty())
182 | assert.isOk(ch.empty())
183 | })
184 |
185 | it('should be true when buffer and puts queue are empty', async () => {
186 | const ch = new Channel(1)
187 | assert.isOk(ch.puts.empty())
188 | assert.isOk(ch.buf.empty())
189 | assert.isOk(ch.empty())
190 | })
191 |
192 | it('should be false when puts queue is non empty', async () => {
193 | const ch = new Channel()
194 | ch.put(1)
195 | assert.isNotOk(ch.puts.empty())
196 | assert.isNotOk(ch.empty())
197 | })
198 |
199 | it('should be false when buffer is non empty', async () => {
200 | const ch = Channel.from([1, 2, 3])
201 | assert.isOk(ch.puts.empty())
202 | assert.isNotOk(ch.buf.empty())
203 | assert.isNotOk(ch.empty())
204 | })
205 |
206 | it('should be true even if takes queue is non empty', async () => {
207 | const ch = new Channel()
208 | ch.take()
209 | assert.isOk(ch.puts.empty())
210 | assert.isNotOk(ch.takes.empty())
211 | assert.isOk(ch.empty())
212 | })
213 |
214 | it('can be empty when open', async () => {
215 | const ch = new Channel()
216 | assert.deepEqual(ch.state, STATES.OPEN)
217 | assert.isOk(ch.empty())
218 | })
219 |
220 | it('can be non-empty when open', async () => {
221 | const ch = new Channel()
222 | ch.put(1)
223 | assert.deepEqual(ch.state, STATES.OPEN)
224 | assert.isNotOk(ch.empty())
225 | })
226 |
227 | it('can be non-empty when closed', async () => {
228 | const ch = new Channel(1)
229 | ch.put(1)
230 | ch.close()
231 | assert.deepEqual(ch.state, STATES.CLOSED)
232 | assert.isNotOk(ch.empty())
233 | })
234 |
235 | it('should be empty when ended', async () => {
236 | const ch = new Channel()
237 | ch.put(1)
238 | ch.close()
239 | await ch.take()
240 | await ch.done()
241 | assert.deepEqual(ch.state, STATES.ENDED)
242 | assert.isOk(ch.empty())
243 | })
244 | })
245 |
246 | describe('#put()', () => {
247 | it('should return a promise', async () => {
248 | const ch = new Channel()
249 | const put = ch.put(1)
250 | assert.instanceOf(put, Promise)
251 | })
252 |
253 | it('should put a value', async () => {
254 | const ch = new Channel()
255 | assert.isOk(ch.empty())
256 | ch.put(1)
257 | assert.isNotOk(ch.empty())
258 | assert.isNotOk(ch.puts.empty())
259 | })
260 |
261 | it('should put values in order', async () => {
262 | const ch = new Channel()
263 | for (const val of [1, 2, 3, 4, 5]) ch.put(val)
264 | assert.deepEqual(await ch.take(), 1)
265 | assert.deepEqual(await ch.take(), 2)
266 | assert.deepEqual(await ch.take(), 3)
267 | assert.deepEqual(await ch.take(), 4)
268 | assert.deepEqual(await ch.take(), 5)
269 | })
270 |
271 | it('should queue puts when buffer is full', async () => {
272 | const ch = new Channel(1)
273 | await ch.put(1)
274 | ch.put(2)
275 | assert.isNotOk(ch.empty())
276 | assert.isOk(ch.buf.full())
277 | assert.isNotOk(ch.puts.empty())
278 | assert.deepEqual(ch.puts.length, 1)
279 | })
280 |
281 | it('should resolve puts when buffer becomes not full', async () => {
282 | const ch = new Channel(1)
283 | await ch.put(1)
284 | const put = ch.put(2)
285 | assert.isOk(ch.buf.full())
286 | assert.isNotOk(ch.puts.empty())
287 | await ch.take()
288 | await put
289 | assert.isOk(ch.puts.empty())
290 | assert.isOk(ch.buf.full())
291 | })
292 | })
293 |
294 | describe('#take()', () => {
295 | it('should return a promise', async () => {
296 | const ch = new Channel()
297 | const take = ch.take()
298 | assert.instanceOf(take, Promise)
299 | })
300 |
301 | it('should take values', async () => {
302 | const ch = new Channel()
303 | ch.put(1)
304 | assert.deepEqual(await ch.take(), 1)
305 | assert.isOk(ch.empty())
306 | assert.isOk(ch.takes.empty())
307 | })
308 |
309 | it('should take values in order', async () => {
310 | const ch = new Channel()
311 | let counter = 0
312 | for (let i = 0; i < 10; i++) ch.put((counter += Math.random()))
313 | const arr = []
314 | for (let i = 0; i < 10; i++) arr.push(await ch.take())
315 | for (let i = 0; i < arr.length - 1; i++) assert.isOk(arr[i] <= arr[i + 1])
316 | })
317 |
318 | it('should queue takes when buffer is empty', async () => {
319 | const ch = new Channel()
320 | assert.isOk(ch.takes.empty())
321 | ch.take()
322 | assert.isNotOk(ch.takes.empty())
323 | })
324 |
325 | it('should resolve takes when buffer becomes non empty', async () => {
326 | const ch = new Channel()
327 | assert.isOk(ch.takes.empty())
328 | const take = ch.take()
329 | assert.isNotOk(ch.takes.empty())
330 | ch.put(1)
331 | assert.deepEqual(await take, 1)
332 | })
333 |
334 | it('should execute any available transform', async () => {
335 | const ch = new Channel(x => x ** 2)
336 | for (const val of [1, 2, 3, 4, 5]) ch.put(val)
337 | assert.deepEqual(await ch.take(), 1)
338 | assert.deepEqual(await ch.take(), 4)
339 | assert.deepEqual(await ch.take(), 9)
340 | assert.deepEqual(await ch.take(), 16)
341 | assert.deepEqual(await ch.take(), 25)
342 | })
343 | })
344 |
345 | describe('#tail()', () => {
346 | // revisit later, less important
347 |
348 | it('should return a promise', () => {
349 | const ch = new Channel()
350 | const tail = ch.tail()
351 | assert.instanceOf(tail, Promise)
352 | })
353 |
354 | it('should append values to the end of the channel', async () => {
355 | const ch = new Channel()
356 | ch.tail(4)
357 | ch.put(1)
358 | ch.tail(5)
359 | ch.put(2)
360 | ch.put(3)
361 | ch.close()
362 | for (let i = 0; i < 5; i++) assert.deepEqual(await ch.take(), i + 1)
363 | })
364 |
365 | it('should keep state at closed until tail is emptied', async () => {
366 | const ch = new Channel()
367 | ch.tail(1)
368 | ch.close()
369 | assert.deepEqual(ch.state, STATES.CLOSED)
370 | await ch.take()
371 | await ch.done()
372 | assert.deepEqual(ch.state, STATES.ENDED)
373 | })
374 |
375 | it('should use transforms', async () => {
376 | const ch = new Channel(async x => x + 2)
377 | ch.tail(1)
378 | ch.tail(2)
379 | ch.tail(3)
380 | ch.close()
381 | for (let i = 0; i < ch.tail.length; i++)
382 | assert.deepEqual(await ch.take(), i + 3)
383 | })
384 |
385 | it('should flush the channel once all values are taken', async () => {
386 | const ch = new Channel()
387 | ch.tail(1)
388 | ch.tail(2)
389 | const take1 = ch.take()
390 | const take2 = ch.take()
391 | const take3 = ch.take()
392 | ch.close()
393 | await ch.done()
394 | const [val1, val2, val3] = await Promise.all([take1, take2, take3])
395 | assert.deepEqual(ch.state, STATES.ENDED)
396 | assert.deepEqual(val1, 1)
397 | assert.deepEqual(val2, 2)
398 | assert.deepEqual(val3, Channel.DONE)
399 | })
400 | })
401 |
402 | describe('#close()', () => {
403 | it('should close a non-empty channel', async () => {
404 | const ch = new Channel()
405 | ch.put(1)
406 | assert.deepEqual(ch.state, STATES.OPEN)
407 | ch.close()
408 | assert.deepEqual(ch.state, STATES.CLOSED)
409 | })
410 |
411 | it('should end an empty channel', async () => {
412 | const ch = new Channel()
413 | assert.deepEqual(ch.state, STATES.OPEN)
414 | ch.close()
415 | await ch.done()
416 | assert.deepEqual(ch.state, STATES.ENDED)
417 | })
418 |
419 | it('should swap from closed to ended on closed channel emptied', async () => {
420 | const ch = new Channel()
421 | assert.deepEqual(ch.state, STATES.OPEN)
422 | ch.put(1)
423 | assert.deepEqual(ch.state, STATES.OPEN)
424 | ch.close()
425 | assert.deepEqual(ch.state, STATES.CLOSED)
426 | await ch.take()
427 | await ch.done()
428 | assert.deepEqual(ch.state, STATES.ENDED)
429 | })
430 | })
431 |
432 | describe('#done()', () => {
433 | it('should return a promise', async () => {
434 | const ch = new Channel()
435 | const d = ch.done()
436 | assert.instanceOf(d, Promise)
437 | })
438 |
439 | it('should resolve when the channel is ended', async () => {
440 | const ch = new Channel()
441 | const d = ch.done()
442 | assert.deepEqual(ch.state, STATES.OPEN)
443 | ch.close()
444 | await d
445 | assert.deepEqual(ch.state, STATES.ENDED)
446 | })
447 |
448 | it('should resolve all promises when the channel is ended', async () => {
449 | const ch = new Channel()
450 | const [d1, d2, d3] = [ch.done(), ch.done(), ch.done()]
451 | assert.deepEqual(ch.state, STATES.OPEN)
452 | ch.close()
453 | await Promise.all([d1, d2, d3])
454 | assert.deepEqual(ch.state, STATES.ENDED)
455 | })
456 | })
457 |
458 | describe('#pipe()', () => {
459 | it('should pipe values from one channel to another channel', async () => {
460 | const ch1 = new Channel()
461 | const ch2 = new Channel()
462 | ch1.pipe(ch2)
463 | await ch1.put(1)
464 | assert.deepEqual(await ch2.take(), 1)
465 | assert.deepEqual(ch1.pipeline, [ch2])
466 | assert.strictEqual(ch2.pipeline.length, 0)
467 | assert.isOk(ch1.empty())
468 | assert.isOk(ch2.empty())
469 | })
470 |
471 | it('should pipe values through multiple chained pipes', async () => {
472 | const ch1 = new Channel()
473 | const ch2 = new Channel()
474 | const ch3 = new Channel()
475 | ch1.pipe(ch2).pipe(ch3)
476 | ch1.put(1)
477 | ch1.put(2)
478 | const take1 = await ch3.take()
479 | const take2 = await ch3.take()
480 | assert.deepEqual(take1, 1)
481 | assert.deepEqual(take2, 2)
482 | assert.isOk(ch1.empty())
483 | assert.isOk(ch2.empty())
484 | assert.isOk(ch3.empty())
485 | })
486 |
487 | it('can pipe from one channel split to multiple channels', async () => {
488 | const ch1 = new Channel()
489 | const ch2 = new Channel()
490 | const ch3 = new Channel()
491 | ch1.pipe(ch2, ch3)
492 | ch1.put(1)
493 | ch1.put(2)
494 |
495 | const take1From2 = await ch2.take()
496 | assert.deepEqual(1, take1From2)
497 |
498 | const take1From3 = await ch3.take()
499 | assert.deepEqual(1, take1From3)
500 |
501 | const take2From2 = await ch2.take()
502 | assert.deepEqual(2, take2From2)
503 |
504 | const take2From3 = await ch3.take()
505 | assert.deepEqual(2, take2From3)
506 |
507 | assert.isOk(ch1.empty())
508 | assert.isOk(ch2.empty())
509 | assert.isOk(ch3.empty())
510 | })
511 |
512 | it('should return a reference to the last channel in the pipe', async () => {
513 | const ch1 = new Channel()
514 | const ch2 = new Channel()
515 | const ref = ch1.pipe(ch2)
516 | assert.deepEqual(ref, ch2)
517 | })
518 |
519 | it('should execute any available transforms in the pipeline', async () => {
520 | const ch1 = new Channel(8, x => x + 2)
521 | const ch2 = new Channel(8, x => ({ x }))
522 | const ch3 = new Channel(8, x => ({ y: x.x }))
523 | ch1.pipe(ch2).pipe(ch3)
524 | for (let i = 0; i < 5; i++) ch1.put(i)
525 | for (let i = 0; i < 5; i++)
526 | assert.deepEqual(await ch3.take(), { y: i + 2 })
527 | })
528 |
529 | it('should accept transforms and turn them into channels', async () => {
530 | const ch1 = new Channel(x => x + 2)
531 | const ch3 = ch1.pipe(x => ({ x })).pipe(x => ({ y: x.x }))
532 | for (let i = 0; i < 5; i++) ch1.put(i)
533 | for (let i = 0; i < 5; i++)
534 | assert.deepEqual(await ch3.take(), { y: i + 2 })
535 | })
536 |
537 | it('should be able to put values onto any channel in the pipeline', async () => {
538 | const ch1 = new Channel()
539 | const ch2 = new Channel()
540 | const ch3 = new Channel()
541 | ch1.pipe(ch2).pipe(ch3)
542 | await ch2.put(2)
543 | assert.deepEqual(await ch3.take(), 2)
544 | ch3.put(3)
545 | assert.deepEqual(await ch3.take(), 3)
546 | assert.isOk(ch1.empty())
547 | assert.isOk(ch2.empty())
548 | assert.isOk(ch3.empty())
549 | })
550 |
551 | it('should pipe values which were already buffered', async () => {
552 | const ch1 = new Channel(4)
553 | const ch2 = new Channel(4)
554 | for (let i = 0; i < 4; i++) await ch1.put(i)
555 | ch1.pipe(ch2)
556 | for (let i = 0; i < 4; i++) assert.deepEqual(await ch2.take(), i)
557 | })
558 |
559 | it('should block pipes when backed up', async () => {
560 | const ch1 = new Channel(2)
561 | const ch2 = new Channel(2)
562 | const ch3 = new Channel(4)
563 | ch1.pipe(ch2, ch3)
564 | await ch1.put(1)
565 | await ch1.put(2)
566 | ch1.put(3)
567 | ch1.put(4)
568 |
569 | // give the pipe time to transfer values from ch1 to ch2 and ch3
570 | await timeout()
571 |
572 | // ch1 should not be empty already, but the 3rd value will be taken off the buffer
573 | // and will be hanging out in a hidden context waiting to be resolved on all pipes
574 | // the 4th value should remain on the buffer until the pipe is unblocked
575 | assert.isNotOk(ch1.empty())
576 | assert.deepEqual(ch1.buf.length, 1)
577 | assert.strictEqual(ch1.puts.length, 0)
578 | assert.strictEqual(ch1.takes.length, 0)
579 |
580 | // the 3rd value will not be placed on the 2nd channel, since the max size of ch2 is 2
581 | assert.isNotOk(ch2.empty())
582 | assert.deepEqual(ch2.buf.length, 2)
583 | assert.deepEqual(ch2.puts.length, 1)
584 | assert.strictEqual(ch2.takes.length, 0)
585 |
586 | // the 3rd value will be placed on the 3rd channel, since the max size of ch3 is 4
587 | assert.isNotOk(ch3.empty())
588 | assert.deepEqual(ch3.buf.length, 3)
589 | assert.strictEqual(ch3.puts.length, 0)
590 | assert.strictEqual(ch3.takes.length, 0)
591 |
592 | let take3 = await ch3.take()
593 |
594 | // ch1 should be unchanged, since the pipe is still blocked
595 | assert.isNotOk(ch1.empty())
596 | assert.deepEqual(ch1.buf.length, 1)
597 | assert.strictEqual(ch1.puts.length, 0)
598 | assert.strictEqual(ch1.takes.length, 0)
599 |
600 | // ch2 should be unchanged, still blocking the pipe
601 | assert.isNotOk(ch2.empty())
602 | assert.deepEqual(ch2.buf.length, 2)
603 | assert.deepEqual(ch2.puts.length, 1)
604 | assert.strictEqual(ch2.takes.length, 0)
605 |
606 | // ch3 should have a value removed from its buffer
607 | assert.isNotOk(ch3.empty())
608 | assert.deepEqual(ch3.buf.length, 2)
609 | assert.strictEqual(ch3.puts.length, 0)
610 | assert.strictEqual(ch3.takes.length, 0)
611 | assert.deepEqual(take3, 1)
612 |
613 | let take2 = await ch2.take()
614 | await timeout() // to ensure the next value is fully pulled from the ch1 before we continue, now that takes will resolve interleaved with async puts
615 |
616 | // ch1 should have had its last value taken
617 | assert.isOk(ch1.empty())
618 | assert.strictEqual(ch1.buf.length, 0)
619 | assert.strictEqual(ch1.puts.length, 0)
620 | assert.strictEqual(ch1.takes.length, 0)
621 |
622 | // ch2 should have unblocked space for 1 value, then reblocked ch1 from putting again
623 | assert.isNotOk(ch2.empty())
624 | assert.deepEqual(take2, 1)
625 | assert.deepEqual(ch2.buf.length, 2)
626 | assert.deepEqual(ch2.puts.length, 1)
627 | assert.strictEqual(ch2.takes.length, 0)
628 |
629 | // ch3 should have the next value from ch1 placed on its buffer
630 | assert.isNotOk(ch3.empty())
631 | assert.deepEqual(ch3.buf.length, 3)
632 | assert.strictEqual(ch3.puts.length, 0)
633 | assert.strictEqual(ch3.takes.length, 0)
634 | ;[take2, take3] = await Promise.all([ch2.take(), ch3.take()])
635 | await timeout() // same as above. ensure that the async put has time to place values from ch1 onto ch2 and ch3 after the takes are successful
636 |
637 | // ch2 should have accepted the last value from ch1
638 | assert.deepEqual(take2, 2)
639 | assert.isNotOk(ch2.empty())
640 | assert.deepEqual(ch2.buf.length, 2)
641 | assert.strictEqual(ch2.puts.length, 0)
642 | assert.strictEqual(ch2.takes.length, 0)
643 |
644 | // ch3 should have another value taken
645 | assert.deepEqual(take3, 2)
646 | assert.isNotOk(ch3.empty())
647 | assert.deepEqual(ch3.buf.length, 2)
648 | assert.strictEqual(ch3.puts.length, 0)
649 | assert.strictEqual(ch3.takes.length, 0)
650 | })
651 |
652 | it('should be able to close single channels', async () => {
653 | const ch1 = new Channel(4)
654 | const ch2 = new Channel(4)
655 | for (let i = 0; i < 4; i++) await ch1.put(i)
656 | ch1.close()
657 | ch1.pipe(ch2)
658 | await ch1.done()
659 | assert.deepEqual(ch1.state, STATES.ENDED)
660 | assert.deepEqual(ch2.state, STATES.OPEN)
661 | })
662 |
663 | it('should be able to close the entire pipeline by flag', async () => {
664 | const ch1 = new Channel()
665 | const ch2 = new Channel()
666 | const ch3 = new Channel()
667 | const ch4 = new Channel()
668 | ch1
669 | .pipe(ch2)
670 | .pipe(ch3)
671 | .pipe(ch4)
672 | for (let i = 0; i < 8; i++) {
673 | ch1.put(i) // if we await this and the cap is >= 4, then we freeze? wtf?
674 | }
675 | ch1.close(true)
676 | for (let i = 0; i < 8; i++) assert.deepEqual(await ch4.take(), i)
677 | await ch4.done()
678 | assert.deepEqual(ch1.state, STATES.ENDED)
679 | assert.deepEqual(ch2.state, STATES.ENDED)
680 | assert.deepEqual(ch3.state, STATES.ENDED)
681 | assert.deepEqual(ch4.state, STATES.ENDED)
682 | })
683 | })
684 |
685 | describe('.pipeline()', () => {
686 | it('should create a pipeline from an iterable of callbacks', async () => {
687 | const [ch1, ch3] = Channel.pipeline(x => x + 2, x => x ** 2, x => x / 2)
688 | assert.deepEqual(ch1.pipeline.length, 1) // since ch1 -> ch2 only
689 | const ch2 = ch1.pipeline[0]
690 | assert.deepEqual(ch2.pipeline.length, 1)
691 | assert.deepEqual(ch2.pipeline[0], ch3)
692 | ch1.put(1)
693 | ch1.put(2)
694 | ch1.put(3)
695 | ch1.close(true)
696 | assert.deepEqual(await ch3.take(), 4.5)
697 | assert.deepEqual(await ch3.take(), 8)
698 | assert.deepEqual(await ch3.take(), 12.5)
699 | await ch1.done()
700 | await ch2.done()
701 | await ch3.done()
702 | assert.deepEqual(ch1.state, STATES.ENDED)
703 | assert.deepEqual(ch2.state, STATES.ENDED)
704 | assert.deepEqual(ch3.state, STATES.ENDED)
705 | })
706 |
707 | it('should accept a mix of channels and callbacks', async () => {
708 | const [ch1, ch3] = Channel.pipeline(
709 | x => x + 2,
710 | new Channel(x => x ** 2),
711 | x => x / 2,
712 | )
713 | assert.deepEqual(ch1.pipeline.length, 1) // since ch1 -> ch2 only
714 | const ch2 = ch1.pipeline[0]
715 | assert.deepEqual(ch2.pipeline.length, 1)
716 | assert.deepEqual(ch2.pipeline[0], ch3)
717 | ch1.put(1)
718 | ch1.put(2)
719 | ch1.put(3)
720 | ch1.close(true)
721 | assert.deepEqual(await ch3.take(), 4.5)
722 | assert.deepEqual(await ch3.take(), 8)
723 | assert.deepEqual(await ch3.take(), 12.5)
724 | await ch1.done()
725 | await ch2.done()
726 | await ch3.done()
727 | assert.deepEqual(ch1.state, STATES.ENDED)
728 | assert.deepEqual(ch2.state, STATES.ENDED)
729 | assert.deepEqual(ch3.state, STATES.ENDED)
730 | })
731 | })
732 |
733 | describe('#unpipe()', () => {
734 | it('should remove a channel from a pipeline', async () => {
735 | const ch1 = new Channel(2)
736 | const ch2 = new Channel(2)
737 | ch1.pipe(ch2)
738 | assert.deepEqual(ch1.pipeline, [ch2])
739 | ch1.unpipe(ch2)
740 | assert.deepEqual(ch1.pipeline, [])
741 | })
742 |
743 | it('should return a reference to an existing channel', async () => {
744 | const ch1 = new Channel()
745 | const ch2 = new Channel()
746 | ch1.pipe(ch2)
747 | const unpiped = ch1.unpipe(ch2)
748 | assert.deepEqual(ch1, unpiped)
749 | })
750 |
751 | it('should stop the automated flow of data', async () => {
752 | const ch1 = new Channel(2)
753 | const ch2 = ch1.pipe(new Channel(2))
754 | await ch1.put(1)
755 | await ch1.put(2)
756 | ch1.put(3)
757 | ch1.put(4)
758 | await timeout()
759 | ch1.unpipe(ch2)
760 | assert.deepEqual(await ch2.take(), 1)
761 | assert.deepEqual(await ch2.take(), 2)
762 |
763 | assert.isNotOk(ch2.empty())
764 | // note this - since 3 is already queued up in ch2.puts,
765 | // it will STILL be received inside channel 2
766 | // is this intended behavior? unexpected? bug to be fixed?
767 | // as of right now, i feel this is intended and OK.
768 | assert.deepEqual(await ch2.take(), 3)
769 | assert.isOk(ch2.empty())
770 | assert.strictEqual(ch2.takes.length, 0)
771 |
772 | assert.isNotOk(ch1.empty())
773 | assert.deepEqual(await ch1.take(), 4)
774 | assert.isOk(ch1.empty())
775 | assert.strictEqual(ch1.takes.length, 0)
776 | })
777 |
778 | it('should keep child pipes intact', async () => {
779 | const ch1 = new Channel(2)
780 | const ch2 = new Channel(2)
781 | const ch3 = new Channel(2)
782 | ch1.pipe(ch2).pipe(ch3)
783 | assert.deepEqual(ch1.pipeline, [ch2])
784 | assert.deepEqual(ch2.pipeline, [ch3])
785 | await ch1.put(1)
786 | await ch1.put(2)
787 | assert.deepEqual(await ch3.take(), 1)
788 | assert.deepEqual(await ch3.take(), 2)
789 | ch1.unpipe(ch2)
790 | assert.deepEqual(ch1.pipeline, [])
791 | assert.deepEqual(ch2.pipeline, [ch3])
792 | await ch2.put(3)
793 | await ch2.put(4)
794 | assert.deepEqual(await ch3.take(), 3)
795 | assert.deepEqual(await ch3.take(), 4)
796 | })
797 |
798 | it('should keep sibling pipes intact', async () => {
799 | const ch1 = new Channel(4)
800 | const ch2 = new Channel(2)
801 | const ch3 = new Channel(2)
802 |
803 | ch1.pipe(ch2, ch3)
804 | assert.deepEqual(ch1.pipeline, [ch2, ch3])
805 | await ch1.put(1)
806 | await ch1.put(2)
807 | await timeout() // make sure the puts are through before unpipe
808 |
809 | ch1.unpipe(ch2)
810 | assert.deepEqual(ch1.pipeline, [ch3])
811 |
812 | await ch1.put(3)
813 | await ch1.put(4)
814 | assert.deepEqual(await ch2.take(), 1)
815 | assert.deepEqual(await ch2.take(), 2)
816 | assert.isOk(ch2.empty())
817 | assert.strictEqual(ch2.takes.length, 0)
818 |
819 | assert.deepEqual(await ch3.take(), 1)
820 | assert.deepEqual(await ch3.take(), 2)
821 | assert.deepEqual(await ch3.take(), 3)
822 | assert.deepEqual(await ch3.take(), 4)
823 | assert.isOk(ch3.empty())
824 | assert.strictEqual(ch3.takes.length, 0)
825 |
826 | assert.isOk(ch1.empty())
827 | assert.deepEqual(ch1.takes.length, 1) // ch3 should still have a take registered with c1
828 | })
829 | })
830 |
831 | describe('#merge()', () => {
832 | it('should put values from multiple channels onto a new channel', async () => {
833 | const ch1 = new Channel()
834 | const ch2 = new Channel()
835 |
836 | const ch3 = ch1.merge(ch2) // or Channel.merge(ch1, ch2)
837 | await ch1.put(1)
838 | await ch2.put(2)
839 | assert.deepEqual(await ch3.take(), 1)
840 | assert.deepEqual(await ch3.take(), 2)
841 | })
842 | })
843 |
844 | describe('#produce()', () => {
845 | it('should automatically produce values when space is available', async () => {
846 | const ch = new Channel()
847 | let counter = 0
848 | ch.produce(() => counter++)
849 | for (let i = 0; i < 10; i++) assert.deepEqual(await ch.take(), i)
850 | ch.close()
851 | await ch.done()
852 | })
853 |
854 | it('can produce values synchronously', async () => {
855 | const ch = new Channel()
856 | ch.produce(Math.random)
857 | for (let i = 0; i < 10; i++) {
858 | const val = await ch.take()
859 | assert.isOk(val >= 0 && val < 1)
860 | }
861 | ch.close()
862 | await ch.done()
863 | })
864 |
865 | it('can produce values asynchronously', async () => {
866 | const ch = new Channel()
867 | let counter = 0
868 | ch.produce(async () => {
869 | await timeout()
870 | return counter++
871 | })
872 | for (let i = 0; i < 10; i++) assert.deepEqual(await ch.take(), i)
873 | ch.close()
874 | await ch.done()
875 | })
876 | })
877 |
878 | describe('#consume()', () => {
879 | it('can consume values synchronously', async () => {
880 | const ch = new Channel()
881 | let counter = 0
882 | ch.consume(x => {
883 | counter += x
884 | })
885 | await ch.put(1)
886 | await ch.put(2)
887 | await ch.put(3)
888 | await ch.put(4)
889 | await ch.put(5)
890 | ch.close()
891 | await ch.done()
892 | assert.deepEqual(counter, 15)
893 | })
894 |
895 | it('can consume values asynchronously', async () => {
896 | const ch = new Channel(async x => x)
897 | let counter = 0
898 | ch.consume(async x => {
899 | await timeout()
900 | counter += x
901 | })
902 | await ch.put(1)
903 | await ch.put(2)
904 | await ch.put(3)
905 | await ch.put(4)
906 | await ch.put(5)
907 | ch.close()
908 | await ch.done()
909 | assert.deepEqual(counter, 15)
910 | })
911 |
912 | it('should consume all values even if put without waiting', async () => {
913 | const ch = new Channel(async x => {
914 | await timeout()
915 | return x
916 | })
917 | const arr = []
918 | ch.consume(x => {
919 | arr.push(x)
920 | })
921 | ch.put(1)
922 | ch.put(2)
923 | ch.put(3)
924 | ch.put(4)
925 | ch.put(5)
926 | ch.put(6)
927 | ch.put(7)
928 | ch.put(8)
929 | ch.close()
930 | await ch.done()
931 | assert.deepEqual(arr, [1, 2, 3, 4, 5, 6, 7, 8])
932 | })
933 | })
934 |
935 | describe('transform', () => {
936 | it('should transform values', async () => {
937 | const ch = new Channel(x => x ** 2)
938 | for (let i = 1; i <= 4; i++) ch.put(i)
939 | for (let i = 0; i < 4; i++)
940 | assert.deepEqual(await ch.take(), (i + 1) ** 2)
941 | })
942 |
943 | it('should drop undefined values', async () => {
944 | const ch = new Channel(x => {
945 | if (x > 2) return x
946 | })
947 | ch.put(1)
948 | ch.put(2)
949 | ch.put(3)
950 | ch.put(4)
951 | assert.deepEqual(await ch.take(), 3)
952 | assert.deepEqual(await ch.take(), 4)
953 | assert.strictEqual(ch.length, 0)
954 | })
955 |
956 | it('should transform values by callback', async () => {
957 | const ch = new Channel((x, push) => {
958 | if (x > 2) push(x)
959 | })
960 | ch.put(1)
961 | ch.put(2)
962 | ch.put(3)
963 | ch.put(4)
964 | assert.deepEqual(await ch.take(), 3)
965 | assert.deepEqual(await ch.take(), 4)
966 | assert.strictEqual(ch.length, 0)
967 | })
968 |
969 | it('should expand values by multiple callback executions', async () => {
970 | const ch = new Channel((x, push) => {
971 | if (x > 2) {
972 | push(x)
973 | push(x)
974 | }
975 | })
976 | ch.put(1)
977 | ch.put(2)
978 | ch.put(3)
979 | ch.put(4)
980 | assert.deepEqual(await ch.take(), 3)
981 | assert.deepEqual(await ch.take(), 3)
982 | assert.deepEqual(await ch.take(), 4)
983 | assert.deepEqual(await ch.take(), 4)
984 | assert.strictEqual(ch.length, 0)
985 | })
986 |
987 | it('should maintain order with multiple callback transforms', async () => {
988 | const ch = new Channel(8, (x, push) => {
989 | if (x < 3) {
990 | push(x)
991 | push(x * 2)
992 | push(x * 3)
993 | } else push(x)
994 | })
995 | const arr = []
996 | ch.consume(async x => {
997 | arr.push(x)
998 | })
999 | await ch.put(1)
1000 | await ch.put(2)
1001 | await ch.put(3)
1002 | await ch.put(4)
1003 | await ch.put(5)
1004 | await ch.put(6)
1005 | ch.close()
1006 | await ch.done()
1007 | assert.deepEqual(arr, [1, 2, 3, 2, 4, 6, 3, 4, 5, 6])
1008 | })
1009 |
1010 | it('should transform values asynchronously when promise is returned', async () => {
1011 | const ch = new Channel(async (val, push) => {
1012 | await timeout(5)
1013 | push(val)
1014 | await timeout(5)
1015 | push(val + 2)
1016 | })
1017 |
1018 | const arr = []
1019 | ch.consume(x => arr.push(x))
1020 | await ch.put(1)
1021 | await ch.put(2)
1022 | ch.close()
1023 | await ch.done()
1024 | assert.deepEqual(arr, [1, 3, 2, 4])
1025 | })
1026 |
1027 | it('should transform values asynchronously when 3 parameters are used', async () => {
1028 | const ch = new Channel((val, push, done) => {
1029 | setTimeout(() => {
1030 | push(val)
1031 | setTimeout(() => {
1032 | push(val + 2)
1033 | done()
1034 | }, 5)
1035 | }, 5)
1036 | })
1037 | const arr = []
1038 | ch.consume(x => arr.push(x))
1039 | await ch.put(1)
1040 | await ch.put(2)
1041 | ch.close()
1042 | await ch.done()
1043 | assert.deepEqual(arr, [1, 3, 2, 4])
1044 | })
1045 | })
1046 |
1047 | describe('general use', () => {
1048 | it('should not block indefinitely with synchronous produce + consume', async () => {
1049 | const ch = new Channel()
1050 | ch.produce(Math.random)
1051 | ch.consume(x => x ** 2)
1052 | await timeout(50) // let it spin for a while
1053 | ch.close() // close, and continue spinning until empty
1054 | await ch.done()
1055 | assert.isOk(ch.empty())
1056 | assert.deepEqual(ch.state, STATES.ENDED)
1057 | })
1058 |
1059 | it('should work with DroppingBuffer', async () => {
1060 | const buf = new DroppingBuffer()
1061 | const transformer = (x, push) => {
1062 | push(x)
1063 | push(x + 1)
1064 | }
1065 | const ch = new Channel(buf, transformer)
1066 | const values = [1, 2, 3, 4, 5]
1067 | for (const val of values) await ch.put(val) // note that we aren't blocked, even while awaiting.
1068 | assert.deepEqual(buf.full(), false)
1069 | await timeout(10)
1070 | const val = await ch.take()
1071 | assert.deepEqual(val, 1)
1072 | assert.deepEqual(buf.full(), false)
1073 | })
1074 |
1075 | it('should work with a SlidingBuffer', async () => {
1076 | const buf1 = new SlidingBuffer(1)
1077 | const ch1 = new Channel(buf1)
1078 | const vals = [1, 2, 3, 4]
1079 | for (const val of vals) await ch1.put(val)
1080 | assert.deepEqual(await ch1.take(), 4)
1081 |
1082 | const buf2 = new SlidingBuffer(2)
1083 | const ch2 = new Channel(buf2)
1084 | await ch2.put(1)
1085 | await ch2.put(2)
1086 | await ch2.put(3)
1087 | await ch2.put(4)
1088 | assert.deepEqual(await ch2.take(), 3)
1089 | await ch2.put(5)
1090 | assert.deepEqual(await ch2.take(), 4)
1091 | assert.deepEqual(await ch2.take(), 5)
1092 | await ch2.put(6)
1093 | await ch2.put(7)
1094 | await ch2.put(8)
1095 | assert.deepEqual(await ch2.take(), 7)
1096 | assert.deepEqual(await ch2.take(), 8)
1097 | })
1098 | })
1099 | })
1100 |
--------------------------------------------------------------------------------