├── .babelrc ├── .github └── FUNDING.yml ├── .gitignore ├── .nvmrc ├── .travis.yml ├── .yarnrc.yml ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── deps.cljs └── rxjs │ ├── externs.js │ ├── rxjs.main.bundle.js │ ├── rxjs.main.bundle.min.js │ ├── rxjs.main.js │ ├── rxjs.operators.bundle.js │ ├── rxjs.operators.bundle.min.js │ └── rxjs.operators.js ├── build.clj ├── deps.edn ├── doc.clj ├── doc ├── Makefile ├── notes.txt └── user-guide.md ├── mvn-upload.sh ├── package.json ├── pom.xml ├── rollup.config.js ├── shadow-cljs.edn ├── src └── beicon │ └── v2 │ ├── core.clj │ ├── core.cljs │ └── operators.cljs ├── test └── beicon │ └── tests │ ├── helpers.cljc │ └── v2_test.cljs ├── tests.edn ├── tools.clj └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", {"targets": {"browsers": "> 1%, maintained node versions"}, "modules": false}], 4 | ], 5 | "plugins": ["@babel/plugin-proposal-export-default-from", 6 | "@babel/plugin-proposal-class-properties"] 7 | } 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: niwinz 2 | patreon: niwinz 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.yarn/patches 2 | !.yarn/plugins 3 | !.yarn/releases 4 | !.yarn/sdks 5 | !.yarn/versions 6 | *.class 7 | *.jar 8 | .pnp.* 9 | .shadow-cljs 10 | .yarn/* 11 | /*-init.clj 12 | /.cpcache 13 | /.lein-* 14 | /.nrepl-port 15 | /.rebel* 16 | /checkouts 17 | /classes 18 | /doc/dist 19 | /nashorn_code_cache 20 | /node_modules 21 | /out 22 | /repl 23 | /target 24 | pom.xml.asc 25 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.14.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | 3 | language: generic 4 | sudo: required 5 | 6 | cache: 7 | directories: 8 | - $HOME/.m2 9 | 10 | branches: 11 | only: 12 | - master 13 | - develop 14 | 15 | install: 16 | - curl -O https://download.clojure.org/install/linux-install-1.10.1.447.sh 17 | - chmod +x linux-install-1.10.1.447.sh 18 | - sudo ./linux-install-1.10.1.447.sh 19 | 20 | before_script: 21 | - env | sort 22 | - node --version 23 | - java -version 24 | 25 | script: 26 | - clojure -Adev tools.clj build:tests 27 | - node out/tests.js 28 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | enableGlobalCache: true 2 | 3 | enableImmutableCache: false 4 | 5 | enableImmutableInstalls: false 6 | 7 | enableTelemetry: false 8 | 9 | nodeLinker: node-modules 10 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changelog # 2 | 3 | ## Version 2.2 4 | 5 | - Fix compatibility issues with shadow-cljs :esm builds 6 | 7 | 8 | ## Version 2.1 ## 9 | 10 | - Fix compatibility issues with shadow-cljs :esm builds 11 | 12 | ## Version 2.0 ## 13 | 14 | This version few numer of BREAKING CHANGES that should be considered 15 | before upgrade. All of them are pretty easy to adapt. 16 | 17 | As this is a breaking change, we decide to rename the `beicon.core` 18 | namespace with `beicon.v2` namespace, so the people can continue using 19 | the old API and the old package if they want without a conflict with 20 | the new version. 21 | 22 | This is a major API cleanup and adapt it more to RX convention of 23 | using `rx/pipe` operator for compose transformations. 24 | 25 | Relevant changes: 26 | 27 | - The internal `rx/pipe` function inverts the arguments order for to 28 | be easy to use with `->>`. 29 | - Make the `rx/pipe` as public API useful for use custom defined 30 | operators. 31 | - Add `beicon.v2.ops` with operator only functions. 32 | - Remove the `flat-map` alias (use `merge-map`). 33 | - The `first` operator now becomes an alias for `(take 1)`. 34 | - The `last` operator becomes an alias for `(take-last 1)`. 35 | - Replace multiarity `with-latest-from` with a specific operator 36 | `with-latest-from*` that can be used through the `rx/pipe` 37 | observable chain helper. Mainly for reduce code complexity. 38 | - Remove the `do` alias for `tap` 39 | - Remove the `log` and `pr-log` operator 40 | - Replace `dedupe` and `dedupe'` with operator only 41 | `distinct-contiguous*` and `distinct*` for make it more similar to 42 | rxjs API. 43 | - The `delay-when` operator arguments order changed to be the same as 44 | rxjs 45 | - Remove `delay-emit` 46 | - Replace `subs` with `subs!` 47 | 48 | 49 | ## Version 2021.07.05-1 ## 50 | 51 | - Fix bug on `delay-emit` operator. 52 | - Minor change on `concat-reduce` operator. 53 | 54 | ## Version 2021.07.05-0 ## 55 | 56 | - Add `flatten` operator. 57 | - Add `delay-emit` operator. 58 | - Add `skip-last` operator. 59 | - Add `concat-reduce` operator. 60 | 61 | 62 | ## Version 2021.06.03-0 ## 63 | 64 | BREAKING CHANGES: `zip` no longer accepts acoomulator function. 65 | 66 | - Add `merge-scan` operator. 67 | - Minor improvements on `concat` and `zip` constructors. 68 | 69 | 70 | ## Version 2021.06.02-0 ## 71 | 72 | - Update bundled rxjs to 7.1.0 73 | - Add missing ^js metadata. 74 | 75 | 76 | ## Version 2021.04.29-0 ## 77 | 78 | - Add the ability to pass config to `throttle`. 79 | - Add `with-latest-from` as replacement to `with-latest` (more arities). 80 | - Deprecate `with-latest`. 81 | 82 | 83 | ## Version 2021.04.27-2 ## 84 | 85 | - Replace `combine-latest` operator with `combine-latest-with` (now it 86 | does not accepts the selector/join function). 87 | - Add new `combine-latest` constructor. 88 | - Add the `delay-at-least` operator. 89 | - Update documentation 90 | 91 | 92 | ## Version 2021.04.12-1 ## 93 | 94 | - Fix `from-atom` factory. 95 | 96 | 97 | ## Version 2021.04.09-1 ## 98 | 99 | **BREAKING CHANGE:** 100 | 101 | - The `create` factory function is simplified, and IObservableValue is 102 | removed. The subscription object is passed as is to the factory 103 | function. 104 | 105 | 106 | Before: 107 | 108 | ```clojure 109 | (rx/create (fn [sink] 110 | (sink 1) 111 | (sink (reduced 2)))) 112 | ``` 113 | 114 | Now: 115 | 116 | ```clojure 117 | (rx/create (fn [subs] 118 | (rx/push! subs 1) 119 | (rx/push! subs 2) 120 | (rx/end! subs))) 121 | ``` 122 | 123 | ## Version 2021.03.31-1 ## 124 | 125 | - Add `if-empty` operator. 126 | - Update to rxjs 7.0.0-beta.13 127 | 128 | 129 | ## Version 2021.01.29-1 ## 130 | 131 | - Accept `nil` on `from` function. 132 | 133 | 134 | ## Version 2020.12.20-1 ## 135 | 136 | - Update to rxjs 7.0.0-beta.9 137 | - Fix minor incompatibilities and unsafe usage of rxjs api. 138 | 139 | 140 | ## Version 2020.05.08-2 ## 141 | 142 | - Add `subs` helper as alternative to `subscribe` that receives the 143 | observable in the last position of arguments. 144 | 145 | 146 | ## Version 2020.05.08-1 ## 147 | 148 | - Add `switch-map` operator. 149 | 150 | 151 | ## Version 2020.03.29-1 ## 152 | 153 | - Minor internal assets loading changes. 154 | 155 | 156 | ## Version 2020.03.28-1 ## 157 | 158 | - Minor internal assets loading changes. 159 | 160 | 161 | ## Version 2020.03.23-1 ## 162 | 163 | This a list of posible **breaking changes**: 164 | 165 | - Rename `from-coll` to `from`. 166 | - Remove `from-promise` (replaced by `from`). 167 | - Remove `just` and `once` (`of` already handles it). 168 | - Remove `never` (alias of `empty`). 169 | - Remove `map-indexed` (already covered by the `map`). 170 | - Changed comparator from `=` to `identical?` in `dedupe` and `dedupe'`. 171 | - Remove arity/1 from `mapcat` and `merge-map`/`flat-map`. 172 | - Replace `ICancellable` protocol with `IDisposable` (and the method 173 | is now `-dispose` instead of `-cancel`). 174 | 175 | Other relevant but backward compatible changes: 176 | 177 | - Update to RxJS 7.0.0-alpha.1 178 | - Add `merge-all` operator. 179 | - Add `concat-all` operator. 180 | - Add `buffer-until` operator. 181 | - Add `first` operator. 182 | - Add `last` operator. 183 | - The `merge-map` becomes the main operator name and `flat-map` an alias. 184 | - Add `fmap` alias to `merge-map` operator. 185 | - Add `finalize` operator. 186 | - Add `dispose!` function for finalize a subscription. 187 | - Add `cancel!` as alias to `dispose!` (for backward compatibility). 188 | - Add `sub!` alias to `subscribe`. 189 | - A disposable returned by `sub!`/`subscribe` is now a callable (you 190 | can call it for dispose the subscription). 191 | 192 | 193 | ## Version 5.1.0 ## 194 | 195 | Date: 2019-08-20 196 | 197 | - Update to RxJS 6.5.2 198 | - Update to RxJava 2.2.11 199 | - Add `map-indexed` operator. 200 | - Make the `observer?` predicate clj only (rxjs does not have a type 201 | defined for it). 202 | - Implement `IDeref` protocol for `BehaviorSubject` (only cljs). 203 | - Add `as-observable` helper cljs. 204 | 205 | On this release we start to use a local rollup based process for 206 | generate the bundle from the npm installed dependency (that will 207 | simplify upgrading the RxJS for the next versions). 208 | 209 | 210 | ## Version 5.0.0 ## 211 | 212 | Date: 2019-03-01 213 | 214 | - Update to RxJS 6.4.0 215 | - Update ro RxJava 2.2.7 216 | 217 | This release reduces the rxjs bundle size including only the rxjs core 218 | and all operators (excluding all tests, ajax, websockets and other 219 | unrelated and not exposted api). Now it is 23k minified and gziped. 220 | 221 | 222 | ## Version 4.1.0 ## 223 | 224 | Date: 2017-11-17 225 | 226 | - Update cljs compiler to the latest version. 227 | - Update RxJS bundle to 5.5.2 228 | - Update RxJava dependency to 2.1.6 229 | - Make `ignore` return an instance of `Observable`. 230 | 231 | ## Version 4.0.0 ## 232 | 233 | Date: 2017-08-01 234 | 235 | - Update cljs compiler to the latest version. 236 | - Update RxJS bundle to 5.4.2 237 | - Update RxJava dependency to 2.1.2. 238 | - Fix `concat` operator with flowables. 239 | - Fix `from-promise` is renamed to `from-future` in jvm (BREAKING CHANGE). 240 | 241 | 242 | ## Version 3.5.0 ## 243 | 244 | Date: 2017-05-28 245 | 246 | - The `:trampoline` scheduler is renamed to `:queue` for consistency 247 | (backward compatibility preserved, but `:trampoline` is deprected 248 | for now). 249 | - Add `from-event` function to the cljs. 250 | - Update RxJS to 5.4.0 251 | 252 | 253 | ## Version 3.4.0 ## 254 | 255 | Date: 2017-04-29 256 | 257 | - Upgrade RxJava to 2.1.0 258 | - Fix unexpected exception on two-arity version of `timer` function. 259 | 260 | 261 | ## Version 3.3.0 ## 262 | 263 | Date: 2017-04-25 264 | 265 | - Update RxJS to 5.3.0 266 | - Update RxJava to 2.0.9 267 | 268 | 269 | ## Version 3.2.0 ## 270 | 271 | Date: 2017-03-12 272 | 273 | - Add `to-serialized` function(jvm). 274 | - Add `single?` predicate (jvm). 275 | - Fix unexpected exception when subscribing to Single (jvm). 276 | 277 | 278 | ## Version 3.1.1 ## 279 | 280 | Date: 2017-02-26 281 | 282 | - Wrap function on `reduce` and `scan` because some functions 283 | such as conj does not works if they are passed as is (and 284 | produces wrong results). 285 | 286 | 287 | ## Version 3.1.0 ## 288 | 289 | Date: 2017-02-22 290 | 291 | - Upgrade to RxJS 5.2.0 292 | - Upgrade to RxJava 2.0.6 293 | 294 | 295 | ## Version 3.0.0 ## 296 | 297 | Date: 2017-02-13 298 | 299 | WARNING: many changes are backward incompatible with the previous version. 300 | They are introduced because for make the library more concise and more 301 | consistent between clj and cljs. 302 | 303 | A list of relevant changes: 304 | 305 | - Upgrade to RxJava2 (2.0.5) 306 | - Scheduler vars are removed in favor to `scheduler` function. 307 | - The functions `subscribe-on` and `observe-on` now accept keywords 308 | as argument that automatically resolves to proper scheduler or raises 309 | an exception if no scheduler found for the provided keyword). 310 | - Add `cancel!` function for cancel subscriptions more conciselly. 311 | - Remove the ability to call the subscription in order to cancel it. 312 | - Introduce backpressure support with rxjava2 flowables through the 313 | new `generate` function, see documentation for more info. 314 | - `to-atom` now return a cancellable variant of atom (a wrapper that 315 | implements the atom interface and ICancellable protocol). 316 | - Remove deprecated `bus?` predicate. 317 | - Remove deprecated `bus` function (subject constructor). 318 | - Remove deprecated second arity of `publish` function. 319 | - Remove deprecated `from-exception` function. 320 | - Remove deprecated `with-latest-from` (replaced by `with-latest`). 321 | - Remove `.close` method on disposable on clojurescript. 322 | 323 | 324 | ## Version 2.9.0 ## 325 | 326 | Date: 2017-01-30 327 | 328 | - Fix wrong behavior of concat combinator. 329 | - The convenience arity for automatically connect on `publish` is 330 | **deprecated**. If you want to connect, just use the `connect!` 331 | function. **The arity will be removed in the next version.** 332 | - The `with-latest-from` function is deprecated in favor of the new 333 | `with-latest` function that has the project function mandatory in 334 | contrast to the deprecated function. **The deprecated function will 335 | be removed in the next version.** 336 | - Update to RxJS 5.0.3 (master at d4533c40) 337 | - Update to RxJava 1.2.5 338 | 339 | 340 | ## Version 2.8.0 ## 341 | 342 | Date: 2016-12-18 343 | 344 | - Add `buffer-time` operator. 345 | 346 | 347 | ## Version 2.7.0 ## 348 | 349 | Date: 2016-12-14 350 | 351 | - Update to RxJS 5.0.1. 352 | 353 | 354 | ## Version 2.6.1 ## 355 | 356 | Date: 2016-12-07 357 | 358 | - Add missing externs. 359 | 360 | 361 | ## Version 2.6.0 ## 362 | 363 | Date: 2016-12-07 364 | 365 | - Update to RxJS 5.0.0.rc5 366 | - Rename `bus` constructor to `subject` (for name consistency with rxjs). 367 | - Add `behavior-subject` constructor. 368 | - Add the ability to use subject's and Observers as parameter to subscribe. 369 | 370 | 371 | ## Version 2.5.0 ## 372 | 373 | Date: 2016-11-27 374 | 375 | - Update to RxJS 5.0.0.rc4 376 | - Update to RxJava 1.2.3 377 | - Add combine-latest combinator. 378 | 379 | 380 | ## Version 2.4.0 ## 381 | 382 | Date: 2016-11-03 383 | 384 | - Update to RxJS 5.0.0.rc1 385 | - Update to RxJava 1.2.1 386 | 387 | 388 | ## Version 2.3.0 ## 389 | 390 | Date: 2016-08-17 391 | 392 | - Update to RxJS 5.0.0.beta11 393 | - Update to RxJava 1.1.9 394 | 395 | 396 | ## Version 2.2.0 ## 397 | 398 | Date: 2016-07-10 399 | 400 | - Update to RxJS 5.0.0.beta10 401 | - Update to RxJava 1.1.7 402 | 403 | 404 | ## Version 2.1.0 ## 405 | 406 | Date: 2016-06-15 407 | 408 | - Add missing ignoreElements to externs. 409 | - Update to RxJS 5.0.0.beta9 410 | 411 | 412 | ## Version 2.0.0 ## 413 | 414 | Date: 2016-06-04 415 | 416 | - Add support for clojure using rxjava as underlying implementation. 417 | 418 | 419 | ## Version 1.4.0 ## 420 | 421 | Date: 2016-05-28 422 | 423 | - Update bundled rxjs to commit ceb9990 (some commits over 5.0.0.beta8). 424 | 425 | 426 | ## Version 1.3.0 ## 427 | 428 | Date: 2016-05-10 429 | 430 | - Update bundled rxjs to 5.0.0.beta7. 431 | 432 | 433 | ## Version 1.2.0 ## 434 | 435 | Date: 2016-04-13 436 | 437 | - Add `merge-map` alias for flat-map. 438 | - Fix wrong impl of merge implementation. 439 | - Simplify impl of `merge` and `concat`. 440 | - Strip `nil` values from `merge` and `concat` func args. 441 | - Add the ability to add predicate for catch function. 442 | - Update bundled rxjs. 443 | 444 | 445 | ## Version 1.1.1 ## 446 | 447 | Date: 2016-03-19 448 | 449 | - Fix wrong parameters order on `dedupe` and `dedupe'` functions. 450 | 451 | 452 | ## Version 1.1.0 ## 453 | 454 | Date: 2016-03-19 455 | 456 | - Rename `from-exception` to `throw`. 457 | - Add backward compatible alias for `from-exception`. 458 | - Update cljs compiler versiont o 1.8.34 459 | - Update promise dependency to 1.1.1 460 | 461 | 462 | ## Version 1.0.3 ## 463 | 464 | Date: 2016-03-16 465 | 466 | - Fix unexpected exception on subscribe function. 467 | 468 | 469 | ## Version 1.0.2 ## 470 | 471 | Date: 2016-03-16 472 | 473 | - Fix wrong call on buffer function impl. 474 | 475 | 476 | ## Version 1.0.1 ## 477 | 478 | Date: 2016-03-16 479 | 480 | - Fix minified rxjs bundle. 481 | 482 | 483 | ## Version 1.0.0 ## 484 | 485 | Date: 2016-03-16 486 | 487 | This is a major release due to big internal changes and some 488 | backward incompatibilities introduced in this version. 489 | 490 | - Switch to RxJS 5.x (5.0.0-beta2) 491 | - Remove `from-callback` observable constructor. 492 | - Remove `from-poll` observable constructor. 493 | - Remove `repeat` operator. 494 | - Remove `slice` operator. 495 | - Remove `to-observable` operator. 496 | - Remove `pausable` operator. 497 | - Remove `immediate` scheduler. 498 | - Add `mapcat` operator (similar to flatmap but maintains the order). 499 | - Add `fjoin` a rxjs forkjoin operator (similar to promise `.all` method). 500 | - Add `range` constructor. 501 | - Add `async` scheduler. 502 | - Change `zip` call signature. 503 | - Rename `delay'` operator to `delay-when`. 504 | - Rename `choice` operator to `race`. 505 | - Rename `sample'` operator to `sample-when`. 506 | - Make `of` consturctor accept more than 6 parameters. 507 | 508 | 509 | ## Version 0.6.1 ## 510 | 511 | Date: 2016-01-28 512 | 513 | - Add `take-until` function. 514 | 515 | 516 | ## Version 0.6.0 ## 517 | 518 | Date: 2016-01-22 519 | 520 | - The old `timeout` function becomes `timer`. 521 | - Add proper `timeout` function. 522 | - Add `delay` function. 523 | - Add `interval` function. 524 | - Add support for schedulers (`subscribe-on` and `observe-on`). 525 | - Make cats dependency optional (only if you require `beicon.monad` ns). 526 | (Is responsability of the user include the appropriate cats version). 527 | - Start using clojure 1.8 and clojurescript 1.7.228. 528 | 529 | ## Version 0.5.1 ## 530 | 531 | Date: 2016-01-08 532 | 533 | - Fix wrong path to the minified version of bundled rxjs. 534 | 535 | 536 | ## Version 0.5.0 ## 537 | 538 | Date: 2015-12-23 539 | 540 | - Add `sample` function. 541 | 542 | 543 | ## Version 0.4.0 ## 544 | 545 | Date: 2015-12-23 546 | 547 | - Add `debounce` function. 548 | - Allow multimethods on `on-value`, `on-error` and `on-end`. 549 | 550 | 551 | ## Version 0.3.0 ## 552 | 553 | Date: 2015-12-08 554 | 555 | - Fix wrong precondition on `repeat` function. 556 | - Add `scan` function. 557 | - Add `from-promise` function. 558 | - Add `retry` function. 559 | - Add `with-latest-from` function. 560 | - Add `catch` function. 561 | - Add `from-exception` function. 562 | - Add `empty` function. 563 | - Add `share` function. 564 | - Add `merge-all` function. 565 | - Add `of` function. 566 | - Add `just` function (once is now an alias for just). 567 | - Implement `never` in function of empty. 568 | - Improve `zip` function allowing passing user defined 569 | join functon. 570 | - Changed call signature of `to-atom` for consistency 571 | with the subscribe related functions. 572 | 573 | 574 | ## Version 0.2.0 ## 575 | 576 | Date: 2015-12-03 577 | 578 | - Fix incompatibilities with advanced compilations. 579 | - Add new and improved externs. 580 | - Update to rxjs 4.0.7 581 | 582 | 583 | ## Version 0.1.1 584 | 585 | Date: 2015-11-03 586 | 587 | - Update bundled rxjs to 4.0.6. 588 | 589 | 590 | ## Version 0.1.0 591 | 592 | Date: 2015-11-02 593 | 594 | - Initial release. 595 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributed Code # 2 | 3 | In order to keep *beicon* completely free and unencumbered by copyright, all new 4 | contributors to the *beicon* code base are asked to dedicate their contributions to 5 | the public domain. If you want to send a patch or enhancement for possible inclusion 6 | in the *beicon* source tree, please accompany the patch with the following 7 | statement: 8 | 9 | The author or authors of this code dedicate any and all copyright interest 10 | in this code to the public domain. We make this dedication for the benefit of 11 | the public at large and to the detriment of our heirs and successors. We 12 | intend this dedication to be an overt act of relinquishment in perpetuity of 13 | all present and future rights to this code under copyright law. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2024 Andrey Antukh 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beicon # 2 | 3 | Reactive Streams for ClojureScript. 4 | 5 | [![Travis Badge](https://img.shields.io/travis/funcool/beicon/master.svg)](https://travis-ci.org/funcool/beicon "Travis Badge") 6 | 7 | [![Clojars Project](http://clojars.org/funcool/beicon/latest-version.svg)](http://clojars.org/funcool/beicon) 8 | 9 |
10 | Beicon frito, 11 | beicon, 12 | beicon frito. 13 | Cojo el beicon 14 | y lo frío 15 | un poquito. 16 | Beicon frito, 17 | esto está muy rico. 18 | beicon fritooooooo 19 | -- Jake El Perro 20 |
21 | 22 | Beicon Frito - Jake el Perro 23 | 24 | 25 | ## Documentation ## 26 | 27 | http://funcool.github.io/beicon/latest/ 28 | -------------------------------------------------------------------------------- /assets/deps.cljs: -------------------------------------------------------------------------------- 1 | {:foreign-libs 2 | [{:file "rxjs/rxjs.main.bundle.js" 3 | :file-min "rxjs/rxjs.main.bundle.min.js" 4 | :provides ["beicon.impl.rxjs"]} 5 | {:file "rxjs/rxjs.operators.bundle.js" 6 | :file-min "rxjs/rxjs.operators.bundle.min.js" 7 | :provides ["beicon.impl.rxjs-operators"]}] 8 | :externs ["rxjs/externs.js"] 9 | } 10 | -------------------------------------------------------------------------------- /assets/rxjs/externs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @const 3 | */ 4 | var rxjsMain = function() {}; 5 | var rxjsOperators = function() {}; 6 | 7 | /** 8 | * @constructor 9 | */ 10 | rxjsMain.Observable = function() {}; 11 | 12 | 13 | /** 14 | * @this {null} 15 | * @return {rxjsMain.Observable} 16 | */ 17 | rxjsMain.Observable.create = function() {}; 18 | 19 | /** 20 | * @constructor 21 | * @extends {rxjsMain.Observable} 22 | */ 23 | rxjsMain.Subject = function() {}; 24 | 25 | /** 26 | * @return {rxjsMain.Observable} 27 | */ 28 | rxjsMain.Subject.prototype.asObservable = function() {}; 29 | 30 | /** 31 | * @constructor 32 | * @extends {rxjsMain.Subject} 33 | */ 34 | rxjsMain.BehaviorSubject = function() {}; 35 | rxjsMain.BehaviorSubject.prototype.getValue = function() {}; 36 | 37 | /** 38 | * @constructor 39 | */ 40 | rxjsMain.Subscriber = function() {}; 41 | 42 | /** 43 | * @constructor 44 | */ 45 | rxjsMain.Scheduler = function() {}; 46 | 47 | /** 48 | * @constructor 49 | */ 50 | rxjsMain.Subscription = function() {}; 51 | 52 | 53 | /** 54 | * @this {null} 55 | * @return {rxjsMain.Subscription} 56 | */ 57 | rxjsMain.Subscription.create = function() {}; 58 | 59 | 60 | /** 61 | * @const 62 | */ 63 | rxjsMain.asapScheduler; 64 | 65 | /** 66 | * @const 67 | */ 68 | rxjsMain.asyncScheduler; 69 | 70 | /** 71 | * @const 72 | */ 73 | rxjsMain.queueScheduler; 74 | 75 | /** 76 | * @const 77 | */ 78 | rxjsMain.animationFrameScheduler; 79 | 80 | /** 81 | * @this {null} 82 | * @return {rxjsMain.Observable} 83 | */ 84 | rxjsMain.Observable.from = function() {}; 85 | 86 | /** 87 | * @this {null} 88 | * @return {rxjsMain.Observable} 89 | */ 90 | rxjsMain.Observable.fromArray = function() {}; 91 | 92 | /** 93 | * @this {null} 94 | * @return {rxjsMain.Observable} 95 | */ 96 | rxjsMain.of = function() {}; 97 | 98 | /** 99 | * @this {null} 100 | * @return {rxjsMain.Observable} 101 | */ 102 | rxjsMain.fromPromise = function() {}; 103 | 104 | /** 105 | * @this {null} 106 | * @return {rxjsMain.Observable} 107 | */ 108 | rxjsMain.fromEvent = function() {}; 109 | 110 | /** 111 | * @this {null} 112 | * @return {rxjsMain.Observable} 113 | */ 114 | rxjsMain.throwError = function() {}; 115 | 116 | /** 117 | * @this {null} 118 | * @return {rxjsMain.Observable} 119 | */ 120 | rxjsMain.Observable.timer = function() {}; 121 | 122 | /** 123 | * @this {null} 124 | * @return {rxjsMain.Observable} 125 | */ 126 | rxjsMain.interval = function() {}; 127 | 128 | /** 129 | * @this {null} 130 | * @return {rxjsMain.Observable} 131 | */ 132 | rxjsMain.range = function() {}; 133 | 134 | /** 135 | * @this {null} 136 | * @return {rxjsMain.Observable} 137 | */ 138 | rxjsMain.forkJoin = function() {}; 139 | 140 | /** 141 | * @this {rxjsMain.Observable} 142 | * @return {rxjsMain.Subscription} 143 | */ 144 | rxjsMain.Observable.prototype.subscribe = function() {}; 145 | 146 | /** 147 | * @this {rxjsMain.Observable} 148 | * @return {rxjsMain.Observable} 149 | */ 150 | rxjsMain.Observable.prototype.pipe = function() {}; 151 | 152 | /** 153 | * @this {rxjsMain.Observable} 154 | * @return {rxjsMain.Observable} 155 | */ 156 | rxjsMain.Observable.prototype.toPromise = function() {}; 157 | 158 | rxjsMain.combineLatest = function() {}; 159 | 160 | rxjsOperators.race = function() {}; 161 | rxjsOperators.zip = function() {}; 162 | rxjsOperators.zipAll = function() {}; 163 | rxjsOperators.publish = function() {}; 164 | rxjsOperators.concat = function() {}; 165 | rxjsOperators.concatAll = function() {}; 166 | rxjsOperators.concatMap = function() {}; 167 | rxjsOperators.concatMapTo = function() {}; 168 | rxjsOperators.scan = function() {}; 169 | rxjsOperators.mergeScan = function() {}; 170 | rxjsOperators.merge = function() {}; 171 | rxjsOperators.mergeAll = function() {}; 172 | rxjsOperators.mergeMap = function() {}; 173 | rxjsOperators.mergeMapTo = function() {}; 174 | rxjsOperators.first = function() {}; 175 | rxjsOperators.last = function() {}; 176 | rxjsOperators.filter = function() {}; 177 | rxjsOperators.map = function() {}; 178 | rxjsOperators.mapTo = function() {}; 179 | rxjsOperators.flatMap = function() {}; 180 | rxjsOperators.skip = function() {}; 181 | rxjsOperators.skipWhile = function() {}; 182 | rxjsOperators.skipUntil = function() {}; 183 | rxjsOperators.take = function() {}; 184 | rxjsOperators.takeWhile = function() {}; 185 | rxjsOperators.takeUntil = function() {}; 186 | rxjsOperators.reduce = function() {}; 187 | rxjsOperators.tap = function() {}; 188 | rxjsOperators.throttle = function() {}; 189 | rxjsOperators.throttleTime = function() {}; 190 | rxjsOperators.debounce = function() {}; 191 | rxjsOperators.debounceTime = function() {}; 192 | rxjsOperators.sample = function() {}; 193 | rxjsOperators.sampleTime = function() {}; 194 | rxjsOperators.delay = function() {}; 195 | rxjsOperators.timeout = function() {}; 196 | rxjsOperators.timeoutWith = function() {}; 197 | rxjsOperators.distinctUntilChanged = function() {}; 198 | rxjsOperators.bufferCount = function() {}; 199 | rxjsOperators.buffer = function() {}; 200 | rxjsOperators.bufferTime = function() {}; 201 | rxjsOperators.bufferWhen = function() {}; 202 | rxjsOperators.buffer = function() {}; 203 | rxjsOperators.retry = function() {}; 204 | rxjsOperators.retryWhen = function() {}; 205 | rxjsOperators.withLatestFrom = function() {}; 206 | rxjsOperators.combineLatest = function() {}; 207 | rxjsOperators.combineLatestWith = function() {}; 208 | rxjsOperators.catchError = function() {}; 209 | rxjsOperators.partition = function() {}; 210 | rxjsOperators.repeat = function() {}; 211 | rxjsOperators.share = function() {}; 212 | rxjsOperators.ignoreElements = function() {}; 213 | rxjsOperators.subscribeOn = function() {}; 214 | rxjsOperators.observeOn = function() {}; 215 | rxjsOperators.defaultIfEmpty = function() {}; 216 | -------------------------------------------------------------------------------- /assets/rxjs/rxjs.main.bundle.min.js: -------------------------------------------------------------------------------- 1 | var rxjsMain=function(){"use strict"; 2 | /*! ***************************************************************************** 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | ***************************************************************************** */var t=function(n,e){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,n){t.__proto__=n}||function(t,n){for(var e in n)Object.prototype.hasOwnProperty.call(n,e)&&(t[e]=n[e])})(n,e)};function n(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function r(){this.constructor=n}t(n,e),n.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}function e(t,n,e,r){return new(e||(e=Promise))((function(o,i){function u(t){try{s(r.next(t))}catch(t){i(t)}}function c(t){try{s(r.throw(t))}catch(t){i(t)}}function s(t){var n;t.done?o(t.value):(n=t.value,n instanceof e?n:new e((function(t){t(n)}))).then(u,c)}s((r=r.apply(t,n||[])).next())}))}function r(t,n){var e,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(e)throw new TypeError("Generator is already executing.");for(;u;)try{if(e=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return u.label++,{value:i[1],done:!1};case 5:u.label++,r=i[1],i=[0];continue;case 7:i=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){u=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=t.length&&(t=void 0),{value:t&&t[r++],done:!t}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")}function i(t,n){var e="function"==typeof Symbol&&t[Symbol.iterator];if(!e)return t;var r,o,i=e.call(t),u=[];try{for(;(void 0===n||n-- >0)&&!(r=i.next()).done;)u.push(r.value)}catch(t){o={error:t}}finally{try{r&&!r.done&&(e=i.return)&&e.call(i)}finally{if(o)throw o.error}}return u}function u(t,n){for(var e=0,r=n.length,o=t.length;e1||s(t,n)}))})}function s(t,n){try{(e=o[t](n)).value instanceof c?Promise.resolve(e.value.v).then(a,l):f(i[0][2],e)}catch(t){f(i[0][3],t)}var e}function a(t){s("next",t)}function l(t){s("throw",t)}function f(t,n){t(n),i.shift(),i.length&&s(i[0][0],i[0][1])}}function a(t){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n,e=t[Symbol.asyncIterator];return e?e.call(t):(t=o(t),n={},r("next"),r("throw"),r("return"),n[Symbol.asyncIterator]=function(){return this},n);function r(e){n[e]=t[e]&&function(n){return new Promise((function(r,o){(function(t,n,e,r){Promise.resolve(r).then((function(n){t({value:n,done:e})}),n)})(r,o,(n=t[e](n)).done,n.value)}))}}}function l(t){return"function"==typeof t}function f(t){var n=t((function(t){Error.call(t),t.stack=(new Error).stack}));return n.prototype=Object.create(Error.prototype),n.prototype.constructor=n,n}var h=f((function(t){return function(n){t(this),this.message=n?n.length+" errors occurred during unsubscription:\n"+n.map((function(t,n){return n+1+") "+t.toString()})).join("\n "):"",this.name="UnsubscriptionError",this.errors=n}}));function d(t,n){if(t){var e=t.indexOf(n);0<=e&&t.splice(e,1)}}var p=function(){function t(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._teardowns=null}return t.prototype.unsubscribe=function(){var t,n,e,r,c;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=o(s),f=a.next();!f.done;f=a.next()){f.value.remove(this)}}catch(n){t={error:n}}finally{try{f&&!f.done&&(n=a.return)&&n.call(a)}finally{if(t)throw t.error}}else s.remove(this);var d=this.initialTeardown;if(l(d))try{d()}catch(t){c=t instanceof h?t.errors:[t]}var p=this._teardowns;if(p){this._teardowns=null;try{for(var v=o(p),y=v.next();!y.done;y=v.next()){var w=y.value;try{b(w)}catch(t){c=null!=c?c:[],t instanceof h?c=u(u([],i(c)),i(t.errors)):c.push(t)}}}catch(t){e={error:t}}finally{try{y&&!y.done&&(r=v.return)&&r.call(v)}finally{if(e)throw e.error}}}if(c)throw new h(c)}},t.prototype.add=function(n){var e;if(n&&n!==this)if(this.closed)b(n);else{if(n instanceof t){if(n.closed||n._hasParent(this))return;n._addParent(this)}(this._teardowns=null!==(e=this._teardowns)&&void 0!==e?e:[]).push(n)}},t.prototype._hasParent=function(t){var n=this._parentage;return n===t||Array.isArray(n)&&n.includes(t)},t.prototype._addParent=function(t){var n=this._parentage;this._parentage=Array.isArray(n)?(n.push(t),n):n?[n,t]:t},t.prototype._removeParent=function(t){var n=this._parentage;n===t?this._parentage=null:Array.isArray(n)&&d(n,t)},t.prototype.remove=function(n){var e=this._teardowns;e&&d(e,n),n instanceof t&&n._removeParent(this)},t.EMPTY=function(){var n=new t;return n.closed=!0,n}(),t}(),v=p.EMPTY;function y(t){return t instanceof p||t&&"closed"in t&&l(t.remove)&&l(t.add)&&l(t.unsubscribe)}function b(t){l(t)?t():t.unsubscribe()}var w={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},m=function(){for(var t=[],n=0;n0},enumerable:!1,configurable:!0}),e.prototype._trySubscribe=function(n){return this._throwIfClosed(),t.prototype._trySubscribe.call(this,n)},e.prototype._subscribe=function(t){return this._throwIfClosed(),this._checkFinalizedStatuses(t),this._innerSubscribe(t)},e.prototype._innerSubscribe=function(t){var n=this,e=n.hasError,r=n.isStopped,o=n.observers;return e||r?v:(o.push(t),new p((function(){return d(o,t)})))},e.prototype._checkFinalizedStatuses=function(t){var n=this,e=n.hasError,r=n.thrownError,o=n.isStopped;e?t.error(r):o&&t.complete()},e.prototype.asObservable=function(){var t=new N;return t.source=this,t},e.create=function(t,n){return new J(t,n)},e}(N),J=function(t){function e(n,e){var r=t.call(this)||this;return r.destination=n,r.source=e,r}return n(e,t),e.prototype.next=function(t){var n,e;null===(e=null===(n=this.destination)||void 0===n?void 0:n.next)||void 0===e||e.call(n,t)},e.prototype.error=function(t){var n,e;null===(e=null===(n=this.destination)||void 0===n?void 0:n.error)||void 0===e||e.call(n,t)},e.prototype.complete=function(){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.complete)||void 0===n||n.call(t)},e.prototype._subscribe=function(t){var n,e;return null!==(e=null===(n=this.source)||void 0===n?void 0:n.subscribe(t))&&void 0!==e?e:v},e}(G),K=function(t){function e(n){var e=t.call(this)||this;return e._value=n,e}return n(e,t),Object.defineProperty(e.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),e.prototype._subscribe=function(n){var e=t.prototype._subscribe.call(this,n);return!e.closed&&n.next(this._value),e},e.prototype.getValue=function(){var t=this,n=t.hasError,e=t.thrownError,r=t._value;if(n)throw e;return this._throwIfClosed(),r},e.prototype.next=function(n){t.prototype.next.call(this,this._value=n)},e}(G),X={now:function(){return(X.delegate||Date).now()},delegate:void 0},Q=function(t){function e(n,e,r){void 0===n&&(n=1/0),void 0===e&&(e=1/0),void 0===r&&(r=X);var o=t.call(this)||this;return o._bufferSize=n,o._windowTime=e,o._timestampProvider=r,o._buffer=[],o._infiniteTimeWindow=!0,o._infiniteTimeWindow=e===1/0,o._bufferSize=Math.max(1,n),o._windowTime=Math.max(1,e),o}return n(e,t),e.prototype.next=function(n){var e=this,r=e.isStopped,o=e._buffer,i=e._infiniteTimeWindow,u=e._timestampProvider,c=e._windowTime;r||(o.push(n),!i&&o.push(u.now()+c)),this._trimBuffer(),t.prototype.next.call(this,n)},e.prototype._subscribe=function(t){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(t),e=this._infiniteTimeWindow,r=this._buffer.slice(),o=0;o0?t.prototype.requestAsyncId.call(this,n,e,r):(n.actions.push(this),n._scheduled||(n._scheduled=st(n.flush.bind(n,void 0))))},e.prototype.recycleAsyncId=function(n,e,r){if(void 0===r&&(r=0),null!=r&&r>0||null==r&&this.delay>0)return t.prototype.recycleAsyncId.call(this,n,e,r);0===n.actions.length&&(at(e),n._scheduled=void 0)},e}(et),ft=function(){function t(n,e){void 0===e&&(e=t.now),this.schedulerActionCtor=n,this.now=e}return t.prototype.schedule=function(t,n,e){return void 0===n&&(n=0),new this.schedulerActionCtor(this,t).schedule(e,n)},t.now=X.now,t}(),ht=function(t){function e(n,e){void 0===e&&(e=ft.now);var r=t.call(this,n,e)||this;return r.actions=[],r._active=!1,r._scheduled=void 0,r}return n(e,t),e.prototype.flush=function(t){var n=this.actions;if(this._active)n.push(t);else{var e;this._active=!0;do{if(e=t.execute(t.state,t.delay))break}while(t=n.shift());if(this._active=!1,e){for(;t=n.shift();)t.unsubscribe();throw e}}},e}(ft),dt=new(function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.flush=function(t){this._active=!0,this._scheduled=void 0;var n,e=this.actions,r=-1;t=t||e.shift();var o=e.length;do{if(n=t.execute(t.state,t.delay))break}while(++r0?t.prototype.schedule.call(this,n,e):(this.delay=e,this.state=n,this.scheduler.flush(this),this)},e.prototype.execute=function(n,e){return e>0||this.closed?t.prototype.execute.call(this,n,e):this._execute(n,e)},e.prototype.requestAsyncId=function(n,e,r){return void 0===r&&(r=0),null!=r&&r>0||null==r&&this.delay>0?t.prototype.requestAsyncId.call(this,n,e,r):n.flush(this)},e}(et),wt=new(function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e}(ht))(bt),mt=wt,_t=function(t){function e(n,e){var r=t.call(this,n,e)||this;return r.scheduler=n,r.work=e,r}return n(e,t),e.prototype.requestAsyncId=function(n,e,r){return void 0===r&&(r=0),null!==r&&r>0?t.prototype.requestAsyncId.call(this,n,e,r):(n.actions.push(this),n._scheduled||(n._scheduled=M((function(){return n.flush(void 0)}))))},e.prototype.recycleAsyncId=function(n,e,r){if(void 0===r&&(r=0),null!=r&&r>0||null==r&&this.delay>0)return t.prototype.recycleAsyncId.call(this,n,e,r);0===n.actions.length&&(U(e),n._scheduled=void 0)},e}(et),gt=new(function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e.prototype.flush=function(t){this._active=!0,this._scheduled=void 0;var n,e=this.actions,r=-1;t=t||e.shift();var o=e.length;do{if(n=t.execute(t.state,t.delay))break}while(++rn.index?1:-1:t.delay>n.delay?1:-1},e}(et),At=new N((function(t){return t.complete()}));function It(t,n){return new N((function(e){var r=0;return n.schedule((function(){r===t.length?e.complete():(e.next(t[r++]),e.closed||this.schedule())}))}))}var kt=function(t){return t&&"number"==typeof t.length&&"function"!=typeof t};function jt(t){return l(null==t?void 0:t.then)}var Pt,Ot="function"==typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator";function Tt(t,n){return new N((function(e){var r;return e.add(n.schedule((function(){r=t[Ot](),function(t,n,e,r){void 0===r&&(r=0);var o=n.schedule((function(){try{e.call(this)}catch(n){t.error(n)}}),r);t.add(o)}(e,n,(function(){var t=r.next(),n=t.value;t.done?e.complete():(e.next(n),this.schedule())}))}))),function(){return l(null==r?void 0:r.return)&&r.return()}}))}function Ct(t,n){if(!t)throw new Error("Iterable cannot be null");return new N((function(e){var r=new p;return r.add(n.schedule((function(){var o=t[Symbol.asyncIterator]();r.add(n.schedule((function(){var t=this;o.next().then((function(n){n.done?e.complete():(e.next(n.value),t.schedule())}))})))}))),r}))}function Nt(t){return l(t[O])}function Ft(t){return l(null==t?void 0:t[Ot])}function qt(t){return Symbol.asyncIterator&&l(null==t?void 0:t[Symbol.asyncIterator])}function Vt(t){return new TypeError("You provided "+(null!==t&&"object"==typeof t?"an invalid object":"'"+t+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zt(t){return s(this,arguments,(function(){var n,e,o;return r(this,(function(r){switch(r.label){case 0:n=t.getReader(),r.label=1;case 1:r.trys.push([1,,9,10]),r.label=2;case 2:return[4,c(n.read())];case 3:return e=r.sent(),o=e.value,e.done?[4,c(void 0)]:[3,5];case 4:return[2,r.sent()];case 5:return[4,c(o)];case 6:return[4,r.sent()];case 7:return r.sent(),[3,2];case 8:return[3,10];case 9:return n.releaseLock(),[7];case 10:return[2]}}))}))}function Lt(t){return l(null==t?void 0:t.getReader)}function Rt(t,n){if(null!=t){if(Nt(t))return function(t,n){return new N((function(e){var r=new p;return r.add(n.schedule((function(){var o=t[O]();r.add(o.subscribe({next:function(t){r.add(n.schedule((function(){return e.next(t)})))},error:function(t){r.add(n.schedule((function(){return e.error(t)})))},complete:function(){r.add(n.schedule((function(){return e.complete()})))}}))}))),r}))}(t,n);if(kt(t))return It(t,n);if(jt(t))return function(t,n){return new N((function(e){return n.schedule((function(){return t.then((function(t){e.add(n.schedule((function(){e.next(t),e.add(n.schedule((function(){return e.complete()})))})))}),(function(t){e.add(n.schedule((function(){return e.error(t)})))}))}))}))}(t,n);if(qt(t))return Ct(t,n);if(Ft(t))return Tt(t,n);if(Lt(t))return function(t,n){return Ct(zt(t),n)}(t,n)}throw Vt(t)}function Dt(t,n){return n?Rt(t,n):Mt(t)}function Mt(t){if(t instanceof N)return t;if(null!=t){if(Nt(t))return r=t,new N((function(t){var n=r[O]();if(l(n.subscribe))return n.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")}));if(kt(t))return Ut(t);if(jt(t))return e=t,new N((function(t){e.then((function(n){t.closed||(t.next(n),t.complete())}),(function(n){return t.error(n)})).then(null,_)}));if(qt(t))return Yt(t);if(Ft(t))return n=t,new N((function(t){var e,r;try{for(var i=o(n),u=i.next();!u.done;u=i.next()){var c=u.value;if(t.next(c),t.closed)return}}catch(t){e={error:t}}finally{try{u&&!u.done&&(r=i.return)&&r.call(i)}finally{if(e)throw e.error}}t.complete()}));if(Lt(t))return Yt(zt(t))}var n,e,r;throw Vt(t)}function Ut(t){return new N((function(n){for(var e=0;e0){var t=void 0;try{t=Mt(r.shift())}catch(t){return void o()}var e=new z(n,void 0,g,g);n.add(t.subscribe(e)),e.add(o)}else n.complete()};o()}))}function Nn(t,n){return function(e,r){return!t.call(n,e,r)}}function Fn(t,n){return V((function(e,r){var o=0;e.subscribe(new z(r,(function(e){return t.call(n,e,o++)&&r.next(e)})))}))}function qn(t){return function(n){for(var e=[],r=function(r){e.push(Mt(t[r]).subscribe(new z(n,(function(t){if(e){for(var o=0;o0&&u[u.length-1])||6!==o[0]&&2!==o[0])){i=0;continue}if(3===o[0]&&(!u||o[1]>u[0]&&o[1]=n.length&&(n=void 0),{value:n&&n[e++],done:!n}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function s(n,t){var r="function"==typeof Symbol&&n[Symbol.iterator];if(!r)return n;var e,u,o=r.call(n),i=[];try{for(;(void 0===t||t-- >0)&&!(e=o.next()).done;)i.push(e.value)}catch(n){u={error:n}}finally{try{e&&!e.done&&(r=o.return)&&r.call(o)}finally{if(u)throw u.error}}return i}function f(n,t){for(var r=0,e=t.length,u=n.length;r1||c(n,t)}))})}function c(n,t){try{(r=u[n](t)).value instanceof l?Promise.resolve(r.value.v).then(s,f):a(o[0][2],r)}catch(n){a(o[0][3],n)}var r}function s(n){c("next",n)}function f(n){c("throw",n)}function a(n,t){n(t),o.shift(),o.length&&c(o[0][0],o[0][1])}}function h(n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,r=n[Symbol.asyncIterator];return r?r.call(n):(n=c(n),t={},e("next"),e("throw"),e("return"),t[Symbol.asyncIterator]=function(){return this},t);function e(r){t[r]=n[r]&&function(t){return new Promise((function(e,u){(function(n,t,r,e){Promise.resolve(e).then((function(t){n({value:t,done:r})}),t)})(e,u,(t=n[r](t)).done,t.value)}))}}}var v=function(n){return n&&"number"==typeof n.length&&"function"!=typeof n};function p(t){return n(null==t?void 0:t.then)}var d="function"==typeof Symbol&&Symbol.observable||"@@observable";function b(n){var t=n((function(n){Error.call(n),n.stack=(new Error).stack}));return t.prototype=Object.create(Error.prototype),t.prototype.constructor=t,t}var y=b((function(n){return function(t){n(this),this.message=t?t.length+" errors occurred during unsubscription:\n"+t.map((function(n,t){return t+1+") "+n.toString()})).join("\n "):"",this.name="UnsubscriptionError",this.errors=t}}));function w(n,t){if(n){var r=n.indexOf(t);0<=r&&n.splice(r,1)}}var m=function(){function t(n){this.initialTeardown=n,this.closed=!1,this._parentage=null,this._teardowns=null}var r;return t.prototype.unsubscribe=function(){var t,r,e,u,o;if(!this.closed){this.closed=!0;var i=this._parentage;if(i)if(this._parentage=null,Array.isArray(i))try{for(var l=c(i),a=l.next();!a.done;a=l.next()){a.value.remove(this)}}catch(n){t={error:n}}finally{try{a&&!a.done&&(r=l.return)&&r.call(l)}finally{if(t)throw t.error}}else i.remove(this);var h=this.initialTeardown;if(n(h))try{h()}catch(n){o=n instanceof y?n.errors:[n]}var v=this._teardowns;if(v){this._teardowns=null;try{for(var p=c(v),d=p.next();!d.done;d=p.next()){var b=d.value;try{_(b)}catch(n){o=null!=o?o:[],n instanceof y?o=f(f([],s(o)),s(n.errors)):o.push(n)}}}catch(n){e={error:n}}finally{try{d&&!d.done&&(u=p.return)&&u.call(p)}finally{if(e)throw e.error}}}if(o)throw new y(o)}},t.prototype.add=function(n){var r;if(n&&n!==this)if(this.closed)_(n);else{if(n instanceof t){if(n.closed||n._hasParent(this))return;n._addParent(this)}(this._teardowns=null!==(r=this._teardowns)&&void 0!==r?r:[]).push(n)}},t.prototype._hasParent=function(n){var t=this._parentage;return t===n||Array.isArray(t)&&t.includes(n)},t.prototype._addParent=function(n){var t=this._parentage;this._parentage=Array.isArray(t)?(t.push(n),t):t?[t,n]:n},t.prototype._removeParent=function(n){var t=this._parentage;t===n?this._parentage=null:Array.isArray(t)&&w(t,n)},t.prototype.remove=function(n){var r=this._teardowns;r&&w(r,n),n instanceof t&&n._removeParent(this)},t.EMPTY=((r=new t).closed=!0,r),t}(),x=m.EMPTY;function g(t){return t instanceof m||t&&"closed"in t&&n(t.remove)&&n(t.add)&&n(t.unsubscribe)}function _(t){n(t)?t():t.unsubscribe()}var S={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},E=function(){for(var n=[],t=0;t=2,!1,!0))}var Pn=function(n,t){return n.push(t),n};function Nn(){return r((function(n,t){jn(Pn,[])(n).subscribe(t)}))}function Wn(n,t){return N(Nn(),Cn((function(t){return n(t)})),t?Sn(t):P)}function zn(n){return Wn(Tn,n)}var Mn=zn,Rn=Array.isArray;function Fn(n){return 1===n.length&&Rn(n[0])?n[0]:n}function Ln(){for(var n=[],t=0;t0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(t){return this._throwIfClosed(),n.prototype._trySubscribe.call(this,t)},t.prototype._subscribe=function(n){return this._throwIfClosed(),this._checkFinalizedStatuses(n),this._innerSubscribe(n)},t.prototype._innerSubscribe=function(n){var t=this,r=t.hasError,e=t.isStopped,u=t.observers;return r||e?x:(u.push(n),new m((function(){return w(u,n)})))},t.prototype._checkFinalizedStatuses=function(n){var t=this,r=t.hasError,e=t.thrownError,u=t.isStopped;r?n.error(e):u&&n.complete()},t.prototype.asObservable=function(){var n=new z;return n.source=this,n},t.create=function(n,t){return new Hn(n,t)},t}(z),Hn=function(n){function t(t,r){var e=n.call(this)||this;return e.destination=t,e.source=r,e}return u(t,n),t.prototype.next=function(n){var t,r;null===(r=null===(t=this.destination)||void 0===t?void 0:t.next)||void 0===r||r.call(t,n)},t.prototype.error=function(n){var t,r;null===(r=null===(t=this.destination)||void 0===t?void 0:t.error)||void 0===r||r.call(t,n)},t.prototype.complete=function(){var n,t;null===(t=null===(n=this.destination)||void 0===n?void 0:n.complete)||void 0===t||t.call(n)},t.prototype._subscribe=function(n){var t,r;return null!==(r=null===(t=this.source)||void 0===t?void 0:t.subscribe(n))&&void 0!==r?r:x},t}(Yn);var Zn={connector:function(){return new Yn}};function Kn(n,t){void 0===t&&(t=Zn);var e=t.connector;return r((function(t,r){var u,o=e();Z(n((u=o,new z((function(n){return u.subscribe(n)}))))).subscribe(r),r.add(t.subscribe(o))}))}function Xn(n){return r((function(t,r){var e=!1;t.subscribe(new Q(r,(function(n){e=!0,r.next(n)}),(function(){e||r.next(n),r.complete()})))}))}function Jn(){for(var n=[],t=0;t=2,!0))}function Pt(n){void 0===n&&(n={});var t=n.connector,e=void 0===t?function(){return new Yn}:t,u=n.resetOnError,o=void 0===u||u,i=n.resetOnComplete,c=void 0===i||i,s=n.resetOnRefCountZero,f=void 0===s||s;return function(n){var t=null,u=null,i=null,s=0,l=!1,a=!1,h=function(){null==u||u.unsubscribe(),u=null},v=function(){h(),t=i=null,l=a=!1},p=function(){var n=t;v(),null==n||n.unsubscribe()};return r((function(n,r){s++,a||l||h();var d=i=null!=i?i:e();r.add((function(){0!==--s||a||l||(u=Nt(p,f))})),d.subscribe(r),t||(t=new k({next:function(n){return d.next(n)},error:function(n){a=!0,h(),u=Nt(v,o,n),d.error(n)},complete:function(){l=!0,h(),u=Nt(v,c),d.complete()}}),Z(n).subscribe(t))}))(n)}}function Nt(n,t){for(var r=[],e=2;e0&&l(o)}),void 0,void 0,(function(){(null==e?void 0:e.closed)||null==e||e.unsubscribe(),i=null}))),l(null!=u?"number"==typeof u?u:+u-f.now():o)}))}function qt(n){throw new Ut(n)}function Bt(){for(var n=[],t=0;t=0?r.add(i.schedule((function(){l(),!this.closed&&r.add(this.schedule(null,s))}),s)):u=!0,l();var a=new Q(r,(function(n){var t,r,u=e.slice();try{for(var i=c(u),s=i.next();!s.done;s=i.next()){var l=s.value,a=l.buffer;a.push(n),f<=a.length&&o(l)}}catch(n){t={error:n}}finally{try{s&&!s.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}}),(function(){for(;null==e?void 0:e.length;)r.next(e.shift().buffer);null==a||a.unsubscribe(),r.complete(),r.unsubscribe()}),void 0,(function(){return e=null}));t.subscribe(a)}))},bufferToggle:function(n,t){return r((function(r,e){var u=[];K(n).subscribe(new Q(e,(function(n){var r=[];u.push(r);var o=new m;o.add(K(t(n)).subscribe(new Q(e,(function(){w(u,r),e.next(r),o.unsubscribe()}),A)))}),A)),r.subscribe(new Q(e,(function(n){var t,r;try{for(var e=c(u),o=e.next();!o.done;o=e.next()){o.value.push(n)}}catch(n){t={error:n}}finally{try{o&&!o.done&&(r=e.return)&&r.call(e)}finally{if(t)throw t.error}}}),(function(){for(;u.length>0;)e.next(u.shift());e.complete()})))}))},bufferWhen:function(n){return r((function(t,r){var e=null,u=null,o=function(){null==u||u.unsubscribe();var t=e;e=[],t&&r.next(t),K(n()).subscribe(u=new Q(r,o,A))};o(),t.subscribe(new Q(r,(function(n){return null==e?void 0:e.push(n)}),(function(){e&&r.next(e),r.complete()}),void 0,(function(){return e=u=null})))}))},catchError:function n(t){return r((function(r,e){var u,o=null,i=!1;o=r.subscribe(new Q(e,void 0,void 0,(function(c){u=K(t(c,n(t)(r))),o?(o.unsubscribe(),o=null,u.subscribe(e)):i=!0}))),i&&(o.unsubscribe(),o=null,u.subscribe(e))}))},combineAll:Mn,combineLatestAll:zn,combineLatest:Ln,combineLatestWith:function(){for(var n=[],t=0;t=2;return function(e){return e.pipe(lt((function(t,r){return r===n})),nt(1),r?Xn(t):ht((function(){return new ft})))}},endWith:function(){for(var n=[],t=0;t=2;return function(e){return e.pipe(n?lt((function(t,r){return n(t,r,e)})):P,nt(1),r?Xn(t):ht((function(){return new at})))}},groupBy:function(n,t,e,u){return r((function(r,o){var i;t&&"function"!=typeof t?(e=t.duration,i=t.element,u=t.connector):i=t;var c=new Map,s=function(n){c.forEach(n),n(o)},f=function(n){return s((function(t){return t.error(n)}))},l=new yt(o,(function(t){try{var r=n(t),s=c.get(r);if(!s){c.set(r,s=u?u():new Yn);var a=(v=r,p=s,(d=new z((function(n){l.activeGroups++;var t=p.subscribe(n);return function(){t.unsubscribe(),0==--l.activeGroups&&l.teardownAttempted&&l.unsubscribe()}}))).key=v,d);if(o.next(a),e){var h=new Q(s,(function(){s.complete(),null==h||h.unsubscribe()}),void 0,void 0,(function(){return c.delete(r)}));l.add(K(e(a)).subscribe(h))}}s.next(i?i(t):t)}catch(n){f(n)}var v,p,d}),(function(){return s((function(n){return n.complete()}))}),f,(function(){return c.clear()}));r.subscribe(l)}))},ignoreElements:tt,isEmpty:function(){return r((function(n,t){n.subscribe(new Q(t,(function(){t.next(!1),t.complete()}),(function(){t.next(!0),t.complete()})))}))},last:function(n,t){var r=arguments.length>=2;return function(e){return e.pipe(n?lt((function(t,r){return n(t,r,e)})):P,wt(1),r?Xn(t):ht((function(){return new at})))}},map:gn,mapTo:rt,materialize:function(){return r((function(n,t){n.subscribe(new Q(t,(function(n){t.next(ot.createNext(n))}),(function(){t.next(ot.createComplete()),t.complete()}),(function(n){t.next(ot.createError(n)),t.complete()})))}))},max:function(t){return jn(n(t)?function(n,r){return t(n,r)>0?n:r}:function(n,t){return n>t?n:t})},merge:mt,mergeAll:Un,flatMap:xt,mergeMap:Cn,mergeMapTo:function(t,r,e){return void 0===e&&(e=1/0),n(r)?Cn((function(){return t}),r,e):("number"==typeof r&&(e=r),Cn((function(){return t}),e))},mergeScan:function(n,t,e){return void 0===e&&(e=1/0),r((function(r,u){var o=t;return kn(r,u,(function(t,r){return n(o,t,r)}),e,(function(n){o=n}),!1,void 0,(function(){return o=null}))}))},mergeWith:function(){for(var n=[],t=0;t0){var n=void 0;try{n=K(r.shift())}catch(n){return void u()}var e=new Q(t,void 0,A,A);t.add(n.subscribe(e)),e.add(u)}else t.complete()};u()}))},pairwise:function(){return r((function(n,t){var r,e=!1;n.subscribe(new Q(t,(function(n){var u=r;r=n,e&&t.next([u,n]),e=!0})))}))},partition:function(n,t){return function(r){return[lt(n,t)(r),lt(Et(n,t))(r)]}},pluck:function(){for(var n=[],t=0;t0?t:n;return r((function(t,r){var u=[new Yn],o=0;r.next(u[0].asObservable()),t.subscribe(new Q(r,(function(t){var i,s;try{for(var f=c(u),l=f.next();!l.done;l=f.next()){l.value.next(t)}}catch(n){i={error:n}}finally{try{l&&!l.done&&(s=f.return)&&s.call(f)}finally{if(i)throw i.error}}var a=o-n+1;if(a>=0&&a%e==0&&u.shift().complete(),++o%e==0){var h=new Yn;u.push(h),r.next(h.asObservable())}}),(function(){for(;u.length>0;)u.shift().complete();r.complete()}),(function(n){for(;u.length>0;)u.shift().error(n);r.error(n)}),(function(){null,u=null})))}))},windowTime:function(n){for(var t,e,u=[],o=1;o=0?r.add(i.schedule((function(){f(),!this.closed&&r.add(this.schedule(null,c))}),c)):u=!0,f();var l=function(n){return e.slice().forEach(n)},a=function(n){l((function(t){var r=t.window;return n(r)})),n(r),r.unsubscribe()};return t.subscribe(new Q(r,(function(n){l((function(t){t.window.next(n),s<=++t.seen&&o(t)}))}),(function(){return a((function(n){return n.complete()}))}),(function(n){return a((function(t){return t.error(n)}))}))),function(){e=null}}))},windowToggle:function(n,t){return r((function(r,e){var u=[],o=function(n){for(;0 v: 1 37 | ;; ==> v: 2 38 | ;; ==> v: 3 39 | ``` 40 | 41 | ### From range 42 | 43 | An other way to create an observable stream is using the `range` constructor, 44 | which is pretty analogous to the Clojures one: 45 | 46 | ```clojure 47 | (def stream (rx/range 3)) 48 | 49 | (rx/sub! stream #(println "v:" %)) 50 | ;; ==> v: 0 51 | ;; ==> v: 1 52 | ;; ==> v: 2 53 | ``` 54 | 55 | ### From Atom 56 | 57 | Atoms in Clojure are watchable, so you can listen for their 58 | changes. This method converts that changes into an infinite observable 59 | sequence of atom changes: 60 | 61 | ```clojure 62 | (def a (atom 1)) 63 | 64 | (def stream (rx/from-atom a)) 65 | 66 | (rx/sub! stream #(println "v:" %)) 67 | (swap! a inc) 68 | ;; ==> v: 2 69 | ``` 70 | 71 | 72 | ### From values. 73 | 74 | There is a way to create an observable sequence from 75 | multiple values, using the `of` constructor: 76 | 77 | ```clojure 78 | (def stream (rx/of 1 2 3)) 79 | 80 | (rx/sub! stream #(println "v:" %)) 81 | ;; ==> v: 1 82 | ;; ==> v: 2 83 | ;; ==> v: 3 84 | ``` 85 | 86 | 87 | ### Empty 88 | 89 | Some times you also want just a terminated stream: 90 | 91 | ```clojure 92 | (def stream (rx/empty)) 93 | ``` 94 | 95 | This stream does not yield any value and just terminates. 96 | 97 | 98 | ### With timeout 99 | 100 | This allows to create an observable sequence of one unique value, that 101 | will be emitted after a specified amount of time: 102 | 103 | ```clojure 104 | (def stream (rx/timeout 1000 10)) 105 | 106 | (rx/sub! stream #(println "v:" %)) 107 | ;; After 1 sec... 108 | ;; ==> v: 10 109 | ``` 110 | 111 | 112 | ### From factory 113 | 114 | This is the most advanced and flexible way to create an observable 115 | sequence. It allows to have control about termination and errors, and 116 | is intended to be used for building other kinds of constructors. 117 | 118 | ```clojure 119 | (def stream 120 | (rx/create (fn [subs] 121 | (rx/push! subs 1) ;; next with `1` as value 122 | (rx/push! subs 2) ;; next with `2` as value 123 | (rx/end! subs) ;; end the stream 124 | (fn [] 125 | ;; function called on unsubscription 126 | )))) 127 | 128 | (rx/sub! stream #(println "v:" %)) 129 | ;; ==> v: 1 130 | ;; ==> v: 2 131 | ``` 132 | 133 | 134 | ## Consuming streams 135 | 136 | ### The stream states 137 | 138 | The observable sequence can be in three different kind of states: 139 | *alive*, *"errored"* or *ended*. If an error is emitted the stream can 140 | be considered ended with an error. So *error* or *end* states are 141 | considered termination states. 142 | 143 | And for convenience you can subscribe to any of that states of an 144 | observable sequence. 145 | 146 | 147 | ### General purpose 148 | 149 | A general purpose subscription is one that allows you to create one 150 | subscription, that watches all the different possible states of an 151 | observable sequence: 152 | 153 | ```clojure 154 | (def sub (rx/sub! stream 155 | #(println "on-value:" %) 156 | #(println "on-error:" %) 157 | #(println "on-end:"))) 158 | ``` 159 | 160 | The return value of the `subscribe` function is a subscription object, 161 | that identifies the current subscription. It can be cancelled by 162 | executing `(rx/dispose! sub)`. 163 | 164 | There is also the `subs!` function usefull for `->>` ready call 165 | convention (expects receive the `observable` on the last argument 166 | position instead of the first position). 167 | 168 | 169 | ## Transformations 170 | 171 | There are two call conventions here: 172 | 173 | - The familiar fluent API, which just works like any clojure sequence 174 | transformations functions (`map`, `filter`, ...) 175 | - The RxJS composition API, using `rx/pipe` and `rx/comp`. 176 | 177 | Let see the `filter` example to understand the differences. 178 | 179 | 180 | ### Filter & Map 181 | 182 | The main advantage of using reactive streams is that you may treat 183 | them like normal sequences, and in this case apply a function and then 184 | filter them with a predicate. Let's use the fluent API: 185 | 186 | ```clojure 187 | (def stream 188 | (->> (rx/from [1 2 3 4 5]) 189 | (rx/map inc) 190 | (rx/filter #(> % 3)))) 191 | 192 | (rx/sub! stream 193 | #(println "on-next:" %) 194 | #(println "on-error:" %) 195 | #(println "on-end")) 196 | 197 | ;; ==> on-next: 4 198 | ;; ==> on-next: 5 199 | ;; ==> on-next: 6 200 | ;; ==> on-end 201 | ``` 202 | 203 | The same can be expressed using the composition API and operators: 204 | 205 | ```clojure 206 | (require '[beicon.v2.ops :as rxo]) 207 | 208 | (def stream 209 | (->> (rx/from [1 2 3 4 5]) 210 | (rx/pipe (rxo/map inc)) 211 | (rx/pipe (rxo/filter #(> % 3))) 212 | (rx/subs! stream 213 | #(println "on-next:" %) 214 | #(println "on-error:" %) 215 | #(println "on-end")))) 216 | ``` 217 | 218 | We also use the `subs!` helper for subscribe to the resulting 219 | observable in a single expression with `->>`. 220 | 221 | And finally, you can compose the transformation and later use it 222 | in the same way as transducers: 223 | 224 | ```clojure 225 | (def rxform 226 | (rx/comp (rxo/map inc) 227 | (rxo/filter #(> % 3)))) 228 | 229 | (def stream 230 | (->> (rx/from [1 2 3 4 5]) 231 | (rx/pipe rxform) 232 | (rx/subs! stream 233 | #(println "on-next:" %) 234 | #(println "on-error:" %) 235 | #(println "on-end")))) 236 | ``` 237 | 238 | NOTE: Functions in the `beicon.v2.ops` are operator only. 239 | 240 | 241 | ### Merge Map 242 | 243 | Converts an observable sequence, that can contain other observable sequences, into a 244 | new observable sequence, that emits just plain values. 245 | 246 | The result is similar to concatenating all the underlying sequences. 247 | 248 | ```clojure 249 | (def stream (->> (rx/from [1 2]) 250 | (rx/merge-map #(rx/from (range % (+ % 2)))))) 251 | 252 | (rx/sub! stream 253 | #(println "on-value:" %) 254 | #(println "on-error:" %) 255 | #(println "on-end")) 256 | 257 | ;; ==> on-value: 1 258 | ;; ==> on-value: 2 259 | ;; ==> on-value: 2 260 | ;; ==> on-value: 3 261 | ;; ==> on-end 262 | ``` 263 | 264 | Aliases: `fmap`, `flat-map`. 265 | 266 | 267 | ### Skip 268 | 269 | Also, sometimes you just want to skip values from stream by different criteria. 270 | 271 | You can skip the first N values: 272 | 273 | ```clojure 274 | (def stream (->> (rx/from [1 2 3 4 5 6]) 275 | (rx/skip 4))) 276 | 277 | (rx/sub! stream 278 | #(println "on-value:" %) 279 | #(println "on-error:" %) 280 | #(println "on-end")) 281 | 282 | ;; ==> on-value: 5 283 | ;; ==> on-value: 6 284 | ;; ==> on-end 285 | ``` 286 | 287 | Skip while some predicate evaluates to `true`: 288 | 289 | ```clojure 290 | (def stream (->> (rx/from [1 1 1 1 2 3]) 291 | (rx/skip-while odd?))) 292 | 293 | (rx/sub! stream 294 | #(println "on-value:" %) 295 | #(println "on-error:" %) 296 | #(println "on-end")) 297 | 298 | ;; ==> on-value: 2 299 | ;; ==> on-value: 3 300 | ;; ==> on-end 301 | ``` 302 | 303 | Or skip until another observable yields a value with `skip-until` (no 304 | example at this moment). 305 | 306 | 307 | ### Take 308 | 309 | You can also limit the observable sequence to an specified number of 310 | elements: 311 | 312 | ```clojure 313 | (def stream (->> (rx/from [1 1 1 1 2 3]) 314 | (rx/take 2))) 315 | 316 | (rx/sub! stream 317 | #(println "on-value:" %) 318 | #(println "on-error:" %) 319 | #(println "on-end")) 320 | 321 | ;; ==> on-value: 1 322 | ;; ==> on-value: 1 323 | ;; ==> on-end 324 | ``` 325 | 326 | Or a predicate evaluates to `true`: 327 | 328 | ```clojure 329 | (def stream (->> (rx/from [1 1 1 1 2 3]) 330 | (rx/take-while odd?))) 331 | 332 | (rx/sub! stream 333 | #(println "on-value:" %) 334 | #(println "on-error:" %) 335 | #(println "on-end")) 336 | 337 | ;; ==> on-value: 1 338 | ;; ==> on-value: 1 339 | ;; ==> on-value: 1 340 | ;; ==> on-value: 1 341 | ;; ==> on-end 342 | ``` 343 | 344 | 345 | ### Reduce 346 | 347 | Allows combining all results of an observable sequence using a 348 | combining function (also called *reducing* function): 349 | 350 | ```clojure 351 | (def stream (->> (rx/from [1 2 3 4]) 352 | (rx/reduce + 0))) 353 | 354 | (rx/sub! stream 355 | #(println "on-value:" %) 356 | #(println "on-error:" %) 357 | #(println "on-end")) 358 | 359 | ;; ==> on-value: 10 360 | ;; ==> on-end 361 | ``` 362 | 363 | 364 | ### Scan 365 | 366 | Like `reduce` (see above), but returns a stream of each intermediate 367 | result instead (similar to `reductions` in Clojure): 368 | 369 | ```clojure 370 | (def stream (->> (rx/from [1 2 3 4]) 371 | (rx/scan + 0))) 372 | 373 | (rx/sub! stream 374 | #(println "on-value:" %) 375 | #(println "on-error:" %) 376 | #(println "on-end")) 377 | 378 | ;; ==> on-value: 1 379 | ;; ==> on-value: 3 380 | ;; ==> on-value: 6 381 | ;; ==> on-value: 10 382 | ;; ==> on-end 383 | ``` 384 | 385 | 386 | ### Buffer 387 | 388 | This transformer function allows to accumulate N values in a buffer 389 | and then emits them as one value (similar to `partition` in Clojure) 390 | 391 | ```clojure 392 | (def stream (->> (rx/from [1 2 3 4]) 393 | (rx/buffer 2))) 394 | 395 | (rx/sub! stream 396 | #(println "on-value:" %) 397 | #(println "on-error:" %) 398 | #(println "on-end")) 399 | 400 | ;; ==> on-value: [1 2] 401 | ;; ==> on-value: [3 4] 402 | ;; ==> on-end 403 | ``` 404 | 405 | 406 | ## Combinators 407 | 408 | ### Zip 409 | 410 | This combinator combines two observable sequences in one. 411 | 412 | ```clojure 413 | (def stream (rx/zip 414 | (rx/from [1 2 3]) 415 | (rx/from [2 3 4]))) 416 | 417 | (rx/sub! stream 418 | #(println "on-value:" %) 419 | #(println "on-error:" %) 420 | #(println "on-end")) 421 | 422 | ;; ==> on-value: [1 2] 423 | ;; ==> on-value: [2 3] 424 | ;; ==> on-value: [3 4] 425 | ;; ==> on-end 426 | ``` 427 | 428 | ### Concat 429 | 430 | This combinator concatenates two or more observable sequences *in 431 | order*. 432 | 433 | ```clojure 434 | (def stream (rx/concat 435 | (rx/from [1 2]) 436 | (rx/from [3 4]))) 437 | 438 | (rx/sub! stream 439 | #(println "on-value:" %) 440 | #(println "on-error:" %) 441 | #(println "on-end")) 442 | 443 | ;; ==> on-value: 1 444 | ;; ==> on-value: 2 445 | ;; ==> on-value: 3 446 | ;; ==> on-value: 4 447 | ;; ==> on-end 448 | ``` 449 | 450 | It ignores nil values from arguments 451 | 452 | 453 | ### Merge 454 | 455 | This combinator merges two or more observable sequences *at random* (see 456 | `concat` for ordered). 457 | 458 | ```clojure 459 | (def stream (rx/merge 460 | (rx/from [1 2]) 461 | (rx/from [3 4]))) 462 | 463 | (rx/sub! stream 464 | #(println "on-value:" %) 465 | #(println "on-error:" %) 466 | #(println "on-end")) 467 | 468 | ;; ==> on-value: 1 469 | ;; ==> on-value: 3 470 | ;; ==> on-value: 2 471 | ;; ==> on-value: 4 472 | ;; ==> on-end 473 | ``` 474 | 475 | It ignores nil values from arguments 476 | 477 | 478 | ## Subject 479 | 480 | This is an abstraction that combines observable sequence with the 481 | observer. So you can push values into it and transform and subscribe 482 | to it like any other sequence. 483 | 484 | ### Creating a subject. 485 | 486 | You can create a subject instance using the `subject` constructor 487 | function. 488 | 489 | This is an example of using `subject` for two things: push values and 490 | subscribe to it. 491 | 492 | ```clojure 493 | (def subject (rx/subject)) 494 | (def stream (->> subject 495 | (rx/skip 1) 496 | (rx/map inc) 497 | (rx/take 2))) 498 | 499 | (rx/sub! stream 500 | #(println "on-value:" %) 501 | #(println "on-error:" %) 502 | #(println "on-end")) 503 | 504 | (rx/push! subject 1) 505 | (rx/push! subject 2) 506 | (rx/push! subject 1) 507 | (rx/push! subject 2) 508 | 509 | ;; ==> on-value: 3 510 | ;; ==> on-value: 2 511 | ;; ==> on-end 512 | ``` 513 | 514 | 515 | ### Ending a subject 516 | 517 | You can end a subject at any moment just by executing the `end!` function: 518 | 519 | ```clojure 520 | (def subject (rx/subject)) 521 | 522 | (rx/sub! subject 523 | #(println "on-value:" %) 524 | #(println "on-error:" %) 525 | #(println "on-end")) 526 | 527 | (rx/end! subject) 528 | ;; ==> on-end 529 | ``` 530 | 531 | 532 | ## Developers Guide 533 | 534 | ### Source Code 535 | 536 | _beicon_ is open source and can be found on 537 | link:https://github.com/funcool/beicon[github]. 538 | 539 | You can clone the public repository with this command: 540 | 541 | ``` 542 | git clone https://github.com/funcool/beicon 543 | ``` 544 | 545 | 546 | ### Run tests 547 | 548 | For running tests just execute this: 549 | 550 | ```bash 551 | yarn run test:watch 552 | ``` 553 | 554 | 555 | ### License 556 | 557 | 558 | _beicon_ is licensed under BSD (2-Clause) license: 559 | 560 | ``` 561 | Copyright (c) 2015-2024 Andrey Antukh 562 | 563 | All rights reserved. 564 | 565 | Redistribution and use in source and binary forms, with or without 566 | modification, are permitted provided that the following conditions are met: 567 | 568 | * Redistributions of source code must retain the above copyright notice, this 569 | list of conditions and the following disclaimer. 570 | 571 | * Redistributions in binary form must reproduce the above copyright notice, 572 | this list of conditions and the following disclaimer in the documentation 573 | and/or other materials provided with the distribution. 574 | 575 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 576 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 577 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 578 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 579 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 580 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 581 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 582 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 583 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 584 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 585 | ``` 586 | -------------------------------------------------------------------------------- /mvn-upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn deploy:deploy-file -Dfile=target/beicon.jar -DpomFile=pom.xml -DrepositoryId=clojars -Durl=https://clojars.org/repo/ 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beicon", 3 | "version": "2.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test:watch": "clojure -M:dev:shadow-cljs watch test", 7 | "test:compile": "clojure -M:dev:shadow-cljs compile test --config-merge '{:autorun false}'", 8 | "test:run": "node target/tests.js", 9 | "test": "yarn run test:compile && yarn run test:run" 10 | }, 11 | "author": "", 12 | "license": "MPL-2.0", 13 | "devDependencies": { 14 | "@types/node": "^22.0.0", 15 | "source-map-support": "^0.5.21", 16 | "typescript": "^5.5.4", 17 | "ws": "^8.18.0" 18 | }, 19 | "dependencies": { 20 | "rxjs": "8.0.0-alpha.14", 21 | "shadow-cljs": "^2.28.11" 22 | }, 23 | "packageManager": "yarn@4.3.1" 24 | } 25 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | funcool 5 | beicon 6 | jar 7 | 2.0.0 8 | beicon 9 | Reactive Streams for Clojure(Script) 10 | https://github.com/funcool/beicon 11 | 12 | 13 | BSD (2-Clause) 14 | http://opensource.org/licenses/BSD-2-Clause 15 | 16 | 17 | 18 | scm:git:git://github.com/funcool/beicon.git 19 | scm:git:ssh://git@github.com/funcool/beicon.git 20 | master 21 | https://github.com/funcool/beicon 22 | 23 | 24 | src 25 | 26 | 27 | 28 | clojars 29 | https://repo.clojars.org/ 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.clojure 38 | clojure 39 | 1.11.1 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import {babel} from '@rollup/plugin-babel'; 2 | import {nodeResolve} from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import replace from '@rollup/plugin-replace'; 5 | 6 | // import globals from 'rollup-plugin-node-globals'; 7 | 8 | const plugins = [ 9 | replace({ 10 | 'process.env.NODE_ENV': JSON.stringify('production'), 11 | preventAssignment: true 12 | }), 13 | 14 | babel({ 15 | exclude: 'node_modules/**', 16 | sourceMap: false, 17 | babelHelpers: 'bundled' 18 | }), 19 | 20 | nodeResolve({ 21 | mainFields: ['module', 'main'], 22 | // preferBuiltins: false, 23 | browser: true 24 | }), 25 | 26 | commonjs({ 27 | include: 'node_modules/**', // Default: undefined 28 | // if true then uses of `global` won't be dealt with by this plugin 29 | ignoreGlobal: false, // Default: false 30 | sourceMap: false, // Default: true 31 | }), 32 | ]; 33 | 34 | export default [{ 35 | input: "./assets/rxjs/rxjs.main.js", 36 | output: { 37 | file: './assets/rxjs/rxjs.main.bundle.js', 38 | compact: true, 39 | format: 'iife', 40 | indent: true, 41 | name: "rxjsMain", 42 | exports: "default" 43 | }, 44 | plugins: plugins 45 | }, { 46 | input: "./assets/rxjs/rxjs.operators.js", 47 | output: { 48 | file: './assets/rxjs/rxjs.operators.bundle.js', 49 | compact: true, 50 | format: 'iife', 51 | indent: true, 52 | name: "rxjsOperators", 53 | exports: "default" 54 | }, 55 | plugins: plugins 56 | }]; 57 | -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:deps {:aliases [:dev]} 2 | :http {:port 3448} 3 | ;; :nrepl {:port 3447 :host "0.0.0.0"} 4 | :jvm-opts ["-Xmx700m" "-Xms100m" "-XX:+UseSerialGC" "-XX:-OmitStackTraceInFastThrow"] 5 | :dev-http {8888 "classpath:public"} 6 | 7 | :builds 8 | {:test 9 | {:target :node-test 10 | :output-to "target/tests.js" 11 | :output-dir "target/test/" 12 | :ns-regexp "^beicon.tests.*-test$" 13 | :autorun true 14 | 15 | :compiler-options 16 | {:output-feature-set :es2020 17 | :output-wrapper false 18 | :source-map true 19 | :source-map-include-sources-content true 20 | :source-map-detail-level :all 21 | :warnings {:fn-deprecated false}}} 22 | 23 | }} 24 | 25 | -------------------------------------------------------------------------------- /src/beicon/v2/core.clj: -------------------------------------------------------------------------------- 1 | (ns beicon.v2.core 2 | (:refer-clojure :exclude [comp])) 3 | 4 | (defmacro push! 5 | [ob val] 6 | `(.next ~(with-meta ob {:tag 'js}) ~val)) 7 | 8 | (defmacro error! 9 | [ob val] 10 | `(.error ~(with-meta ob {:tag 'js}) ~val)) 11 | 12 | (defmacro end! 13 | [ob] 14 | `(.complete ~(with-meta ob {:tag 'js}))) 15 | 16 | (defmacro comp 17 | [& items] 18 | (let [source-s (gensym "source") 19 | binds (map-indexed 20 | (fn [index item] 21 | (gensym (str "item" index))) 22 | items)] 23 | 24 | `(fn [~source-s] 25 | (let [~@(->> (map vector binds items) 26 | (mapcat identity))] 27 | 28 | ~(reduce (fn [acc bind] 29 | `(beicon.v2.core/internal-call ~bind ~acc)) 30 | source-s 31 | binds))))) 32 | -------------------------------------------------------------------------------- /src/beicon/v2/core.cljs: -------------------------------------------------------------------------------- 1 | (ns beicon.v2.core 2 | (:refer-clojure :exclude [map filter reduce merge repeat first 3 | last mapcat repeatedly zip take take-while 4 | map-indexed concat empty take-last delay 5 | range throw flatten comp]) 6 | (:require-macros [beicon.v2.core :refer [push! error! end! comp]]) 7 | 8 | (:require 9 | ["rxjs" :as rx] 10 | [beicon.v2.operators :as ops] 11 | [cljs.core :as c])) 12 | 13 | (def ^:const Observable rx/Observable) 14 | (def ^:const Subject rx/Subject) 15 | (def ^:const BehaviorSubject rx/BehaviorSubject) 16 | (def ^:const Subscriber rx/Subscriber) 17 | (def ^:const Disposable rx/Subscription) 18 | (def ^:const Scheduler rx/Scheduler) 19 | 20 | (defn ^:no-doc internal-call 21 | [f source] 22 | (f source)) 23 | 24 | ;; --- Interop Helpers 25 | 26 | (declare subject?) 27 | 28 | (def ^function noop rx/noop) 29 | (def ^function comp ops/comp) 30 | (def ^function pipe ops/pipe) 31 | 32 | (defn push! 33 | "Pushes the given value to the bus stream." 34 | [b v] 35 | (.next ^js b v)) 36 | 37 | (defn error! 38 | "Pushes the given error to the bus stream." 39 | [b e] 40 | (.error ^js b e)) 41 | 42 | (defn end! 43 | "Ends the given bus stream." 44 | [b] 45 | (.complete ^js b)) 46 | 47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 48 | ;; PREDICATES 49 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 50 | 51 | (defn observable? 52 | "Return true if `ob` is a instance 53 | of Rx.Observable." 54 | ^boolean 55 | [ob] 56 | (instance? Observable ob)) 57 | 58 | (defn disposable? 59 | "Check if the provided object is disposable (jvm) or subscription (js)." 60 | ^boolean 61 | [v] 62 | (instance? Disposable v)) 63 | 64 | (defn scheduler? 65 | "Check if the provided value is Scheduler instance." 66 | ^boolean 67 | [v] 68 | (instance? Scheduler v)) 69 | 70 | (defn subject? 71 | "Check if the provided value is Subject instance." 72 | ^boolean 73 | [b] 74 | (instance? Subject b)) 75 | 76 | (defn subscriber? 77 | ^boolean 78 | [o] 79 | (instance? Subscriber o)) 80 | 81 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 82 | ;; CONSTRUCTORS 83 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 84 | 85 | (defn create 86 | "Creates an observable sequence from a specified subscribe method 87 | implementation." 88 | [sf] 89 | (assert (fn? sf) "expected a plain function") 90 | (Observable. (fn [subs] 91 | (try 92 | (sf subs) 93 | (catch :default e 94 | (.error subs e)))))) 95 | 96 | (defn subject 97 | "Subject that, once an Observer has subscribed, emits all 98 | subsequently observed items to the subscriber." 99 | [] 100 | (Subject.)) 101 | 102 | (defn behavior-subject 103 | "Bus that emits the most recent item it has observed and 104 | all subsequent observed items to each subscribed Observer." 105 | [v] 106 | (BehaviorSubject. v)) 107 | 108 | (defn range 109 | "Generates an observable sequence that repeats the 110 | given element." 111 | ([b] (range 0 b)) 112 | ([a b] (rx/range a b))) 113 | 114 | (defn from 115 | "Creates an observable from js arrays, clojurescript collections, and 116 | promise instance." 117 | [v] 118 | (if (nil? v) 119 | rx/EMPTY 120 | (rx/from v))) 121 | 122 | (defn from-atom 123 | ([atm] (from-atom atm nil)) 124 | ([atm {:keys [emit-current-value?] :or {emit-current-value? false}}] 125 | (create (fn [subs] 126 | (let [key (keyword (gensym "beicon"))] 127 | (when emit-current-value? (push! subs @atm)) 128 | (add-watch atm key (fn [_ _ _ val] (push! subs val))) 129 | (fn [] (remove-watch atm key))))))) 130 | 131 | (defn from-event 132 | "Creates an Observable by attaching an event listener to an event target" 133 | [et ev] 134 | (rx/fromEvent et ev)) 135 | 136 | (def ^function timer 137 | "Returns an observable sequence that produces a value after 138 | `ms` has elapsed and then after each period." 139 | rx/timer) 140 | 141 | (defn interval 142 | "Returns an observable sequence that produces a 143 | value after each period." 144 | [ms] 145 | (rx/interval ms)) 146 | 147 | (defn empty 148 | "Returns an observable sequence that is already 149 | in end state." 150 | [] 151 | rx/EMPTY) 152 | 153 | (defn throw 154 | "Returns an exceptionally terminated observable with provided cause." 155 | [e] 156 | (if (fn? e) 157 | (rx/throwError e) 158 | (rx/throwError #(-> e)))) 159 | 160 | (defn error 161 | "Same as `throw`" 162 | [e] 163 | (throw e)) 164 | 165 | (def ^function fjoin 166 | "Runs all observable sequences in parallel and collect their last 167 | elements." 168 | (js* "function forkJoin(...args) { 169 | const resultSelector = (typeof args[0] === 'function') ? args.shift() : undefined; 170 | if (resultSelector === undefined) { 171 | return ~{}(...args); 172 | } else { 173 | return ~{}(...args, resultSelector); 174 | } 175 | }" rx/forkJoin rx/forkJoin)) 176 | 177 | (def ^function of 178 | "Converts arguments to an observable sequence" 179 | rx/of) 180 | 181 | (def ^function race 182 | "Create an observable that surfaces any of the given 183 | sequences, whichever reacted first." 184 | rx/race) 185 | 186 | (def ^function zip 187 | "Merges the specified observable sequences or Promises (cljs) into one 188 | observable sequence." 189 | (js* "function zip(...sources) { 190 | const projectFunction = (typeof sources[0] === 'function') ? sources.shift() : undefined; 191 | 192 | if (projectFunction === undefined) { 193 | return ~{}(...sources); 194 | } else { 195 | return ~{}(...sources, projectFunction); 196 | } 197 | }" rx/zip rx/zip)) 198 | 199 | 200 | (def ^function concat 201 | "Concatenates all of the specified observable 202 | sequences, as long as the previous observable 203 | sequence terminated successfully." 204 | (js* "function(...args) { args = args.filter(~{}); return ~{}(...args); }" some? rx/concat)) 205 | 206 | (def ^function merge 207 | "Merges all the observable sequences and Promises 208 | into a single observable sequence." 209 | (js* "function(...args) { 210 | const sources = args.filter(~{}); 211 | return !sources.length ? ~{} : sources.length === 1 ? ~{}(sources[0]) : ~{}(Infinity)(~{}(sources)) 212 | }" some? rx/EMPTY rx/from rx/mergeAll rx/from)) 213 | 214 | 215 | (def ^function combine-latest 216 | "Combines multiple Observables to create an Observable whose values 217 | are calculated from the latest values of each of its input 218 | Observables (constructor)." 219 | (js* "function(...sources) { 220 | const projectFunction = (typeof sources[0] === 'function') ? sources.shift() : undefined; 221 | return ~{}(sources, projectFunction); 222 | }" rx/combineLatest)) 223 | 224 | (defn combine-latest-all 225 | "Comboines multiple Observables to create an Observable whose values 226 | are calculated from the latest values of each of its input 227 | Observables (constructor). 228 | 229 | A variant of `conbine-latest*` that accepts an array or sequential" 230 | [obs] 231 | (cond 232 | (array? obs) 233 | (rx/combineLatest obs) 234 | 235 | (sequential? obs) 236 | (rx/combineLatest (into-array obs)) 237 | 238 | :else 239 | (throw (ex-info "unexpected arguments" {:obs obs})))) 240 | 241 | (defn scheduler 242 | "Get the scheduler instance by type. The posible types are: `:asap`, 243 | `:async`, `:queue`. Old `:trampoline` type is renamed as `:queue` 244 | and is deprecated." 245 | [type] 246 | (ops/scheduler type)) 247 | 248 | (defn publish! 249 | "Create a connectable (hot) observable 250 | from other observable." 251 | [ob] 252 | (.publish ^Observable ob)) 253 | 254 | (defn connect! 255 | "Connect the connectable observable." 256 | [ob] 257 | (.connect ^Observable ob)) 258 | 259 | (defn to-observable 260 | "Coerce a object to an observable instance." 261 | [ob] 262 | (assert (subject? ob) "`ob` should be a Subject instance") 263 | (.asObservable ^Subject ob)) 264 | 265 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 266 | ;; SUBSCRIPTIONS 267 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 268 | 269 | (defprotocol ^:no-doc IDisposable 270 | (^:no-doc -dispose [_] "dispose resources.")) 271 | 272 | (defn dispose! 273 | "Dispose resources acquired by the subscription." 274 | [v] 275 | (-dispose v)) 276 | 277 | (extend-type Subscriber 278 | cljs.core/IFn 279 | (-invoke ([this] (.unsubscribe ^Subscriber this))) 280 | 281 | IDisposable 282 | (-dispose [this] (.unsubscribe ^Subscriber this))) 283 | 284 | (extend-type BehaviorSubject 285 | cljs.core/IDeref 286 | (-deref [self] 287 | (.getValue ^js self))) 288 | 289 | (defn subscribe 290 | "Subscribes an observer to the observable sequence." 291 | ([ob nf] 292 | (cond 293 | (or (ops/plain-object? nf) 294 | (subject? nf)) 295 | (.subscribe ^js ob nf) 296 | 297 | (map? nf) 298 | (subscribe ob 299 | (get nf :next noop) 300 | (get nf :error noop) 301 | (get nf :complete noop)) 302 | 303 | 304 | :else 305 | (do 306 | (.subscribe ^js ob #js {:next nf})))) 307 | 308 | ([ob next-fn error-fn] 309 | (let [observer #js {:next next-fn 310 | :error error-fn}] 311 | (.subscribe ^js ob observer))) 312 | 313 | ([ob next-fn error-fn complete-fn] 314 | (let [observer #js {:next next-fn 315 | :error error-fn 316 | :complete complete-fn}] 317 | (.subscribe ^js ob observer)))) 318 | 319 | (defn sub! 320 | "Subscribes an observer to the observable sequence." 321 | ([ob nf] (subscribe ob nf)) 322 | ([ob next-fn error-fn] (subscribe ob next-fn error-fn)) 323 | ([ob next-fn error-fn complete-fn] (subscribe ob next-fn error-fn complete-fn))) 324 | 325 | (defn ^:no-doc on-error 326 | [ob on-error] 327 | (subscribe ob noop on-error)) 328 | 329 | (defn ^:no-doc on-end 330 | [ob on-complete] 331 | (subscribe ob noop noop on-complete)) 332 | 333 | (defn subs! 334 | "A specialized version of `subscribe` with inverted arguments." 335 | ([nf ob] (subscribe ob nf)) 336 | ([nf ef ob] (subscribe ob nf ef)) 337 | ([nf ef cf ob] (subscribe ob nf ef cf))) 338 | 339 | (defn- disposable-atom 340 | [ref disposable] 341 | (specify! ref 342 | IFn 343 | (-invoke ([this] (-dispose this))) 344 | 345 | IDisposable 346 | (-dispose [_] 347 | (.unsubscribe disposable)))) 348 | 349 | (defn to-atom 350 | "Materialize the observable sequence into an atom." 351 | ([ob] 352 | (let [a (atom nil)] 353 | (to-atom ob a))) 354 | ([ob a] 355 | (let [disposable (subscribe ob #(reset! a %))] 356 | (disposable-atom a disposable))) 357 | ([ob a f] 358 | (let [disposable (subscribe ob #(swap! a f %))] 359 | (disposable-atom a disposable)))) 360 | 361 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 362 | ;; OPERATORS 363 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 364 | 365 | (defn share 366 | "Returns an observable sequence that shares a single 367 | subscription to the underlying sequence." 368 | [ob] 369 | (ops/pipe (ops/share) ob)) 370 | 371 | (defn if-empty 372 | "Emits a given value if the source Observable completes without 373 | emitting any next value, otherwise mirrors the source Observable." 374 | [default ob] 375 | (ops/pipe (ops/if-empty default) ob)) 376 | 377 | (defn merge-all 378 | "Merges an observable sequence of observable sequences into an 379 | observable sequence." 380 | ([ob] (ops/pipe (ops/merge-all) ob)) 381 | ([concurrency ob] (ops/pipe (ops/merge-all concurrency) ob))) 382 | 383 | (defn filter 384 | "Filters the elements of an observable sequence 385 | based on a predicate." 386 | [f ob] 387 | (ops/pipe (ops/filter f) ob)) 388 | 389 | (defn map 390 | "Apply a function to each element of an observable 391 | sequence." 392 | [f ob] 393 | (ops/pipe (ops/map f) ob)) 394 | 395 | (defn map-indexed 396 | "Same as `map` but also projects an index." 397 | [f ob] 398 | (ops/pipe (ops/map-indexed f) ob)) 399 | 400 | (defn merge-map 401 | "Projects each element of an observable sequence to an observable 402 | sequence and merges the resulting observable sequences or Promises 403 | or array/iterable into one observable sequence. 404 | 405 | In other languages is called: flatMap or mergeMap." 406 | [f ob] 407 | (ops/pipe (ops/merge-map f) ob)) 408 | 409 | (defn switch-map 410 | [f ob] 411 | (ops/pipe (ops/switch-map f) ob)) 412 | 413 | (defn mapcat 414 | "Projects each element of an observable sequence to an observable 415 | sequence and concatenates the resulting observable sequences or 416 | Promises or array/iterable into one observable sequence." 417 | [f ob] 418 | (ops/pipe (ops/mapcat f) ob)) 419 | 420 | (defn concat-all 421 | [ob] 422 | (ops/pipe (ops/merge-all 1) ob)) 423 | 424 | (defn skip 425 | "Bypasses a specified number of elements in an 426 | observable sequence and then returns the remaining 427 | elements." 428 | [n ob] 429 | (ops/pipe (ops/skip n) ob)) 430 | 431 | (defn skip-while 432 | "Bypasses elements in an observable sequence as long 433 | as a specified condition is true and then returns the 434 | remaining elements." 435 | [f ob] 436 | (ops/pipe (ops/skip-while f) ob)) 437 | 438 | (defn skip-until 439 | "Returns the values from the source observable sequence only after the 440 | other observable sequence produces a value." 441 | [pob ob] 442 | (ops/pipe (ops/skip-until pob) ob)) 443 | 444 | (defn skip-last 445 | "Skip a specified number of values before the completion of an observable." 446 | [n ob] 447 | (ops/pipe (ops/skip-last n) ob)) 448 | 449 | (defn take 450 | "Bypasses a specified number of elements in an observable sequence and 451 | then returns the remaining elements." 452 | [n ob] 453 | (ops/pipe (ops/take n) ob)) 454 | 455 | (defn take-last 456 | [n ob] 457 | (ops/pipe (ops/take-last n) ob)) 458 | 459 | (defn take-while 460 | "Returns elements from an observable sequence as long as a specified 461 | predicate returns true." 462 | [f ob] 463 | (ops/pipe (ops/take-while f) ob)) 464 | 465 | (defn take-until 466 | "Returns the values from the source observable sequence until the 467 | other observable sequence or Promise produces a value." 468 | [other ob] 469 | (ops/pipe (ops/take-until other) ob)) 470 | 471 | (defn first 472 | "Return an observable that only has the first value of the provided 473 | observable. You can optionally pass a predicate and default value." 474 | [ob] 475 | (ops/pipe (ops/take 1) ob)) 476 | 477 | (defn last 478 | "Return an observable that only has the last value of the provided 479 | observable. You can optionally pass a predicate and default value." 480 | [ob] 481 | (ops/pipe (ops/take-last 1) ob)) 482 | 483 | (defn reduce 484 | "Applies an accumulator function over an observable sequence, 485 | returning the result of the aggregation as a single element in the 486 | result sequence." 487 | ([f ob] 488 | (ops/pipe (ops/reduce f) ob)) 489 | ([f seed ob] 490 | (ops/pipe (ops/reduce f seed) ob))) 491 | 492 | (defn scan 493 | "Applies an accumulator function over an observable sequence and 494 | returns each intermediate result. Same as reduce but with 495 | intermediate results" 496 | ([f ob] 497 | (ops/pipe (ops/scan f) ob)) 498 | ([f seed ob] 499 | (ops/pipe (ops/scan f seed) ob))) 500 | 501 | (defn merge-scan 502 | "Applies an accumulator function over the source Observable where 503 | the accumulator function itself returns an Observable, then each 504 | intermediate Observable returned is merged into the output 505 | Observable." 506 | [f seed ob] 507 | (ops/pipe (ops/merge-scan f seed) ob)) 508 | 509 | (defn expand 510 | "Recursively projects each source value to an Observable 511 | which is merged in the output Observable." 512 | [f ob] 513 | (ops/pipe (ops/expand f) ob)) 514 | 515 | (defn with-latest-from 516 | "Merges the specified observable sequences into one observable 517 | sequence by using the selector function only when the source 518 | observable sequence (the instance) produces an element." 519 | ([o1 source] (ops/pipe (ops/with-latest o1) source)) 520 | ([o1 o2 source] (ops/pipe (ops/with-latest o1 o2) source)) 521 | ([o1 o2 o3 source] (ops/pipe (ops/with-latest o1 o2 o3) source)) 522 | ([o1 o2 o3 o4 source] (ops/pipe (ops/with-latest o1 o2 o3 o4) source)) 523 | ([o1 o2 o3 o4 o5 source] (ops/pipe (ops/with-latest o1 o2 o3 o4 o5) source)) 524 | ([o1 o2 o3 o4 o5 o6 source] (ops/pipe (ops/with-latest o1 o2 o3 o4 o5 o6) source))) 525 | 526 | (defn combine-latest-with 527 | "Combines multiple Observables to create an Observable whose values 528 | are calculated from the latest values of each of its input 529 | Observables (operator)." 530 | ([o1 ob] (ops/pipe (ops/combine-latest o1) ob)) 531 | ([o1 o2 ob] (ops/pipe (ops/combine-latest o1 o2) ob)) 532 | ([o1 o2 o3 ob] (ops/pipe (ops/combine-latest o1 o2 o3) ob)) 533 | ([o1 o2 o3 o4 ob] (ops/pipe (ops/combine-latest o1 o2 o3 o4) ob)) 534 | ([o1 o2 o3 o4 o5 ob] (ops/pipe (ops/combine-latest o1 o2 o3 o4 o5) ob)) 535 | ([o1 o2 o3 o4 o5 o6 ob] (ops/pipe (ops/combine-latest o1 o2 o3 o4 o5 o6) ob))) 536 | 537 | (defn catch 538 | "Continues an observable sequence that is terminated 539 | by an exception with the next observable sequence." 540 | ([handler ob] (ops/pipe (ops/catch handler) ob)) 541 | ([pred handler ob] 542 | (ops/pipe (ops/catch (fn [value] 543 | (if (pred value) 544 | (handler value) 545 | (throw value)))) 546 | ob))) 547 | 548 | (defn tap 549 | "Invokes an action for each element in the 550 | observable sequence." 551 | ([f ob] (ops/pipe (ops/tap f) ob)) 552 | ([f e ob] (ops/pipe (ops/tap f e) ob)) 553 | ([f e c ob] (ops/pipe (ops/tap f e c) ob))) 554 | 555 | (defn throttle 556 | "Returns an observable sequence that emits only the first item emitted 557 | by the source Observable during sequential time windows of a 558 | specified duration." 559 | ([ms ob] (ops/pipe (ops/throttle ms) ob)) 560 | ([ms config ob] (ops/pipe (ops/throttle ms config) ob))) 561 | 562 | (defn debounce 563 | "Emits an item from the source Observable after a 564 | particular timespan has passed without the Observable 565 | omitting any other items." 566 | [ms ob] 567 | (ops/pipe (ops/debounce ms) ob)) 568 | 569 | (defn sample 570 | "Samples the observable sequence at each interval." 571 | [ms ob] 572 | (ops/pipe (ops/sample ms) ob)) 573 | 574 | (defn sample-when 575 | "Samples the observable sequence at each interval." 576 | [other ob] 577 | (ops/pipe (ops/sample-when other) ob)) 578 | 579 | (defn ignore 580 | "Ignores all elements in an observable sequence leaving only the 581 | termination messages." 582 | [ob] 583 | (ops/pipe (ops/ignore) ob)) 584 | 585 | (defn finalize 586 | "Returns an Observable that mirrors the source Observable, but will 587 | call a specified function when the source terminates on complete or 588 | error." 589 | [f ob] 590 | (ops/pipe (ops/finalize f) ob)) 591 | 592 | (defn buffer 593 | "Projects each element of an observable sequence into zero 594 | or more buffers which are produced based on element count 595 | information." 596 | ([n ob] (ops/pipe (ops/buffer n) ob)) 597 | ([n o ob] (ops/pipe (ops/buffer n o) ob))) 598 | 599 | (defn buffer-time 600 | "Buffers the source Observable values for a specific time period." 601 | ([ms ob] (ops/pipe (ops/buffer-time ms) ob)) 602 | ([ms start ob] (ops/pipe (ops/buffer-time ms start) ob)) 603 | ([ms start max ob] (ops/pipe (ops/buffer-time ms start max) ob))) 604 | 605 | (defn buffer-until 606 | "Buffers the source Observable values until notifier emits." 607 | [notifier ob] 608 | (ops/pipe (ops/buffer-until notifier) ob)) 609 | 610 | (defn retry 611 | "Given an optional number of retries and an observable, 612 | repeats the source observable the specified number of 613 | times or until it terminates. If no number of retries 614 | is given, it will be retried indefinitely." 615 | ([ob] (ops/pipe (ops/retry) ob)) 616 | ([n ob] (ops/pipe (ops/retry n) ob))) 617 | 618 | (defn transform 619 | "Transform the observable sequence using transducers." 620 | [xform ob] 621 | (ops/pipe (ops/transform xform) ob)) 622 | 623 | (defn timeout 624 | "Returns the source observable sequence or the other 625 | observable sequence if dueTime elapses." 626 | ([ms ob] (ops/pipe (ops/timeout ms) ob)) 627 | ([ms with ob] (ops/pipe (ops/timeout ms with) ob))) 628 | 629 | (defn delay 630 | "Time shifts the observable sequence by dueTime. The relative 631 | time intervals between the values are preserved." 632 | [ms ob] 633 | (ops/pipe (ops/delay ms) ob)) 634 | 635 | (defn delay-at-least 636 | "Time shifts at least `ms` milisseconds." 637 | [ms ob] 638 | (ops/pipe (ops/delay-at-least ms) ob)) 639 | 640 | (defn delay-when 641 | "Time shifts the observable sequence based on a subscription 642 | delay and a delay selector function for each element." 643 | ([sf ob] (ops/pipe (ops/delay-when sf) ob)) 644 | ([sf sd ob] (ops/pipe (ops/delay-when sf sd) ob))) 645 | 646 | (defn flatten 647 | "Just like clojure collections flatten but for rx streams. Given a stream 648 | off collections will emit every value separately" 649 | [ob] 650 | (ops/pipe (rx/concatMap #(-> %1)) ob)) 651 | 652 | (defn concat-reduce 653 | "Like reduce but accepts a function that returns a stream. Will use as 654 | value for the next step in the reduce the last valued emited by the stream 655 | in the function." 656 | [f seed ob] 657 | (let [current-acc (volatile! seed)] 658 | (->> (concat 659 | (of seed) 660 | (->> ob 661 | (mapcat #(f @current-acc %)) 662 | (tap #(vreset! current-acc %)))) 663 | (last)))) 664 | 665 | (defn observe-on 666 | [sch ob] 667 | (ops/pipe (ops/observe-on sch) ob)) 668 | 669 | (defn subscribe-on 670 | [sch ob] 671 | (ops/pipe (ops/subscribe-on sch) ob)) 672 | -------------------------------------------------------------------------------- /src/beicon/v2/operators.cljs: -------------------------------------------------------------------------------- 1 | (ns beicon.v2.operators 2 | "RxJS operators only" 3 | (:refer-clojure :exclude [map filter reduce last mapcat take take-while comp 4 | map-indexed concat take-last delay distinct]) 5 | (:require 6 | ["rxjs" :as rx] 7 | [cljs.core :as c])) 8 | 9 | (defn scheduler 10 | {:no-doc true} 11 | [type] 12 | (case type 13 | :asap rx/asapScheduler 14 | :async rx/asyncScheduler 15 | :queue rx/queueScheduler 16 | :af rx/animationFrameScheduler 17 | :animation-frame rx/animationFrameScheduler)) 18 | 19 | (defn ^:no-doc plain-object? 20 | ^boolean 21 | [o] 22 | (and (some? o) 23 | (identical? (.getPrototypeOf js/Object o) 24 | (.-prototype js/Object)))) 25 | 26 | (def ^function share 27 | "Returns an observable sequence that shares a single subscription to 28 | the underlying sequence." 29 | rx/share) 30 | 31 | (def ^function if-empty 32 | "Emits a given value if the source Observable completes without 33 | emitting any next value, otherwise mirrors the source Observable." 34 | rx/defaultIfEmpty) 35 | 36 | (def ^function merge-all 37 | "Merges an observable sequence of observable sequences into an 38 | observable sequence." 39 | rx/mergeAll) 40 | 41 | (defn filter 42 | "Filters the elements of an observable sequence 43 | based on a predicate." 44 | [f] 45 | (rx/filter #(boolean (f %)))) 46 | 47 | (defn map 48 | [f] 49 | (rx/map #(f %))) 50 | 51 | (defn map-indexed 52 | "Same as `map` but also projects an index." 53 | ([] (rx/map (fn [a b] #js [a b]))) 54 | ([f] (rx/map #(f %2 %1)))) 55 | 56 | (defn merge-map 57 | "Projects each element of an observable sequence to an observable 58 | sequence and merges the resulting observable sequences or Promises 59 | or array/iterable into one observable sequence. 60 | 61 | In other languages is called: flatMap or mergeMap." 62 | ([f] (rx/mergeMap #(f %))) 63 | ([f concurrency] (rx/mergeMap #(f %) concurrency))) 64 | 65 | (defn switch-map 66 | [f] 67 | (rx/switchMap #(f %))) 68 | 69 | (defn mapcat 70 | "Projects each element of an observable sequence to an observable 71 | sequence and concatenates the resulting observable sequences or 72 | Promises or array/iterable into one observable sequence." 73 | [f] 74 | (rx/concatMap #(f %))) 75 | 76 | (defn mapcat-indexed 77 | "Indexed variant of `mapcat`" 78 | [f] 79 | (rx/concatMap #(f %2 %1))) 80 | 81 | (def ^function skip 82 | "Bypasses a specified number of elements in an 83 | observable sequence and then returns the remaining 84 | elements." 85 | rx/skip) 86 | 87 | (defn skip-while 88 | "Bypasses elements in an observable sequence as long 89 | as a specified condition is true and then returns the 90 | remaining elements." 91 | [f] 92 | (rx/skipWhile #(boolean (f %)))) 93 | 94 | (defn skip-until 95 | "Returns the values from the source observable sequence only after the 96 | other observable sequence produces a value." 97 | [pob] 98 | (rx/skipUntil pob)) 99 | 100 | (defn skip-last 101 | "Skip a specified number of values before the completion of an observable." 102 | [n] 103 | (rx/skipLast n)) 104 | 105 | (def ^function take 106 | "Bypasses a specified number of elements in an observable sequence and 107 | then returns the remaining elements." 108 | rx/take) 109 | 110 | (def ^function take-last 111 | rx/takeLast) 112 | 113 | (defn take-while 114 | "Returns elements from an observable sequence as long as a specified 115 | predicate returns true." 116 | [f] 117 | (rx/takeWhile #(boolean (f %)))) 118 | 119 | (def ^function take-until 120 | "Returns the values from the source observable sequence until the 121 | other observable sequence or Promise produces a value." 122 | rx/takeUntil) 123 | 124 | (defn reduce 125 | "Applies an accumulator function over an observable sequence, 126 | returning the result of the aggregation as a single element in the 127 | result sequence." 128 | ([f] 129 | (rx/reduce #(f %1 %2))) 130 | ([f seed] 131 | (rx/reduce #(f %1 %2) seed))) 132 | 133 | (defn scan 134 | "Applies an accumulator function over an observable sequence and 135 | returns each intermediate result. Same as reduce but with 136 | intermediate results" 137 | ([f] 138 | (rx/scan #(f %1 %2))) 139 | ([f seed] 140 | (rx/scan #(f %1 %2) seed))) 141 | 142 | (defn merge-scan 143 | "Applies an accumulator function over the source Observable where 144 | the accumulator function itself returns an Observable, then each 145 | intermediate Observable returned is merged into the output 146 | Observable." 147 | [f seed] 148 | (rx/mergeScan #(f %1 %2) seed)) 149 | 150 | (defn expand 151 | "Recursively projects each source value to an Observable 152 | which is merged in the output Observable." 153 | ([f] (rx/expand #(f %))) 154 | ([f c] (rx/expand #(f %) c))) 155 | 156 | (def ^function with-latest 157 | "Merges the specified observable sequences into one observable 158 | sequence by using the selector function only when the source 159 | observable sequence (the instance) produces an element. 160 | 161 | (operator)" 162 | (js* "function withLatestFrom(...args) { 163 | const resultSelector = (typeof args[0] === 'function') ? args.shift() : undefined; 164 | if (resultSelector === undefined) { 165 | return ~{}(...args); 166 | } else { 167 | return ~{}(...args, resultSelector); 168 | } 169 | }" rx/withLatestFrom rx/withLatestFrom)) 170 | 171 | (def ^function combine-latest 172 | "Combines multiple Observables to create an Observable whose values 173 | are calculated from the latest values of each of its input 174 | Observables (operator)." 175 | rx/combineLatestWith) 176 | 177 | (defn catch 178 | "Continues an observable sequence that is terminated 179 | by an exception with the next observable sequence." 180 | [handler] 181 | (rx/catchError (fn [error source] 182 | (let [value (handler error source)] 183 | (if (instance? rx/Observable value) 184 | value 185 | rx/EMPTY))))) 186 | 187 | (defn tap 188 | "Invokes an action for each element in the 189 | observable sequence." 190 | ([f] 191 | (if (or (plain-object? f) 192 | (fn? f)) 193 | (rx/tap f) 194 | (throw (ex-info "invalid argiments" {:f f})))) 195 | ([f e] 196 | (rx/tap #js {:next f :error e :complete rx/noop})) 197 | ([f e c] 198 | (rx/tap #js {:next f :error e :complete c}))) 199 | 200 | (defn throttle 201 | "Returns an observable sequence that emits only the first item emitted 202 | by the source Observable during sequential time windows of a 203 | specified duration. 204 | 205 | (operator only)" 206 | ([ms] 207 | (rx/throttleTime ms)) 208 | ([ms config] 209 | (cond 210 | (plain-object? config) 211 | (rx/throttleTime ms config) 212 | 213 | (map? config) 214 | (rx/throttleTime ms #js {:leading (:leading config true) 215 | :trailing (:trailing config false)}) 216 | :else 217 | (rx/throttleTime ms)))) 218 | 219 | (def ^function debounce 220 | "Emits an item from the source Observable after a 221 | particular timespan has passed without the Observable 222 | omitting any other items." 223 | rx/debounceTime) 224 | 225 | (def ^function sample 226 | "Samples the observable sequence at each interval." 227 | rx/sampleTime) 228 | 229 | (def ^function sample-when 230 | "Samples the observable sequence at each interval." 231 | rx/sample) 232 | 233 | (def ^function ignore 234 | "Ignores all elements in an observable sequence leaving only the 235 | termination messages." 236 | rx/ignoreElements) 237 | 238 | (def ^function finalize 239 | "Returns an Observable that mirrors the source Observable, but will 240 | call a specified function when the source terminates on complete or 241 | error." 242 | rx/finalize) 243 | 244 | (defn distinct-contiguous 245 | "Returns an observable sequence that contains only 246 | distinct contiguous elements." 247 | ([] (rx/distinctUntilChanged)) 248 | ([comparator-fn] 249 | (rx/distinctUntilChanged #(comparator-fn %1 %2))) 250 | ([comparator-fn key-fn] 251 | (rx/distinctUntilChanged #(comparator-fn %1 %2) #(key-fn %)))) 252 | 253 | (defn distinct 254 | "Returns an observable sequence that contains only distinct 255 | elements. 256 | 257 | Usage of this operator should be considered carefully due to the 258 | maintenance of an internal lookup structure which can grow large." 259 | ([] (rx/distinct)) 260 | ([comparator-fn] (rx/distinct #(comparator-fn %1 %2))) 261 | ([comparator-fn key-fn] (rx/distinct #(comparator-fn %1 %2) #(key-fn %)))) 262 | 263 | (def ^function buffer 264 | "Projects each element of an observable sequence into zero 265 | or more buffers which are produced based on element count 266 | information." 267 | rx/bufferCount) 268 | 269 | (def ^function buffer-time 270 | "Buffers the source Observable values for a specific time period. 271 | (operator only)" 272 | rx/bufferTime) 273 | 274 | (def ^function buffer-until 275 | "Buffers the source Observable values until notifier emits." 276 | rx/buffer) 277 | 278 | (def ^function retry 279 | "Given an optional number of retries and an observable, 280 | repeats the source observable the specified number of 281 | times or until it terminates. If no number of retries 282 | is given, it will be retried indefinitely." 283 | rx/retry) 284 | 285 | (defn transform 286 | [xform] 287 | (fn [source] 288 | (rx/Observable. 289 | (fn [subs] 290 | (let [xsubs (xform 291 | (fn 292 | ([r] (.complete ^js subs) r) 293 | ([_ input] (.next ^js subs input) input))) 294 | obs #js {:next 295 | (fn [input] 296 | (let [v (xsubs nil input)] 297 | (when (reduced? v) 298 | (xsubs @v)))) 299 | :error 300 | (fn [cause] 301 | (.error ^js subs cause)) 302 | 303 | :complete 304 | (fn [] 305 | (xsubs nil) 306 | (.complete subs))} 307 | sub (.subscribe source obs)] 308 | (fn [] 309 | (.unsubscribe ^js subs))))))) 310 | 311 | (defn timeout 312 | "Returns the source observable sequence or the other 313 | observable sequence if dueTime elapses." 314 | ([ms] 315 | (cond 316 | (or (number? ms) 317 | (instance? js/Data ms) 318 | (plain-object? ms)) 319 | (rx/timeout ms) 320 | 321 | (map? ms) 322 | (rx/timeout #js {:first (get ms :first) 323 | :each (get ms :each) 324 | :with (get ms :with)}) 325 | 326 | :else 327 | (throw (ex-info "invalid arguments" {:ms ms})))) 328 | ([ms with] 329 | (rx/timeout #js {:each ms 330 | :with (if (instance? rx/Observable with) 331 | #(-> with) 332 | with)}))) 333 | 334 | (def ^function delay 335 | "Time shifts the observable sequence by dueTime. The relative 336 | time intervals between the values are preserved." 337 | rx/delay) 338 | 339 | (def ^function delay-when 340 | "Time shifts the observable sequence based on a subscription 341 | delay and a delay selector function for each element." 342 | rx/delayWhen) 343 | 344 | (def ^function pipe 345 | (js* "function pipeWith(...fns) { const input = fns.pop(); return fns.reduce((prev, fn) => fn(prev), input); }")) 346 | 347 | (def ^function comp 348 | (js* "function pipeComp(...fns) { return (source) => fns.reduce((prev, fn) => fn(prev), source); }")) 349 | 350 | (defn delay-at-least 351 | "Time shifts at least `ms` milisseconds." 352 | [ms] 353 | (comp (combine-latest (rx/timer ms)) 354 | (map c/first))) 355 | 356 | (defn observe-on 357 | ([sch] 358 | (cond 359 | (instance? rx/Scheduler sch) 360 | (rx/observeOn sch) 361 | 362 | (keyword? sch) 363 | (observe-on (scheduler sch)) 364 | 365 | :else 366 | (throw (ex-info "invalid arguments" {:sch sch})))) 367 | ([sch delay] 368 | (cond 369 | (instance? rx/Scheduler sch) 370 | (rx/observeOn sch delay) 371 | 372 | (keyword? sch) 373 | (observe-on (scheduler sch) delay) 374 | 375 | :else 376 | (throw (ex-info "invalid arguments" {:sch sch :delay delay}))))) 377 | 378 | (defn subscribe-on 379 | ([sch] 380 | (cond 381 | (instance? rx/Scheduler sch) 382 | (rx/subscribeOn sch) 383 | 384 | (keyword? sch) 385 | (subscribe-on (scheduler sch)) 386 | 387 | :else 388 | (throw (ex-info "invalid arguments" {:sch sch})))) 389 | ([sch delay] 390 | (cond 391 | (instance? rx/Scheduler sch) 392 | (rx/subscribeOn sch delay) 393 | 394 | (keyword? sch) 395 | (subscribe-on (scheduler sch) delay) 396 | 397 | :else 398 | (throw (ex-info "invalid arguments" {:sch sch :delay delay}))))) 399 | -------------------------------------------------------------------------------- /test/beicon/tests/helpers.cljc: -------------------------------------------------------------------------------- 1 | (ns beicon.tests.helpers 2 | #?(:cljs 3 | (:require [beicon.v2.core :as rx]))) 4 | 5 | (def noop (constantly nil)) 6 | 7 | #?(:clj 8 | (defmacro with-timeout 9 | [ms & body] 10 | `(do 11 | (js/setTimeout 12 | (fn [] 13 | (do 14 | ~@body)) 15 | ~ms) 16 | nil))) 17 | 18 | #?(:cljs 19 | (defn drain! 20 | ([ob cb] 21 | (drain! ob cb #(println "Error: " %))) 22 | ([ob cb errb] 23 | (let [values (volatile! [])] 24 | (rx/subscribe ob 25 | #(vswap! values conj %) 26 | #(errb %) 27 | #(cb @values)))))) 28 | -------------------------------------------------------------------------------- /test/beicon/tests/v2_test.cljs: -------------------------------------------------------------------------------- 1 | (ns beicon.tests.v2-test 2 | (:require 3 | [cljs.test :as t] 4 | [beicon.v2.core :as rx] 5 | [beicon.v2.operators :as rxo] 6 | [beicon.tests.helpers 7 | :refer (noop drain!) 8 | :refer-macros (with-timeout)])) 9 | 10 | ;; event stream 11 | 12 | (t/deftest observable-from-values 13 | (t/async done 14 | (let [s (rx/of 1 2 3 4 5 6 7 8 9)] 15 | (t/is (rx/observable? s)) 16 | (drain! s #(do 17 | (t/is (= % [1 2 3 4 5 6 7 8 9])) 18 | (done)))))) 19 | 20 | (t/deftest observable-from-values-with-nil 21 | (t/async done 22 | (let [s (rx/of 1 nil 2)] 23 | (t/is (rx/observable? s)) 24 | (drain! s #(do 25 | (t/is (= % [1 nil 2])) 26 | (done)))))) 27 | 28 | (t/deftest observable-from-vector 29 | (t/async done 30 | (let [coll [1 2 3] 31 | s (rx/from coll)] 32 | (t/is (rx/observable? s)) 33 | (drain! s #(do 34 | (t/is (= % coll)) 35 | (done)))))) 36 | 37 | (t/deftest observable-from-vector-with-take 38 | (t/async done 39 | (let [coll [1 2 3 4 5 6] 40 | s (->> (rx/from coll) 41 | (rx/take 2))] 42 | (t/is (rx/observable? s)) 43 | (drain! s #(t/is (= % [1 2]))) 44 | (rx/on-end s done)))) 45 | 46 | (t/deftest observable-from-atom 47 | (t/async done 48 | (let [a (atom 0) 49 | s (->> (rx/from-atom a) 50 | (rx/take 4))] 51 | (t/is (rx/observable? s)) 52 | (drain! s #(do 53 | (t/is (= % [1 2 3 4])) 54 | (done))) 55 | (swap! a inc) 56 | (swap! a inc) 57 | (swap! a inc) 58 | (swap! a inc)))) 59 | 60 | (t/deftest observable-from-set 61 | (t/async done 62 | (let [coll #{1 2 3} 63 | s (rx/from coll)] 64 | (t/is (rx/observable? s)) 65 | (drain! s #(t/is (= (set %) coll))) 66 | (rx/on-end s done)))) 67 | 68 | (t/deftest observable-from-create 69 | (t/async done 70 | (let [s (rx/create (fn [sink] 71 | (with-timeout 10 72 | (rx/push! sink 1) 73 | (rx/push! sink 2) 74 | (rx/push! sink 3) 75 | (rx/end! sink))))] 76 | (t/is (rx/observable? s)) 77 | (drain! s #(t/is (= % [1 2 3]))) 78 | (rx/on-end s done)))) 79 | 80 | (t/deftest observable-from-event 81 | (t/async done 82 | (let [target #js {:addEventListener #(do 83 | (t/is (= %1 "poked")) 84 | (%2 "once")) 85 | :removeEventListener #()} 86 | s (rx/from-event target "poked")] 87 | (t/is (rx/observable? s)) 88 | (rx/end! (drain! s #(do 89 | (t/is (= % ["once"])) 90 | (done))))))) 91 | 92 | (t/deftest observable-with-timeout 93 | (t/async done 94 | (let [s (->> (rx/timer 200) 95 | (rx/timeout 100 (rx/of :timeout)))] 96 | 97 | (t/is (rx/observable? s)) 98 | (drain! s #(do 99 | (t/is (= % [:timeout])) 100 | (rx/on-end s done)))))) 101 | 102 | (t/deftest observable-pause-from-timer 103 | (t/async done 104 | (let [s (rx/timer 100)] 105 | (t/is (rx/observable? s)) 106 | (drain! s #(do 107 | (t/is (= % [0])) 108 | (rx/on-end s done)))))) 109 | 110 | (t/deftest observable-interval-from-timer 111 | (t/async done 112 | (let [s (->> (rx/timer 100 100) 113 | (rx/take 2))] 114 | (t/is (rx/observable? s)) 115 | (drain! s #(do 116 | (t/is (= % [0 1])) 117 | (rx/on-end s done)))))) 118 | 119 | (t/deftest observable-errors-from-create 120 | (t/async done 121 | (let [s (rx/create (fn [sink] 122 | (with-timeout 10 123 | (rx/push! sink 1) 124 | (rx/error! sink (ex-info "oh noes" {})))))] 125 | (t/is (rx/observable? s)) 126 | (drain! s 127 | #(t/is (= % [1])) 128 | #(t/is (= (ex-message %) "oh noes"))) 129 | (rx/on-error s done)))) 130 | 131 | (t/deftest observable-from-promise 132 | (t/async done 133 | (let [p (js/Promise.resolve 42) 134 | s (rx/from p)] 135 | (t/is (rx/observable? s)) 136 | (drain! s 137 | #(t/is (= % [42]))) 138 | (rx/on-end s done)))) 139 | 140 | (t/deftest observable-range 141 | (t/async done 142 | (let [s (rx/range 5)] 143 | (t/is (rx/observable? s)) 144 | (drain! s #(t/is (= % [0 1 2 3 4]))) 145 | (rx/on-end s done)))) 146 | 147 | (t/deftest observable-of 148 | (t/async done 149 | (let [s (rx/of 1)] 150 | (t/is (rx/observable? s)) 151 | (drain! s #(t/is (= % [1]))) 152 | (rx/on-end s done)))) 153 | 154 | (t/deftest observable-empty 155 | (t/async done 156 | (let [n (rx/empty)] 157 | (rx/on-end n done)))) 158 | 159 | (t/deftest observable-concat-1 160 | (t/async done 161 | (let [s1 (rx/from [1 2 3]) 162 | s2 (rx/from [4 5 6]) 163 | cs (rx/concat s1 s2)] 164 | (drain! cs #(t/is (= % [1 2 3 4 5 6]))) 165 | (rx/on-end cs done)))) 166 | 167 | (t/deftest observable-concat-2 168 | (t/async done 169 | (let [s1 (rx/from [1 2 3]) 170 | s2 (rx/from [4 5 6]) 171 | cs (rx/concat s1 s2 nil nil)] 172 | (drain! cs #(t/is (= % [1 2 3 4 5 6]))) 173 | (rx/on-end cs done)))) 174 | 175 | (t/deftest observable-zip-1 176 | (t/async done 177 | (let [s1 (rx/from [1 2]) 178 | s2 (rx/from [4 5]) 179 | s3 (rx/from [7 8]) 180 | cs (->> (rx/zip s1 s2 s3) 181 | (rx/map vec))] 182 | (drain! cs #(t/is (= % [[1 4 7] [2 5 8]]))) 183 | (rx/on-end cs done)))) 184 | 185 | (t/deftest observable-zip-2 186 | (t/async done 187 | (let [s1 (rx/from [1 2]) 188 | s2 (rx/from [4 5]) 189 | s3 (rx/from [7 8]) 190 | cs (rx/zip vector s1 s2 s3)] 191 | (drain! cs #(t/is (= % [[1 4 7] [2 5 8]]))) 192 | (rx/on-end cs done)))) 193 | 194 | (t/deftest observable-fjoin 195 | (t/async done 196 | (let [s1 (rx/from [1 2]) 197 | s2 (rx/from [4 5]) 198 | s3 (rx/from [7 8]) 199 | cs (rx/fjoin vector 200 | s1 s2 s3)] 201 | (drain! cs #(t/is (= % [[2 5 8]]))) 202 | (rx/on-end cs done)))) 203 | 204 | (t/deftest observable-merge 205 | (t/async done 206 | (let [s1 (rx/from [1 2 3]) 207 | s2 (rx/from [:1 :2 :3]) 208 | ms (rx/merge s1 s2)] 209 | (drain! ms #(t/is (= (set %) #{:1 1 :2 2 :3 3}))) 210 | (rx/on-end ms done)))) 211 | 212 | (t/deftest observable-skip-while 213 | (t/async done 214 | (let [nums (rx/from [1 1 1 2 3 4 5]) 215 | sample (rx/skip-while odd? nums)] 216 | (drain! sample #(t/is (= % [2 3 4 5]))) 217 | (rx/on-end sample done)))) 218 | 219 | (t/deftest subject-as-ideref 220 | (t/async done 221 | (let [nums (rx/from [1 1 1 2 3 4 5]) 222 | sub (rx/behavior-subject nil)] 223 | (rx/on-end sub #(t/is (= @sub 5) 224 | (done))) 225 | (rx/subscribe nums sub)))) 226 | 227 | (t/deftest subject-push 228 | (t/async done 229 | (let [b (rx/subject)] 230 | (t/is (rx/subject? b)) 231 | (drain! b #(t/is (= % [1 2 3]))) 232 | (rx/push! b 1) 233 | (rx/push! b 2) 234 | (rx/push! b 3) 235 | (rx/end! b) 236 | (rx/on-end b done)))) 237 | 238 | (t/deftest behavior-subject 239 | (t/async done 240 | (let [b (rx/behavior-subject -1)] 241 | (t/is (rx/subject? b)) 242 | (drain! b #(do 243 | (t/is (= % [-1 1 2 3])) 244 | (done))) 245 | (rx/push! b 1) 246 | (rx/push! b 2) 247 | (rx/push! b 3) 248 | (rx/end! b)))) 249 | 250 | (t/deftest observable-reduce 251 | (t/async done 252 | (let [s (->> (rx/from [4 5 6]) 253 | (rx/reduce conj [1 2]))] 254 | (drain! s #(do (t/is (= % [[1 2 4 5 6]])) 255 | (done)))))) 256 | 257 | 258 | (t/deftest observable-scan 259 | (t/async done 260 | (let [s (->> (rx/from [4 5 6]) 261 | (rx/scan conj [1]))] 262 | (drain! s #(do (t/is (= % [[1 4] [1 4 5] [1 4 5 6]])) 263 | (done)))))) 264 | 265 | (t/deftest observable-merge-scan 266 | (t/async done 267 | (let [s (->> (rx/from [4 5 6]) 268 | (rx/merge-scan (fn [acc i] (rx/of (conj acc i))) [1]))] 269 | (drain! s #(do (t/is (= % [[1 4] [1 4 5] [1 4 5 6]])) 270 | (done)))))) 271 | 272 | (t/deftest observable-filter-with-ifn 273 | (t/async done 274 | (let [s (rx/from [1 2 3 4 5]) 275 | fs (rx/filter #{3 5} s)] 276 | (drain! fs #(t/is (= % [3 5]))) 277 | (rx/on-end fs done)))) 278 | 279 | (t/deftest observable-map-with-ifn 280 | (t/async done 281 | (let [s (rx/from [{:foo 1} {:foo 2}]) 282 | fs (rx/map :foo s)] 283 | (drain! fs #(t/is (= % [1 2]))) 284 | (rx/on-end fs done)))) 285 | 286 | (t/deftest observable-map-filter-comp-kk 287 | (t/async done 288 | (let [s (rx/of 1 2 3 4 5 6 7) 289 | x (rx/comp (rxo/map inc) 290 | (rxo/filter odd?)) 291 | s (rx/pipe x s)] 292 | (drain! s #(t/is (= % [3 5 7]))) 293 | (rx/on-end s done)))) 294 | 295 | (t/deftest observable-map-indexed 296 | (t/async done 297 | (let [s (rx/from [:a :b :c]) 298 | fs (rx/map-indexed vector s)] 299 | (drain! fs #(t/is (= % [[0 :a] [1 :b] [2 :c]]))) 300 | (rx/on-end fs done)))) 301 | 302 | (t/deftest observable-retry 303 | (t/async done 304 | (let [errored? (volatile! false) 305 | s (rx/create (fn [sink] 306 | (if @errored? 307 | (do 308 | (rx/push! sink 2) 309 | (rx/push! sink 3) 310 | (rx/end! sink)) 311 | (do 312 | (vreset! errored? true) 313 | (rx/error! sink (js/Error.)))))) 314 | rs (rx/retry 2 s)] 315 | (t/is (rx/observable? rs)) 316 | (drain! rs #(t/is (= % [2 3]))) 317 | (rx/on-end rs done)))) 318 | 319 | (t/deftest observable-with-latest-from-1 320 | (t/async done 321 | (let [s1 (rx/from [1 2 3]) 322 | s2 (rx/from [0]) 323 | s3 (rx/from [4 5 6]) 324 | s4 (->> s1 325 | (rx/pipe (rxo/with-latest s2 s3)) 326 | (rx/map vec))] 327 | (t/is (rx/observable? s3)) 328 | (drain! s4 #(t/is (= % [[1 0 6] [2 0 6] [3 0 6]]))) 329 | (rx/on-end s3 done)))) 330 | 331 | (t/deftest observable-with-latest-from-2 332 | (t/async done 333 | (let [s1 (rx/from [1 2 3]) 334 | s2 (rx/from [0]) 335 | s3 (rx/from [4 5 6]) 336 | s4 (->> s1 337 | (rx/pipe (rxo/with-latest vector s2 s3)))] 338 | (t/is (rx/observable? s3)) 339 | (drain! s4 #(t/is (= % [[1 0 6] [2 0 6] [3 0 6]]))) 340 | (rx/on-end s3 done)))) 341 | 342 | (t/deftest observable-combine-latest-2 343 | (t/async done 344 | (let [s1 (rx/delay 10 (rx/from [9])) 345 | s2 (rx/delay 10 (rx/from [2])) 346 | s3 (->> (rx/combine-latest s1 s2) 347 | (rx/map vec) 348 | (rx/delay-at-least 100))] 349 | (t/is (rx/observable? s3)) 350 | (drain! s3 #(t/is (= % [[9 2]]))) 351 | (rx/on-end s3 done)))) 352 | 353 | (t/deftest observable-combine-latest-3 354 | (t/async done 355 | (let [s1 (rx/delay 10 (rx/from [9])) 356 | s2 (rx/delay 10 (rx/from [2])) 357 | s3 (->> (rx/combine-latest-all [s1 s2]) 358 | (rx/map vec) 359 | (rx/delay-at-least 100))] 360 | (t/is (rx/observable? s3)) 361 | (drain! s3 #(t/is (= % [[9 2]]))) 362 | (rx/on-end s3 done)))) 363 | 364 | (t/deftest observable-combine-latest-4 365 | (t/async done 366 | (let [s1 (rx/delay 10 (rx/from [9])) 367 | s2 (rx/delay 10 (rx/from [2])) 368 | s3 (rx/delay 10 (rx/from [1])) 369 | s4 (rx/delay 10 (rx/from [3])) 370 | s5 (rx/delay 10 (rx/from [4])) 371 | s6 (rx/delay 10 (rx/from [5])) 372 | s3 (->> (rx/combine-latest s1 s2 s3 s4 s5 s6) 373 | (rx/map vec) 374 | (rx/delay-at-least 100))] 375 | (t/is (rx/observable? s3)) 376 | (drain! s3 #(t/is (= % [[9 2 1 3 4 5]]))) 377 | (rx/on-end s3 done)))) 378 | 379 | (t/deftest observable-combine-latest-5 380 | (t/async done 381 | (let [s1 (rx/delay 10 (rx/from [9])) 382 | s2 (rx/delay 10 (rx/from [2])) 383 | s3 (rx/delay 10 (rx/from [1])) 384 | s4 (rx/delay 10 (rx/from [3])) 385 | s5 (rx/delay 10 (rx/from [4])) 386 | s6 (rx/delay 10 (rx/from [5])) 387 | s3 (->> (rx/combine-latest vector s1 s2 s3 s4 s5 s6) 388 | (rx/delay-at-least 100))] 389 | (t/is (rx/observable? s3)) 390 | (drain! s3 #(t/is (= % [[9 2 1 3 4 5]]))) 391 | (rx/on-end s3 done)))) 392 | 393 | (t/deftest observable-catch-0 394 | (t/async done 395 | (let [s1 (rx/throw (fn [] (ex-info "error" {:foo :bar}))) 396 | s2 (rx/catch (fn [error] 397 | (rx/of (ex-data error))) 398 | s1)] 399 | (t/is (rx/observable? s2)) 400 | (drain! s2 #(t/is (= % [{:foo :bar}]))) 401 | (rx/on-end s2 done)))) 402 | 403 | (t/deftest observable-catch-1 404 | (t/async done 405 | (let [s1 (rx/throw (ex-info "error" {:foo :bar})) 406 | s2 (rx/catch (fn [error] 407 | (rx/of (ex-data error))) 408 | s1)] 409 | (t/is (rx/observable? s2)) 410 | (drain! s2 #(t/is (= % [{:foo :bar}]))) 411 | (rx/on-end s2 done)))) 412 | 413 | (t/deftest observable-catch-2 414 | (t/async done 415 | (let [type1? #(= 1 (:type (ex-data %))) 416 | s1 (->> (rx/throw (ex-info "error" {:type 1})) 417 | (rx/catch type1? #(rx/of (ex-data %))))] 418 | (t/is (rx/observable? s1)) 419 | (drain! s1 #(t/is (= % [{:type 1}]))) 420 | (rx/on-end s1 done)))) 421 | 422 | (t/deftest observable-catch-3 423 | (t/async done 424 | (let [type1? #(= 1 (:type (ex-data %))) 425 | type2? #(= 2 (:type (ex-data %))) 426 | s1 (->> (rx/throw (ex-info "error" {:type 1})) 427 | (rx/catch type2? #(rx/of (ex-data %))) 428 | (rx/catch type1? #(rx/of (ex-data %))))] 429 | (t/is (rx/observable? s1)) 430 | (drain! s1 #(t/is (= % [{:type 1}]))) 431 | (rx/on-end s1 done)))) 432 | 433 | (t/deftest observable-to-atom 434 | (t/async done 435 | (let [st (rx/from [1 2 3]) 436 | a (rx/to-atom st)] 437 | (rx/on-end st #(do (t/is (= @a 3)) 438 | (done)))))) 439 | 440 | (t/deftest observable-to-atom-with-atom 441 | (t/async done 442 | (let [st (rx/from [1 2 3]) 443 | vacc (volatile! []) 444 | a (atom 0)] 445 | (add-watch a :acc 446 | (fn [_ _ _ v] 447 | (vswap! vacc conj v))) 448 | (rx/to-atom st a) 449 | (rx/on-end st #(do (t/is (= @a 3)) 450 | (t/is (= @vacc [1 2 3])) 451 | (done)))))) 452 | 453 | (t/deftest observable-to-atom-with-atom-and-function 454 | (t/async done 455 | (let [st (rx/from [1 2 3]) 456 | a (atom [])] 457 | (rx/to-atom st a conj) 458 | (rx/on-end st #(do (t/is (= @a [1 2 3])) 459 | (done)))))) 460 | 461 | (t/deftest transform-with-stateless-transducers 462 | (t/async done 463 | (let [s (rx/from [1 2 3 4 5 6]) 464 | xf (comp 465 | (map inc) 466 | (filter odd?)) 467 | ts (rx/transform xf s)] 468 | 469 | (drain! ts #(t/is (= % [3 5 7]))) 470 | (rx/on-end ts done)))) 471 | 472 | (t/deftest transform-with-stateful-transducers 473 | (t/async done 474 | (let [s (rx/from [1 2 3 4 5 6]) 475 | ts (rx/transform (comp 476 | (partition-all 2) 477 | (take 2)) 478 | s)] 479 | (drain! ts #(t/is (= % [[1 2] [3 4]]))) 480 | (rx/on-end ts done)))) 481 | 482 | (t/deftest observe-on 483 | (t/async done 484 | (let [coll [1 2 3] 485 | s (rx/observe-on :asap (rx/from coll))] 486 | (t/is (rx/observable? s)) 487 | (drain! s #(t/is (= % coll))) 488 | (rx/on-end s done)))) 489 | 490 | (t/deftest subscribe-on 491 | (t/async done 492 | (let [coll [1 2 3] 493 | s (rx/subscribe-on :queue (rx/from coll))] 494 | (t/is (rx/observable? s)) 495 | (drain! s #(t/is (= % coll))) 496 | (rx/on-end s done)))) 497 | 498 | (t/deftest scheduler-predicate-and-resolver 499 | (t/is (rx/scheduler? (rx/scheduler :asap))) 500 | (t/is (rx/scheduler? (rx/scheduler :queue))) 501 | (t/is (rx/scheduler? (rx/scheduler :async))) 502 | (t/is (rx/scheduler? (rx/scheduler :af))) 503 | (t/is (rx/scheduler? (rx/scheduler :animation-frame)))) 504 | -------------------------------------------------------------------------------- /tests.edn: -------------------------------------------------------------------------------- 1 | #kaocha/v1 2 | {:tests [{:id :unit 3 | :test-paths ["test/beicon/tests"] 4 | :source-paths ["src"] 5 | :ns-patterns ["test-"]}] 6 | ;; :reporter kaocha.report.progress/progress 7 | ;; :plugins [:profiling :notifier] 8 | } 9 | -------------------------------------------------------------------------------- /tools.clj: -------------------------------------------------------------------------------- 1 | (require '[clojure.java.shell :as shell]) 2 | (require '[rebel-readline.core] 3 | '[rebel-readline.clojure.main] 4 | '[rebel-readline.clojure.line-reader] 5 | '[rebel-readline.clojure.service.local] 6 | '[rebel-readline.cljs.service.local] 7 | '[rebel-readline.cljs.repl]) 8 | (require '[cljs.build.api :as api] 9 | '[cljs.repl :as repl] 10 | '[cljs.repl.node :as node]) 11 | 12 | (defmulti task first) 13 | 14 | (defmethod task :default 15 | [args] 16 | (let [all-tasks (-> task methods (dissoc :default) keys sort) 17 | interposed (->> all-tasks (interpose ", ") (apply str))] 18 | (println "Unknown or missing task. Choose one of:" interposed) 19 | (System/exit 1))) 20 | 21 | (defmethod task "repl:jvm" 22 | [args] 23 | (rebel-readline.core/with-line-reader 24 | (rebel-readline.clojure.line-reader/create 25 | (rebel-readline.clojure.service.local/create)) 26 | (clojure.main/repl 27 | :prompt (fn []) ;; prompt is handled by line-reader 28 | :read (rebel-readline.clojure.main/create-repl-read)))) 29 | 30 | (defmethod task "repl:node" 31 | [args] 32 | (rebel-readline.core/with-line-reader 33 | (rebel-readline.clojure.line-reader/create 34 | (rebel-readline.cljs.service.local/create)) 35 | (cljs.repl/repl 36 | (node/repl-env) 37 | :prompt (fn []) ;; prompt is handled by line-reader 38 | :read (rebel-readline.cljs.repl/create-repl-read) 39 | :output-dir "out" 40 | :cache-analysis false))) 41 | 42 | (def build-options 43 | {:main 'beicon.tests.main 44 | :output-to "out/tests.js" 45 | :output-dir "out/tests" 46 | :source-map "out/tests.js.map" 47 | :language-in :ecmascript6 48 | :language-out :no-transpile 49 | :target :nodejs 50 | :optimizations :advanced 51 | :pretty-print true 52 | :pseudo-names true 53 | :verbose true}) 54 | 55 | (defmethod task "build:tests" 56 | [args] 57 | (api/build (api/inputs "src" "test") build-options)) 58 | 59 | (defmethod task "watch:tests" 60 | [args] 61 | (println "Start watch loop...") 62 | (letfn [(run-tests [] 63 | (let [{:keys [out err]} (shell/sh "node" "out/tests.js")] 64 | (println out err))) 65 | (start-watch [] 66 | (try 67 | (api/watch (api/inputs "src" "test") 68 | (assoc build-options 69 | :watch-fn run-tests 70 | :source-map true 71 | :optimizations :none)) 72 | (catch Exception e 73 | (println "ERROR:" e) 74 | (Thread/sleep 2000) 75 | start-watch)))] 76 | (trampoline start-watch))) 77 | 78 | ;;; Build script entrypoint. This should be the last expression. 79 | 80 | (task *command-line-args*) 81 | --------------------------------------------------------------------------------