From af6e9841d7fbeffa1dfb615bdce94494f7139c43 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Sun, 2 Aug 2026 12:49:34 -0500 Subject: [PATCH] docs: port VitePress site to master Co-authored-by: Jessica Sachs --- .gitignore | 4 + apps/rxjs.dev-next/.vitepress/config.ts | 143 + apps/rxjs.dev-next/.vitepress/theme/index.ts | 8 + apps/rxjs.dev-next/.vitepress/theme/shiki.ts | 14 + apps/rxjs.dev-next/.vitepress/theme/style.css | 66 + apps/rxjs.dev-next/README.md | 21 + apps/rxjs.dev-next/components/ApiSection.vue | 119 + apps/rxjs.dev-next/components/Search.vue | 94 + apps/rxjs.dev-next/docs/black-lives-matter.md | 27 + apps/rxjs.dev-next/docs/code-of-conduct.md | 73 + .../deprecations/6-to-7-change-summary.md | 439 +++ .../docs/deprecations/array-argument.md | 33 + .../docs/deprecations/breaking-changes.md | 126 + apps/rxjs.dev-next/docs/deprecations/index.md | 11 + .../docs/deprecations/multicasting.md | 439 +++ .../docs/deprecations/result-selector.md | 43 + .../docs/deprecations/scheduler-argument.md | 85 + .../docs/deprecations/subscribe-arguments.md | 56 + .../docs/deprecations/to-promise.md | 98 + .../docs/guide/core-semantics.md | 33 + .../docs/guide/glossary-and-semantics.md | 149 + .../docs/guide/higher-order-observables.md | 23 + apps/rxjs.dev-next/docs/guide/importing.md | 189 ++ apps/rxjs.dev-next/docs/guide/installation.md | 55 + apps/rxjs.dev-next/docs/guide/observable.md | 450 +++ apps/rxjs.dev-next/docs/guide/observer.md | 38 + apps/rxjs.dev-next/docs/guide/operators.md | 346 ++ apps/rxjs.dev-next/docs/guide/overview.md | 124 + apps/rxjs.dev-next/docs/guide/scheduler.md | 147 + apps/rxjs.dev-next/docs/guide/subject.md | 377 +++ apps/rxjs.dev-next/docs/guide/subscription.md | 47 + .../docs/guide/testing/marble-testing.md | 292 ++ apps/rxjs.dev-next/docs/index.md | 38 + apps/rxjs.dev-next/docs/license.md | 23 + .../docs/maintainer-guidelines.md | 35 + apps/rxjs.dev-next/docs/next/index.md | 20 + .../docs/public/Rx_Logo-512-512.png | Bin 0 -> 55894 bytes .../images/favicons/apple-touch-icon.png | Bin 0 -> 20198 bytes .../public/images/favicons/favicon-96x96.png | Bin 0 -> 8952 bytes .../docs/public/images/favicons/favicon.ico | Bin 0 -> 15086 bytes .../docs/public/images/favicons/favicon.svg | 3 + .../favicons/web-app-manifest-192x192.png | Bin 0 -> 20424 bytes .../favicons/web-app-manifest-512x512.png | Bin 0 -> 75769 bytes .../images/guide/marble-diagram-anatomy.svg | 1 + .../images/marble-diagrams/audit-dark.svg | 6 + .../images/marble-diagrams/audit-light.svg | 1 + .../images/marble-diagrams/auditTime.png | Bin 0 -> 53615 bytes .../public/images/marble-diagrams/buffer.png | Bin 0 -> 74530 bytes .../images/marble-diagrams/bufferCount.png | Bin 0 -> 73492 bytes .../images/marble-diagrams/bufferTime.png | Bin 0 -> 63212 bytes .../images/marble-diagrams/bufferToggle.png | Bin 0 -> 73593 bytes .../marble-diagrams/bufferWhen-dark.svg | 6 + .../marble-diagrams/bufferWhen-light.svg | 1 + .../public/images/marble-diagrams/catch.png | Bin 0 -> 49025 bytes .../images/marble-diagrams/combineAll.png | Bin 0 -> 90568 bytes .../images/marble-diagrams/combineLatest.png | Bin 0 -> 79654 bytes .../public/images/marble-diagrams/concat.png | Bin 0 -> 50283 bytes .../images/marble-diagrams/concatAll-dark.svg | 6 + .../marble-diagrams/concatAll-light.svg | 1 + .../images/marble-diagrams/concatAll.png | Bin 0 -> 105910 bytes .../images/marble-diagrams/concatMap.png | Bin 0 -> 74068 bytes .../images/marble-diagrams/concatMapTo.png | Bin 0 -> 72185 bytes .../public/images/marble-diagrams/count.png | Bin 0 -> 31773 bytes .../public/images/marble-diagrams/create.png | Bin 0 -> 24279 bytes .../images/marble-diagrams/debounce-dark.svg | 6 + .../images/marble-diagrams/debounce-light.svg | 1 + .../images/marble-diagrams/debounceTime.png | Bin 0 -> 49196 bytes .../images/marble-diagrams/defaultIfEmpty.png | Bin 0 -> 26866 bytes .../public/images/marble-diagrams/defer.png | Bin 0 -> 31252 bytes .../images/marble-diagrams/delay-dark.svg | 6 + .../images/marble-diagrams/delay-light.svg | 1 + .../images/marble-diagrams/delayWhen.png | Bin 0 -> 67310 bytes .../images/marble-diagrams/dematerialize.png | Bin 0 -> 44822 bytes .../marble-diagrams/distinctUntilChanged.png | Bin 0 -> 46083 bytes .../distinctUntilKeyChanged.png | Bin 0 -> 55520 bytes .../images/marble-diagrams/elementAt.png | Bin 0 -> 39798 bytes .../public/images/marble-diagrams/empty.png | Bin 0 -> 4890 bytes .../public/images/marble-diagrams/endWith.png | Bin 0 -> 41262 bytes .../public/images/marble-diagrams/every.png | Bin 0 -> 48991 bytes .../marble-diagrams/exhaustAll-dark.svg | 6 + .../marble-diagrams/exhaustAll-light.svg | 1 + .../images/marble-diagrams/exhaustMap.png | Bin 0 -> 69554 bytes .../public/images/marble-diagrams/expand.png | Bin 0 -> 52018 bytes .../public/images/marble-diagrams/filter.png | Bin 0 -> 48035 bytes .../public/images/marble-diagrams/find.png | Bin 0 -> 43283 bytes .../images/marble-diagrams/findIndex.png | Bin 0 -> 44057 bytes .../public/images/marble-diagrams/first.png | Bin 0 -> 35187 bytes .../images/marble-diagrams/forkJoin.png | Bin 0 -> 32130 bytes .../public/images/marble-diagrams/from.png | Bin 0 -> 28492 bytes .../images/marble-diagrams/fromEvent.png | Bin 0 -> 26468 bytes .../marble-diagrams/fromEventPattern.png | Bin 0 -> 27584 bytes .../images/marble-diagrams/generate.png | Bin 0 -> 28523 bytes .../public/images/marble-diagrams/groupBy.png | Bin 0 -> 97782 bytes .../images/marble-diagrams/ignoreElements.png | Bin 0 -> 35358 bytes .../images/marble-diagrams/interval.png | Bin 0 -> 34220 bytes .../public/images/marble-diagrams/isEmpty.png | Bin 0 -> 18507 bytes .../public/images/marble-diagrams/last.png | Bin 0 -> 31264 bytes .../public/images/marble-diagrams/map.png | Bin 0 -> 46606 bytes .../public/images/marble-diagrams/mapTo.png | Bin 0 -> 41809 bytes .../images/marble-diagrams/materialize.png | Bin 0 -> 45719 bytes .../public/images/marble-diagrams/max.png | Bin 0 -> 32378 bytes .../public/images/marble-diagrams/merge.png | Bin 0 -> 65315 bytes .../images/marble-diagrams/mergeAll.png | Bin 0 -> 93986 bytes .../images/marble-diagrams/mergeMap.png | Bin 0 -> 74065 bytes .../images/marble-diagrams/mergeMapTo.png | Bin 0 -> 72208 bytes .../public/images/marble-diagrams/min.png | Bin 0 -> 28841 bytes .../public/images/marble-diagrams/never.png | Bin 0 -> 3911 bytes .../images/marble-diagrams/observeOn.png | Bin 0 -> 39020 bytes .../docs/public/images/marble-diagrams/of.png | Bin 0 -> 23438 bytes .../marble-diagrams/onErrorResumeNext.png | Bin 0 -> 51981 bytes .../images/marble-diagrams/pairwise.png | Bin 0 -> 54276 bytes .../images/marble-diagrams/partition.png | Bin 0 -> 49324 bytes .../public/images/marble-diagrams/race.png | Bin 0 -> 22306 bytes .../public/images/marble-diagrams/range.png | Bin 0 -> 49796 bytes .../public/images/marble-diagrams/reduce.png | Bin 0 -> 39039 bytes .../public/images/marble-diagrams/repeat.png | Bin 0 -> 47272 bytes .../images/marble-diagrams/repeatWhen.png | Bin 0 -> 57276 bytes .../public/images/marble-diagrams/retry.png | Bin 0 -> 51365 bytes .../images/marble-diagrams/retryWhen.png | Bin 0 -> 52128 bytes .../public/images/marble-diagrams/sample.png | Bin 0 -> 61409 bytes .../images/marble-diagrams/sampleTime.png | Bin 0 -> 50326 bytes .../public/images/marble-diagrams/scan.png | Bin 0 -> 45317 bytes .../images/marble-diagrams/sequenceEqual.png | Bin 0 -> 51448 bytes .../public/images/marble-diagrams/share.png | Bin 0 -> 44438 bytes .../public/images/marble-diagrams/single.png | Bin 0 -> 29225 bytes .../public/images/marble-diagrams/skip.png | Bin 0 -> 46060 bytes .../images/marble-diagrams/skipLast.png | Bin 0 -> 36679 bytes .../images/marble-diagrams/skipUntil.png | Bin 0 -> 49803 bytes .../images/marble-diagrams/skipWhile.png | Bin 0 -> 53838 bytes .../images/marble-diagrams/startWith.png | Bin 0 -> 39983 bytes .../images/marble-diagrams/subscribeOn.png | Bin 0 -> 38652 bytes .../images/marble-diagrams/switchAll.png | Bin 0 -> 87695 bytes .../images/marble-diagrams/switchMap.png | Bin 0 -> 73211 bytes .../images/marble-diagrams/switchMapTo.png | Bin 0 -> 69614 bytes .../public/images/marble-diagrams/take.png | Bin 0 -> 35627 bytes .../images/marble-diagrams/takeLast.png | Bin 0 -> 37912 bytes .../images/marble-diagrams/takeUntil.png | Bin 0 -> 44166 bytes .../images/marble-diagrams/takeWhile.png | Bin 0 -> 49180 bytes .../public/images/marble-diagrams/tap.png | Bin 0 -> 47093 bytes .../images/marble-diagrams/throttle-dark.svg | 6 + .../images/marble-diagrams/throttle-light.svg | 1 + .../images/marble-diagrams/throttle.svg | 1 + .../images/marble-diagrams/throttleTime.png | Bin 0 -> 71267 bytes .../public/images/marble-diagrams/throw.png | Bin 0 -> 6268 bytes .../images/marble-diagrams/throwIfEmpty.png | Bin 0 -> 7907 bytes .../images/marble-diagrams/timeInterval.png | Bin 0 -> 42359 bytes .../public/images/marble-diagrams/timeout.png | Bin 0 -> 28732 bytes .../images/marble-diagrams/timeoutWith.png | Bin 0 -> 57313 bytes .../public/images/marble-diagrams/timer.png | Bin 0 -> 30655 bytes .../images/marble-diagrams/timestamp.png | Bin 0 -> 42177 bytes .../public/images/marble-diagrams/toArray.png | Bin 0 -> 29561 bytes .../public/images/marble-diagrams/window.png | Bin 0 -> 109006 bytes .../images/marble-diagrams/windowCount.png | Bin 0 -> 108238 bytes .../images/marble-diagrams/windowTime.png | Bin 0 -> 79837 bytes .../images/marble-diagrams/windowToggle.png | Bin 0 -> 62509 bytes .../marble-diagrams/windowWhen-dark.svg | 6 + .../marble-diagrams/windowWhen-light.svg | 1 + .../images/marble-diagrams/windowWhen.svg | 1 + .../images/marble-diagrams/withLatestFrom.png | Bin 0 -> 66303 bytes .../public/images/marble-diagrams/zipAll.png | Bin 0 -> 86624 bytes .../images/marketing/home/Rx_Logo-512-512.png | Bin 0 -> 46717 bytes .../marketing/home/rxjs-live-london.svg | 98 + .../images/marketing/home/rxjs-live.svg | 1 + .../docs/public/images/support/rxjs-404.png | Bin 0 -> 62996 bytes .../docs/public/site.webmanifest | 21 + apps/rxjs.dev-next/docs/team.md | 244 ++ apps/rxjs.dev-next/package.json | 36 + apps/rxjs.dev-next/postcss.config.mjs | 7 + apps/rxjs.dev-next/tailwind.config.mjs | 18 + .../tools/api-generator/clean.ts | 5 + .../tools/api-generator/index.ts | 22 + .../tools/api-generator/pages/index.md | 49 + .../api-generator/typedoc-plugin-rxjs.mjs | 597 ++++ .../tools/marbles/diagrams/audit.txt | 11 + .../tools/marbles/diagrams/bufferWhen.txt | 15 + .../tools/marbles/diagrams/concatAll.txt | 11 + .../tools/marbles/diagrams/debounce.txt | 11 + .../tools/marbles/diagrams/delay.txt | 8 + .../tools/marbles/diagrams/exhaustAll.txt | 12 + .../tools/marbles/diagrams/throttle.txt | 11 + .../tools/marbles/diagrams/windowWhen.txt | 20 + .../tools/marbles/generate-diagrams.ts | 146 + apps/rxjs.dev-next/tsconfig.typedoc.json | 33 + apps/rxjs.dev-next/typedoc.config.mjs | 119 + package.json | 4 +- pnpm-lock.yaml | 2952 ++++++++++++++++- pnpm-workspace.yaml | 7 +- 187 files changed, 8619 insertions(+), 186 deletions(-) create mode 100644 apps/rxjs.dev-next/.vitepress/config.ts create mode 100644 apps/rxjs.dev-next/.vitepress/theme/index.ts create mode 100644 apps/rxjs.dev-next/.vitepress/theme/shiki.ts create mode 100644 apps/rxjs.dev-next/.vitepress/theme/style.css create mode 100644 apps/rxjs.dev-next/README.md create mode 100644 apps/rxjs.dev-next/components/ApiSection.vue create mode 100644 apps/rxjs.dev-next/components/Search.vue create mode 100644 apps/rxjs.dev-next/docs/black-lives-matter.md create mode 100644 apps/rxjs.dev-next/docs/code-of-conduct.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/6-to-7-change-summary.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/array-argument.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/breaking-changes.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/index.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/multicasting.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/result-selector.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/scheduler-argument.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/subscribe-arguments.md create mode 100644 apps/rxjs.dev-next/docs/deprecations/to-promise.md create mode 100644 apps/rxjs.dev-next/docs/guide/core-semantics.md create mode 100644 apps/rxjs.dev-next/docs/guide/glossary-and-semantics.md create mode 100644 apps/rxjs.dev-next/docs/guide/higher-order-observables.md create mode 100644 apps/rxjs.dev-next/docs/guide/importing.md create mode 100644 apps/rxjs.dev-next/docs/guide/installation.md create mode 100644 apps/rxjs.dev-next/docs/guide/observable.md create mode 100644 apps/rxjs.dev-next/docs/guide/observer.md create mode 100644 apps/rxjs.dev-next/docs/guide/operators.md create mode 100644 apps/rxjs.dev-next/docs/guide/overview.md create mode 100644 apps/rxjs.dev-next/docs/guide/scheduler.md create mode 100644 apps/rxjs.dev-next/docs/guide/subject.md create mode 100644 apps/rxjs.dev-next/docs/guide/subscription.md create mode 100644 apps/rxjs.dev-next/docs/guide/testing/marble-testing.md create mode 100644 apps/rxjs.dev-next/docs/index.md create mode 100644 apps/rxjs.dev-next/docs/license.md create mode 100644 apps/rxjs.dev-next/docs/maintainer-guidelines.md create mode 100644 apps/rxjs.dev-next/docs/next/index.md create mode 100644 apps/rxjs.dev-next/docs/public/Rx_Logo-512-512.png create mode 100644 apps/rxjs.dev-next/docs/public/images/favicons/apple-touch-icon.png create mode 100644 apps/rxjs.dev-next/docs/public/images/favicons/favicon-96x96.png create mode 100644 apps/rxjs.dev-next/docs/public/images/favicons/favicon.ico create mode 100644 apps/rxjs.dev-next/docs/public/images/favicons/favicon.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/favicons/web-app-manifest-192x192.png create mode 100644 apps/rxjs.dev-next/docs/public/images/favicons/web-app-manifest-512x512.png create mode 100644 apps/rxjs.dev-next/docs/public/images/guide/marble-diagram-anatomy.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/audit-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/audit-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/auditTime.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/buffer.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/bufferCount.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/bufferTime.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/bufferToggle.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/bufferWhen-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/bufferWhen-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/catch.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/combineAll.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/combineLatest.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/concat.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/concatAll-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/concatAll-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/concatAll.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/concatMap.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/concatMapTo.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/count.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/create.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/debounce-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/debounce-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/debounceTime.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/defaultIfEmpty.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/defer.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/delay-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/delay-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/delayWhen.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/dematerialize.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/distinctUntilChanged.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/distinctUntilKeyChanged.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/elementAt.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/empty.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/endWith.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/every.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/exhaustAll-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/exhaustAll-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/exhaustMap.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/expand.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/filter.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/find.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/findIndex.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/first.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/forkJoin.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/from.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/fromEvent.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/fromEventPattern.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/generate.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/groupBy.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/ignoreElements.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/interval.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/isEmpty.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/last.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/map.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/mapTo.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/materialize.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/max.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/merge.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/mergeAll.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/mergeMap.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/mergeMapTo.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/min.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/never.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/observeOn.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/of.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/onErrorResumeNext.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/pairwise.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/partition.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/race.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/range.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/reduce.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/repeat.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/repeatWhen.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/retry.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/retryWhen.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/sample.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/sampleTime.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/scan.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/sequenceEqual.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/share.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/single.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/skip.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/skipLast.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/skipUntil.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/skipWhile.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/startWith.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/subscribeOn.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/switchAll.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/switchMap.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/switchMapTo.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/take.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/takeLast.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/takeUntil.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/takeWhile.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/tap.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/throttle-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/throttle-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/throttle.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/throttleTime.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/throw.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/throwIfEmpty.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/timeInterval.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/timeout.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/timeoutWith.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/timer.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/timestamp.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/toArray.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/window.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/windowCount.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/windowTime.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/windowToggle.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/windowWhen-dark.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/windowWhen-light.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/windowWhen.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/withLatestFrom.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marble-diagrams/zipAll.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marketing/home/Rx_Logo-512-512.png create mode 100644 apps/rxjs.dev-next/docs/public/images/marketing/home/rxjs-live-london.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/marketing/home/rxjs-live.svg create mode 100644 apps/rxjs.dev-next/docs/public/images/support/rxjs-404.png create mode 100644 apps/rxjs.dev-next/docs/public/site.webmanifest create mode 100644 apps/rxjs.dev-next/docs/team.md create mode 100644 apps/rxjs.dev-next/package.json create mode 100644 apps/rxjs.dev-next/postcss.config.mjs create mode 100644 apps/rxjs.dev-next/tailwind.config.mjs create mode 100644 apps/rxjs.dev-next/tools/api-generator/clean.ts create mode 100644 apps/rxjs.dev-next/tools/api-generator/index.ts create mode 100644 apps/rxjs.dev-next/tools/api-generator/pages/index.md create mode 100644 apps/rxjs.dev-next/tools/api-generator/typedoc-plugin-rxjs.mjs create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/audit.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/bufferWhen.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/concatAll.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/debounce.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/delay.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/exhaustAll.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/throttle.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/diagrams/windowWhen.txt create mode 100644 apps/rxjs.dev-next/tools/marbles/generate-diagrams.ts create mode 100644 apps/rxjs.dev-next/tsconfig.typedoc.json create mode 100644 apps/rxjs.dev-next/typedoc.config.mjs diff --git a/.gitignore b/.gitignore index e965e4b9fe..45df06cf88 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ typings/ # Generated dist/ .tshy-build +apps/rxjs.dev-next/docs/api/ +apps/rxjs.dev-next/tsconfig.typedoc.tsbuildinfo +apps/rxjs.dev-next/.vitepress/cache/ +apps/rxjs.dev-next/.vitepress/dist/ # Import location artifacts packages/rxjs/ajax/ diff --git a/apps/rxjs.dev-next/.vitepress/config.ts b/apps/rxjs.dev-next/.vitepress/config.ts new file mode 100644 index 0000000000..50e17f19a7 --- /dev/null +++ b/apps/rxjs.dev-next/.vitepress/config.ts @@ -0,0 +1,143 @@ +import { defineConfig, type UserConfig } from 'vitepress' +import type { DefaultTheme, Outline } from 'vitepress/theme' +import typedocSidebar from '../docs/api/typedoc-sidebar.json'; + + +export default defineConfig({ + title: 'RxJS', + description: 'Reactive Extensions Library for JavaScript', + srcDir: 'docs', + + // Migrated stable guides still use the Angular site's API URL scheme. Keep + // every non-API link checked while versioned API redirects are designed. + ignoreDeadLinks: [ + /^\/api(?:\/|$)/, + /^\.\.?\/api\//, + /^\.\/\.\.\/\.\.\/\.\.\/(?:CODE_OF_CONDUCT|CONTRIBUTING)$/, + ], + + // Markdown configuration + markdown: { + // Shiki is enabled by default in VitePress + // Code blocks will be highlighted automatically + theme: { + light: 'github-light', + dark: 'github-dark' + }, + lineNumbers: true, + }, + + head: [ + ['link', { rel: 'icon', type: 'image/png', href: '/images/favicons/favicon-96x96.png', sizes: '96x96' }], + ['link', { rel: 'icon', type: 'image/svg+xml', href: '/images/favicons/favicon.svg' }], + ['link', { rel: 'shortcut icon', href: '/images/favicons/favicon.ico' }], + ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/images/favicons/apple-touch-icon.png' }], + ['link', { rel: 'manifest', href: '/site.webmanifest' }] + ], + + // Theme configuration + themeConfig: { + outline: { + level: [2, 3, 4, 5] as Outline['level'], + }, + logo: { src: '/images/favicons/favicon.svg', width: 24, height: 24 }, + socialLinks: [ + { icon: 'github', link: 'https://github.com/ReactiveX/rxjs' } + ], + search: { + provider: 'local' + }, + nav: [ + { text: 'RxJS Next', link: '/next/' }, + { text: 'API Reference', link: '/api' }, + { text: 'RxJS 7 Guides', link: '/guide/overview' }, + { + text: 'About', + items: [ + { text: 'Team', link: '/team' }, + { text: 'Code of Conduct', link: '/code-of-conduct' }, + { text: 'Black Lives Matter', link: '/black-lives-matter' } + ] + }, + + ], + sidebar: { + '/next/': [ + { + text: 'RxJS Next', + items: [{ text: 'Overview', link: '/next/' }] + } + ], + '/guide': [ + { + text: 'Getting Started', + items: [ + { + text: 'Overview', collapsed: true, items: [ + { text: 'Introduction', link: '/guide/overview' }, + { text: 'Observables', link: '/guide/observable' }, + { text: 'Observer', link: '/guide/observer' }, + { text: 'Operators', link: '/guide/operators' }, + { text: 'Subscription', link: '/guide/subscription' }, + { text: 'Subjects', link: '/guide/subject' }, + { text: 'Scheduler', link: '/guide/scheduler' }, + { text: 'Higher-Order Observables', link: '/guide/higher-order-observables' }, + ] + }, + { + text: 'Installation', + link: '/guide/installation' + }, + { + text: 'Importing', + link: '/guide/importing' + }, + { + text: 'Glossary', + collapsed: true, + items: [ + { text: 'Core Semantics', link: '/guide/core-semantics' }, + { text: 'Glossary', link: '/guide/glossary-and-semantics' } + ] + }, + { + text: 'Testing', + items: [ + { text: 'Marble Testing', link: '/guide/testing/marble-testing' } + ] + }, + { text: 'Migration Guide', link: '/deprecations' } + ] + }, + ], + '/deprecations': [ + { + text: 'Deprecations & Breaking Changes', + items: [ + { + text: 'v7.x Breaking Changes', + link: '/deprecations/breaking-changes', + items: [ + { text: 'v6 to v7 change summary', link: '/deprecations/6-to-7-change-summary' }, + { text: 'Scheduler Argument', link: '/deprecations/scheduler-argument' }, + { text: 'Subscribe Arguments', link: '/deprecations/subscribe-arguments' }, + { text: 'ResultSelector Arguments', link: '/deprecations/result-selector' }, + { text: 'Array Arguments', link: '/deprecations/array-argument' }, + { text: 'Multicasting', link: '/deprecations/multicasting' }, + { text: 'Conversion to Promises', link: '/deprecations/to-promise' }, + + ] + }, + { text: 'v6.x Changelog', link: 'https://github.com/ReactiveX/rxjs/blob/6.x/CHANGELOG.md' }, + ] + }, + ], + '/api': [ + { + text: 'API Reference', + items: typedocSidebar, + } + ] + } + }, +} satisfies UserConfig); diff --git a/apps/rxjs.dev-next/.vitepress/theme/index.ts b/apps/rxjs.dev-next/.vitepress/theme/index.ts new file mode 100644 index 0000000000..1029ca6521 --- /dev/null +++ b/apps/rxjs.dev-next/.vitepress/theme/index.ts @@ -0,0 +1,8 @@ +import DefaultTheme from 'vitepress/theme' +import type { Theme } from 'vitepress' + +import './style.css' + +export default { + extends: DefaultTheme, +} satisfies Theme diff --git a/apps/rxjs.dev-next/.vitepress/theme/shiki.ts b/apps/rxjs.dev-next/.vitepress/theme/shiki.ts new file mode 100644 index 0000000000..470fd0c966 --- /dev/null +++ b/apps/rxjs.dev-next/.vitepress/theme/shiki.ts @@ -0,0 +1,14 @@ +// Shiki is already integrated in VitePress by default +// This file can be used for custom Shiki configuration if needed + +import type { UserConfig } from 'vitepress'; + +// VitePress uses Shiki by default for syntax highlighting +// No additional configuration needed unless we want custom themes or languages + +export const shikiConfig = { + // VitePress default Shiki theme + theme: 'github-dark', + // Additional languages can be added here if needed + langs: ['typescript', 'javascript', 'json', 'bash'], +}; diff --git a/apps/rxjs.dev-next/.vitepress/theme/style.css b/apps/rxjs.dev-next/.vitepress/theme/style.css new file mode 100644 index 0000000000..86d0b65dbe --- /dev/null +++ b/apps/rxjs.dev-next/.vitepress/theme/style.css @@ -0,0 +1,66 @@ +@import 'tailwindcss'; + +/* Tell Tailwind where to scan for classes */ +@source "../../components"; +@source "../../docs"; + +:root { + /* brand-1: most solid color for colored text (must satisfy contrast on brand-soft) */ + --vp-c-brand-1: #f03aa0; + --vp-c-brand-2: #ee1090; + --vp-c-brand-3: #d00e80; + /* brand-soft: subtle background (semi-transparent, must satisfy contrast with brand-1 text) */ + --vp-c-brand-soft: rgba(238, 16, 144, 0.14); + + /* Hero section customization */ + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: linear-gradient(-45deg, var(--vp-c-brand-3) 30%, var(--vp-c-brand-1) 50%); +} + +.dark { + /* Dark mode brand colors - adjust if needed for better contrast */ + --vp-c-brand-1: #f03aa0; + --vp-c-brand-2: #ee1090; + --vp-c-brand-3: #d00e80; + --vp-c-brand-soft: rgba(238, 16, 144, 0.16); + + --vp-home-hero-name-background: linear-gradient(-45deg, var(--vp-c-brand-1) 30%, var(--vp-c-brand-3) 50%); + --vp-home-hero-image-filter: blur(160px); +} + +@theme static { + /* Define new custom color */ + --color-brand-300: #f03aa0; + --color-brand-400: #ee1090; + --color-brand-500: #ee1090; + --color-brand-600: #d00e80; +} + +.only-dark { + display: none; +} +.dark .only-dark { + display: initial; +} +.dark .only-light { + display: none; +} + +img[src*='marble'] { + border-radius: 8px; +} + +button, +input, +optgroup, +select, +textarea { + border: revert-layer; + padding: revert-layer; + background-color: revert-layer; +} + +.image-bg { + top: 60% !important; + left: 50% !important; +} diff --git a/apps/rxjs.dev-next/README.md b/apps/rxjs.dev-next/README.md new file mode 100644 index 0000000000..44a9bc1a27 --- /dev/null +++ b/apps/rxjs.dev-next/README.md @@ -0,0 +1,21 @@ +# RxJS Dev Next + +This is the next-generation documentation site for RxJS, built with VitePress. + +## Development + +```bash +pnpm dev +``` + +## Build + +```bash +pnpm build +``` + +## Preview + +```bash +pnpm preview +``` diff --git a/apps/rxjs.dev-next/components/ApiSection.vue b/apps/rxjs.dev-next/components/ApiSection.vue new file mode 100644 index 0000000000..15a2953445 --- /dev/null +++ b/apps/rxjs.dev-next/components/ApiSection.vue @@ -0,0 +1,119 @@ + + + + + + + diff --git a/apps/rxjs.dev-next/components/Search.vue b/apps/rxjs.dev-next/components/Search.vue new file mode 100644 index 0000000000..18de641ec7 --- /dev/null +++ b/apps/rxjs.dev-next/components/Search.vue @@ -0,0 +1,94 @@ + + + + diff --git a/apps/rxjs.dev-next/docs/black-lives-matter.md b/apps/rxjs.dev-next/docs/black-lives-matter.md new file mode 100644 index 0000000000..87df36c2ab --- /dev/null +++ b/apps/rxjs.dev-next/docs/black-lives-matter.md @@ -0,0 +1,27 @@ + + +
+ +# Black Lives Matter + +We stand in solidarity with the Black Lives Matter movement. We believe that technologists must not be silent in the fight to end racial inequality. + +We ask you to stand with us and help educate your team members and those in your network on how to help dismantle a system that oppresses Black people. Find a list of starting resources here: + +- [Let's get to the root of racial injustice by Megan Ming Francis](https://www.youtube.com/watch?v=-aCn72iXO9s) +- [What Leaders can do for Black Employees by Dr. Akilah Cadet](http://www.changecadet.com/blog/2020/5/30/what-leaders-can-do-for-black-employees) +- [Hey Employers: Do Black Lives Matter? by Pariss Athena](https://blacktechpipeline.substack.com/p/hey-employers-do-black-lives-matter) +- [Algorithms of Oppression by Safiya Umoja Noble](https://safiyaunoble.com/) +- [Rage Inside The Machine by Robert Smith](https://www.rageinsidethemachine.com/) +- [Technically Wrong by Sara Wachter-Boettcher](https://www.sarawb.com/) + +In solidarity, we ask you to consider financially supporting efforts such as [Black Lives Matter](https://blacklivesmatter.com/), [The Equal Justice Initiative](https://support.eji.org/give/153413/#!/donation/checkout) or local charity organizations. diff --git a/apps/rxjs.dev-next/docs/code-of-conduct.md b/apps/rxjs.dev-next/docs/code-of-conduct.md new file mode 100644 index 0000000000..9c68fef5d0 --- /dev/null +++ b/apps/rxjs.dev-next/docs/code-of-conduct.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting Ben Lesh (ben@benlesh.com), Tracy Lee (tracy@thisdot.co) or OJ Kwon (kwon.ohjoong@gmail.com). All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/apps/rxjs.dev-next/docs/deprecations/6-to-7-change-summary.md b/apps/rxjs.dev-next/docs/deprecations/6-to-7-change-summary.md new file mode 100644 index 0000000000..e2d1625e04 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/6-to-7-change-summary.md @@ -0,0 +1,439 @@ +# RxJS 6.x to 7.x Detailed Change List + +This document contains a detailed list of changes between RxJS 6.x and RxJS 7.x, presented in the order they can be found when diffing the TypeScript APIs in various module files. + +## module `rxjs` + +### Breaking changes + +#### AsyncSubject + +- `_subscribe` method is no longer `public` and is now `protected`. +- no longer has its own implementation of the `error` method inherited from `Subject`. + +#### BehaviorSubject + +- `_subscribe` method is no longer `public` and is now `protected`. +- `value` property is a getter `get value()` instead of `readonly value`, and can no longer be forcibly set. + +#### bindCallback + +- Generic signatures have changed. Do not explicitly pass generics. + +#### combineLatest + +- Generic signatures have changed. Do not explicitly pass generics. + +#### concat + +- Generic signatures have changed. Do not explicitly pass generics. + +#### ConnectableObservable + +- `_isComplete` is no longer a property. +- `_subscribe` method is no longer `public` and is now `protected`. + +#### defer + +- Generic argument no longer extends `void`. +- `defer` no longer allows factories to return void or undefined. All factories passed to `defer` must return a proper `ObservableInput`, such as `Observable`, `Promise`, et al. To get the same behavior as you may have relied on previously, `return EMPTY` or `return of()` from the factory. + +#### forkJoin + +- Generic signatures have changed. Do not explicitly pass generics. + +#### fromEvent + +- The `fromEvent` signatures have been changed and there are now separate signatures for each type of target - DOM, Node, jQuery, etc. That means that an attempt to pass options - like `{ once: true }` - to a target that does not support an options argument will result in a TypeScript error. + +#### GroupedObservable + +- No longer publicly exposes `_subscribe` +- `key` properly is `readonly`. +- No longer publicly exposes `constructor`. + +#### iif + +- Generic signatures have changed. Do not explicitly pass generics. +- `iif` will no longer allow result arguments that are `undefined`. This was a bad call pattern that was likely an error in most cases. If for some reason you are relying on this behavior, simply substitute `EMPTY` in place of the `undefined` argument. This ensures that the behavior was intentional and desired, rather than the result of an accidental `undefined` argument. + +#### isObservable + +- No longer has a generic and returns `Observable`, you must cast the result. + +#### merge + +- Generic signatures have changed. Do not explicitly pass generics. + +#### Notification + +- The `error` property is now `readonly`. +- The `hasValue` property is now `readonly`. +- The `kind` property is now `readonly`. +- The `value` property is now `readonly` and may be `undefined`. +- `constructor` signature now only allows valid construction. For example `new Notification('C', 'some_value')` will be an error in TypeScript. + +#### Observable + +- `_isScalar` property removed. +- `_subscribe` method is no longer `public` and is now marked `@internal`. +- `_trySubscribe` method is no longer `public` and is now `@internal`. +- `pipe` method calls with `9` or more arguments will now return `Observable` rather than `Observable<{}>`. +- `toPromise` method now correctly returns `Promise` instead of `Promise`. This is a correction without a runtime change, because if the observable does not emit a value before completion, the promise will resolve with `undefined`. +- `static if` and `static throw` properties are no longer defined. They were unused in version 6. +- `lift`, `source`, and `operator` properties are still **deprecated**, and should not be used. They are implementation details, and will very likely be renamed or missing in version 8. + +#### of + +- Generic signatures have changed. Do not explicitly pass generics. + +#### onErrorResumeNext + +- Generic signatures have changed. Do not explicitly pass generics. + +#### pairs + +- Generic signatures have changed. Do not explicitly pass generics. +- `pairs` will no longer function in IE without a polyfill for `Object.entries`. `pairs` itself is also deprecated in favor of users just using `from(Object.entries(obj))`. + +#### partition + +- Generic signatures have changed. Do not explicitly pass generics. + +#### pipe + +- Calls with `9` or more arguments will now return `(arg: A) => unknown` rather than `(arg: A) => {}`. + +#### race + +- Generic signatures have changed. Do not explicitly pass generics. +- `race` will no longer subscribe to subsequent observables if a provided source synchronously errors or completes. This means side effects that might have occurred during subscription in those rare cases will no longer occur. + +#### ReplaySubject + +- `_getNow` method has been removed. +- `_subscribe` method is no longer `public` and is now `protected`. + +#### Subscribable + +- `subscribe` will accept `Partial>` now. All overloads with functions as arguments have been removed. This is because `Subscribable` is intended to map to the basic observable contract from the TC39 proposal and the return type of a call to `[Symbol.observable]()`. + +#### SubscribableOrPromise + +- See notes on `Subscribable` above. + +#### Subscriber + +- `destination` property must now be a `Subscriber` or full `Observer`. +- `syncErrorThrowable` property has been removed. +- `syncErrorThrown` property has been removed. +- `syncErrorValue` property has been removed. +- `_unsubscribeAndRecycle` method has been removed. + +#### Subscription + +- `_parentOrParents` property has been removed. +- `add` method returns `void` and no longer returns a `Subscription`. Returning `Subscription` was an old behavior from the early days of version 5. If you add a function to a subscription (i.e. `subscription.add(fn)`), you can remove that function directly by calling `remove` with the same function instance. (i.e. `subscription.remove(fn)`). Previously, you needed to get the returned `Subscription` object and pass _that_ to `remove`. In version 6 and lower, the `Subscription` returned by calling `add` with another `Subscription` was always the same subscription you passed in. (meaning `subscription.add(subs1).add(subs2)` was an antipattern and the same as `subscription.add(subs1); subs1.add(subs2);`. + +#### VirtualAction + +- The static `sortActions` method has been removed. + +#### zip + +- Generic signatures have changed. Do not explicitly pass generics. +- Zipping a single array will now have a different result. This is an extreme corner-case, because it is very unlikely that anyone would want to zip an array with nothing at all. The workaround would be to wrap the array in another array `zip([[1,2,3]])`. But again, that's pretty weird. + +--- + +### New Features + +#### animationFrames + +- A new method for creating a stream of animation frames. Each event will carry with it a high-resolution timestamp, and an elapsed time since observation was started. + +#### config + +##### onUnhandledError + +- A handler for dealing with errors that make it all the way down to the "end" of the observation chain when there is no error handler in the observer. Useful for doing things like logging unhandled errors in RxJS observable chains. + +##### onStoppedNotification + +- A handler for edge cases where a subscriber within RxJS is notified after it has already "stopped", that is, a point in time where it has received an error or complete, but hasn't yet finalized. This is mostly useful for logging purposes. + +##### useDeprecatedNextContext + +- In RxJS 6, a little-used feature allowed users to access the `subscriber` directly as `this` within a call to the `next` handler. The problem with this is it incurred heavy performance penalties. That behavior has been changed (because it wasn't really documented and it was barely ever used) to not change the `this` context of any user-provided subscription handlers. If you need to get that feature back, you can switch it on with this flag. Note this behavior will be removed completely in version 8. + +#### connectable + +- This is the new means for creating a `ConnectableObservable`, and really is a replacement for non-selector usage of `multicast` and `publish` variants. Simply pass your source observable to `connectable` with the `Subject` you'd like to connect through. + +#### firstValueFrom + +- A better, more tree-shakable replacement for `toPromise()` (which is now deprecated). This function allows the user to convert any `Observable` into a `Promise` that will resolve when the source observable emits its first value. If the source observable closes without emitting a value, the returned promise will reject with an `EmptyError`, or it will resolve with a configured `defaultValue`. For more information, see the [deprecation guide](/deprecations/to-promise). + +#### lastValueFrom + +- A better, more tree-shakable replacement for `toPromise()` (which is now deprecated). This function allows the user to convert any `Observable` in to a `Promise` that will resolve when the source observable emits the last value. If the source observable closes without emitting a value, the returned promise will reject with an `EmptyError`, or it will resolve with a configured `defaultValue`. For more information, see the [deprecation guide](/deprecations/to-promise). + +#### ObservableInput + +- This is just a type, but it's important. This type defines the allowed types that can be passed to almost every API within RxJS that accepts an Observable. It has always accepted `Observable`, `Promise`, `Iterable`, and `ArrayLike`. Now it will also accept `AsyncIterable` and `ReadableStream`. + +##### AsyncIterable + +- `AsyncIterables` such as those defined by `IxJS` or by async generators (`async function*`), may now be passed to any API that accepts an observable, and can be converted to an `Observable` directly using `from`. + +##### ReadableStream + +- `ReadableStream` such as those returned by `fetch`, et al, can be passed to any API that accepts an observable, and can be converted to `Observable` directly using `from`. + +#### ReplaySubject + +- A [bug was fixed](https://github.com/ReactiveX/rxjs/pull/5696) that prevented a completed or errored `ReplaySubject` from accumulating values in its buffer when resubscribed to another source. This breaks some uses - like [this StackOverflow answer](https://stackoverflow.com/a/54957061) - that depended upon the buggy behavior. + +#### Subscription + +- Now allows adding and removing of functions directly via `add` and `remove` methods. + +#### throwError + +- Now accepts an `errorFactory` of `() => any` to defer the creation of the error until the time it will be emitted. It is recommended to use this method, as Errors created in most popular JavaScript runtimes will retain all values in the current scope for debugging purposes. + +## module `rxjs/operators` + +### Breaking Changes + +#### audit + +- The observable returned by the `audit` operator's duration selector must emit a next notification to end the duration. Complete notifications no longer end the duration. +- `audit` now emits the last value from the source when the source completes. Previously, `audit` would mirror the completion without emitting the value. + +#### auditTime + +- `auditTime` now emits the last value from the source when the source completes, after the audit duration elapses. Previously, `auditTime` would mirror the completion without emitting the value and without waiting for the audit duration to elapse. + +#### buffer + +- `buffer` now subscribes to the source observable before it subscribes to the closing notifier. Previously, it subscribed to the closing notifier first. +- Final buffered values will now always be emitted. To get the same behavior as the previous release, you can use `endWith` and `skipLast(1)`, like so: `source$.pipe(buffer(notifier$.pipe(endWith(true))), skipLast(1))` +- `closingNotifier` completion no longer completes the result of `buffer`. If that is truly a desired behavior, then you should use `takeUntil`. Something like: `source$.pipe(buffer(notifier$), takeUntil(notifier$.pipe(ignoreElements(), endWith(true))))`, where `notifier$` is multicast, although there are many ways to compose this behavior. + +#### bufferToggle + +- The observable returned by the `bufferToggle` operator's closing selector must emit a next notification to close the buffer. Complete notifications no longer close the buffer. + +#### bufferWhen + +- The observable returned by the `bufferWhen` operator's closing selector must emit a next notification to close the buffer. Complete notifications no longer close the buffer. + +#### combineLatest + +- Generic signatures have changed. Do not explicitly pass generics. + +#### concat + +- Generic signatures have changed. Do not explicitly pass generics. +- Still deprecated, use the new `concatWith`. + +#### concatAll + +- Generic signatures have changed. Do not explicitly pass generics. + +#### concatMapTo + +- Generic signatures have changed. Do not explicitly pass generics. + +#### count + +- No longer passes `source` observable as a third argument to the predicate. That feature was rarely used, and of limited value. The workaround is to simply close over the source inside of the function if you need to access it in there. + +#### debounce + +- The observable returned by the `debounce` operator's duration selector must emit a next notification to end the duration. Complete notifications no longer end the duration. + +#### debounceTime + +- The `debounceTime` implementation is more efficient and no longer schedules an action for each received next notification. However, because the implementation now uses the scheduler's concept of time, any tests using Jasmine's `clock` will need to ensure that [`jasmine.clock().mockDate()`](https://jasmine.github.io/api/edge/Clock.html#mockDate) is called after `jasmine.clock().install()` - because Jasmine does not mock `Date.now()` by default. + +#### defaultIfEmpty + +- Generic signatures have changed. Do not explicitly pass generics. +- `defaultIfEmpty` requires a value be passed. Will no longer convert `undefined` to `null` for no good reason. + +#### delayWhen + +- `delayWhen` will no longer emit if the duration selector simply completes without a value. Notifiers must notify with a value, not a completion. + +#### endWith + +- Generic signatures have changed. Do not explicitly pass generics. + +#### expand + +- Generic signatures have changed. Do not explicitly pass generics. + +#### finalize + +- `finalize` will now unsubscribe from its source _before_ it calls its callback. That means that `finalize` callbacks will run in the order in which they occur in the pipeline: `source.pipe(finalize(() => console.log(1)), finalize(() => console.log(2)))` will log `1` and then `2`. Previously, callbacks were called in the reverse order. + +#### map + +- `thisArg` will now default to `undefined`. The previous default of `MapSubscriber` never made any sense. This will only affect code that calls map with a `function` and references `this` like so: `source.pipe(map(function () { console.log(this); }))`. There wasn't anything useful about doing this, so the breakage is expected to be very minimal. If anything we're no longer leaking an implementation detail. + +#### merge + +- Generic signatures have changed. Do not explicitly pass generics. +- Still deprecated, use the new `mergeWith`. + +#### mergeAll + +- Generic signatures have changed. Do not explicitly pass generics. + +#### mergeScan + +- `mergeScan` will no longer emit its inner state again upon completion. + +#### pluck + +- Generic signatures have changed. Do not explicitly pass generics. + +#### race + +- Generic signatures have changed. Do not explicitly pass generics. + +#### reduce + +- Generic signatures have changed. Do not explicitly pass generics. + +#### sample + +- The `sample` operator's notifier observable must emit a next notification to effect a sample. Complete notifications no longer effect a sample. + +#### scan + +- Generic signatures have changed. Do not explicitly pass generics. + +#### single + +- The `single` operator will now throw for scenarios where values coming in are either not present, or do not match the provided predicate. Error types have thrown have also been updated, please check documentation for changes. + +#### skipLast + +- `skipLast` will no longer error when passed a negative number, rather it will simply return the source, as though `0` was passed. + +#### startWith + +- Generic signatures have changed. Do not explicitly pass generics. + +#### switchAll + +- Generic signatures have changed. Do not explicitly pass generics. + +#### switchMapTo + +- Generic signatures have changed. Do not explicitly pass generics. + +#### take + +- `take` and will now throw runtime error for arguments that are negative or NaN, this includes non-TS calls like `take()`. + +#### takeLast + +- `takeLast` now has runtime assertions that throw `TypeError`s for invalid arguments. Calling `takeLast` without arguments or with an argument that is `NaN` will throw a `TypeError`. + +#### throttle + +- The observable returned by the `throttle` operator's duration selector must emit a next notification to end the duration. Complete notifications no longer end the duration. + +#### throwError + +- In an extreme corner case for usage, `throwError` is no longer able to emit a function as an error directly. If you need to push a function as an error, you will have to use the factory function to return the function like so: `throwError(() => functionToEmit)`, in other words `throwError(() => () => console.log('called later'))`. + +#### window + +- The `windowBoundaries` observable no longer completes the result. It was only ever meant to notify of the window boundary. To get the same behavior as the old behavior, you would need to add an `endWith` and a `skipLast(1)` like so: `source$.pipe(window(notifier$.pipe(endWith(true))), skipLast(1))`. + +#### windowToggle + +- The observable returned by the `windowToggle` operator's closing selector must emit a next notification to close the window. Complete notifications no longer close the window. + +#### withLatestFrom + +- Generic signatures have changed. Do not explicitly pass generics. + +#### zip + +- Generic signatures have changed. Do not explicitly pass generics. +- Still deprecated, use the new `zipWith`. +- `zip` operators will no longer iterate provided iterables "as needed", instead the iterables will be treated as push-streams just like they would be everywhere else in RxJS. This means that passing an endless iterable will result in the thread locking up, as it will endlessly try to read from that iterable. This puts us in line with all other Rx implementations. To work around this, it is probably best to use `map` or some combination of `map` and `zip`. For example, `zip(source$, iterator)` could be `source$.pipe(map(value => [value, iterator.next().value]))`. + +### New Features + +#### connect + +- New operator to cover the use cases of `publish` variants that use a `selector`. Wherein the selector allows the user to define multicast behavior prior to connection to the source observable for the multicast. + +#### share + +- Added functionality to allow complete configuration of what type of `Subject` is used to multicast, and when that subject is reset. + +#### timeout + +- Added more configuration options to `timeout`, so it could be used to timeout just if the first item doesn't arrive quickly enough, or it could be used as a timeout between each item. Users may also pass a `Date` object to define an absolute time for a timeout for the first time to arrive. Adds additional information to the timeout error, and the ability to pass along metadata with the timeout for identification purposes. + +#### zipWith, concatWith, mergeWith, raceWith + +- Simply renamed versions of the operators `zip`, `concat`, `merge`, and `race`. So we can deprecate those old names and use the new names without collisions. + +## module `rxjs/ajax` + +### Breaking Changes + +#### ajax + +- `ajax` body serialization will now use default XHR behavior in all cases. If the body is a `Blob`, `ArrayBuffer`, any array buffer view (like a byte sequence, e.g. `Uint8Array`, etc), `FormData`, `URLSearchParams`, `string`, or `ReadableStream`, default handling is used. If the `body` is otherwise `typeof` `"object"`, then it will be converted to JSON via `JSON.stringify`, and the `Content-Type` header will be set to `application/json;charset=utf-8`. All other types will emit an error. +- The `Content-Type` header passed to `ajax` configuration no longer has any effect on the serialization behavior of the AJAX request. +- For TypeScript users, `AjaxRequest` is no longer the type that should be explicitly used to create an `ajax`. It is now `AjaxConfig`, although the two types are compatible, only `AjaxConfig` has `progressSubscriber` and `createXHR`. +- Ajax implementation drops support for IE10 and lower. This puts us in line with other implementations and helps clean up code in this area + +#### AjaxRequest + +- `AjaxRequest` is no longer used to type the configuration argument for calls to `ajax`. The new type is `AjaxConfig`. This was done to disambiguate two very similar types with different use cases. `AjaxRequest` is still there, but properties have changed, and it is used to show what final request information was sent as part of an event response. + +### New Features + +#### AjaxResponse + +- Now includes `responseHeaders`. +- Now includes event `type` and `total` numbers for examining upload and download progress (see `includeUploadProgress` and `includeDownloadProgress`). + +#### includeUploadProgress + +- A flag to make a request that will include streaming upload progress events in the returned observable. + +#### includeDownloadProgress + +- A flag to make a request that will include streaming upload progress events in the returned observable. + +#### queryParams + +- Configuration for setting query parameters in the URL of the request to be made. + +#### XSRF (CSRF) additions: + +- `xsrfCookieName` and `xsrfHeaderName` were added for cross-site request forgery prevention capabilities. + +## module `rxjs/fetch` + +No changes. + +## module `rxjs/testing` + +### New Features + +#### TestScheduler expectObservable().toEqual() + +- A new means of comparing the equality of two observables. If all emissions are the same, and at the same time, then they are equal. This is primarily useful for refactoring operator chains and making sure that they are equivalent. diff --git a/apps/rxjs.dev-next/docs/deprecations/array-argument.md b/apps/rxjs.dev-next/docs/deprecations/array-argument.md new file mode 100644 index 0000000000..64b2dac3a8 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/array-argument.md @@ -0,0 +1,33 @@ +# Array Arguments + +To unify the API surface of `forkJoin` and `combineLatest` we deprecated some signatures. +Since that it is recommended to either pass an Object or an Array to these operators. + +
+ + This deprecation was introduced in RxJS 6.5. + +
+ +## Operators affected by this Change + +- [combineLatest](/api/index/function/combineLatest) +- [forkJoin](/api/index/function/forkJoin) + +## How to Refactor + +We deprecated the signatures, where just pass all Observables directly as parameters to these operators. + +```ts +import { forkJoin, from } from 'rxjs'; + +const odd$ = from([1, 3, 5]); +const even$ = from([2, 4, 6]); + +// deprecated +forkJoin(odd$, even$); +// suggested change +forkJoin([odd$, even$]); +// or +forkJoin({ odd: odd$, even: even$ }); +``` diff --git a/apps/rxjs.dev-next/docs/deprecations/breaking-changes.md b/apps/rxjs.dev-next/docs/deprecations/breaking-changes.md new file mode 100644 index 0000000000..856b8428c6 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/breaking-changes.md @@ -0,0 +1,126 @@ +# Breaking Changes in Version 7 + +## General + +- **TS:** RxJS requires TS 4.2 + +- **rxjs-compat:** `rxjs-compat` is not published for v7 + +- **toPromise:** toPromise return type now returns `T | undefined` in TypeScript, which is correct, but may break builds. + +- **Subscription:** `add` no longer returns an unnecessary Subscription reference. This was done to prevent confusion caused by a legacy behavior. You can now add and remove functions and Subscriptions as teardowns to and from a `Subscription` using `add` and `remove` directly. Before this, `remove` only accepted subscriptions. + +- **Observable:** `lift` no longer exposed. It was _NEVER_ documented that end users of the library should be creating operators using `lift`. Lift has a [variety of issues](https://github.com/ReactiveX/rxjs/issues/5431) and was always an internal implementation detail of rxjs that might have been used by a few power users in the early days when it had the most value. The value of `lift`, originally, was that subclassed `Observable`s would compose through all operators that implemented lift. The reality is that feature is not widely known, used, or supported, and it was never documented as it was very experimental when it was first added. Until the end of v7, `lift` will remain on Observable. Standard JavaScript users will notice no difference. However, TypeScript users might see complaints about `lift` not being a member of observable. To workaround this issue there are two things you can do: 1. Rewrite your operators as [outlined in the documentation](https://rxjs.dev/guide/operators), such that they return `new Observable`. or 2. cast your observable as `any` and access `lift` that way. Method 1 is recommended if you do not want things to break when we move to version 8. + +- **Subscriber:** `new Subscriber` no longer takes 0-3 arguments. To create a `Subscriber` with 0-3 arguments, use `Subscriber.create`. However, please note that there is little to no reason that you should be creating `Subscriber` references directly, and `Subscriber.create` and `new Subscriber` are both deprecated. + +- **onUnhandledError:** Errors that occur during setup of an observable subscription after the subscription has emitted an error or completed will now throw in their own call stack. Before it would call `console.warn`. This is potentially breaking in edge cases for node applications, which may be configured to terminate for unhandled exceptions. In the unlikely event this affects you, you can configure the behavior to `console.warn` in the new configuration setting like so: `import { config } from 'rxjs'; config.onUnhandledError = (err) => console.warn(err);` + +- **RxJS Error types** Tests that are written with naive expectations against errors may fail now that errors have a proper `stack` property. In some testing frameworks, a deep equality check on two error instances will check the values in `stack`, which could be different. + +- `unsubscribe` no longer available via the `this` context of observer functions. To reenable, set `config.useDeprecatedNextContext = true` on the rxjs `config` found at `import { config } from 'rxjs';`. Note that enabling this will result in a performance penalty for all consumer subscriptions. + +- Leaked implementation detail `_unsubscribeAndRecycle` of `Subscriber` has been removed. Just use new `Subscription` objects + +- The static `sortActions` method on `VirtualTimeScheduler` is no longer publicly exposed by our TS types. + +- `Notification.createNext(undefined)` will no longer return the exact same reference every time. + +- Type signatures tightened up around `Notification` and `dematerialize`, may uncover issues with invalid types passed to those operators. + +- Experimental support for `for await` has been removed. Use https://github.com/benlesh/rxjs-for-await instead. + +- `ReplaySubject` no longer schedules emissions when a scheduler is provided. If you need that behavior, + please compose in `observeOn` using `pipe`, for example: `new ReplaySubject(2, 3000).pipe(observeOn(asap))` + +- **rxjs-compat:** `rxjs/Rx` is no longer a valid import site. + +## Operators + +### concat + +- **concat:** Generic signature changed. Recommend not explicitly passing generics, just let inference do its job. If you must, cast with `as`. +- **of:** Generic signature changed, do not specify generics, allow them to be inferred or use `as` + +### count + +- **count:** No longer passes `source` observable as a third argument to the predicate. That feature was rarely used, and of limited value. The workaround is to simply close over the source inside of the function if you need to access it in there. + +### defer + +- `defer` no longer allows factories to return `void` or `undefined`. All factories passed to defer must return a proper `ObservableInput`, such as `Observable`, `Promise`, et al. To get the same behavior as you may have relied on previously, `return EMPTY` or `return of()` from the factory. + +### map + +- **map:** `thisArg` will now default to `undefined`. The previous default of `MapSubscriber` never made any sense. This will only affect code that calls map with a `function` and references `this` like so: `source.pipe(map(function () { console.log(this); }))`. There wasn't anything useful about doing this, so the breakage is expected to be very minimal. If anything we're no longer leaking an implementation detail. + +### mergeScan + +- **mergeScan:** `mergeScan` will no longer emit its inner state again upon completion. + +### of + +- **of:** Use with more than 9 arguments, where the last argument is a `SchedulerLike` may result in the wrong type which includes the `SchedulerLike`, even though the run time implementation does not support that. Developers should be using `scheduled` instead + +### pairs + +- **pairs:** `pairs` will no longer function in IE without a polyfill for `Object.entries`. `pairs` itself is also deprecated in favor of users just using `from(Object.entries(obj))`. + +### race + +- **race:** `race()` will no longer subscribe to subsequent observables if a provided source synchronously errors or completes. This means side effects that might have occurred during subscription in those rare cases will no longer occur. + +### repeat + +- An undocumented behavior where passing a negative count argument to `repeat` would result in an observable that repeats forever. + +### retry + +- Removed an undocumented behavior where passing a negative count argument to `retry` would result in an observable that repeats forever. + +### single + +- `single` operator will now throw for scenarios where values coming in are either not present, or do not match the provided predicate. Error types have thrown have also been updated, please check documentation for changes. + +### skipLast + +- **skipLast:** `skipLast` will no longer error when passed a negative number, rather it will simply return the source, as though `0` was passed. + +### startWith + +- **startWith:** `startWith` will return incorrect types when called with more than 7 arguments and a scheduler. Passing scheduler to startWith is deprecated + +### take + +- `take` and will now throw runtime error for arguments that are negative or NaN, this includes non-TS calls like `take()`. + +### takeLast + +- `takeLast` now has runtime assertions that throw `TypeError`s for invalid arguments. Calling takeLast without arguments or with an argument that is `NaN` will throw a `TypeError` + +### throwError + +- **throwError:** In an extreme corner case for usage, `throwError` is no longer able to emit a function as an error directly. If you need to push a function as an error, you will have to use the factory function to return the function like so: `throwError(() => functionToEmit)`, in other words `throwError(() => () => console.log('called later'))`. + +### timestamp + +- `timestamp` operator accepts a `TimestampProvider`, which is any object with a `now` method + that returns a number. This means pulling in less code for the use of the `timestamp` operator. This may cause + issues with `TestScheduler` run mode. (see [Issue here](https://github.com/ReactiveX/rxjs/issues/5553)) + +### zip + +- **zip:** Zipping a single array will now have a different result. This is an extreme corner-case, because it is very unlikely that anyone would want to zip an array with nothing at all. The workaround would be to wrap the array in another array `zip([[1,2,3]])`. But again, that's pretty weird. + +- **zip:** `zip` operators will no longer iterate provided iterables "as needed", instead the iterables will be treated as push-streams just like they would be everywhere else in RxJS. This means that passing an endless iterable will result in the thread locking up, as it will endlessly try to read from that iterable. This puts us in-line with all other Rx implementations. To work around this, it is probably best to use `map` or some combination of `map` and `zip`. For example, `zip(source$, iterator)` could be `source$.pipe(map(value => [value, iterator.next().value]))`. + +## ajax + +- `ajax` body serialization will now use default XHR behavior in all cases. If the body is a `Blob`, `ArrayBuffer`, any array buffer view (like a byte sequence, e.g. `Uint8Array`, etc), `FormData`, `URLSearchParams`, `string`, or `ReadableStream`, default handling is use. If the `body` is otherwise `typeof` `"object"`, then it will be converted to JSON via `JSON.stringify`, and the `Content-Type` header will be set to `application/json;charset=utf-8`. All other types will emit an error. + +- The `Content-Type` header passed to `ajax` configuration no longer has any effect on the serialization behavior of the AJAX request. +- For TypeScript users, `AjaxRequest` is no longer the type that should be explicitly used to create an `ajax`. It is now `AjaxConfig`, although the two types are compatible, only `AjaxConfig` has `progressSubscriber` and `createXHR`. + +- **ajax:** In an extreme corner-case... If an error occurs, the responseType is `"json"`, we're in IE, and the `responseType` is not valid JSON, the `ajax` observable will no longer emit a syntax error, rather it will emit a full `AjaxError` with more details. + +- **ajax:** Ajax implementation drops support for IE10 and lower. This puts us in-line with other implementations and helps clean up code in this area diff --git a/apps/rxjs.dev-next/docs/deprecations/index.md b/apps/rxjs.dev-next/docs/deprecations/index.md new file mode 100644 index 0000000000..093d4b9d70 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/index.md @@ -0,0 +1,11 @@ +# Deprecations and Breaking Changes + +While the core team always tries to limit changes, sometimes we have to deprecate APIs or do breaking changes for various reasons. +This section aims to describe some of the deprecations and breaking changes we did more in detail. Some of the changes are to extensive to describe +them appropriately in a changelog. Additionally, we can provide code examples in the documentation, to make required changes more comprehensible and therefore +lower migration efforts. + +Do notice that this is not a complete list, please see the following changelogs for the complete list: + +- [v6.x changelog](https://github.com/ReactiveX/rxjs/blob/6.x/CHANGELOG.md) +- [v7.x changelog](./breaking-changes.md) diff --git a/apps/rxjs.dev-next/docs/deprecations/multicasting.md b/apps/rxjs.dev-next/docs/deprecations/multicasting.md new file mode 100644 index 0000000000..2e77adc612 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/multicasting.md @@ -0,0 +1,439 @@ +# Multicasting + +In version 7, the multicasting APIs were simplified to just a few functions: + +- [connectable](/api/index/function/connectable) +- [connect](/api/operators/connect) +- [share](/api/operators/share) + +And [shareReplay](/api/operators/shareReplay) - which is a thin wrapper around the now highly-configurable [share](/api/operators/share) operator. + +Other APIs that relate to multicasting are now deprecated. + +
+ + These deprecations were introduced in RxJS 7.0 and will become breaking in RxJS 8. + +
+ +## APIs affected by this Change + +- [ConnectableObservable](/api/index/class/ConnectableObservable) +- [multicast](/api/operators/multicast) +- [publish](/api/operators/publish) +- [publishBehavior](/api/operators/publishBehavior) +- [publishLast](/api/operators/publishLast) +- [publishReplay](/api/operators/publishReplay) +- [refCount](/api/operators/refCount) + +## How to refactor + +### ConnectableObservable + +Instead of creating a [ConnectableObservable](/api/index/class/ConnectableObservable) instance, call the [connectable](/api/index/function/connectable) function to obtain a connectable observable. + + +```ts +import { ConnectableObservable, timer, Subject } from 'rxjs'; + +// deprecated +const tick$ = new ConnectableObservable( + timer(1_000), + () => new Subject()); +tick$.connect(); +``` + + +```ts +import { connectable, timer, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new Subject() +}); +tick$.connect(); +``` + +In situations in which the `refCount` method is used, the [share](/api/operators/share) operator can be used instead. + + +```ts +import { ConnectableObservable, timer, Subject } from 'rxjs'; + +// deprecated +const tick$ = new ConnectableObservable( + timer(1_000), + () => new Subject() +).refCount(); +``` + + +```ts +import { timer, share, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + share({ connector: () => new Subject() }) +); +``` + +### multicast + +Where [multicast](/api/operators/multicast) is called with a subject factory, can be replaced with [connectable](/api/index/function/connectable). + + +```ts +import { timer, multicast, Subject, ConnectableObservable } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + multicast(() => new Subject()) +) as ConnectableObservable; +``` + + +```ts +import { connectable, timer, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new Subject() +}); +``` + +Where [multicast](/api/operators/multicast) is called with a subject instance, it can be replaced with [connectable](/api/index/function/connectable) and a local subject instance. + + +```ts +import { timer, multicast, Subject, ConnectableObservable } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + multicast(new Subject()) +) as ConnectableObservable; +``` + + +```ts +import { connectable, timer, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new Subject(), + resetOnDisconnect: false +}); +``` + +Where [multicast](/api/operators/multicast) is used in conjunction with [refCount](/api/operators/refCount), it can be replaced with [share](/api/index/function/connectable). + + +```ts +import { timer, multicast, Subject, refCount } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + multicast(() => new Subject()), + refCount() +); +``` + + +```ts +import { timer, share, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + share({ connector: () => new Subject() }) +); +``` + +Where [multicast](/api/operators/multicast) is used with a selector, it can be replaced with [connect](/api/index/function/connect). + + +```ts +import { timer, multicast, Subject, combineLatest } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + multicast( + () => new Subject(), + (source) => combineLatest([source, source]) + ) +); +``` + + +```ts +import { timer, connect, combineLatest, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + connect((source) => combineLatest([source, source]), { + connector: () => new Subject() + }) +); +``` + +### publish + +If you're using [publish](/api/operators/publish) to create a [ConnectableObservable](/api/index/class/ConnectableObservable), you can use [connectable](/api/index/function/connectable) instead. + + +```ts +import { timer, publish, ConnectableObservable } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publish() +) as ConnectableObservable; +``` + + +```ts +import { connectable, timer, Subject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new Subject(), + resetOnDisconnect: false +}); +``` + +And if [refCount](/api/operators/refCount) is being applied to the result of [publish](/api/operators/publish), you can use [share](/api/operators/share) to replace both. + + +```ts +import { timer, publish, refCount } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publish(), + refCount() +); +``` + + +```ts +import { timer, share } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + share({ + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) +); +``` + +If [publish](/api/operators/publish) is being called with a selector, you can use the [connect](/api/operators/connect) operator instead. + + +```ts +import { timer, publish, combineLatest } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publish((source) => combineLatest([source, source])) +); +``` + + +```ts +import { timer, connect, combineLatest } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + connect((source) => combineLatest([source, source])) +); +``` + +### publishBehavior + +If you're using [publishBehavior](/api/operators/publishBehavior) to create a [ConnectableObservable](/api/index/class/ConnectableObservable), you can use [connectable](/api/index/function/connectable) and a [BehaviorSubject](api/index/class/BehaviorSubject) instead. + + +```ts +import { timer, publishBehavior, ConnectableObservable } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishBehavior(0) +) as ConnectableObservable; +``` + + +```ts +import { connectable, timer, BehaviorSubject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new BehaviorSubject(0), + resetOnDisconnect: false +}); +``` + +And if [refCount](/api/operators/refCount) is being applied to the result of [publishBehavior](/api/operators/publishBehavior), you can use the [share](/api/operators/share) operator - with a [BehaviorSubject](api/index/class/BehaviorSubject) connector - to replace both. + + +```ts +import { timer, publishBehavior, refCount } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishBehavior(0), + refCount() +); +``` + + +```ts +import { timer, share, BehaviorSubject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + share({ + connector: () => new BehaviorSubject(0), + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) +); +``` + +### publishLast + +If you're using [publishLast](/api/operators/publishLast) to create a [ConnectableObservable](/api/index/class/ConnectableObservable), you can use [connectable](/api/index/function/connectable) and an [AsyncSubject](api/index/class/AsyncSubject) instead. + + +```ts +import { timer, publishLast, ConnectableObservable } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishLast() +) as ConnectableObservable; +``` + + +```ts +import { connectable, timer, AsyncSubject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new AsyncSubject(), + resetOnDisconnect: false +}); +``` + +And if [refCount](/api/operators/refCount) is being applied to the result of [publishLast](/api/operators/publishLast), you can use the [share](/api/operators/share) operator - with an [AsyncSubject](api/index/class/AsyncSubject) connector - to replace both. + + +```ts +import { timer, publishLast, refCount } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishLast(), + refCount() +); +``` + + +```ts +import { timer, share, AsyncSubject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + share({ + connector: () => new AsyncSubject(), + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) +); +``` + +### publishReplay + +If you're using [publishReplay](/api/operators/publishReplay) to create a [ConnectableObservable](/api/index/class/ConnectableObservable), you can use [connectable](/api/index/function/connectable) and a [ReplaySubject](api/index/class/ReplaySubject) instead. + + +```ts +import { timer, publishReplay, ConnectableObservable } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishReplay(1) +) as ConnectableObservable; +``` + + +```ts +import { connectable, timer, ReplaySubject } from 'rxjs'; + +// suggested refactor +const tick$ = connectable(timer(1_000), { + connector: () => new ReplaySubject(1), + resetOnDisconnect: false +}); +``` + +And if [refCount](/api/operators/refCount) is being applied to the result of [publishReplay](/api/operators/publishReplay), you can use the [share](/api/operators/share) operator - with a [ReplaySubject](api/index/class/ReplaySubject) connector - to replace both. + + +```ts +import { timer, publishReplay, refCount } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishReplay(1), + refCount() +); +``` + + +```ts +import { timer, share, ReplaySubject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + share({ + connector: () => new ReplaySubject(1), + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) +); +``` + +If [publishReplay](/api/operators/publishReplay) is being called with a selector, you can use the [connect](/api/operators/connect) operator - with a [ReplaySubject](api/index/class/ReplaySubject) connector - instead. + + +```ts +import { timer, publishReplay, combineLatest } from 'rxjs'; + +// deprecated +const tick$ = timer(1_000).pipe( + publishReplay(1, undefined, (source) => combineLatest([source, source])) +); +``` + + +```ts +import { timer, connect, combineLatest, ReplaySubject } from 'rxjs'; + +// suggested refactor +const tick$ = timer(1_000).pipe( + connect((source) => combineLatest([source, source]), { + connector: () => new ReplaySubject(1) + }) +); +``` + +### refCount + +Instead of applying the [refCount](/api/operators/refCount) operator to the [ConnectableObservable](/api/index/class/ConnectableObservable) obtained from a [multicast](/api/operators/multicast) +or [publish](/api/operators/publish) operator, use the [share](/api/operators/share) operator to replace both. + +The properties passed to [share](/api/operators/share) will depend upon the operators that are being replaced. The refactors for using [refCount](/api/operators/refCount) with [multicast](/api/operators/multicast), [publish](/api/operators/publish), [publishBehavior](/api/operators/publishBehavior), [publishLast](/api/operators/publishLast) and [publishReplay](/api/operators/publishReplay) are detailed above. diff --git a/apps/rxjs.dev-next/docs/deprecations/result-selector.md b/apps/rxjs.dev-next/docs/deprecations/result-selector.md new file mode 100644 index 0000000000..0d14021a09 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/result-selector.md @@ -0,0 +1,43 @@ +# ResultSelector Parameter + +Some operator supported a resultSelector argument that acted as mapping function on the result of that operator. +The same behavior can be reproduced with the `map` operator, therefore this argument became deprecated. + +
+ + This deprecation was introduced in RxJS 6.0 and will become breaking with RxJS 8. + +
+ +There were two reasons for actually deprecating those parameters: + +1. It increases the bundle size of every operator +2. In some scenarios values had to be retained in memory causing a general memory pressure + +## Operators affected by this Change + +- [concatMap](/api/operators/concatMap) +- [concatMapTo](/api/operators/concatMapTo) +- [exhaustMap](/api/operators/exhaustMap) +- [mergeMap](/api/operators/mergeMap) +- [mergeMapTo](/api/operators/mergeMapTo) +- [switchMap](/api/operators/switchMap) +- [switchMapTo](/api/operators/switchMapTo) + +## How to Refactor + +Instead of using the `resultSelector` Argument, you can leverage the [`map`](/api/operators/map) operator on the inner Observable: + + +```ts +import { fromEvent, switchMap, interval, map } from 'rxjs'; + +// deprecated +fromEvent(document, 'click').pipe( + switchMap((x) => interval(1000), (_, x) => x + 1) +); +// suggested change +fromEvent(document, 'click').pipe( + switchMap((x) => interval(1000).pipe(map((x) => x + 1))) +); +``` diff --git a/apps/rxjs.dev-next/docs/deprecations/scheduler-argument.md b/apps/rxjs.dev-next/docs/deprecations/scheduler-argument.md new file mode 100644 index 0000000000..5883b4fc56 --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/scheduler-argument.md @@ -0,0 +1,85 @@ +# Scheduler Argument + +To limit the API surface of some operators, but also prepare for a [major refactoring in V8](https://github.com/ReactiveX/rxjs/pull/4583), we +agreed on deprecating the `scheduler` argument from many operators. It solely deprecates those methods where this argument is rarely used. So `time` related +operators, like [`interval`](https://rxjs.dev/api/index/function/interval) are not affected by this deprecation. + +To support this transition the [scheduled creation function](/api/index/function/scheduled) was added. + +
+ + This deprecation was introduced in RxJS 6.5 and will become breaking with RxJS 8. + +
+ +## Operators affected by this Change + +- [from](/api/index/function/from) +- [of](/api/index/function/of) +- [merge](/api/index/function/merge) +- [concat](/api/index/function/concat) +- [startWith](/api/operators/startWith) +- [endWith](/api/operators/endWith) +- [combineLatest](/api/index/function/combineLatest) + +## How to Refactor + +If you use any operator from the above list and you're passing the `scheduler` argument, you have three potential refactoring options. + +### Refactoring of `of` and `from` + +`scheduled` is kinda copying the behavior of `from`. Therefore if you used `from` with a `scheduler` argument, you can just replace them. + +For the `of` creation function you need to replace this Observable with `scheduled` and instead of passing the `scheduler` argument to `of` pass it to `scheduled`. +Following code example demonstrate this process. + +```ts +import { of, asyncScheduler, scheduled } from 'rxjs'; + +// Deprecated approach +of(1, 2, 3, asyncScheduler).subscribe((x) => console.log(x)); +// suggested approach +scheduled([1, 2, 3], asyncScheduler).subscribe((x) => console.log(x)); +``` + +### Refactoring of `merge`, `concat`, `combineLatest`, `startWith` and `endWith` + +In case you used to pass a scheduler argument to one of these operators you probably had code like this: + +```ts +import { concat, of, asyncScheduler } from 'rxjs'; + +concat(of('hello '), of('World'), asyncScheduler).subscribe((x) => console.log(x)); +``` + +To work around this deprecation you can leverage the [`scheduled`](/api/index/function/scheduled) function. + +```ts +import { scheduled, of, asyncScheduler, concatAll } from 'rxjs'; + +scheduled([of('hello '), of('World')], asyncScheduler) + .pipe(concatAll()) + .subscribe((x) => console.log(x)); +``` + +You can apply this pattern to refactor deprecated usage of `concat`, `startWith` and `endWith` but do notice that you will want to use [mergeAll](/api/operators/mergeAll) to refactor the deprecated usage of `merge`. + +With `combineLatest`, you will want to use [combineLatestAll](/api/operators/combineLatestAll) + +E.g. code that used to look like this: + +```ts +import { combineLatest, of, asyncScheduler } from 'rxjs'; + +combineLatest(of('hello '), of('World'), asyncScheduler).subscribe(console.log); +``` + +would become: + +```ts +import { scheduled, of, asyncScheduler, combineLatestAll } from 'rxjs'; + +scheduled([of('hello '), of('World')], asyncScheduler) + .pipe(combineLatestAll()) + .subscribe((x) => console.log(x)); +``` diff --git a/apps/rxjs.dev-next/docs/deprecations/subscribe-arguments.md b/apps/rxjs.dev-next/docs/deprecations/subscribe-arguments.md new file mode 100644 index 0000000000..6fdc1c8ccd --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/subscribe-arguments.md @@ -0,0 +1,56 @@ +# Subscribe Arguments + +You might have seen that we deprecated some signatures of the `subscribe` method, which might have caused some confusion. +The `subscribe` method itself is not deprecated. This deprecation also affects the [`tap` operator](/api/operators/tap), as tap supports the same signature as the `subscribe` method. + +This is to get ready for a future where we may allow configuration of `subscribe` via the second argument, for things like `AbortSignal` or the like (imagine `source$.subscribe(fn, { signal })`, etc). This deprecation is also because 2-3 function arguments can contribute to harder-to-read code. For example someone could name functions poorly and confuse the next reader: `source$.subscribe(doSomething, doSomethingElse, lol)` With that signature, you have to know unapparent details about `subscribe`, where using a partial observer solves that neatly: `source$.subscribe({ next: doSomething, error: doSomethingElse, complete: lol })`. + +
+ + This deprecation was introduced in RxJS 6.4. + +
+ +In short we deprecated all signatures where you specified an anonymous `error` or `complete` callback and passed an empty function to one of the callbacks before. + +## What Signature is affected + +**We have deprecated all signatures of `subscribe` that take more than 1 argument.** + +We deprecated signatures for just passing the `complete` callback. + +```ts +import { of } from 'rxjs'; + +// deprecated +of([1, 2, 3]).subscribe(null, null, console.info); // difficult to read +// suggested change +of([1, 2, 3]).subscribe({ complete: console.info }); +``` + +Similarly, we also deprecated signatures for solely passing the `error` callback. + +```ts +import { throwError } from 'rxjs'; + +// deprecated +throwError('I am an error').subscribe(null, console.error); +// suggested change +throwError('I am an error').subscribe({ error: console.error }); +``` + +Do notice, in general it is recommended only to use the anonymous function if you only specify the `next` callback otherwise +we recommend to pass an `Observer` + +```ts +import { of } from 'rxjs'; + +// recommended +of([1, 2, 3]).subscribe((v) => console.info(v)); +// also recommended +of([1, 2, 3]).subscribe({ + next: (v) => console.log(v), + error: (e) => console.error(e), + complete: () => console.info('complete'), +}); +``` diff --git a/apps/rxjs.dev-next/docs/deprecations/to-promise.md b/apps/rxjs.dev-next/docs/deprecations/to-promise.md new file mode 100644 index 0000000000..f3141a792a --- /dev/null +++ b/apps/rxjs.dev-next/docs/deprecations/to-promise.md @@ -0,0 +1,98 @@ +# Conversion to Promises + +The similarity between Observables and Promises is that both [collections](/guide/observable) may produce values over +time, but the difference is that Observables may produce none or more than one value, while Promises produce only one +value when resolved successfully. + +## Issues + +For this reason, in RxJS 7, the return type of the Observable's [`toPromise()`](/api/index/class/Observable#toPromise) +method has been fixed to better reflect the fact that Observables can yield zero values. This may be a **breaking +change** to some projects as the return type was changed from `Promise` to `Promise`. + +Also, `toPromise()` method name was never indicating what emitted value a Promise will resolve with because Observables +can produce multiple values over time. When converting to a Promise, you might want to choose which value to pick - +either the first value that has arrived or the last one. To fix all these issues, we decided to deprecate `toPromise()`, +and to introduce the two new helper functions for conversion to Promises. + +## Use one of the two new functions + +As a replacement to the deprecated `toPromise()` method, you should use one of the two built in static conversion +functions [`firstValueFrom`](/api/functions/firstValueFrom) or [`lastValueFrom`](/api/functions/lastValueFrom). + +### `lastValueFrom` + +The `lastValueFrom` is almost exactly the same as `toPromise()` meaning that it will resolve with the last value that has +arrived when the Observable completes, but with the difference in behavior when Observable completes without emitting a +single value. When Observable completes without emitting, `toPromise()` will successfully resolve with `undefined` (thus +the return type change), while the `lastValueFrom` will reject with the [`EmptyError`](/api/classes/EmptyError). Thus, the return type of the +`lastValueFrom` is `Promise`, just like `toPromise()` had in RxJS 6. + +#### Example + +```ts +import { interval, take, lastValueFrom } from 'rxjs'; + +async function execute() { + const source$ = interval(2000).pipe(take(10)); + const finalNumber = await lastValueFrom(source$); + console.log(`The final number is ${finalNumber}`); +} + +execute(); + +// Expected output: +// "The final number is 9" +``` + +### `firstValueFrom` + +However, you might want to take the first value as it arrives without waiting an Observable to complete, thus you can +use `firstValueFrom`. The `firstValueFrom` will resolve a Promise with the first value that was emitted from the +Observable and will immediately unsubscribe to retain resources. The `firstValueFrom` will also reject with an +[`EmptyError`](/api/classes/EmptyError) if the Observable completes with no values emitted. + +#### Example + +```ts +import { interval, firstValueFrom } from 'rxjs'; + +async function execute() { + const source$ = interval(2000); + const firstNumber = await firstValueFrom(source$); + console.log(`The first number is ${firstNumber}`); +} + +execute(); + +// Expected output: +// "The first number is 0" +``` + +Both functions will return a Promise that rejects if the source Observable errors. The Promise +will reject with the same error that the Observable has errored with. + +## Use default value + +If you don't want Promises created by `lastValueFrom` or `firstValueFrom` to reject with [`EmptyError`](/api/classes/EmptyError) if there +were no emissions before completion, you can use the second parameter. The second parameter is expected to be an object +with `defaultValue` parameter. The value in the `defaultValue` will be used to resolve a Promise when source Observable +completes without emitted values. + +```ts +import { firstValueFrom, EMPTY } from 'rxjs'; + +const result = await firstValueFrom(EMPTY, { defaultValue: 0 }); +console.log(result); + +// Expected output: +// 0 +``` + +## Warning + +Only use `lastValueFrom` function if you _know_ an Observable will eventually complete. The `firstValueFrom` function should +be used if you _know_ an Observable will emit at least one value _or_ will eventually complete. If the source Observable +does not complete or emit, you will end up with a Promise that is hung up, and potentially all of the state of an async +function hanging out in memory. To avoid this situation, look into adding something like [`timeout`](/api/operators/timeout), [`take`](/api/operators/take), +[`takeWhile`](/api/operators/takeWhile), or [`takeUntil`](/api/operators/takeUntil) amongst others. diff --git a/apps/rxjs.dev-next/docs/guide/core-semantics.md b/apps/rxjs.dev-next/docs/guide/core-semantics.md new file mode 100644 index 0000000000..ba2884780d --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/core-semantics.md @@ -0,0 +1,33 @@ +# RxJS Core Semantics + +Starting in version 8, all RxJS operators that are provided in the core library MUST meet the following semantics. In the current version, version 7, all operators SHOULD meet the following semantics (as guidelines). If they do not, we need to track the issue on [GitHub](https://github.com/ReactiveX/rxjs/issues). + +## Purpose + +The purpose of these semantics is provide predictable behavior for the users of our library, and to ensure consistent behavior between our many different operators. It should be noted that at the time of this writing, we don't always adhere to these semantic guidelines. This document is to serve as a goalpost for upcoming changes and work as much as it is to help describe the library. This is also a "living document" and is subject to change. + +## General Design Guidelines + +**Functions such as operators, constructors, and creation functions, should use named parameters in cases where there is more than 1 argument, and arguments after the first are non-obvious.** The primary use case should be streamlined to work without configuration. For example, `fakeFlattenMap(n => of(n))` is fine, but `fakeFlattenMap(n => of(n), 1)` is less readable than `fakeFlattenMap(n => of(n), { maxConcurrent: 1 })`. Other things, like `of(1, 2, 3)` are obvious enough that named parameters don't make sense. + +## Operators + +- MUST be a function that returns an [operator function](https://rxjs.dev/api/index/interface/OperatorFunction). That is `(source: Observable) => Observable`. +- The returned operator function MUST be [referentially transparent](https://en.wikipedia.org/wiki/Referential_transparency). That is to say, that if you capture the return value of the operator (e.g. `const double => map(x => x + x)`), you can use that value to operate on any many observables as you like without changing any underlying state in the operator reference. (e.g. `a$.pipe(double)` and `b$.pipe(double)`). +- The observable returned by the operator function MUST subscribe to the source. +- If the operation performed by the operator can tell it not change anything about the output of the source, it MUST return the reference to the source. For example `take(Infinity)` or `skip(0)`. +- Operators that accept a "notifier", that is another observable source that is used to trigger some behavior, must accept any type that can be converted to an `Observable` with `from`. For example `takeUntil`. +- Operators that accept "notifiers" (as described above), MUST ONLY recognized next values from the notifier as "notifications". Emitted completions may not be used a source of notification. +- "Notifiers" provided directly to the operator MUST be subscribed to _before_ the source is subscribed to. "Notifiers" created via factory function provided to the operator SHOULD be subscribed to at the earliest possible moment. +- The observable returned by the operator function is considered to be the "consumer" of the source. As such, the consumer MUST unsubscribe from the source as soon as it knows it no longer needs values before proceeding to do _any_ action. +- Events that happen after the completion of a source SHOULD happen after the source finalizes. This is to ensure that finalization always happens in a predictable time frame relative to the event. +- `Error` objects MUST NOT be retained longer than necessary. This is a possible source of memory pressure. +- `Promise` references MUST NOT be retained longer than necessary. This is a possible source of memory pressure. +- IF they perform a related operation to a creation function, they SHOULD share the creation function's name only with the suffix `With`. (e.g. `concat` and `concatWith`). +- SHOULD NOT have "result selectors". This is a secondary argument that provides the ability to "map" values after performing the primary operation of the operator. + +## Creation Functions + +- Names MUST NOT end in `With`. That is reserved for the operator counter parts of creation functions. +- MAY have "result selectors". This is a secondary argument that provides the ability to "map" values before they're emitted from the resulting observable. +- IF the creation function accepts a "result selector", it must not accept "n-arguments" ahead of that result selector. Instead, it should accept an array or possibly an object. (bad: `combineThings(sourceA$, sourceB$, (a, b) => a + b)`, good: `combineThings([sourceA$, sourceB$], (a, b) => a + b)`. In this case, it may be okay to provide the result selector as a second argument, rather than as a named parameter, as the use should be fairly obvious. diff --git a/apps/rxjs.dev-next/docs/guide/glossary-and-semantics.md b/apps/rxjs.dev-next/docs/guide/glossary-and-semantics.md new file mode 100644 index 0000000000..72e548f95a --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/glossary-and-semantics.md @@ -0,0 +1,149 @@ +# RxJS: Glossary And Semantics + +When discussing and documenting observables, it's important to have a common language and a known set of rules around what is going on. This document is an attempt to standardize these things so we can try to control the language in our docs, and hopefully other publications about RxJS, so we can discuss reactive programming with RxJS on consistent terms. + +While not all of the documentation for RxJS reflects this terminology, it is a goal of the team to ensure it does, and to ensure the language and names around the library use this document as a source of truth and unified language. + +## Major Entities + +There are high level entities that are frequently discussed. It's important to define them separately from other lower-level concepts, because they relate to the nature of observable. + +### Consumer + +The code that is subscribing to the observable. This is whoever is being _notified_ of [nexted](#next) values, and [errors](#error) or [completions](#complete). + +### Producer + +Any system or thing that is the source of values that are being pushed out of the observable subscription to the consumer. This can be a wide variety of things, from a `WebSocket` to a simple iteration over an `Array`. The producer is most often created during the [subscribe](#subscribe) action, and therefor "owned" by a [subscription](#subscription) in a 1:1 way, but that is not always the case. A producer may be shared between many subscriptions, if it is created outside of the [subscribe](#subscribe) action, in which case it is one-to-many, resulting in a [multicast](#multicast). + +### Subscription + +A contract where a [consumer](#consumer) is [observing](#observation) values pushed by a [producer](#producer). The subscription (not to be confused with the `Subscription` class or type), is an ongoing process that amounts to the function of the observable from the Consumer's perspective. Subscription starts the moment a [subscribe](#subscribe) action is initiated, even before the [subscribe](#subscribe) action is finished. + +### Observable + +The primary type in RxJS. At its highest level, an observable represents a template for connecting an [Observer](#observer), as a [consumer](#consumer), to a [producer](#producer), via a [subscribe](#subscribe) action, resulting in a [subscription](#subscription). + +### Observer + +The manifestation of a [consumer](#consumer). A type that may have some (or all) handlers for each type of [notification](#notification): [next](#next), [error](#error), and [complete](#complete). Having all three types of handlers generally gets this to be called an "observer", where if it is missing any of the notification handlers, it may be called a ["partial observer"](#partial-observer). + +## Major Actions + +There are specific actions and events that occur between major entities in RxJS that need to be defined. These major actions are the highest level events that occur within various parts in RxJS. + +### Subscribe + +The act of a [consumer](#consumer) requesting from an Observable to set up a [subscription](#subscription) so that it may [observe](#observation) a [producer](#producer). A subscribe action can occur with an observable via many different mechanisms. The primary mechanism is the [`subscribe` method](/api/index/class/Observable#subscribe) on the [Observable class](/api/index/class/Observable). Other mechanisms include the [`forEach` method](/api/index/class/Observable#forEach), functions like [`lastValueFrom`](/api/index/function/lastValueFrom), and [`firstValueFrom`](/api/index/function/firstValueFrom), and the deprecated [`toPromise` method](/api/index/class/Observable#forEach). + +### Finalization + +The act of cleaning up resources used by a producer. This is guaranteed to happen on `error`, `complete`, or if unsubscription occurs. This is not to be confused with [unsubscription](#unsubscription), but it does always happen during unsubscription. + +### Unsubscription + +The act of a [consumer](#consumer) telling a [producer](#producer) is no longer interested in receiving values. Causes [Finalization](#finalization) + +### Observation + +A [consumer](#consumer) reacting to [next](#next), [error](#error), or [complete](#complete) [notifications](#notification). This can only happen _during_ [subscription](#subscription). + +### Observation Chain + +When an [observable](#observable) uses another [observable](#observable) as a [producer](#producer), an "observation chain" is set up. That is a chain of [observation](#observation) such that multiple [observers](#observer) are [notifying](#notification) each other in a unidirectional way toward the final [consumer](#consumer). + +### Next + +A value has been pushed to the [consumer](#consumer) to be [observed](#observation). Will only happen during [subscription](#subscription), and cannot happen after [error](#error), [complete](#error), or [unsubscription](#unsubscription). Logically, this also means it cannot happen after [finalization](#finalization). + +### Error + +The [producer](#producer) has encountered a problem and is notifying the [consumer](#consumer). This is a notification that the [producer](#producer) will no longer send values and will [finalize](#finalization). This cannot occur after [complete](#complete), any other [error](#error), or [unsubscription](#unsubscription). Logically, this also means it cannot happen after [finalization](#finalization). + +### Complete + +The [producer](#producer) is notifying the [consumer](#consumer) that it is done [nexting](#Next) values, without error, will send no more values, and it will [finalize](#finalization). [Completion](#complete) cannot occur after an [error](#error), or [unsubscribe](#unsubscription). [Complete](#complete) cannot be called twice. [Complete](#complete), if it occurs, will always happen before [finalization](#finalization). + +### Notification + +The act of a [producer](#producer) pushing [nexted](#next) values, [errors](#error) or [completions](#complete) to a [consumer](#consumer) to be [observed](#observation). Not to be confused with the [`Notification` type](/api/index/class/Notification), which is notification manifested as a JavaScript object. + +## Major Concepts + +Some of what we discuss is conceptual. These are mostly common traits of behaviors that can manifest in observables or in push-based reactive systems. + +### Multicast + +The act of one [producer](#producer) being [observed](#observation) by **many** [consumers](#consumer). + +### Unicast + +The act of one [producer](#producer) being [observed](#observation) by **only one** [consumer](#consumer). An observable is "unicast" when it only connects one [producer](#producer) to one [consumer](#consumer). Unicast doesn't necessarily mean ["cold"](#cold). + +### Cold + +An observable is "cold" when it creates a new [producer](#producer) during [subscribe](#subscribe) for every new [subscription](#subscription). As a result, "cold" observables are _always_ [unicast](#unicast), being one [producer](#producer) [observed](#observation) by one [consumer](#consumer). Cold observables can be made [hot](#hot) but not the other way around. + +### Hot + +An observable is "hot", when its [producer](#producer) was created outside of the context of the [subscribe](#subscribe) action. This means that the "hot" observable is almost always [multicast](#multicast). It is possible that a "hot" observable is still _technically_ unicast, if it is engineered to only allow one [subscription](#subscription) at a time, however, there is no straightforward mechanism for this in RxJS, and the scenario is an unlikely one. For the purposes of discussion, all "hot" observables can be assumed to be [multicast](#multicast). Hot observables cannot be made [cold](#cold). + +### Push + +[Observables](#observable) are a push-based type. That means rather than having the [consumer](#consumer) call a function or perform some other action to get a value, the [consumer](#consumer) receives values as soon as the [producer](#producer) has produced them, via a registered [next](#next) handler. + +### Pull + +Pull-based systems are the opposite of [push](#push)-based. In a pull-based type or system, the [consumer](#consumer) must request each value the [producer](#producer) has produced manually, perhaps long after the [producer](#producer) has actually done so. Examples of such systems are [Functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) and [Iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) + +## Minor Entities + +### Operator + +A factory function that creates an [operator function](#operator-function). Examples of this in rxjs are functions like [`map`](/api/operators/map) and [`mergeMap`](/api/operators/mergeMap), which are generally passed to [`pipe`](/api/index/class/Observable#pipe). The result of calling many operators, and passing their resulting [operator functions](#operator-function) into pipe on an observable [source](#source) will be another [observable](#observable), and will generally not result in [subscription](#subscription). + +### Operator Function + +A function that takes an [observable](#observable), and maps it to a new [observable](#observable). Nothing more, nothing less. Operator functions are created by [operators](#operator). If you were to call an rxjs operator like [map](/api/operators/map) and put the return value in a variable, the returned value would be an operator function. + +### Operation + +An action taken while handling a [notification](#notification), as set up by an [operator](#operator) and/or [operator function](#operator-function). In RxJS, a developer can chain several [operator functions](#operator-function) together by calling [operators](#operator) and passing the created [operator functions](#operator-function) to the [`pipe`](/api/index/class/Observable#pipe) method of [`Observable`](/api/index/class/Observable), which results in a new [observable](#observable). During [subscription](#subscription) to that observable, operations are performed in an order dictated by the [observation chain](#observation-chain). + +### Stream + +A "stream" or "streaming" in the case of observables, refers to the collection of [operations](#operation), as they are processed during a [subscription](#subscription). This is not to be confused with node [Streams](https://nodejs.org/api/stream.html), and the word "stream", on its own, should be used _sparingly_ in documentation and articles. Instead, prefer [observation chain](#observation-chain), [operations](#operation), or [subscription](#subscription). "Streaming" is less ambiguous, and is fine to use given this defined meaning. + +### Source + +An [observable](#observable) or [valid observable input](#observable-inputs) having been converted to an observable, that will supply values to another [observable](#observable), either as the result of an [operator](#operator) or other function that creates one observable as another. This [source](#source), will be the [producer](#producer) for the resulting [observable](#observable) and all of its [subscriptions](#subscriptions). Sources may generally be any type of observable. + +### Observable Inputs + +An "observable input" ([defined as a type here](/api/index/type-alias/ObservableInput)), is any type that can be easily converted to an [Observable](#observable). Observable Inputs may sometimes be referred to as "valid observable sources". + +### Notifier + +An [observable](#observable) that is being used to notify another [observable](#observable) that it needs to perform some action. The action should only occur on a [next notification](#next), and never on [error](#error) or [complete](#complete). Generally, notifiers are used with specific operators, such as [`takeUntil`](/api/operators/takeUntil), [`buffer`](/api/operators/buffer), or [`delayWhen`](/api/operators/delayWhen). A notifier may be passed directly, or it may be returned by a callback. + +### Inner Source + +One, of possibly many [sources](#source), which are [subscribed](#subscribe) to automatically within a single [subscription](#subscription) to another observable. Examples of an "inner source" include the [observable inputs](#observable-inputs) returned by the mapping function in a [mergeMap](/api/operators/mergeMap) [operator](#operator). (e.g. `source.pipe(mergeMap(value => createInnerSource(value)))`, where `createInnerSource` returns any valid [observable input](#observable-inputs)). + +### Partial Observer + +An [observer](#observer) that lacks all necessary [notification](#notification) handlers. Generally these are supplied by user-land [consumer](#consumer) code. A "full observer" or "observer" would simply be an observer that has all [notification](#notification) handlers. + +## Other Concepts + +### Unhandled Errors + +An "unhandled error" is any [error](#error) that is not handled by a [consumer](#consumer)-provided function, which is generally provided during the [subscribe](#subscribe) action. If no error handler was provided, RxJS will assume the error is "unhandled" and rethrow the error on a new callstack to prevent ["producer interference"](#producer-interference). + +### Producer Interference + +[Producer](#producer) interference happens when an error is allowed to unwind the RxJS callstack during [notification](#notification). When this happens, the error could break things like for-loops in [upstream](#upstream-and-downstream) [sources](#source) that are [notifying](#notification) [consumers](#consumer) during a [multicast](#multicast). That would cause the other [consumers](#consumer) in that [multicast](#multicast) to suddenly stop receiving values without logical explanation. As of version 6, RxJS goes out of its way to prevent producer interference by ensuring that all unhandled errors are thrown on a separate callstack. + +### Upstream And Downstream + +The order in which [notifications](#notification) are processed by [operations](#operation) in a [stream](#stream) have a directionality to them. "Upstream" refers to an [operation](#operation) that was already processed before the current [operation](#operation), and "downstream" refers to an [operation](#operation) that _will_ be processed _after_ the current [operation](#operation). See also: [Streaming](#stream). diff --git a/apps/rxjs.dev-next/docs/guide/higher-order-observables.md b/apps/rxjs.dev-next/docs/guide/higher-order-observables.md new file mode 100644 index 0000000000..4b40d27264 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/higher-order-observables.md @@ -0,0 +1,23 @@ +# Higher-order Observables + +Observables most commonly emit ordinary values like strings and numbers, but surprisingly often, it is necessary to handle Observables _of_ Observables, so-called higher-order Observables. For example, imagine you have an Observable emitting strings that are the URLs of files you want to fetch. The code might look like this: + +```ts +const fileObservable = urlObservable.pipe(map((url) => http.get(url))); +``` + +`http.get()` returns an Observable for each URL. Now you have an Observable _of_ Observables, a higher-order Observable. + +But how do you work with a higher-order Observable? Typically, by _flattening_: by converting a higher-order Observable into an ordinary Observable. For example: + +```ts +const fileObservable = urlObservable.pipe(concatMap((url) => http.get(url))); +``` + +The Observable returned in the `concatMap` function is usually referred to as a so-called "inner" Observable, while in this context the `urlObservable` is the so-called "outer" Observable. + +The [`concatMap()`](/api/operators/concatMap) operator subscribes to each "inner" Observable, buffers all further emissions of the "outer" Observable, and copies all the emitted values until the inner Observable completes, and continues processing the values of the "outer Observable". All of the values are in that way concatenated. Other useful flattening operators are + +- [`mergeMap()`](/api/operators/mergeMap) — subscribes to each inner Observable as it arrives, then emits each value as it arrives +- [`switchMap()`](/api/operators/switchMap) — subscribes to the first inner Observable when it arrives, and emits each value as it arrives, but when the next inner Observable arrives, unsubscribes to the previous one, and subscribes to the new one. +- [`exhaustMap()`](/api/operators/exhaustMap) — subscribes to the first inner Observable when it arrives, and emits each value as it arrives, discarding all newly arriving inner Observables until that first one completes, then waits for the next inner Observable. diff --git a/apps/rxjs.dev-next/docs/guide/importing.md b/apps/rxjs.dev-next/docs/guide/importing.md new file mode 100644 index 0000000000..46b08b0658 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/importing.md @@ -0,0 +1,189 @@ +# Importing instructions + +There are different ways you can [install](/guide/installation) RxJS. Using/importing RxJS depends on +the used RxJS version, but also depends on the used installation method. + +[Pipeable operators](https://v6.rxjs.dev/guide/v6/pipeable-operators) were introduced in RxJS version +5.5. This enabled all operators to be exported from a single place. This new export site was introduced +with RxJS version 6 where all pipeable operators could have been imported from `'rxjs/operators'`. For +example, `import { map } from 'rxjs/operators'`. + +## New in RxJS v7.2.0 + +**With RxJS v7.2.0, most operators have been moved to `['rxjs'](/api)` +export site. This means that the preferred way to import operators is from `'rxjs'`, while +`'rxjs/operators'` export site has been deprecated.** + +For example, instead of using: + +```ts +import { map } from 'rxjs/operators'; +``` + +**the preferred way** is to use: + +```ts +import { map } from 'rxjs'; +``` + +Although the old way of importing operators is still active, it will be removed in one of the next major +versions. + +Click [here to see](#how-to-migrate) how to migrate. + +## Export sites + +RxJS v7 exports 6 different locations out of which you can import what you need. Those are: + +- `['rxjs'](/api)` - for example: `import { of } from 'rxjs';` +- `['rxjs/operators'](/api/operators)` - for example: `import { map } from 'rxjs/operators';` +- `['rxjs/ajax'](/api/ajax)` - for example: `import { ajax } from 'rxjs/ajax';` +- `['rxjs/fetch'](/api/fetch)` - for example: `import { fromFetch } from 'rxjs/fetch';` +- `['rxjs/webSocket'](/api/webSocket)` - for example: `import { webSocket } from 'rxjs/webSocket';` +- `['rxjs/testing'](/api/testing)` - for example: `import { TestScheduler } from 'rxjs/testing';` + +### How to migrate? + +While nothing has been removed from `'rxjs/operators'`, it is strongly recommended doing the operator +imports from `'rxjs'`. Almost all operator function exports have been moved to `'rxjs'`, but only a +couple of old and deprecated operators have stayed in the `'rxjs/operators'`. Those operator functions +are now mostly deprecated and most of them have their either static operator substitution or are kept as +operators, but have a new name so that they are different to their static creation counter-part (usually +ending with `With`). Those are: + +| `'rxjs/operators'` Operator | Replace With Static Creation Operator | Replace With New Operator Name | +| ------------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------------- | +| [`combineLatest`](/api/operators/combineLatest) | [`combineLatest`](/api/functions/combineLatest) | [`combineLatestWith`](/api/operators/combineLatestWith) | +| [`concat`](/api/operators/concat) | [`concat`](/api/functions/concat) | [`concatWith`](/api/operators/concatWith) | +| [`merge`](/api/operators/merge) | [`merge`](/api/functions/merge) | [`mergeWith`](/api/operators/mergeWith) | +| [`onErrorResumeNext`](/api/operators/onErrorResumeNext) | [`onErrorResumeNext`](/api/functions/onErrorResumeNext) | [`onErrorResumeNextWith`](/api/operators/onErrorResumeNextWith) | +| [`race`](/api/operators/race) | [`race`](/api/functions/race) | [`raceWith`](/api/operators/raceWith) | +| [`zip`](/api/operators/zip) | [`zip`](/api/functions/zip) | [`zipWith`](/api/operators/zipWith) | + +`partition`, the operator, is a special case, as it is deprecated and you should be using the `partition` creation function exported from `'rxjs'` instead. + +For example, the old and deprecated way of using [`merge`](/api/operators/merge) from `'rxjs/operators'` +is: + +```ts +import { merge } from 'rxjs/operators'; + +a$.pipe(merge(b$)).subscribe(); +``` + +But this should be avoided and replaced with one of the next two examples. + +For example, this could be replaced by using a static creation [`merge`](/api/functions/merge) function: + +```ts +import { merge } from 'rxjs'; + +merge(a$, b$).subscribe(); +``` + +Or it could be written using a pipeable [`mergeWith`](/api/operators/mergeWith) operator: + +```ts +import { mergeWith } from 'rxjs'; + +a$.pipe(mergeWith(b$)).subscribe(); +``` + +Depending on the preferred style, you can choose which one to follow, they are completely equal. + +Since a new way of importing operators is introduced with RxJS v7.2.0, instructions will be split to +prior and after this version. + +### ES6 via npm + +If you've installed RxJS using [ES6 via npm](/guide/installation#es6-via-npm) and installed version +is: + +#### v7.2.0 or later + +Import only what you need: + +```ts +import { of, map } from 'rxjs'; + +of(1, 2, 3).pipe(map((x) => x + '!!!')); // etc +``` + +To import the entire set of functionality: + +```ts +import * as rxjs from 'rxjs'; + +rxjs.of(1, 2, 3).pipe(rxjs.map((x) => x + '!!!')); // etc; +``` + +To use with a globally imported bundle: + +```js +const { of, map } = rxjs; + +of(1, 2, 3).pipe(map((x) => x + '!!!')); // etc +``` + +If you installed RxJS version: + +#### v7.1.0 or older + +Import only what you need: + +```ts +import { of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +of(1, 2, 3).pipe(map((x) => x + '!!!')); // etc +``` + +To import the entire set of functionality: + +```ts +import * as rxjs from 'rxjs'; +import * as operators from 'rxjs'; + +rxjs.of(1, 2, 3).pipe(operators.map((x) => x + '!!!')); // etc; +``` + +To use with a globally imported bundle: + +```js +const { of } = rxjs; +const { map } = rxjs.operators; + +of(1, 2, 3).pipe(map((x) => x + '!!!')); // etc +``` + +### CDN + +If you installed a library [using CDN](/guide/installation#cdn), the global namespace for rxjs is +`rxjs`. + +#### v7.2.0 or later + +```js +const { range, filter, map } = rxjs; + +range(1, 200) + .pipe( + filter((x) => x % 2 === 1), + map((x) => x + x) + ) + .subscribe((x) => console.log(x)); +``` + +#### v7.1.0 or older + +```js +const { range } = rxjs; +const { filter, map } = rxjs.operators; + +range(1, 200) + .pipe( + filter((x) => x % 2 === 1), + map((x) => x + x) + ) + .subscribe((x) => console.log(x)); +``` diff --git a/apps/rxjs.dev-next/docs/guide/installation.md b/apps/rxjs.dev-next/docs/guide/installation.md new file mode 100644 index 0000000000..86962d77ec --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/installation.md @@ -0,0 +1,55 @@ +# Installation Instructions + +Here are different ways you can install RxJS: + +## ES2015 via npm + +```shell +npm install rxjs +``` + +By default, RxJS 7.x will provide different variants of the code based on the consumer: + +- When RxJS 7.x is used on Node.js regardless of whether it is consumed via `require` or `import`, CommonJS code targeting ES5 will be provided for execution. +- When RxJS 7.4+ is used via a bundler targeting a browser (or other non-Node.js platform) ES module code targeting ES5 will be provided by default with the option to use ES2015 code. + 7.x versions prior to 7.4.0 will only provide ES5 code. + +If the target browsers for a project support ES2015+ or the bundle process supports down-leveling to ES5 then the bundler can optionally be configured to allow the ES2015 RxJS code to be used instead. +You can enable support for using the ES2015 RxJS code by configuring a bundler to use the `es2015` custom export condition during module resolution. +Configuring a bundler to use the `es2015` custom export condition is specific to each bundler. +If you are interested in using this option, please consult the documentation of your bundler for additional information. +However, some general information can be found here: + +- https://webpack.js.org/guides/package-exports/#conditions-custom +- https://github.com/rollup/plugins/blob/node-resolve-v11.0.0/packages/node-resolve/README.md#exportconditions + +To import only what you need, please [check out this](/guide/importing#es6-via-npm) guide. + +## CommonJS via npm + +If you receive an error like error TS2304: Cannot find name 'Promise' or error TS2304: Cannot find name +'Iterable' when using RxJS you may need to install a supplemental set of typings. + +1. For typings users: + +```shell +typings install es6-shim --ambient +``` + +2. If you're not using typings the interfaces can be copied from /es6-shim/es6-shim.d.ts. + +3. Add type definition file included in tsconfig.json or CLI argument. + +## All Module Types (CJS/ES6/AMD/TypeScript) via npm + +To install this library via npm version 3, use the following command: + +```shell +npm install @reactivex/rxjs +``` + +If you are using npm version 2, you need to specify the library version explicitly: + +```shell +npm install @reactivex/rxjs@7.3.0 +``` diff --git a/apps/rxjs.dev-next/docs/guide/observable.md b/apps/rxjs.dev-next/docs/guide/observable.md new file mode 100644 index 0000000000..4e0e7bd114 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/observable.md @@ -0,0 +1,450 @@ +# Observable + +Observables are lazy Push collections of multiple values. They fill the missing spot in the following table: + +| | Single | Multiple | +| -------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| **Pull** | [`Function`](https://developer.mozilla.org/en-US/docs/Glossary/Function) | [`Iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) | +| **Push** | [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) | [`Observable`](/api/index/class/Observable) | + +**Example.** The following is an Observable that pushes the values `1`, `2`, `3` immediately (synchronously) when subscribed, and the value `4` after one second has passed since the subscribe call, then completes: + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable((subscriber) => { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + setTimeout(() => { + subscriber.next(4); + subscriber.complete(); + }, 1000); +}); +``` + +To invoke the Observable and see these values, we need to _subscribe_ to it: + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable((subscriber) => { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + setTimeout(() => { + subscriber.next(4); + subscriber.complete(); + }, 1000); +}); + +console.log('just before subscribe'); +observable.subscribe({ + next(x) { + console.log('got value ' + x); + }, + error(err) { + console.error('something wrong occurred: ' + err); + }, + complete() { + console.log('done'); + }, +}); +console.log('just after subscribe'); +``` + +Which executes as such on the console: + +```plaintext +just before subscribe +got value 1 +got value 2 +got value 3 +just after subscribe +got value 4 +done +``` + +## Pull versus Push + +_Pull_ and _Push_ are two different protocols that describe how a data _Producer_ can communicate with a data _Consumer_. + +**What is Pull?** In Pull systems, the Consumer determines when it receives data from the data Producer. The Producer itself is unaware of when the data will be delivered to the Consumer. + +Every JavaScript Function is a Pull system. The function is a Producer of data, and the code that calls the function is consuming it by "pulling" out a _single_ return value from its call. + +ES2015 introduced [generator functions and iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) (`function*`), another type of Pull system. Code that calls `iterator.next()` is the Consumer, "pulling" out _multiple_ values from the iterator (the Producer). + +| | Producer | Consumer | +| -------- | ------------------------------------------ | ------------------------------------------- | +| **Pull** | **Passive:** produces data when requested. | **Active:** decides when data is requested. | +| **Push** | **Active:** produces data at its own pace. | **Passive:** reacts to received data. | + +**What is Push?** In Push systems, the Producer determines when to send data to the Consumer. The Consumer is unaware of when it will receive that data. + +Promises are the most common type of Push system in JavaScript today. A Promise (the Producer) delivers a resolved value to registered callbacks (the Consumers), but unlike functions, it is the Promise which is in charge of determining precisely when that value is "pushed" to the callbacks. + +RxJS introduces Observables, a new Push system for JavaScript. An Observable is a Producer of multiple values, "pushing" them to Observers (Consumers). + +- A **Function** is a lazily evaluated computation that synchronously returns a single value on invocation. +- A **generator** is a lazily evaluated computation that synchronously returns zero to (potentially) infinite values on iteration. +- A **Promise** is a computation that may (or may not) eventually return a single value. +- An **Observable** is a lazily evaluated computation that can synchronously or asynchronously return zero to (potentially) infinite values from the time it's invoked onwards. + +For more info about what to use when converting Observables to Promises, please refer to [this guide](/deprecations/to-promise). + +## Observables as generalizations of functions + +Contrary to popular claims, Observables are not like EventEmitters nor are they like Promises for multiple values. Observables _may act_ like EventEmitters in some cases, namely when they are multicasted using RxJS Subjects, but usually they don't act like EventEmitters. + +Observables are like functions with zero arguments, but generalize those to allow multiple values. + +Consider the following: + +```ts +function foo() { + console.log('Hello'); + return 42; +} + +const x = foo.call(); // same as foo() +console.log(x); +const y = foo.call(); // same as foo() +console.log(y); +``` + +We expect to see as output: + +```plaintext +"Hello" +42 +"Hello" +42 +``` + +You can write the same behavior above, but with Observables: + +```ts +import { Observable } from 'rxjs'; + +const foo = new Observable((subscriber) => { + console.log('Hello'); + subscriber.next(42); +}); + +foo.subscribe((x) => { + console.log(x); +}); +foo.subscribe((y) => { + console.log(y); +}); +``` + +And the output is the same: + +```plaintext +"Hello" +42 +"Hello" +42 +``` + +This happens because both functions and Observables are lazy computations. If you don't call the function, the `console.log('Hello')` won't happen. Also with Observables, if you don't "call" it (with `subscribe`), the `console.log('Hello')` won't happen. Plus, "calling" or "subscribing" is an isolated operation: two function calls trigger two separate side effects, and two Observable subscribes trigger two separate side effects. As opposed to EventEmitters which share the side effects and have eager execution regardless of the existence of subscribers, Observables have no shared execution and are lazy. + +Subscribing to an Observable is analogous to calling a Function. + +Some people claim that Observables are asynchronous. That is not true. If you surround a function call with logs, like this: + +```ts +console.log('before'); +console.log(foo.call()); +console.log('after'); +``` + +You will see the output: + +```plaintext +"before" +"Hello" +42 +"after" +``` + +And this is the same behavior with Observables: + +```ts +console.log('before'); +foo.subscribe((x) => { + console.log(x); +}); +console.log('after'); +``` + +And the output is: + +```plaintext +"before" +"Hello" +42 +"after" +``` + +Which proves the subscription of `foo` was entirely synchronous, just like a function. + +Observables are able to deliver values either synchronously or asynchronously. + +What is the difference between an Observable and a function? **Observables can "return" multiple values over time**, something which functions cannot. You can't do this: + +```ts +function foo() { + console.log('Hello'); + return 42; + return 100; // dead code. will never happen +} +``` + +Functions can only return one value. Observables, however, can do this: + +```ts +import { Observable } from 'rxjs'; + +const foo = new Observable((subscriber) => { + console.log('Hello'); + subscriber.next(42); + subscriber.next(100); // "return" another value + subscriber.next(200); // "return" yet another +}); + +console.log('before'); +foo.subscribe((x) => { + console.log(x); +}); +console.log('after'); +``` + +With synchronous output: + +```plaintext +"before" +"Hello" +42 +100 +200 +"after" +``` + +But you can also "return" values asynchronously: + +```ts +import { Observable } from 'rxjs'; + +const foo = new Observable((subscriber) => { + console.log('Hello'); + subscriber.next(42); + subscriber.next(100); + subscriber.next(200); + setTimeout(() => { + subscriber.next(300); // happens asynchronously + }, 1000); +}); + +console.log('before'); +foo.subscribe((x) => { + console.log(x); +}); +console.log('after'); +``` + +With output: + +```plaintext +"before" +"Hello" +42 +100 +200 +"after" +300 +``` + +Conclusion: + +- `func.call()` means "_give me one value synchronously_" +- `observable.subscribe()` means "_give me any amount of values, either synchronously or asynchronously_" + +## Anatomy of an Observable + +Observables are **created** using `new Observable` or a creation operator, are **subscribed** to with an Observer, **execute** to deliver `next` / `error` / `complete` notifications to the Observer, and their execution may be **disposed**. These four aspects are all encoded in an Observable instance, but some of these aspects are related to other types, like Observer and Subscription. + +Core Observable concerns: + +- **Creating** Observables +- **Subscribing** to Observables +- **Executing** the Observable +- **Disposing** Observables + +### Creating Observables + +The `Observable` constructor takes one argument: the `subscribe` function. + +The following example creates an Observable to emit the string `'hi'` every second to a subscriber. + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable(function subscribe(subscriber) { + const id = setInterval(() => { + subscriber.next('hi'); + }, 1000); +}); +``` + +Observables can be created with `new Observable`. Most commonly, observables are created using creation functions, like `of`, `from`, `interval`, etc. + +In the example above, the `subscribe` function is the most important piece to describe the Observable. Let's look at what subscribing means. + +### Subscribing to Observables + +The Observable `observable` in the example can be _subscribed_ to, like this: + +```ts +observable.subscribe((x) => console.log(x)); +``` + +It is not a coincidence that `observable.subscribe` and `subscribe` in `new Observable(function subscribe(subscriber) {...})` have the same name. In the library, they are different, but for practical purposes you can consider them conceptually equal. + +This shows how `subscribe` calls are not shared among multiple Observers of the same Observable. When calling `observable.subscribe` with an Observer, the function `subscribe` in `new Observable(function subscribe(subscriber) {...})` is run for that given subscriber. Each call to `observable.subscribe` triggers its own independent setup for that given subscriber. + +Subscribing to an Observable is like calling a function, providing callbacks where the data will be delivered to. + +This is drastically different to event handler APIs like `addEventListener` / `removeEventListener`. With `observable.subscribe`, the given Observer is not registered as a listener in the Observable. The Observable does not even maintain a list of attached Observers. + +A `subscribe` call is simply a way to start an "Observable execution" and deliver values or events to an Observer of that execution. + +### Executing Observables + +The code inside `new Observable(function subscribe(subscriber) {...})` represents an "Observable execution", a lazy computation that only happens for each Observer that subscribes. The execution produces multiple values over time, either synchronously or asynchronously. + +There are three types of values an Observable Execution can deliver: + +- "Next" notification: sends a value such as a Number, a String, an Object, etc. +- "Error" notification: sends a JavaScript Error or exception. +- "Complete" notification: does not send a value. + +"Next" notifications are the most important and most common type: they represent actual data being delivered to a subscriber. "Error" and "Complete" notifications may happen only once during the Observable Execution, and there can only be either one of them. + +These constraints are expressed best in the so-called _Observable Grammar_ or _Contract_, written as a regular expression: + +```plaintext +next*(error|complete)? +``` + +In an Observable Execution, zero to infinite Next notifications may be delivered. If either an Error or Complete notification is delivered, then nothing else can be delivered afterwards. + +The following is an example of an Observable execution that delivers three Next notifications, then completes: + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable(function subscribe(subscriber) { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.complete(); +}); +``` + +Observables strictly adhere to the Observable Contract, so the following code would not deliver the Next notification `4`: + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable(function subscribe(subscriber) { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.complete(); + subscriber.next(4); // Is not delivered because it would violate the contract +}); +``` + +It is a good idea to wrap any code in `subscribe` with `try`/`catch` block that will deliver an Error notification if it catches an exception: + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable(function subscribe(subscriber) { + try { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.complete(); + } catch (err) { + subscriber.error(err); // delivers an error if it caught one + } +}); +``` + +### Disposing Observable Executions + +Because Observable Executions may be infinite, and it's common for an Observer to want to abort execution in finite time, we need an API for canceling an execution. Since each execution is exclusive to one Observer only, once the Observer is done receiving values, it has to have a way to stop the execution, in order to avoid wasting computation power or memory resources. + +When `observable.subscribe` is called, the Observer gets attached to the newly created Observable execution. This call also returns an object, the `Subscription`: + +```ts +const subscription = observable.subscribe((x) => console.log(x)); +``` + +The Subscription represents the ongoing execution, and has a minimal API which allows you to cancel that execution. Read more about the [`Subscription` type here](/guide/subscription). With `subscription.unsubscribe()` you can cancel the ongoing execution: + +```ts +import { from } from 'rxjs'; + +const observable = from([10, 20, 30]); +const subscription = observable.subscribe((x) => console.log(x)); +// Later: +subscription.unsubscribe(); +``` + +When you subscribe, you get back a Subscription, which represents the ongoing execution. Just call `unsubscribe()` to cancel the execution. + +Each Observable must define how to dispose resources of that execution when we create the Observable using `create()`. You can do that by returning a custom `unsubscribe` function from within `function subscribe()`. + +For instance, this is how we clear an interval execution set with `setInterval`: + +```ts +import { Observable } from 'rxjs'; + +const observable = new Observable(function subscribe(subscriber) { + // Keep track of the interval resource + const intervalId = setInterval(() => { + subscriber.next('hi'); + }, 1000); + + // Provide a way of canceling and disposing the interval resource + return function unsubscribe() { + clearInterval(intervalId); + }; +}); +``` + +Just like `observable.subscribe` resembles `new Observable(function subscribe() {...})`, the `unsubscribe` we return from `subscribe` is conceptually equal to `subscription.unsubscribe`. In fact, if we remove the ReactiveX types surrounding these concepts, we're left with rather straightforward JavaScript. + +```ts +function subscribe(subscriber) { + const intervalId = setInterval(() => { + subscriber.next('hi'); + }, 1000); + + return function unsubscribe() { + clearInterval(intervalId); + }; +} + +const unsubscribe = subscribe({ next: (x) => console.log(x) }); + +// Later: +unsubscribe(); // dispose the resources +``` + +The reason why we use Rx types like Observable, Observer, and Subscription is to get safety (such as the Observable Contract) and composability with Operators. diff --git a/apps/rxjs.dev-next/docs/guide/observer.md b/apps/rxjs.dev-next/docs/guide/observer.md new file mode 100644 index 0000000000..c59e54c8d0 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/observer.md @@ -0,0 +1,38 @@ +# Observer + +**What is an Observer?** An Observer is a consumer of values delivered by an Observable. Observers are simply a set of callbacks, one for each type of notification delivered by the Observable: `next`, `error`, and `complete`. The following is an example of a typical Observer object: + +```ts +const observer = { + next: (x) => console.log('Observer got a next value: ' + x), + error: (err) => console.error('Observer got an error: ' + err), + complete: () => console.log('Observer got a complete notification'), +}; +``` + +To use the Observer, provide it to the `subscribe` of an Observable: + +```ts +observable.subscribe(observer); +``` + +Observers are just objects with three callbacks, one for each type of notification that an Observable may deliver. + +Observers in RxJS may also be _partial_. If you don't provide one of the callbacks, the execution of the Observable will still happen normally, except some types of notifications will be ignored, because they don't have a corresponding callback in the Observer. + +The example below is an `Observer` without the `complete` callback: + +```ts +const observer = { + next: (x) => console.log('Observer got a next value: ' + x), + error: (err) => console.error('Observer got an error: ' + err), +}; +``` + +When subscribing to an `Observable`, you may also just provide the next callback as an argument, without being attached to an `Observer` object, for instance like this: + +```ts +observable.subscribe((x) => console.log('Observer got a next value: ' + x)); +``` + +Internally in `observable.subscribe`, it will create an `Observer` object using the callback argument as the `next` handler. diff --git a/apps/rxjs.dev-next/docs/guide/operators.md b/apps/rxjs.dev-next/docs/guide/operators.md new file mode 100644 index 0000000000..3cfd991f85 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/operators.md @@ -0,0 +1,346 @@ +# RxJS Operators + +RxJS is mostly useful for its _operators_, even though the Observable is the foundation. Operators are the essential pieces that allow complex asynchronous code to be easily composed in a declarative manner. + +## What are operators? + +Operators are **functions**. There are two kinds of operators: + +**Pipeable Operators** are the kind that can be piped to Observables using the syntax `observableInstance.pipe(operator)` or, more commonly, `observableInstance.pipe(operatorFactory())`. Operator factory functions include, [`filter(...)`](/api/operators/filter), and [`mergeMap(...)`](/api/operators/mergeMap). + +When Pipeable Operators are called, they do not _change_ the existing Observable instance. Instead, they return a _new_ Observable, whose subscription logic is based on the first Observable. + +A Pipeable Operator is a function that takes an Observable as its input and returns another Observable. It is a pure operation: the previous Observable stays unmodified. + +A Pipeable Operator Factory is a function that can take parameters to set the context and return a Pipeable Operator. The factory’s arguments belong to the operator’s lexical scope. + +A Pipeable Operator is essentially a pure function which takes one Observable as input and generates another Observable as output. Subscribing to the output Observable will also subscribe to the input Observable. + +**Creation Operators** are the other kind of operator, which can be called as standalone functions to create a new Observable. For example: `of(1, 2, 3)` creates an observable that will emit 1, 2, and 3, one right after another. Creation operators will be discussed in more detail in a later section. + +For example, the operator called [`map`](/api/operators/map) is analogous to the Array method of the same name. Just as `[1, 2, 3].map(x => x * x)` will yield `[1, 4, 9]`, the Observable created like this: + +```ts +import { of, map } from 'rxjs'; + +of(1, 2, 3) + .pipe(map((x) => x * x)) + .subscribe((v) => console.log(`value: ${v}`)); + +// Logs: +// value: 1 +// value: 4 +// value: 9 +``` + +will emit `1`, `4`, `9`. Another useful operator is [`first`](/api/operators/first): + +```ts +import { of, first } from 'rxjs'; + +of(1, 2, 3) + .pipe(first()) + .subscribe((v) => console.log(`value: ${v}`)); + +// Logs: +// value: 1 +``` + +Note that `map` logically must be constructed on the fly, since it must be given the mapping function to. By contrast, `first` could be a constant, but is nonetheless constructed on the fly. As a general practice, all operators are constructed, whether they need arguments or not. + +## Piping + +Pipeable operators are functions, so they _could_ be used like ordinary functions: `op()(obs)` — but in practice, there tend to be many of them convolved together, and quickly become unreadable: `op4()(op3()(op2()(op1()(obs))))`. For that reason, Observables have a method called `.pipe()` that accomplishes the same thing while being much easier to read: + +```ts +obs.pipe(op1(), op2(), op3(), op4()); +``` + +As a stylistic matter, `op()(obs)` is never used, even if there is only one operator; `obs.pipe(op())` is universally preferred. + +## Creation Operators + +**What are creation operators?** Distinct from pipeable operators, creation operators are functions that can be used to create an Observable with some common predefined behavior or by joining other Observables. + +A typical example of a creation operator would be the `interval` function. It takes a number (not an Observable) as input argument, and produces an Observable as output: + +```ts +import { interval } from 'rxjs'; + +const observable = interval(1000 /* number of milliseconds */); +``` + +See the list of all static creation operators [here](#creation-operators-list). + +## Higher-order Observables + +Observables most commonly emit ordinary values like strings and numbers, but surprisingly often, it is necessary to handle Observables _of_ Observables, so-called higher-order Observables. For example, imagine you had an Observable emitting strings that were the URLs of files you wanted to see. The code might look like this: + +```ts +const fileObservable = urlObservable.pipe(map((url) => http.get(url))); +``` + +`http.get()` returns an Observable (of string or string arrays probably) for each individual URL. Now you have an Observable _of_ Observables, a higher-order Observable. + +But how do you work with a higher-order Observable? Typically, by _flattening_: by (somehow) converting a higher-order Observable into an ordinary Observable. For example: + +```ts +const fileObservable = urlObservable.pipe( + map((url) => http.get(url)), + concatAll() +); +``` + +The [`concatAll()`](/api/operators/concatAll) operator subscribes to each "inner" Observable that comes out of the "outer" Observable, and copies all the emitted values until that Observable completes, and goes on to the next one. All of the values are in that way concatenated. Other useful flattening operators (called [_join operators_](#join-operators)) are + +- [`mergeAll()`](/api/operators/mergeAll) — subscribes to each inner Observable as it arrives, then emits each value as it arrives +- [`switchAll()`](/api/operators/switchAll) — subscribes to the first inner Observable when it arrives, and emits each value as it arrives, but when the next inner Observable arrives, unsubscribes to the previous one, and subscribes to the new one. +- [`exhaustAll()`](/api/operators/exhaustAll) — subscribes to the first inner Observable when it arrives, and emits each value as it arrives, discarding all newly arriving inner Observables until that first one completes, then waits for the next inner Observable. + +Just as many array libraries combine [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [`flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) (or `flatten()`) into a single [`flatMap()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap), there are mapping equivalents of all the RxJS flattening operators [`concatMap()`](/api/operators/concatMap), [`mergeMap()`](/api/operators/mergeMap), [`switchMap()`](/api/operators/switchMap), and [`exhaustMap()`](/api/operators/exhaustMap). + +## Marble diagrams + +To explain how operators work, textual descriptions are often not enough. Many operators are related to time, they may for instance delay, sample, throttle, or debounce value emissions in different ways. Diagrams are often a better tool for that. _Marble Diagrams_ are visual representations of how operators work, and include the input Observable(s), the operator and its parameters, and the output Observable. + +In a marble diagram, time flows to the right, and the diagram describes how values ("marbles") are emitted on the Observable execution. + +Below you can see the anatomy of a marble diagram. + + + +Throughout this documentation site, we extensively use marble diagrams to explain how operators work. They may be really useful in other contexts too, like on a whiteboard or even in our unit tests (as ASCII diagrams). + +## Categories of operators + +There are operators for different purposes, and they may be categorized as: creation, transformation, filtering, joining, multicasting, error handling, utility, etc. In the following list you will find all the operators organized in categories. + +For a complete overview, see the [references page](/api). + +### Creation Operators + +- [`ajax`](/api/ajax/ajax) +- [`bindCallback`](/api/index/function/bindCallback) +- [`bindNodeCallback`](/api/index/function/bindNodeCallback) +- [`defer`](/api/index/function/defer) +- [`EMPTY`](/api/index/const/EMPTY) +- [`from`](/api/index/function/from) +- [`fromEvent`](/api/index/function/fromEvent) +- [`fromEventPattern`](/api/index/function/fromEventPattern) +- [`generate`](/api/index/function/generate) +- [`interval`](/api/index/function/interval) +- [`of`](/api/index/function/of) +- [`range`](/api/index/function/range) +- [`throwError`](/api/index/function/throwError) +- [`timer`](/api/index/function/timer) +- [`iif`](/api/index/function/iif) + +### Join Creation Operators + +These are Observable creation operators that also have join functionality -- emitting values of multiple source Observables. + +- [`combineLatest`](/api/index/function/combineLatest) +- [`concat`](/api/index/function/concat) +- [`forkJoin`](/api/index/function/forkJoin) +- [`merge`](/api/index/function/merge) +- [`partition`](/api/index/function/partition) +- [`race`](/api/index/function/race) +- [`zip`](/api/index/function/zip) + +### Transformation Operators + +- [`buffer`](/api/operators/buffer) +- [`bufferCount`](/api/operators/bufferCount) +- [`bufferTime`](/api/operators/bufferTime) +- [`bufferToggle`](/api/operators/bufferToggle) +- [`bufferWhen`](/api/operators/bufferWhen) +- [`concatMap`](/api/operators/concatMap) +- [`concatMapTo`](/api/operators/concatMapTo) +- [`exhaustMap`](/api/operators/exhaustMap) +- [`expand`](/api/operators/expand) +- [`groupBy`](/api/operators/groupBy) +- [`map`](/api/operators/map) +- [`mapTo`](/api/operators/mapTo) +- [`mergeMap`](/api/operators/mergeMap) +- [`mergeMapTo`](/api/operators/mergeMapTo) +- [`mergeScan`](/api/operators/mergeScan) +- [`pairwise`](/api/operators/pairwise) +- [`partition`](/api/operators/partition) +- [`scan`](/api/operators/scan) +- [`switchScan`](/api/operators/switchScan) +- [`switchMap`](/api/operators/switchMap) +- [`switchMapTo`](/api/operators/switchMapTo) +- [`window`](/api/operators/window) +- [`windowCount`](/api/operators/windowCount) +- [`windowTime`](/api/operators/windowTime) +- [`windowToggle`](/api/operators/windowToggle) +- [`windowWhen`](/api/operators/windowWhen) + +### Filtering Operators + +- [`audit`](/api/operators/audit) +- [`auditTime`](/api/operators/auditTime) +- [`debounce`](/api/operators/debounce) +- [`debounceTime`](/api/operators/debounceTime) +- [`distinct`](/api/operators/distinct) +- [`distinctUntilChanged`](/api/operators/distinctUntilChanged) +- [`distinctUntilKeyChanged`](/api/operators/distinctUntilKeyChanged) +- [`elementAt`](/api/operators/elementAt) +- [`filter`](/api/operators/filter) +- [`first`](/api/operators/first) +- [`ignoreElements`](/api/operators/ignoreElements) +- [`last`](/api/operators/last) +- [`sample`](/api/operators/sample) +- [`sampleTime`](/api/operators/sampleTime) +- [`single`](/api/operators/single) +- [`skip`](/api/operators/skip) +- [`skipLast`](/api/operators/skipLast) +- [`skipUntil`](/api/operators/skipUntil) +- [`skipWhile`](/api/operators/skipWhile) +- [`take`](/api/operators/take) +- [`takeLast`](/api/operators/takeLast) +- [`takeUntil`](/api/operators/takeUntil) +- [`takeWhile`](/api/operators/takeWhile) +- [`throttle`](/api/operators/throttle) +- [`throttleTime`](/api/operators/throttleTime) + +### Join Operators + +Also see the [Join Creation Operators](#join-creation-operators) section above. + +- [`combineLatestAll`](/api/operators/combineLatestAll) +- [`concatAll`](/api/operators/concatAll) +- [`exhaustAll`](/api/operators/exhaustAll) +- [`mergeAll`](/api/operators/mergeAll) +- [`switchAll`](/api/operators/switchAll) +- [`startWith`](/api/operators/startWith) +- [`withLatestFrom`](/api/operators/withLatestFrom) + +### Multicasting Operators + +- [`share`](/api/operators/share) + +### Error Handling Operators + +- [`catchError`](/api/operators/catchError) +- [`retry`](/api/operators/retry) +- [`retryWhen`](/api/operators/retryWhen) + +### Utility Operators + +- [`tap`](/api/operators/tap) +- [`delay`](/api/operators/delay) +- [`delayWhen`](/api/operators/delayWhen) +- [`dematerialize`](/api/operators/dematerialize) +- [`materialize`](/api/operators/materialize) +- [`observeOn`](/api/operators/observeOn) +- [`subscribeOn`](/api/operators/subscribeOn) +- [`timeInterval`](/api/operators/timeInterval) +- [`timestamp`](/api/operators/timestamp) +- [`timeout`](/api/operators/timeout) +- [`timeoutWith`](/api/operators/timeoutWith) +- [`toArray`](/api/operators/toArray) + +### Conditional and Boolean Operators + +- [`defaultIfEmpty`](/api/operators/defaultIfEmpty) +- [`every`](/api/operators/every) +- [`find`](/api/operators/find) +- [`findIndex`](/api/operators/findIndex) +- [`isEmpty`](/api/operators/isEmpty) + +### Mathematical and Aggregate Operators + +- [`count`](/api/operators/count) +- [`max`](/api/operators/max) +- [`min`](/api/operators/min) +- [`reduce`](/api/operators/reduce) + +## Creating custom operators + +### Use the `pipe()` function to make new operators + +If there is a commonly used sequence of operators in your code, use the `pipe()` function to extract the sequence into a new operator. Even if a sequence is not that common, breaking it out into a single operator can improve readability. + +For example, you could make a function that discarded odd values and doubled even values like this: + +```ts +import { pipe, filter, map } from 'rxjs'; + +function discardOddDoubleEven() { + return pipe( + filter((v) => !(v % 2)), + map((v) => v + v) + ); +} +``` + +(The `pipe()` function is analogous to, but not the same thing as, the `.pipe()` method on an Observable.) + +### Creating new operators from scratch + +It is more complicated, but if you have to write an operator that cannot be made from a combination of existing operators (a rare occurrence), you can write an operator from scratch using the Observable constructor, like this: + +```ts +import { Observable, of } from 'rxjs'; + +function delay(delayInMillis: number) { + return (observable: Observable) => + new Observable((subscriber) => { + // this function will be called each time this + // Observable is subscribed to. + const allTimerIDs = new Set(); + let hasCompleted = false; + const subscription = observable.subscribe({ + next(value) { + // Start a timer to delay the next value + // from being pushed. + const timerID = setTimeout(() => { + subscriber.next(value); + // after we push the value, we need to clean up the timer timerID + allTimerIDs.delete(timerID); + // If the source has completed, and there are no more timers running, + // we can complete the resulting observable. + if (hasCompleted && allTimerIDs.size === 0) { + subscriber.complete(); + } + }, delayInMillis); + + allTimerIDs.add(timerID); + }, + error(err) { + // We need to make sure we're propagating our errors through. + subscriber.error(err); + }, + complete() { + hasCompleted = true; + // If we still have timers running, we don't want to complete yet. + if (allTimerIDs.size === 0) { + subscriber.complete(); + } + }, + }); + + // Return the finalization logic. This will be invoked when + // the result errors, completes, or is unsubscribed. + return () => { + subscription.unsubscribe(); + // Clean up our timers. + for (const timerID of allTimerIDs) { + clearTimeout(timerID); + } + }; + }); +} + +// Try it out! +of(1, 2, 3).pipe(delay(1000)).subscribe(console.log); +``` + +Note that you must + +1. implement all three Observer functions, `next()`, `error()`, and `complete()` when subscribing to the input Observable. +2. implement a "finalization" function that cleans up when the Observable completes (in this case by unsubscribing and clearing any pending timeouts). +3. return that finalization function from the function passed to the Observable constructor. + +Of course, this is only an example; the [`delay()`](/api/operators/delay) operator already exists. diff --git a/apps/rxjs.dev-next/docs/guide/overview.md b/apps/rxjs.dev-next/docs/guide/overview.md new file mode 100644 index 0000000000..8267959103 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/overview.md @@ -0,0 +1,124 @@ +# Introduction + +RxJS is a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the [Observable](/guide/observable), satellite types (Observer, Schedulers, Subjects) and operators inspired by `Array` methods (`map`, `filter`, `reduce`, `every`, etc) to allow handling asynchronous events as collections. + +Think of RxJS as Lodash for events. + +ReactiveX combines the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) with the [Iterator pattern](https://en.wikipedia.org/wiki/Iterator_pattern) and [functional programming with collections](http://martinfowler.com/articles/collection-pipeline/#NestedOperatorExpressions) to fill the need for an ideal way of managing sequences of events. + +The essential concepts in RxJS which solve async event management are: + +- **Observable:** represents the idea of an invokable collection of future values or events. +- **Observer:** is a collection of callbacks that knows how to listen to values delivered by the Observable. +- **Subscription:** represents the execution of an Observable, is primarily useful for cancelling the execution. +- **Operators:** are pure functions that enable a functional programming style of dealing with collections with operations like `map`, `filter`, `concat`, `reduce`, etc. +- **Subject:** is equivalent to an EventEmitter, and the only way of multicasting a value or event to multiple Observers. +- **Schedulers:** are centralized dispatchers to control concurrency, allowing us to coordinate when computation happens on e.g. `setTimeout` or `requestAnimationFrame` or others. + +## First examples + +Normally you register event listeners. + +```ts +document.addEventListener('click', () => console.log('Clicked!')); +``` + +Using RxJS you create an observable instead. + +```ts +import { fromEvent } from 'rxjs'; + +fromEvent(document, 'click').subscribe(() => console.log('Clicked!')); +``` + +### Purity + +What makes RxJS powerful is its ability to produce values using pure functions. That means your code is less prone to errors. + +Normally you would create an impure function, where other +pieces of your code can mess up your state. + +```ts +let count = 0; +document.addEventListener('click', () => console.log(`Clicked ${++count} times`)); +``` + +Using RxJS you isolate the state. + +```ts +import { fromEvent, scan } from 'rxjs'; + +fromEvent(document, 'click') + .pipe(scan((count) => count + 1, 0)) + .subscribe((count) => console.log(`Clicked ${count} times`)); +``` + +The **scan** operator works just like **reduce** for arrays. It takes a value which is exposed to a callback. The returned value of the callback will then become the next value exposed the next time the callback runs. + +### Flow + +RxJS has a whole range of operators that helps you control how the events flow through your observables. + +This is how you would allow at most one click per second, with plain JavaScript: + +```ts +let count = 0; +let rate = 1000; +let lastClick = Date.now() - rate; +document.addEventListener('click', () => { + if (Date.now() - lastClick >= rate) { + console.log(`Clicked ${++count} times`); + lastClick = Date.now(); + } +}); +``` + +With RxJS: + +```ts +import { fromEvent, throttleTime, scan } from 'rxjs'; + +fromEvent(document, 'click') + .pipe( + throttleTime(1000), + scan((count) => count + 1, 0) + ) + .subscribe((count) => console.log(`Clicked ${count} times`)); +``` + +Other flow control operators are [**filter**](/api/operators/filter), [**delay**](/api/operators/delay), [**debounceTime**](/api/operators/debounceTime), [**take**](/api/operators/take), [**takeUntil**](/api/operators/takeUntil), [**distinct**](/api/operators/distinct), [**distinctUntilChanged**](/api/operators/distinctUntilChanged) etc. + +### Values + +You can transform the values passed through your observables. + +Here's how you can add the current mouse x position for every click, in plain JavaScript: + +```ts +let count = 0; +const rate = 1000; +let lastClick = Date.now() - rate; +document.addEventListener('click', (event) => { + if (Date.now() - lastClick >= rate) { + count += event.clientX; + console.log(count); + lastClick = Date.now(); + } +}); +``` + +With RxJS: + +```ts +import { fromEvent, throttleTime, map, scan } from 'rxjs'; + +fromEvent(document, 'click') + .pipe( + throttleTime(1000), + map((event) => event.clientX), + scan((count, clientX) => count + clientX, 0) + ) + .subscribe((count) => console.log(count)); +``` + +Other value producing operators are [**pairwise**](/api/operators/pairwise), [**sample**](/api/operators/sample) etc. diff --git a/apps/rxjs.dev-next/docs/guide/scheduler.md b/apps/rxjs.dev-next/docs/guide/scheduler.md new file mode 100644 index 0000000000..93087b6422 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/scheduler.md @@ -0,0 +1,147 @@ +# Scheduler + +**What is a Scheduler?** A scheduler controls when a subscription starts and when notifications are delivered. It consists of three components. + +- **A Scheduler is a data structure.** It knows how to store and queue tasks based on priority or other criteria. +- **A Scheduler is an execution context.** It denotes where and when the task is executed (e.g. immediately, or in another callback mechanism such as setTimeout or process.nextTick, or the animation frame). +- **A Scheduler has a (virtual) clock.** It provides a notion of "time" by a getter method `now()` on the scheduler. Tasks being scheduled on a particular scheduler will adhere only to the time denoted by that clock. + +A Scheduler lets you define in what execution context will an Observable deliver notifications to its Observer. + +In the example below, we take the usual simple Observable that emits values `1`, `2`, `3` synchronously, and use the operator `observeOn` to specify the `asyncScheduler` scheduler to use for delivering those values. + + +```ts +import { Observable, observeOn, asyncScheduler } from 'rxjs'; + +const observable = new Observable((observer) => { + observer.next(1); + observer.next(2); + observer.next(3); + observer.complete(); +}).pipe( + observeOn(asyncScheduler) +); + +console.log('just before subscribe'); +observable.subscribe({ + next(x) { + console.log('got value ' + x); + }, + error(err) { + console.error('something wrong occurred: ' + err); + }, + complete() { + console.log('done'); + }, +}); +console.log('just after subscribe'); +``` + +Which executes with the output: + +```plaintext +just before subscribe +just after subscribe +got value 1 +got value 2 +got value 3 +done +``` + +Notice how the notifications `got value...` were delivered after `just after subscribe`, which is different to the default behavior we have seen so far. This is because `observeOn(asyncScheduler)` introduces a proxy Observer between `new Observable` and the final Observer. Let's rename some identifiers to make that distinction obvious in the example code: + + +```ts +import { Observable, observeOn, asyncScheduler } from 'rxjs'; + +const observable = new Observable((proxyObserver) => { + proxyObserver.next(1); + proxyObserver.next(2); + proxyObserver.next(3); + proxyObserver.complete(); +}).pipe( + observeOn(asyncScheduler) +); + +const finalObserver = { + next(x) { + console.log('got value ' + x); + }, + error(err) { + console.error('something wrong occurred: ' + err); + }, + complete() { + console.log('done'); + }, +}; + +console.log('just before subscribe'); +observable.subscribe(finalObserver); +console.log('just after subscribe'); +``` + +The `proxyObserver` is created in `observeOn(asyncScheduler)`, and its `next(val)` function is approximately the following: + + +```ts +const proxyObserver = { + next(val) { + asyncScheduler.schedule( + (x) => finalObserver.next(x), + 0 /* delay */, + val /* will be the x for the function above */ + ); + }, + + // ... +}; +``` + +The `asyncScheduler` Scheduler operates with a `setTimeout` or `setInterval`, even if the given `delay` was zero. As usual, in JavaScript, `setTimeout(fn, 0)` is known to run the function `fn` earliest on the next event loop iteration. This explains why `got value 1` is delivered to the `finalObserver` after `just after subscribe` happened. + +The `schedule()` method of a Scheduler takes a `delay` argument, which refers to a quantity of time relative to the Scheduler's own internal clock. A Scheduler's clock need not have any relation to the actual wall-clock time. This is how temporal operators like `delay` operate not on actual time, but on time dictated by the Scheduler's clock. This is specially useful in testing, where a _virtual time Scheduler_ may be used to fake wall-clock time while in reality executing scheduled tasks synchronously. + +## Scheduler Types + +The `asyncScheduler` Scheduler is one of the built-in schedulers provided by RxJS. Each of these can be created and returned by using static properties of the `Scheduler` object. + +| Scheduler | Purpose | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `null` | By not passing any scheduler, notifications are delivered synchronously and recursively. Use this for constant-time operations or tail recursive operations. | +| `queueScheduler` | Schedules on a queue in the current event frame (trampoline scheduler). Use this for iteration operations. | +| `asapScheduler` | Schedules on the micro task queue, which is the same queue used for promises. Basically after the current job, but before the next job. Use this for asynchronous conversions. | +| `asyncScheduler` | Schedules work with `setInterval`. Use this for time-based operations. | +| `animationFrameScheduler` | Schedules task that will happen just before next browser content repaint. Can be used to create smooth browser animations. | + +## Using Schedulers + +You may have already used schedulers in your RxJS code without explicitly stating the type of schedulers to be used. This is because all Observable operators that deal with concurrency have optional schedulers. If you do not provide the scheduler, RxJS will pick a default scheduler by using the principle of least concurrency. This means that the scheduler which introduces the least amount of concurrency that satisfies the needs of the operator is chosen. For example, for operators returning an observable with a finite and small number of messages, RxJS uses no Scheduler, i.e. `null` or `undefined`. For operators returning a potentially large or infinite number of messages, `queueScheduler` Scheduler is used. For operators which use timers, `asyncScheduler` is used. + +Because RxJS uses the least concurrency scheduler, you can pick a different scheduler if you want to introduce concurrency for performance purpose. To specify a particular scheduler, you can use those operator methods that take a scheduler, e.g., `from([10, 20, 30], asyncScheduler)`. + +**Static creation operators usually take a Scheduler as argument.** For instance, `from(array, scheduler)` lets you specify the Scheduler to use when delivering each notification converted from the `array`. It is usually the last argument to the operator. The following static creation operators take a Scheduler argument: + +- `bindCallback` +- `bindNodeCallback` +- `combineLatest` +- `concat` +- `empty` +- `from` +- `fromPromise` +- `interval` +- `merge` +- `of` +- `range` +- `throw` +- `timer` + +**Use `subscribeOn` to schedule in what context will the `subscribe()` call happen.** By default, a `subscribe()` call on an Observable will happen synchronously and immediately. However, you may delay or schedule the actual subscription to happen on a given Scheduler, using the instance operator `subscribeOn(scheduler)`, where `scheduler` is an argument you provide. + +**Use `observeOn` to schedule in what context will notifications be delivered.** As we saw in the examples above, instance operator `observeOn(scheduler)` introduces a mediator Observer between the source Observable and the destination Observer, where the mediator schedules calls to the destination Observer using your given `scheduler`. + +**Instance operators may take a Scheduler as argument.** + +Time-related operators like `bufferTime`, `debounceTime`, `delay`, `auditTime`, `sampleTime`, `throttleTime`, `timeInterval`, `timeout`, `timeoutWith`, `windowTime` all take a Scheduler as the last argument, and otherwise operate by default on the `asyncScheduler`. + +Other instance operators that take a Scheduler as argument: `combineLatest`, `concat`, `expand`, `merge`, `startWith`. diff --git a/apps/rxjs.dev-next/docs/guide/subject.md b/apps/rxjs.dev-next/docs/guide/subject.md new file mode 100644 index 0000000000..2938b633d9 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/subject.md @@ -0,0 +1,377 @@ +# Subject + +**What is a Subject?** An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast. + +A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners. + +**Every Subject is an Observable.** Given a Subject, you can `subscribe` to it, providing an Observer, which will start receiving values normally. From the perspective of the Observer, it cannot tell whether the Observable execution is coming from a plain unicast Observable or a Subject. + +Internally to the Subject, `subscribe` does not invoke a new execution that delivers values. It simply registers the given Observer in a list of Observers, similarly to how `addListener` usually works in other libraries and languages. + +**Every Subject is an Observer.** It is an object with the methods `next(v)`, `error(e)`, and `complete()`. To feed a new value to the Subject, just call `next(theValue)`, and it will be multicasted to the Observers registered to listen to the Subject. + +In the example below, we have two Observers attached to a Subject, and we feed some values to the Subject: + +```ts +import { Subject } from 'rxjs'; + +const subject = new Subject(); + +subject.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); +subject.subscribe({ + next: (v) => console.log(`observerB: ${v}`), +}); + +subject.next(1); +subject.next(2); + +// Logs: +// observerA: 1 +// observerB: 1 +// observerA: 2 +// observerB: 2 +``` + +Since a Subject is an Observer, this also means you may provide a Subject as the argument to the `subscribe` of any Observable, like the example below shows: + +```ts +import { Subject, from } from 'rxjs'; + +const subject = new Subject(); + +subject.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); +subject.subscribe({ + next: (v) => console.log(`observerB: ${v}`), +}); + +const observable = from([1, 2, 3]); + +observable.subscribe(subject); // You can subscribe providing a Subject + +// Logs: +// observerA: 1 +// observerB: 1 +// observerA: 2 +// observerB: 2 +// observerA: 3 +// observerB: 3 +``` + +With the approach above, we essentially just converted a unicast Observable execution to multicast, through the Subject. This demonstrates how Subjects are the only way of making any Observable execution be shared to multiple Observers. + +There are also a few specializations of the `Subject` type: `BehaviorSubject`, `ReplaySubject`, and `AsyncSubject`. + +## Multicasted Observables + +A "multicasted Observable" passes notifications through a Subject which may have many subscribers, whereas a plain "unicast Observable" only sends notifications to a single Observer. + +A multicasted Observable uses a Subject under the hood to make multiple Observers see the same Observable execution. + +Under the hood, this is how the `multicast` operator works: Observers subscribe to an underlying Subject, and the Subject subscribes to the source Observable. The following example is similar to the previous example which used `observable.subscribe(subject)`: + +```ts +import { from, Subject, multicast } from 'rxjs'; + +const source = from([1, 2, 3]); +const subject = new Subject(); +const multicasted = source.pipe(multicast(subject)); + +// These are, under the hood, `subject.subscribe({...})`: +multicasted.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); +multicasted.subscribe({ + next: (v) => console.log(`observerB: ${v}`), +}); + +// This is, under the hood, `source.subscribe(subject)`: +multicasted.connect(); +``` + +`multicast` returns an Observable that looks like a normal Observable, but works like a Subject when it comes to subscribing. `multicast` returns a `ConnectableObservable`, which is simply an Observable with the `connect()` method. + +The `connect()` method is important to determine exactly when the shared Observable execution will start. Because `connect()` does `source.subscribe(subject)` under the hood, `connect()` returns a Subscription, which you can unsubscribe from in order to cancel the shared Observable execution. + +### Reference counting + +Calling `connect()` manually and handling the Subscription is often cumbersome. Usually, we want to _automatically_ connect when the first Observer arrives, and automatically cancel the shared execution when the last Observer unsubscribes. + +Consider the following example where subscriptions occur as outlined by this list: + +1. First Observer subscribes to the multicasted Observable +2. **The multicasted Observable is connected** +3. The `next` value `0` is delivered to the first Observer +4. Second Observer subscribes to the multicasted Observable +5. The `next` value `1` is delivered to the first Observer +6. The `next` value `1` is delivered to the second Observer +7. First Observer unsubscribes from the multicasted Observable +8. The `next` value `2` is delivered to the second Observer +9. Second Observer unsubscribes from the multicasted Observable +10. **The connection to the multicasted Observable is unsubscribed** + +To achieve that with explicit calls to `connect()`, we write the following code: + +```ts +import { interval, Subject, multicast } from 'rxjs'; + +const source = interval(500); +const subject = new Subject(); +const multicasted = source.pipe(multicast(subject)); +let subscription1, subscription2, subscriptionConnect; + +subscription1 = multicasted.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); +// We should call `connect()` here, because the first +// subscriber to `multicasted` is interested in consuming values +subscriptionConnect = multicasted.connect(); + +setTimeout(() => { + subscription2 = multicasted.subscribe({ + next: (v) => console.log(`observerB: ${v}`), + }); +}, 600); + +setTimeout(() => { + subscription1.unsubscribe(); +}, 1200); + +// We should unsubscribe the shared Observable execution here, +// because `multicasted` would have no more subscribers after this +setTimeout(() => { + subscription2.unsubscribe(); + subscriptionConnect.unsubscribe(); // for the shared Observable execution +}, 2000); +``` + +If we wish to avoid explicit calls to `connect()`, we can use ConnectableObservable's `refCount()` method (reference counting), which returns an Observable that keeps track of how many subscribers it has. When the number of subscribers increases from `0` to `1`, it will call `connect()` for us, which starts the shared execution. Only when the number of subscribers decreases from `1` to `0` will it be fully unsubscribed, stopping further execution. + +`refCount` makes the multicasted Observable automatically start executing when the first subscriber arrives, and stop executing when the last subscriber leaves. + +Below is an example: + +```ts +import { interval, Subject, multicast, refCount } from 'rxjs'; + +const source = interval(500); +const subject = new Subject(); +const refCounted = source.pipe(multicast(subject), refCount()); +let subscription1, subscription2; + +// This calls `connect()`, because +// it is the first subscriber to `refCounted` +console.log('observerA subscribed'); +subscription1 = refCounted.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); + +setTimeout(() => { + console.log('observerB subscribed'); + subscription2 = refCounted.subscribe({ + next: (v) => console.log(`observerB: ${v}`), + }); +}, 600); + +setTimeout(() => { + console.log('observerA unsubscribed'); + subscription1.unsubscribe(); +}, 1200); + +// This is when the shared Observable execution will stop, because +// `refCounted` would have no more subscribers after this +setTimeout(() => { + console.log('observerB unsubscribed'); + subscription2.unsubscribe(); +}, 2000); + +// Logs +// observerA subscribed +// observerA: 0 +// observerB subscribed +// observerA: 1 +// observerB: 1 +// observerA unsubscribed +// observerB: 2 +// observerB unsubscribed +``` + +The `refCount()` method only exists on ConnectableObservable, and it returns an `Observable`, not another ConnectableObservable. + +## BehaviorSubject + +One of the variants of Subjects is the `BehaviorSubject`, which has a notion of "the current value". It stores the latest value emitted to its consumers, and whenever a new Observer subscribes, it will immediately receive the "current value" from the `BehaviorSubject`. + +BehaviorSubjects are useful for representing "values over time". For instance, an event stream of birthdays is a Subject, but the stream of a person's age would be a BehaviorSubject. + +In the following example, the BehaviorSubject is initialized with the value `0` which the first Observer receives when it subscribes. The second Observer receives the value `2` even though it subscribed after the value `2` was sent. + +```ts +import { BehaviorSubject } from 'rxjs'; +const subject = new BehaviorSubject(0); // 0 is the initial value + +subject.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); + +subject.next(1); +subject.next(2); + +subject.subscribe({ + next: (v) => console.log(`observerB: ${v}`), +}); + +subject.next(3); + +// Logs +// observerA: 0 +// observerA: 1 +// observerA: 2 +// observerB: 2 +// observerA: 3 +// observerB: 3 +``` + +## ReplaySubject + +A `ReplaySubject` is similar to a `BehaviorSubject` in that it can send old values to new subscribers, but it can also _record_ a part of the Observable execution. + +A `ReplaySubject` records multiple values from the Observable execution and replays them to new subscribers. + +When creating a `ReplaySubject`, you can specify how many values to replay: + +```ts +import { ReplaySubject } from 'rxjs'; +const subject = new ReplaySubject(3); // buffer 3 values for new subscribers + +subject.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); + +subject.next(1); +subject.next(2); +subject.next(3); +subject.next(4); + +subject.subscribe({ + next: (v) => console.log(`observerB: ${v}`), +}); + +subject.next(5); + +// Logs: +// observerA: 1 +// observerA: 2 +// observerA: 3 +// observerA: 4 +// observerB: 2 +// observerB: 3 +// observerB: 4 +// observerA: 5 +// observerB: 5 +``` + +You can also specify a _window time_ in milliseconds, besides of the buffer size, to determine how old the recorded values can be. In the following example we use a large buffer size of `100`, but a window time parameter of just `500` milliseconds. + + + +```ts +import { ReplaySubject } from 'rxjs'; +const subject = new ReplaySubject(100, 500 /* windowTime */); + +subject.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); + +let i = 1; +setInterval(() => subject.next(i++), 200); + +setTimeout(() => { + subject.subscribe({ + next: (v) => console.log(`observerB: ${v}`), + }); +}, 1000); + +// Logs +// observerA: 1 +// observerA: 2 +// observerA: 3 +// observerA: 4 +// observerA: 5 +// observerB: 3 +// observerB: 4 +// observerB: 5 +// observerA: 6 +// observerB: 6 +// ... +``` + +## AsyncSubject + +The AsyncSubject is a variant where only the last value of the Observable execution is sent to its observers, and only when the execution completes. + +```js +import { AsyncSubject } from 'rxjs'; +const subject = new AsyncSubject(); + +subject.subscribe({ + next: (v) => console.log(`observerA: ${v}`), +}); + +subject.next(1); +subject.next(2); +subject.next(3); +subject.next(4); + +subject.subscribe({ + next: (v) => console.log(`observerB: ${v}`), +}); + +subject.next(5); +subject.complete(); + +// Logs: +// observerA: 5 +// observerB: 5 +``` + +The AsyncSubject is similar to the [`last()`](/api/operators/last) operator, in that it waits for the `complete` notification in order to deliver a single value. + +## Void subject + +Sometimes the emitted value doesn't matter as much as the fact that a value was emitted. + +For instance, the code below signals that one second has passed. + +```ts +const subject = new Subject(); +setTimeout(() => subject.next('dummy'), 1000); +``` + +Passing a dummy value this way is clumsy and can confuse users. + +By declaring a _void subject_, you signal that the value is irrelevant. Only the event itself matters. + +```ts +const subject = new Subject(); +setTimeout(() => subject.next(), 1000); +``` + +A complete example with context is shown below: + +```ts +import { Subject } from 'rxjs'; + +const subject = new Subject(); // Shorthand for Subject + +subject.subscribe({ + next: () => console.log('One second has passed'), +}); + +setTimeout(() => subject.next(), 1000); +``` + +Before version 7, the default type of Subject values was `any`. `Subject` disables type checking of the emitted values, whereas `Subject` prevents accidental access to the emitted value. If you want the old behavior, then replace `Subject` with `Subject`. diff --git a/apps/rxjs.dev-next/docs/guide/subscription.md b/apps/rxjs.dev-next/docs/guide/subscription.md new file mode 100644 index 0000000000..398ea8207a --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/subscription.md @@ -0,0 +1,47 @@ +# Subscription + +**What is a Subscription?** A Subscription is an object that represents a disposable resource, usually the execution of an Observable. A Subscription has one important method, `unsubscribe`, that takes no argument and just disposes the resource held by the subscription. In previous versions of RxJS, Subscription was called "Disposable". + +```ts +import { interval } from 'rxjs'; + +const observable = interval(1000); +const subscription = observable.subscribe((x) => console.log(x)); +// Later: +// This cancels the ongoing Observable execution which +// was started by calling subscribe with an Observer. +subscription.unsubscribe(); +``` + +A Subscription essentially just has an `unsubscribe()` function to release resources or cancel Observable executions. + +Subscriptions can also be put together, so that a call to an `unsubscribe()` of one Subscription may unsubscribe multiple Subscriptions. You can do this by "adding" one subscription into another: + +```ts +import { interval } from 'rxjs'; + +const observable1 = interval(400); +const observable2 = interval(300); + +const subscription = observable1.subscribe((x) => console.log('first: ' + x)); +const childSubscription = observable2.subscribe((x) => console.log('second: ' + x)); + +subscription.add(childSubscription); + +setTimeout(() => { + // Unsubscribes BOTH subscription and childSubscription + subscription.unsubscribe(); +}, 1000); +``` + +When executed, we see in the console: + +```plaintext +second: 0 +first: 0 +second: 1 +first: 1 +second: 2 +``` + +Subscriptions also have a `remove(otherSubscription)` method, in order to undo the addition of a child Subscription. diff --git a/apps/rxjs.dev-next/docs/guide/testing/marble-testing.md b/apps/rxjs.dev-next/docs/guide/testing/marble-testing.md new file mode 100644 index 0000000000..14e2fd7632 --- /dev/null +++ b/apps/rxjs.dev-next/docs/guide/testing/marble-testing.md @@ -0,0 +1,292 @@ +# Testing RxJS Code with Marble Diagrams + +:::info +This guide refers to usage of marble diagrams when using the new [`testScheduler.run(callback)`](/api/testing/classes/TestScheduler#run). Some details here do not apply to using the TestScheduler manually, without using the `run()` helper. +::: + +We can test our _asynchronous_ RxJS code _synchronously_ and deterministically by virtualizing time using the TestScheduler. **Marble diagrams** provide a visual way for us to represent the behavior of an Observable. We can use them to assert that a particular Observable behaves as expected, as well as to create [hot and cold Observables](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339) we can use as mocks. + +> At this time, the TestScheduler can only be used to test code that uses RxJS schedulers - `AsyncScheduler`, etc. If the code consumes a Promise, for example, it cannot be reliably tested with `TestScheduler`, but instead should be tested more traditionally. See the [Known Issues](#known-issues) section for more details. + +```ts +import { TestScheduler } from 'rxjs/testing'; +import { throttleTime } from 'rxjs'; + +const testScheduler = new TestScheduler((actual, expected) => { + // asserting the two objects are equal - required + // for TestScheduler assertions to work via your test framework + // e.g. using chai. + expect(actual).deep.equal(expected); +}); + +// This test runs synchronously. +it('generates the stream correctly', () => { + testScheduler.run((helpers) => { + const { cold, time, expectObservable, expectSubscriptions } = helpers; + const e1 = cold(' -a--b--c---|'); + const e1subs = ' ^----------!'; + const t = time(' ---| '); // t = 3 + const expected = '-a-----c---|'; + + expectObservable(e1.pipe(throttleTime(t))).toBe(expected); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + }); +}); +``` + +## API + +The callback function you provide to `testScheduler.run(callback)` is called with `helpers` object that contains functions you'll use to write your tests. + +:::info +When the code inside this callback is being executed, any operator that uses timers/AsyncScheduler (like delay, debounceTime, etc.,) will automatically use the TestScheduler instead, so that we have "virtual time". You do not need to pass the TestScheduler to them, like in the past. +::: + +```ts +testScheduler.run((helpers) => { + const { cold, hot, expectObservable, expectSubscriptions, flush, time, animate } = helpers; + // use them +}); +``` + +Although `run()` executes entirely synchronously, the helper functions inside your callback function do not! These functions **schedule assertions** that will execute either when your callback completes or when you explicitly call `flush()`. Be wary of calling synchronous assertions, for example `expect`, from your testing library of choice, from within the callback. See [Synchronous Assertion](#synchronous-assertion) for more information on how to do this. + +- `cold(marbleDiagram: string, values?: object, error?: any)` - creates a "cold" observable whose subscription starts when the test begins. +- `hot(marbleDiagram: string, values?: object, error?: any)` - creates a "hot" observable (like a subject) that will behave as though it's already "running" when the test begins. An interesting difference is that `hot` marbles allow a `^` character to signal where the "zero frame" is. That is the point at which the subscription to observables being tested begins. +- `expectObservable(actual: Observable, subscriptionMarbles?: string).toBe(marbleDiagram: string, values?: object, error?: any)` - schedules an assertion for when the TestScheduler flushes. Give `subscriptionMarbles` as parameter to change the schedule of subscription and unsubscription. If you don't provide the `subscriptionMarbles` parameter it will subscribe at the beginning and never unsubscribe. Read below about subscription marble diagram. +- `expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]).toBe(subscriptionMarbles: string)` - like `expectObservable` schedules an assertion for when the testScheduler flushes. Both `cold()` and `hot()` return an observable with a property `subscriptions` of type `SubscriptionLog[]`. Give `subscriptions` as parameter to `expectSubscriptions` to assert whether it matches the `subscriptionsMarbles` marble diagram given in `toBe()`. Subscription marble diagrams are slightly different than Observable marble diagrams. Read more below. +- `flush()` - immediately starts virtual time. Not often used since `run()` will automatically flush for you when your callback returns, but in some cases you may wish to flush more than once or otherwise have more control. +- `time()` - converts marbles into a number indicating number of frames. It can be used by operators expecting a specific timeout. It measures time based on the position of the complete (`|`) signal: + + ```ts + testScheduler.run((helpers) => { + const { time, cold } = helpers; + const source = cold('---a--b--|'); + const t = time(' --| '); + // --| + const expected = ' -----a--b|'; + const result = source.pipe(delay(t)); + expectObservable(result).toBe(expected); + }); + ``` + +- `animate()` - specifies when requested animation frames will be 'painted'. `animate` accepts a marble diagram and each value emission in the diagram indicates when a 'paint' occurs - at which time, any queued `requestAnimationFrame` callbacks will be executed. Call `animate` at the beginning of your test and align the marble diagrams so that it's clear when the callbacks will be executed: + + ```ts + testScheduler.run((helpers) => { + const { animate, cold } = helpers; + animate(' ---x---x---x---x'); + const requests = cold('-r-------r------'); + /* ... */ + const expected = ' ---a-------b----'; + }); + ``` + +## Marble syntax + +In the context of TestScheduler, a marble diagram is a string containing special syntax representing events happening over virtual time. Time progresses by _frames_. The first character of any marble string always represents the _zero frame_, or the start of time. Inside of `testScheduler.run(callback)` the frameTimeFactor is set to 1, which means one frame is equal to one virtual millisecond. + +How many virtual milliseconds one frame represents depends on the value of `TestScheduler.frameTimeFactor`. For legacy reasons the value of `frameTimeFactor` is 1 _only_ when your code inside the `testScheduler.run(callback)` callback is running. Outside of it, it's set to 10. This will likely change in a future version of RxJS so that it is always 1. + +> IMPORTANT: This syntax guide refers to usage of marble diagrams when using the new `testScheduler.run(callback)`. The semantics of marble diagrams when using the TestScheduler manually are different, and some features like the new time progression syntax are not supported. + +- `' '` whitespace: horizontal whitespace is ignored, and can be used to help vertically align multiple marble diagrams. +- `'-'` frame: 1 "frame" of virtual time passing (see above description of frames). +- `[0-9]+[ms|s|m]` time progression: the time progression syntax lets you progress virtual time by a specific amount. It's a number, followed by a time unit of `ms` (milliseconds), `s` (seconds), or `m` (minutes) without any space between them, e.g. `a 10ms b`. See [Time progression syntax](#time-progression-syntax) for more details. +- `'|'` complete: The successful completion of an observable. This is the observable producer signaling `complete()`. +- `'#'` error: An error terminating the observable. This is the observable producer signaling `error()`. +- `[a-z0-9]` e.g. `'a'` any alphanumeric character: Represents a value being emitted by the producer signaling `next()`. Also consider that you could map this into an object or an array like this: + + + ```ts + const expected = '400ms (a-b|)'; + const values = { + a: 'value emitted', + b: 'another value emitted', + }; + + expectObservable(someStreamForTesting).toBe(expected, values); + + // This would work also + const expected = '400ms (0-1|)'; + const values = [ + 'value emitted', + 'another value emitted' + ]; + + expectObservable(someStreamForTesting).toBe(expected, values); + ``` + +- `'()'` sync groupings: When multiple events need to be in the same frame synchronously, parentheses are used to group those events. You can group next'd values, a completion, or an error in this manner. The position of the initial `(` determines the time at which its values are emitted. While it can be counter-intuitive at first, after all the values have synchronously emitted time will progress a number of frames equal to the number of ASCII characters in the group, including the parentheses. e.g. `'(abc)'` will emit the values of a, b, and c synchronously in the same frame and then advance virtual time by 5 frames, `'(abc)'.length === 5`. This is done because it often helps you vertically align your marble diagrams, but it's a known pain point in real-world testing. [Learn more about known issues](#known-issues). +- `'^'` subscription point: (hot observables only) shows the point at which the tested observables will be subscribed to the hot observable. This is the "zero frame" for that observable, every frame before the `^` will be negative. Negative time might seem pointless, but there are in fact advanced cases where this is necessary, usually involving ReplaySubjects. + +### Time progression syntax + +The new time progression syntax takes inspiration from the CSS duration syntax. It's a number (integer or floating point) immediately followed by a unit; ms (milliseconds), s (seconds), m (minutes). e.g. `100ms`, `1.4s`, `5.25m`. + +When it's not the first character of the diagram it must be padded a space before/after to disambiguate it from a series of marbles. e.g. `a 1ms b` needs the spaces because `a1msb` will be interpreted as `['a', '1', 'm', 's', 'b']` where each of these characters is a value that will be next()'d as-is. + +**NOTE**: You may have to subtract 1 millisecond from the time you want to progress because the alphanumeric marbles (representing an actual emitted value) _advance time 1 virtual frame_ themselves already, after they emit. This can be counter-intuitive and frustrating, but for now it is indeed correct. + + +```ts +const input = ' -a-b-c|'; +const expected = '-- 9ms a 9ms b 9ms (c|)'; + +// Depending on your personal preferences you could also +// use frame dashes to keep vertical alignment with the input. +// const input = ' -a-b-c|'; +// const expected = '------- 4ms a 9ms b 9ms (c|)'; +// or +// const expected = '-----------a 9ms b 9ms (c|)'; + +const result = cold(input).pipe( + concatMap((d) => of(d).pipe( + delay(10) + )) +); + +expectObservable(result).toBe(expected); +``` + +### Examples + +`'-'` or `'------'`: Equivalent to [`NEVER`](/api/functions/NEVER), or an observable that never emits or errors or completes. + +`|`: Equivalent to [`EMPTY`](/api/functions/EMPTY), or an observable that never emits and completes immediately. + +`#`: Equivalent to [`throwError`](/api/functions/throwError), or an observable that never emits and errors immediately. + +`'--a--'`: An observable that waits 2 "frames", emits value `a` on frame 2 and then never completes. + +`'--a--b--|'`: On frame 2 emit `a`, on frame 5 emit `b`, and on frame 8, `complete`. + +`'--a--b--#'`: On frame 2 emit `a`, on frame 5 emit `b`, and on frame 8, `error`. + +`'-a-^-b--|'`: In a hot observable, on frame -2 emit `a`, then on frame 2 emit `b`, and on frame 5, `complete`. + +`'--(abc)-|'`: on frame 2 emit `a`, `b`, and `c`, then on frame 8, `complete`. + +`'-----(a|)'`: on frame 5 emit `a` and `complete`. + +`'a 9ms b 9s c|'`: on frame 0 emit `a`, on frame 10 emit `b`, on frame 9,011 emit `c`, then on frame 9,012 `complete`. + +`'--a 2.5m b'`: on frame 2 emit `a`, on frame 150,003 emit `b` and never complete. + +## Subscription marbles + +The `expectSubscriptions` helper allows you to assert that a `cold()` or `hot()` Observable you created was subscribed/unsubscribed to at the correct point in time. The `subscriptionMarbles` parameter to `expectObservable` allows your test to defer subscription to a later virtual time, and/or unsubscribe even if the observable being tested has not yet completed. + +The subscription marble syntax is slightly different to conventional marble syntax. + +- `'-'` time: 1 frame time passing. +- `[0-9]+[ms|s|m]` time progression: the time progression syntax lets you progress virtual time by a specific amount. It's a number, followed by a time unit of `ms` (milliseconds), `s` (seconds), or `m` (minutes) without any space between them, e.g. `a 10ms b`. See [Time progression syntax](#time-progression-syntax) for more details. +- `'^'` subscription point: shows the point in time at which a subscription happens. +- `'!'` unsubscription point: shows the point in time at which a subscription is unsubscribed. + +There should be **at most one** `^` point in a subscription marble diagram, and **at most one** `!` point. Other than that, the `-` character is the only one allowed in a subscription marble diagram. + +### Examples + +`'-'` or `'------'`: no subscription ever happened. + +`'--^--'`: a subscription happened after 2 "frames" of time passed, and the subscription was not unsubscribed. + +`'--^--!-'`: on frame 2 a subscription happened, and on frame 5 was unsubscribed. + +`'500ms ^ 1s !'`: on frame 500 a subscription happened, and on frame 1,501 was unsubscribed. + +Given a hot source, test multiple subscribers that subscribe at different times: + +```ts +testScheduler.run(({ hot, expectObservable }) => { + const source = hot('--a--a--a--a--a--a--a--'); + const sub1 = ' --^-----------!'; + const sub2 = ' ---------^--------!'; + const expect1 = ' --a--a--a--a--'; + const expect2 = ' -----------a--a--a-'; + + expectObservable(source, sub1).toBe(expect1); + expectObservable(source, sub2).toBe(expect2); +}); +``` + +Manually unsubscribe from a source that will never complete: + +```ts +it('should repeat forever', () => { + const testScheduler = createScheduler(); + + testScheduler.run(({ expectObservable }) => { + const foreverStream$ = interval(1).pipe(mapTo('a')); + + // Omitting this arg may crash the test suite. + const unsub = '------!'; + + expectObservable(foreverStream$, unsub).toBe('-aaaaa'); + }); +}); +``` + +## Synchronous Assertion + +Sometimes, we need to assert changes in state _after_ an observable stream has completed - such as when a side effect like `tap` updates a variable. Outside of Marbles testing with TestScheduler, we might think of this as creating a delay or waiting before making our assertion. + +For example: + +```ts +let eventCount = 0; + +const s1 = cold('--a--b|', { a: 'x', b: 'y' }); + +// side effect using 'tap' updates a variable +const result = s1.pipe(tap(() => eventCount++)); + +expectObservable(result).toBe('--a--b|', { a: 'x', b: 'y' }); + +// flush - run 'virtual time' to complete all outstanding hot or cold observables +flush(); + +expect(eventCount).toBe(2); +``` + +In the above situation we need the observable stream to complete so that we can test the variable was set to the correct value. The TestScheduler runs in 'virtual time' (synchronously), but doesn't normally run (and complete) until the testScheduler callback returns. The flush() method manually triggers the virtual time so that we can test the local variable after the observable completes. + +--- + +## Known issues + +### RxJS code that consumes Promises cannot be directly tested + +If you have RxJS code that uses asynchronous scheduling - e.g. Promises, etc. - you can't reliably use marble diagrams _for that particular code_. This is because those other scheduling methods won't be virtualized or known to TestScheduler. + +The solution is to test that code in isolation, with the traditional asynchronous testing methods of your testing framework. The specifics depend on your testing framework of choice, but here's a pseudo-code example: + +```ts +// Some RxJS code that also consumes a Promise, so TestScheduler won't be able +// to correctly virtualize and the test will always be really asynchronous. +const myAsyncCode = () => from(Promise.resolve('something')); + +it('has async code', (done) => { + myAsyncCode().subscribe((d) => { + assertEqual(d, 'something'); + done(); + }); +}); +``` + +On a related note, you also can't currently assert delays of zero, even with `AsyncScheduler`, e.g. `delay(0)` is like saying `setTimeout(work, 0)`. This schedules a new ["task" aka "macrotask"](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/), so it's asynchronous, but without an explicit passage of time. + +### Behavior is different outside of `testScheduler.run(callback)` + +The `TestScheduler` has been around since v5, but was actually intended for testing RxJS itself by the maintainers, rather than for use in regular user apps. Because of this, some of the default behaviors and features of the TestScheduler did not work well (or at all) for users. In v6 we introduced the `testScheduler.run(callback)` method which allowed us to provide new defaults and features in a non-breaking way, but it's still possible to [use the TestScheduler outside](https://github.com/ReactiveX/rxjs/blob/7113ae4b451dd8463fae71b68edab96079d089df/docs_app/content/guide/testing/internal-marble-tests.md) of `testScheduler.run(callback)`. It's important to note that if you do so, there are some major differences in how it will behave. + +- `TestScheduler` helper methods have more verbose names, like `testScheduler.createColdObservable()` instead of `cold()`. +- The testScheduler instance is _not_ automatically used by operators that use `AsyncScheduler`, e.g. `delay`, `debounceTime`, etc., so you have to explicitly pass it to them. +- There is NO support for time progression syntax e.g. `-a 100ms b-|`. +- 1 frame is 10 virtual milliseconds by default. i.e. `TestScheduler.frameTimeFactor = 10`. +- Each whitespace `' '` equals 1 frame, same as a hyphen `'-'`. +- There is a hard maximum number of frames set at 750 i.e. `maxFrames = 750`. After 750 they are silently ignored. +- You must explicitly flush the scheduler. + +While at this time usage of the TestScheduler outside of `testScheduler.run(callback)` has not been officially deprecated, it is discouraged because it is likely to cause confusion. diff --git a/apps/rxjs.dev-next/docs/index.md b/apps/rxjs.dev-next/docs/index.md new file mode 100644 index 0000000000..02f4032196 --- /dev/null +++ b/apps/rxjs.dev-next/docs/index.md @@ -0,0 +1,38 @@ +--- +layout: home + +hero: + name: 'RxJS' + text: 'Reactive Extensions Library for JavaScript' + image: + src: /Rx_Logo-512-512.png + alt: RxJS logo + actions: + - theme: brand + text: Explore RxJS Next + link: /next/ + - theme: alt + text: API Docs + link: /api +--- + +## RxJS 9 is the next generation + +The `master` branch is building RxJS 9 on the web-platform Observable model. It is not an incremental RxJS 7 release. The new package family keeps platform behavior and RxJS compatibility behavior in explicit architectural layers. + +- `@rxjs/observable-polyfill` supplies the platform Observable only when a conforming native implementation is unavailable. +- `rxjs` adds the RxJS contract through exported Symbols and focused subpath exports. +- `@rxjs/test` provides the current test helpers. +- `@rxjs/migrate` provides migration analysis and tooling. + +[Read the RxJS Next overview →](/next/) + +RxJS 7 remains the current stable release. Its existing [guides](/guide/overview) and [migration notes](/deprecations/) remain available while the RxJS 9 documentation evolves. + +## Reactive Extensions Library for JavaScript + +RxJS is a library for reactive programming using Observables, making asynchronous and event-based code easier to compose. + +## Code of Conduct + +When participating in our community, you must follow our [Code of Conduct](/code-of-conduct). diff --git a/apps/rxjs.dev-next/docs/license.md b/apps/rxjs.dev-next/docs/license.md new file mode 100644 index 0000000000..12023baa47 --- /dev/null +++ b/apps/rxjs.dev-next/docs/license.md @@ -0,0 +1,23 @@ +@title +@description +The MIT License + +Copyright (c) 2014-2018 Google, Inc., RxJS Team Members and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/apps/rxjs.dev-next/docs/maintainer-guidelines.md b/apps/rxjs.dev-next/docs/maintainer-guidelines.md new file mode 100644 index 0000000000..51a8d82490 --- /dev/null +++ b/apps/rxjs.dev-next/docs/maintainer-guidelines.md @@ -0,0 +1,35 @@ +# Maintainer Guidelines + +These are guidelines for maintainers of this repository as (mostly) [gifted to us by](https://github.com/ReactiveX/RxJS/issues/121#issue-97747542) +His Beardliness, @jeffbcross. They are words to live by for those that are tasked with reviewing and merging pull requests and otherwise +shepherding the community. As the roster of trusted maintainers grows, we'll expect these guidelines to stay pretty +much the same (but suggestions are always welcome). + +### The ~~10~~ 6 Commandments + +- **[Code of Conduct](../../../CODE_OF_CONDUCT.md)**. We should be setting a good example and be welcoming to all. We should be listening + to all feedback from everyone in our community and respect their viewpoints and opinions. +- **Be sure PRs meet [Contribution Guidelines](../../../CONTRIBUTING.md)**. It's important we keep our code base + and repository consistent. The best way to do this is to know and enforce the contribution guidelines. +- **Clean, flat commit history**. We never click the green merge button on PRs, but instead we pull down + the PR branch and rebase it against master then replace master with the PR branch. See + [example gist](https://gist.github.com/jeffbcross/307c6da45d26e29030ef). This reduces noise in the commit + history, removing all of the merge commits, and keeps history flat. The flat history is beneficial + to tools/scripts that analyze commit ancestry. +- **Always green master**. Failing master builds tend to cascade into other broken builds, and + frustration among other contributors who have rebased against a broken master. Much of our deployment + and other infrastructure is based on the assumption that master is always green, nothing should be + merged before Travis has confirmed that a PR is green, even for seemingly insignificant changes. + Nothing should be merged into a red master, and whomever broke it should drop everything and fix it + right away. Fixes should be submitted as a PR and verified as green instead of immediately merging + to master. +- **No force pushes to master**. Only in rare circumstances should a force push to master be made, + and other maintainers should be notified beforehand. The most common situation for a justified force + push is when a commit has been pushed with an invalid message. The force push should be made as soon + as possible to reduce side effects. +- **Small, logical commits**. A PR should be focused on a single problem, though that problem may be + reasonable to be broken into a few logical commits. For example, a global renaming may be best to be + broken into a single commit that renames all files, and then a commit that renames symbols within files. + This makes the review process simpler easier, so the diff of the meaty commit (where symbols are + renamed) can be easily understood than if both were done in the same commit, in which case github would + just show a deleted file and an added file. diff --git a/apps/rxjs.dev-next/docs/next/index.md b/apps/rxjs.dev-next/docs/next/index.md new file mode 100644 index 0000000000..e036b491fd --- /dev/null +++ b/apps/rxjs.dev-next/docs/next/index.md @@ -0,0 +1,20 @@ +# RxJS Next (planned RxJS 9) + +RxJS Next is a platform-based generation of RxJS under active development on `master`. RxJS 7 remains the stable release for production users today. + +## Package model + +- **`@rxjs/observable-polyfill`** provides the web-platform `Observable` contract when the host does not already provide a conforming implementation. +- **`rxjs`** exports exact Symbol keys and focused subpaths for RxJS behavior. Symbol-based RxJS operators coexist with platform string methods such as `map` and `filter`. +- **`@rxjs/test`** contains the test-facing utilities for the new runtime model. +- **`@rxjs/migrate`** analyzes applications and helps plan migration from RxJS 7. + +## Semantics + +The platform layer uses `AbortSignal` and the platform subscriber lifecycle as its cancellation foundation. RxJS 7 cold-observable compatibility is kept behind an explicit compatibility boundary instead of silently changing the platform contract. + +The implementation is exploratory and the prerelease contract can still change. For the accepted architecture, decisions, compatibility policy, and current execution status, see the [RxJS Next project documentation](https://github.com/ReactiveX/rxjs/tree/master/docs/rxjs-next). + +## API reference + +The [generated API reference](/api) is built directly from the public exports in the current `master` packages. diff --git a/apps/rxjs.dev-next/docs/public/Rx_Logo-512-512.png b/apps/rxjs.dev-next/docs/public/Rx_Logo-512-512.png new file mode 100644 index 0000000000000000000000000000000000000000..675907962ce67728deeb4d2897ef1c946927a833 GIT binary patch literal 55894 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

IC*#T(7b1RkE9`LCcA?&ooAbKZGZcM zMa<~(+JdmSq&8G7%0!DiYw)8ampCet^6jb+x z&Qe3W(?|nfO2CFV_{r#ctM?5WAc*pbZzH-7!%pSvg!?|M83k__JAwr=Jj>Un1n(7? zxIzTY4QF<4fh6~Pfj1P!is(E=5`uCp6gVnN!DtF#C~;so$x)yO$b$pha8NS|KRfCJ z?Q|byk9kV(lzH!c5Gw3i6@Wn{du)LNm=;4f;?RaWfhVOW*mOe;WcH43bp==0Wl8E& zdt{wQUej4Qln(>|C+W~dN%~>~iGVZ#sF~;Ffjpi>S*dX{wftlTR(Y^;b!?@9=d!Xieo&EHYQYsd_pjyACC>Zb!|Tg%fGAG?>pZOqyIba91W$FOuhYos zMKEjk3J+WmDVj*Gu?q#$3D*Q@gAEB<9V->gDO%2}1=+Ak`#f?!MY~|BT5`8S+Ha3% zcFu$60|nS3OA;{A`=MY0Qty>dHNW5@1LiDF9|y>I6K{NqS8L%tCBSmp6yBCXLFCo{ zJL45Ja9kDc4FVsuM6%g%2w(C?%+68~ZC6D>IP4A>2SB{=x&I`qE_VAsuisZkXARN{ ze-ocWZAB zc2p?O)v^uyn zes_Kk0BTQa$Ur!wno)4n?4_a~si+8o(o_$$Z%_q6&Oo(z+e|B<`dAy8+m}=kYSJ*k zP9If$w)mB6oV7rBZ4#2_fWk+I$UZ>W-8-|)^*=i74gAc*4gWOI>us_qm|8B7qIXXN zYWxL6E2qYVXHGVC>_Cv+Vd2se-vxN71a#sFRRnIYyRb>2|=* zKlff?a^)!lR^F+bMOWgkGr1fK$ltC3|IGBv2p$~GZr#^QASe=B-r^uWU720RIy9(Ml$ueo!7-W2#SyDX=ilLM;qYoh0Ool!A;vIgjdf z+y^iIt$r{k2d=;igIcW9nJ!b8Otq$s$6y5izjMvYw_s(`Sh}>;`E=K23J!bPh8uyg zmAc~pxx3Yw8R01^)RAFAfPJgemj;7(YM`wE$)h&Z`Qrum%ox+zb$x>%9P}g|la0gh z=szQGm-XUn2Z8T7Jp+t!aQ0dZ&*Y1>-6pW?^%DXC@hO_Cl^QpkDquYJ*e)0WD`U+; z2`zByf_()nA-N@2CI$Nj!LQ0wf>wI(kihR)`+`WBlZRZS$dF^u2qv*vg(-Ny&JZ|q z{WBqZY=|y&XH&ju1x9F&B%4eTo%l`IWsl&eI6_w+3A?z?$_gN?1rPn~^i9KG%!lu- zCgA-4pXmYkuoaX;4C`g`CO24Gx(4TlM9A{d3e9|zq1(#tqDK@xet4Z^DVmp z`y<`gfrbgtb``W1&{A{a^Q>#oA-r!8Y_og+fn{bu1JFmcR6XYb36AZ_IuDNrmL=ZB zK~yetDhWC8uQ?X7B;QJL%rV0e9IQ#h&KaypKQJqcf%fU7!s}{Ntuwhg;Q_a6q9GDr zWV+fWH$xX&42($#knQ>4yal`;fx1V$pPM;(_k}+X<2*q&*+MVeXpPDJVP$%SOQ(^> zMjtiI-DXo!gcY%TY|-nek20e+bI{it`0IQGAp|~}1iRZL9%tctJp36T);fSrLA7+^ zgTgBC9S5*KKL!=|}u;I)?G1lxli`xdxZ7Dq|`SY^zhlHUCo-`tN13nwKnD zJA{AUg*^s4r~8SdDlD0T8=eMU|9~&}{USVa%%M{z`(d9TEL1b3>fSQYfhs8g;n56x zqh7R;{~0X1K<8=nKN;1bLEUCZw)G>_qugDLEmKANZYLR0t3?`su+$>U$r3ExiJdY; zRo8I@WY0X3<%{tUMQUV z6saqBrxN&&NEG)Ho!hDU4p!3mf6Qcdeh@ojuml99uC-Q3$rX-w!MLwL3T_#gSv%$1 zMd9Zp3;|*H9l(fr`29XS_b=RW@WF!wU$sbfqh?=a01TZLJ>j)pc_LByY_ZM9_vMXYC%=MB6+w!HN!*Nyu#tUCerFv0giP8j7Kz{6tUw+!`E2?%8t|p2 zod?|25^S zDSci$u;pwp{{xETv-Umnz%1=12Lc$E&I`YT8x}y2Kk~o=F%x`j)cA_8PWfBe@ykJ{ zJb@9C%!J12;hzfgd@<=`-~#sOwD9K^S*UzK^8*HH5WJ)R%}`-tRkwF1<^VNHpXFoD zuH`%daCdhT2xk5fA5uHAZ=RFpn(~kCqb(KxtaYui(+wF^H357xFl9Oj%jptu{&mfH z-Jv%|J66DVp$iHaUK9CSvMH5(C3D&kg~PT7YI+DJ<=e>;Tc(0%8+`nYgnvbGMndP5 z``*8G_WU+*5BO)-GjUWo^^M>Q+JB8s_PW(I6wa3ga01K`@Mi#xK$Z;51IyiiR(|SG zxCiG)8($yoSc(9$Z~{zHPUvt&7<%Q^8$4_TDG}%)x%@o<>WaRkL{rmqaENX%5nnvj`0fg$3TnlT=DAWeW@hJfmrgWmkq^zQI_QmSLAA^z@-@B99}wqq*z?=)5DWhBL|8L zzaJ|5=srjk#`h~HK*eCyuE_egzNGyMt#Av}zeT@@&l|(}t-ky2e*m)vtCRoc$tS}` z_;Cs+)y#z3Rlw99-<9^E@BiB~-~t2SVlpj8J>X3az#2FW*yo8+Z#X~ErQ!aCuG>yBk~$PVM(^ z`p+w^{6pp1ci;g63=bIDn`(5Vz5IazpdaSm_FQ*g-n)muJ3wAu5Z>VWl=FeT+T(Y= zx-Zn%yblH{Kj8kY{e$@Cr1nD&_ur~W?2qNv&d*N+Z$rEu0E}>B{{wo!isf*hkbv9m z6FZt%{!RYAMfZ<2Xc7>pyWvhjNs`_Tb93Mh-Ssgm!`XoQGi!gZ-`c$V-|w$BbF_e^ zFR;5{u|>D%ozm+6!P{Mb^{3aK2Zk_%>9Ki_FB*cD4DJ-Dm*?D+fA!d@eO2fGtgT{t zp>m}AxKz@I^S6Lw2-=MD6A$cfIwPLj&BX~m?Uo@RJw@5Vsp;Z*;3|?(6_2$WmG;lB zWO|{%1)RBe0iHPHa7@^`DMO9(m-?oC`9P~0*qm$P=15Qbr^b0JxTx{V-;}z&Vqu@Y z@pNsCR|hT{e^Je3cSqLC-Y@K*%7f+me|`~M>Aw+lU<$ATN;Wtpza6+dgIQ>c+T@K9 zpM?G#jPG~&d2KYpRp;G;+&Aqr1L+MY z{U$fvY8uPod|p0xd)3YZH&p~@xP~1HpZMf~pOD4;6Kt2(Px{GF`F_J@(LMW1K`UIh z16TN&7VSCz#3|!sf3`2-!3Xn|4%S%yd|&N4HOb&aym#F9$(4MZzn=59 vU0Mz*I6Jk!?_IkxIdVQQa|}J;@a#XcQ`@=2dG0SjS%$&W)z4*}Q$iB}f{Ok! literal 0 HcmV?d00001 diff --git a/apps/rxjs.dev-next/docs/public/images/favicons/apple-touch-icon.png b/apps/rxjs.dev-next/docs/public/images/favicons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..517d001a8eaef71a23a9cc07cae7ca4bd011fe02 GIT binary patch literal 20198 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 S5YU7G0000O29-r@ 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 MTZ`|$*CpwH08miuB>(^b literal 0 HcmV?d00001 diff --git a/apps/rxjs.dev-next/docs/public/images/favicons/favicon.svg b/apps/rxjs.dev-next/docs/public/images/favicons/favicon.svg new file mode 100644 index 0000000000..3784e51584 --- /dev/null +++ b/apps/rxjs.dev-next/docs/public/images/favicons/favicon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/apps/rxjs.dev-next/docs/public/images/favicons/web-app-manifest-192x192.png b/apps/rxjs.dev-next/docs/public/images/favicons/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..3c7162cd3f243c9370f2eb650f19e5d112f85272 GIT binary patch literal 20424 zcmV);K!(4GP)du^Z@9_ou58+AUy!mS0{4`Kzabg>&KvnU|ALFHk6@u)ppb_-HMu}TTr`vD{9y7K;4#I@G5HJ z)lBocSOn4oz;+mu-rHS`%2$_Q$1i7M>(?H~mM=eq%@_R#n=ZHyo6oq+dq@l{m5z7$?%Ex`KLR2p}k2y~+Xkf2kzyzzF|pmNr7Y`*wE*z)zqQU1{T zsCs85XZ@|H+gy%dX%&LygdG*ATe}lA3pbj?FT3kqY`OL^Y&hmtl-)TOb?ZyvRo4L? zjV5R=&F)+g=-vZ>d0NYa-u~m~v6YEm{pm)aHpm2SW+y4psb9Bw7k2*fHEf!CKX%^m zGHO?Drwtl!rkS)_$3>v~4glt7&7w`%{H1Bw{rqR}s<@0vrXOgpZetnBr+t8pr{0P3 z2j;`$k}4UK>AGznf$li~n5;F6H)GrN(^0o(C(Qbtyrjb|8@w0rYU;4_53ga{_nt-V zvaLujiIR?Nn;GbS1AxmGt<|?)`#9I~o7yHhIS_P3sd!~Ew*K$as99GEug-&~!4V7e z2L{@#kJE-5i9oj-0NTFX@vE7rU9%(Q$zSI>*d1`NGsD5w3>TYNUbS>H7JTh7EPCKQ zeDUBseDR-oSo+8ZSpLMvSUqDQHoUPETjsAp>9Q@T=1n#@cpB-i)``-!5=Nj~4FJu` zTJ}`DwghI`lQ1la1+Kk@US4@;d95s z!sS~q|Ie@C!#}=?kN)&3KKT7iy#K2gFz<)c@y@p%$JCL947Ge75#;@eSI1V>cw*Pml2$~Z`wRdkQ29OYq4Kq*ElPgnTn&?b6KC~B zSq|oBJ9s0HM!g4F)o52acRK8+-w(5aCU!%iT{44gQWprE2CB&UaN3|H%U$wC<>Kn zK!ZTE_#7kTdvIAt`l7|Ra$YDXLj)Z9t3l#0$vr-!DF2+hRotms@sGFg>Hj{7_fEeP zA6&)s8oJa~+pe5)f`w)LFCtE@r! z2W#>9cb~z#NBk3CT=z6KJn|tbR%~n0)s5!6{h_$-xk2EPqw2lY*#5)k5ZqOr@I1Ai zU%|wGlFOPIO!|+whTq1l^9Xwt=*fce|_tDhZE51$XbUitf z!>g|4!>D=Ka`mIoy4q|t`p$$ZDCgOK%4N+nyper@iNB4st`SOJlWf!{i^&?E!7Uxe zR>xo(G?+5gH}#l-!u2%FGAVX2Sh>Tx(l>J2Q&~I%q6J|c@r;~2vs~gmM6k39JGk}w zIUnwQ!Smky-;Yqep$yu7WwrDXCjyFL9CjCFF&T7YShuMRTfh1kcK-2oK8>{tn9w;% zMN6HJctiUTXZ%;WwqMJ2iTFw?C8MwcV=$LJ2D7@wCP7KFnz=&gN?>Ic+nHbXWXk{> zv9!@kKuc!9v_(BrpKS_DX8CGcya;f~r>)+yx0YkoFJ8cB=iP^mcf5l-T>&ZMn532T zVI!cp*>cy$?A%&Db=3=MdMBrH)(WoqqhD{RVCHDXpT@+0g#)0RG;_-=aTJJV0V}YY zsbg?l+A{;gCcClg!SGb}*Hc-bga$v73oOrI@`Vl1j&!Y#c#$$?YSRGgfLDdujb&K( zr#JBVIrn1Iee-xlD3v4);Rq;p;bIp+Omc7=(cB2u)R{Lg>-dy)_freZTbY|Ldk{PS z^tyQ~6P&s288I)QS^l9s2hVeZ>e$gK0xN(E`H*xI6=_eiR>wwCOMw)yubH-A%7yiQagf_CA7og_}0f%|D7+ z=L~^5hc~got-DY?Z#8y5@d{_&gLs0!KSnB!G^>Ci4DiyhHd}vIkUg7w=&HD zQC*&ukk<7xxy-qjyE^M#M=PZI#GsA>;0GrTFCwQwm8}j{<6H^6DErbC*m_FVc(iy1 zJk;A{1JYOGJ1j?Ruq?iSMbn0vxQ8i};Y8Guq-L~EEc$p6+ zy(-q9N)W&T?&jRh<<1-~cY+itF?7Oy5YGUs8*KHMG*(Z1e-etXiKO*Pd0=l7l<~PHVyXICC})R*FcZ5kiN$&g2yXD2)jH7&Hj=_zylw;)uH^&Wmx&m zC$Z*dGf=y|qT6ntDlX_uAAQ}h?BDNT;~96M?9O*l%S*S}#nJMo8IH1lv4dx&?aarx zyR(yJRde`9i6bnQ#VT76j}LcI$SY6(8V|#&J(Lg^kQQHh((!}kxxBj+KPt)TOhnGI z&OJot#jXFxK0NgWd3Jo{FJj+9^Qa zw%W#h?9!XxLXfk&xim8on(JJUitD&F^B>Oov$?&gTVN%FPSOvSXR(qtz}a#zuO^4? zuEUhwK}@Xhuy>`0afE$Y-lrmnAyq--b1?XKUX-k|ZEVq3gXu>+ES`a|o+(S0bfY#X zS3Ar=c(ABt_qpCD(U z(to^-npVy}RqW+wvt7*K&947>FWFJc3Z1MU`qX+jnu&iH6Tc6ifJwixU`Q^=mFzxNBht9oY&yBNB^@8=-{p>=Ro7on6vy;90 zXs(0#yy4YA@K>iODp@S8nSU5(@Dt1H%#5GmuhfFFf~PBht{6QzAd0Ji0^T!ZFp-pu zGNptxn19s1>Ie%WWe?GQhH2>tqz8Vq%{yWZfW7+ zO#UOe&d-UhRWN7<3Q1z>E3?a1e9?zFAtPSN(qKf#{*eFVjer`&OewmWM+(Ja$^AK0hZ z$fSRi54F}3#~V6P$-oMh)_VRRuHm!8yymysGEF6u0bUI}4u)WDkQX?QlPMdfDeI30 zg34r>**BIJU&${lJp(i$tj-e_Sh=vkZ^zL5BfUgGu{lgV+83cEQSG{&*!bh;vE{Zo z2v%@`$t$*GdkQFq9eOTx>q}C04LmFLd*dp70C_ogbgNBn8?^sk}B~n?U&iIWCGEC5KmxvDhos# z%r<2oe2QE?{WGk)emZJ4l}ofofcm3DPsF^Z&vkNhoLI}`f0Pfm^m>E(B~cx9o!-uyH+(v3a9H1zEl)ViXVDCX2674vk9t~?nRZyV zIKs1fi7@McSFhb~F2&k!Jb}v3H$gPjJJr!rXB7XI)=8DO1h+Xi z(__(%sGCHP+;lt(7PE-e8IGCIRBmt3Zc`vU0MxepGB|uf^|WWsL9_u6Q8es_d|Q;B zEel)PCc331iv`J~r@)wUSV3hKBUtTmvZ~~>8eIvhSMTWZgMjIzQ|ve}%OB2mjd`$} zN%JIUe%<)04;s_8GIg2-%M)Imx6L?wIT zac*@kViHPN0$~gK`s1>!_P+`Q=sox6MTR9D3wBnPf|ZFUTA-Lz zQ=soUFp`jrq(pIJUNPcTl9VM`|`e|jR3nh)pmfzMmgUK&H8bH_y@r@6@tD$&) zFmnD8j|fWo*SR4ut%r*T2v6rZ*u+>#CLKSB*BJP9BW3y)DTgbIHbu$kYxNhKvF5s` zQN<_7x-uA97y8q&1AswpD)jfmTo?9L=@6&w{$@B%D52SeT|PK0+e2l6 zXp8-2@dV;oP>ep~1Q8pbU9N+LoHVSS#i}o`w6JJ_%6_msVFA0T-hV0@9lqNP*s6n(N9-0Q5JCa)Vi5& z_|;NdW}}Y7KeQV9%R>0jUATL>p!5WW))gieD3DyhV&w4C zrW_L_@dUPlt^{RIF2KgWy#X}jD+n)v^z(E-PLi9 z==W-BR&0+rpl(~v#GjXu+%8SLjyDM8^f$>W@h2W^!&1Ty5jB4M8CYe4o6i4F@=NS{PG}9s_>vrmE73S3~vgd zKjK>mal(JYdXj}X5hR{K3}J!Fv0(VLQ@5i6Yk&Tt`DN=^oz6xpRvqILv#0#dx$yKH zfF6*9`YsD0n@p$%rI?b7Kho*My;)<1_p8v99ik%cqaaMeHTH@W z>gju@Y+Wq<#}az#OERfLwqgy$<5?ZaV!_IUB@-6-%c3pBUOrMJ#T=`EZU zcok5?`VF6#c(dsbocJk$u+eNLc`tTGKepMMP)ItDz()<>s>|FoN>ziPh7BwEwU;j6 z^u2=(94%`Is~N2=%o7a^>04lx&_P+K5bAHqEu9D_(n-XRXD2oZtxm*-t^xup@MG}> zD8K7nm^VP#tWJFaXa3R@d%;|Ve#WF9lYRsfei*0kUTn-JysiLh zQlOj_*s{UYHJCo?APJg)9z1R0ZqO3?`~^qOIx5L`T{)y9-B=J`Ail*a3zWFRv0(Kq zmfV!(C<&DXEJhvT3H;0`n#qS)2rEbFkrzvo6?I51u*^uAKB_E`OwR_kKK?Pxl7|d6 z-+yQ?3?5aCXYZPWa_$(B-RdLRitNHI65?5~+u zbh4m5>0V5D?S_oy!=S-5+=D%#lQduaU<2$Q2rMm`Xn`#ota^k7{IHEG`o4jKVKE28 zCj?y`5)K3!p()nnl_6ZVw3sRjY+2ZX_yRxeFI$<#lKIP)7SBQ=p7;XkMd7f|mS^QA zkYrj~SU^_&!TzsWun}cnY~smVse>cWnt;`dH{+TYk4S-@5X*UxKBStrqlwhv&^AIPb?H{;A_SzP0XtVynBY|^VNu(XvM z9;-xQ+Oo9tbak<`FvX~%U=;_!95%X`SBCAR;|&Js(tN_hh7-104CS90i zkmU;t#I#uDSZI#S>UcgMEKl}qISTu9jSkO8awYgS?)Y&mhz+ta{@|g=%nIOxHu|Dc<^JxBY8^ zn8ZEs{u~6l3W%>0Gnfq+OzQ^6qsg=SM#t(}9F|+1Sgbk%k3FlG_&(-&eaI`@CMxnG z1OZLZ8qB?*$`N2iTb6=s2wU)CqBdxc%Mi`{TfhAj>b7tn zn1dmd9{`zqzzt0T{$~j;!`Dh_gN~Cgx^VFHtybkGT-0fp~)g{OIKUdY1O{ zG~B))EjSMa$| zM_dKeZ^q%2{oz^HZ7jo%-*IhM#XS`!W2|keKMXM(C8rW%T0Z2i%!UYs&l+sT)%U zuZCnr4sCpxA3z9mGO{wzvu_VW)z;MEz8hzvHoyGlT{JRVf8lFWQX7AFeg!_WYxMOkI0C>)lT+Ko!^iU^$*)%|0(? zYoQWPnwZBJqd%8ChcWq!X+(}2U^_}=m8Ii~rWut5{y{*txiL0;62hu0995q-EFC2a z*RgF_9buO}nZbk}d-Nz={HL>V)omBzvOk}TNoVYfyuutfu45p(Pd-L`_h^j2=OPUI z!|BMKG#JPXK+7XGYrzD}&nPgBpl#7Asw5l3jB9*gnqQ9t4f zm26pqS7owHy?EI0!u3_QP}Dmg;CX@{y(01PY(5v@1V^&n4mXu8>B}va?4L*;kI&elKX-Tz<$Aw}(H5;Fl=G&nN@2dCRThvRqCFTKq0uNUU4galFI%44 zSKkGg*agM;*yp4%xcWa|#>F?EhjGX6jXrzzMu3T}#5av0{Gl{|2qkClgMIG5 z1jGJ*4)PA&3jx~kcy*M%M|G2^!WLN9@GTw2MxQd0CJCkEFIzehFCO34kq^eclnCNm z5TA1qxuHR*f~TOcARQuj^h@Ay%^xVxlNfPBkgth>a#33Ue>QIwG!R6$7pOl|(-zOT zZha|gwBOr^#g%N4_pFU*M2i%kflU6xnf&>TwrHAA?9I)sEek6tg(EujDMD*GL|QU| zbPWzS=cmPYq)V{%#j|B$<~_19Gcfs*198zmFTmM1oQVPBOW+1vIF7W#h8siF3*=;= z?}>Y3+#MHS)SVZg-*rd9-K!U%1}OIgv_V&3zzHQALqTq+Z1|)+$pk950Awp-gY^t8 zI|P!Uc3JT2s4eM{9ogmbVtu91)^UdSMs}|}a_WzqJnkDK+z8ogmu$ht)%D*GA*;;~ zH+3y|s9v}cLH(y|35+UWn*9}&1S$>KeYotI!sO3mC56f+s>%k)*GZT%Yb$XrEv()X zmRu`t$yF8@TfX{+rj2@inC`K(sRJOBNju{35xDZPt8o1F$D?Fi2?E^Hrl{42%Nep@ z-vJovs#x)qkt3vjaVWF!6msWIBrxF4=!j=?I%O@FEkwlc%>(S{#IdkwA}i3WwdGK0bc zWGCP{=r^VxPQBp_oc_nN(TmF*hdd>Wbdv^$7HCg{so%+Cu=gEPG5qceQS$Q>k$uE) zc$_iKy#nvxvvo=HUEScuY-Zc{)P{5Jf9EMhCHBN>Z`O@7rkx)jRaEc#w@A5pMhkb3LUeb1@Xb+NX$E^Kz_6h8)XKOpy!3o4sY;AQO;lJUf zU4@S(U~rgL*#K!PYl_yD06u+FkF=>rFDz}aXbQ4&GcoeG(KzS!3o+`%G05h6)^RAH zye$q5tPHkUuulmFd}$KK-hB~9K6xd2e0j22A~|f|3-CcqVg)XQPys+Y&L>rp>G5hruj&nxZ$#}OCp_hEYumq7)i`(orTPQ>0E2!n5* zir(Kl3W0-%pe`p9_RIJh3G0&3`h+~nt zj0KsYt-Uzn2urnx@d4MQY9NE$cu6iDmokGHL4(6|O9p|wiPcx!t?dA>Ry~LG!dZ7; ziZN$TfXiJSHq+rpR#7f`O&N;8*B**-|DkWMe;q|XJpq_Fqp6Wj000mGNkl5b3nyf)X_%cL0;X#1US&end~QIEiocC6hkm=8Wuvy*Wq+az{*8GsO8+ zn)^l7^b0*~Ub_RvSwFjt%IfQsnGOspg4-*%fjym~-kfAkH;~0L^uE59J^>2k9Anz# z-o27oVD(j2J%QoT0fVhgA02NHsJ@ba(7}7*lc985we(E=`V`9yW- zl?P$`w97E;(aSO5uVfs$Qd%fcSp z3lcmyM(+7^J=tM^wImA;E<$g%J?_p6Q9NM)8jIWR@=9pNr>XjbhPQ7nZ?F6Z-L7vt zsS< zeGA3+U5rfLJJgIShR3~k=w_O_9+y?RNd*Uz$8!t;Ctrua(`ASd;weI8h;M+j>M%~w z?pIzGYPoe=^rK^O;B!}F^f!+{;gFt?Ppp;jDBIrff16R8nr@Ova-A!+gdDPU$+idTztg*yzr{40EVsi$8Ec%n))bkJ;EP%n%F^LaA zUOB{3H1#dtWuXtZIgk3wxyb9+gPMER;j$0%`xcU!d|>mjQrhc7<21+ z*yo`yVZZ5L!Nk|Tfyr~egM;UP7l(ZKJxuxV`-pRWz(O4!h|LWN_Al z<6w{c=(Gr$r6a(ljII_2xGZv-0$y2+E(ersv?f;C0otKokBbxJASc5(>^5Q9oJpB+ zhwDLIQvC+?4XH8)-OuxvGkGU8nQQ+HCZ!ybYW*zjL9`NBb*x;p_!cZ3E4Q@t8q*0d z2`2sYBovM63)3LdpSFyEei1@@{ten-M>!7vG!V26L-~qi;(_-7fD zizx0P2ac)mU|)4m;)|ycs4N`29J2C639UK;zg&&S<4H#^txdS@X!M`57t+a(cKPfo zt;7!Q_QY!}gS%EvJ9UI|DV`5W@!aBkWO6MHj${phmAnB?WU#o-Gs_=`3UWrE#5Y(x z8VJ!Iq@%LHFQ-~$eXoQzdSnV)+Uhsp(Z}Nb2V(ft{oy#0vSRvZ(+Kc_k0l?kLrt|_ zj%nCbHkY7|I;^26<(xKY#^Fvt=J@^q^T`9SCczbeVvOS2KbJ~2QN@!}Hpy8%l-$zM zP7N8-v2vAdT~m+pM6`4*7+n$>IT;wsZM#hFV98%rPao|Nfe+tWig;U{>=NX&FC0gu zXh*bTv}N7SXl9a>@*qhQ4XZm`24zhg2ql#MyPjPQ&jtFZ@qPHzR~HDWD5(vmP%_a5 zN70_l`UV9IEnPuG-wW|$X=^{E>tGmX{^CRSf_z5$(=HLvTc01ly$nj|cnucx$^#tr z6rw)bHG2Av*{m1EPKr=|BxN9OWjdcZmd^v=pp=7wxfoY%IA{OYIAuvY(Egn*bh~3Y()^}SK2&C zHI&eR*_M=J(lIzJ_tP2#(KPD;Asf^;^+-DcA8iJKXa>6P5yO!e_LFMqqa7lk^}oLP zm1r7QfAt;I6JY7KKslctn_N`e z@FCZ-Pu3^Of4J{|w>U zU;F+_Mhj*TFtO#7;rYsj7l)2F2vj$bzTwl)-Ukjw;XWlSrH}TEfVMwZE!xB;*_r(7 zKOD!w(7pSa9}$S~saCdqH=g=8*Z$$}50vk$pqK*wP4-YeBx@sX{kpBY;c=U*O4%5r zA=X~zou80Ev;{e2%Lem2EzjUMbnGB7^=R6!Pcm|42r$q?E}3Lz8}ZZ7J`phM|AiZ{ zZDT2C@`i1)TX676W1?n%)p`H*#n{ZJ@b3} z%vqMBkG*&gq(rq`mF0}Vtl(ersBEqdRF>S(;j*bi{n&c~qeq>3IUt_PKRG1@C^EZL zB#{2JR|JB5=JV1+^W!GJe8|Ykz#%7%Q!xbW*Sgv|y!haJ=+%i36VNh>JK~m13M)U7 zG7zt_mOhoQEiv!ZGS8z_lyZGJ`xkNjA0-GAa!5KBt85{P$KcpOVfdmM73tQ~2qisW zZmdHx2ZiJMBcm`E=}-GcV9nAk_~QN52^$?esu+dZ0u5{E#&`NXbFr57bR`J0Ie^&N zmPisSSCTXlQ=Pumt=)-=nTw30+~tf>0QRX4n#61%2s7lANmknO&-SJ)9`&MhNJ~E? z_l#}HIUYjG9?C^auFGs8pZw}IVY1UlJ4HYV{LYMp&{ZIAlkC9J`wxOPC#-zUlFfMe zk@rm=S)Q;PpfV4YlLh5PvNodX2e<9Qw(IME5!#EA!X)RI8Z`NlV>(A+DGQ4hs4Sk! z0(&J;S>ST9XfTzO^j5BV4m~J3a1b03l&6pOi9i{*FBiVIvOzN2)&l_-V3;v=Y@Vfg-Ja;fAalPab(4LojfmHuNEs<~Zdy$YR%K+YA6J z|6=}X%zl1h+yRrDpKaBW#>$tZjTi$SZ%!6&gnk+kOmf5HpV&0h+F!3eP}HnL4$6jw zSXz9`Q|pNgFW+ILu4`J`2IpDnsgzT#QY24FJvHk~bIRq-?34 zAeNTQ@-zsd>ChmM+@+v!@?bX00{^CuwvT|;{tMnnPwU9yEFGmn4$hc+9PjFy*|AP`nwj~kZctZbQC zo=sWWTop(g8LJ74Cv0^z2pDVRjxMG~`e=^`Xf|K@`9|p7f0g64slES$PaJDz^ex;y z{O1pzLWPz-?9W(z$8j)lcpvJjXJe(Uqm>`6s~KBMi5zBp;79GdIkkNfHZt_rQ z&;Pmm=3?WT9e$PQvIFy~L^LxwO1YAp1ykEW=GeY~rdUJQ|0xJ3q2*+gw&m7hhZO~K zUNgPH;?cIbIb4R7f9f9)USCKEuLyS0-#Hhy8KIPfmi%EzsAW_!zUF z`xNoMBx4OYj)M{UnMKP;0ZvUeFdGB?3y?i=5I`s4`}hX{)y#iuCt!_45u{jaoM6_c|IbM2rK7t9>@=B9~PaI>e0H4fWh6n%jGO8*X ze?X+~3J>Ql9+0{|RQ@0(AD&T<|2YVbeksgn(Cqs7=-8WLpQyz}g+ZW`NHho}7ibW; znE^QI0np+&x3q%R`?H^0h;_@hfO{TstLwi>J@U*6*u*8zLw|YAJRwe`YYp`3TY&t+ z960Jw%S;k0cak&_u1=@24;;d!P(OfWgg^R!e03EFlL**Cim;jz=tPnlsBoe1-Ew| z^3L5C;0Yr9QF0FPJ^v6L7Nrcyra&jt=pblnib`iu%FCcjs@?m;L<04paWcn9(L+@{N>w^ zHTq^~tb4BOV%$MPIk&1OW2LR9l_#yQ9jB&tS$f@Y3IYQPfv~T`Y9mFxpozBns5yZ- zDaKz|v_SU&dRIxE)K=|xce`tsZN;O1dp+UONAXc2>%o|Q6|NocA%TT8W7H!mccQwn& zMtlAD|Lz5B=aNS8PZWyk%9`+N4v944^QW9Lj!%oT;|r7EDo>KAo}g;LMZuK^A(z`+ z8U$*jZ#eIGF-TA;b!>G&1lT!hU$n;uGF}yLNXScFYiUcm@qKmLe5_iy5e_Fh8}HUc zCSD!A{&2*Z`@#*V7vpKPo+n>hU%Qc-`c;TtzdadQ`<5VsdVQI9M5syqImm1SP%{x= z!kZK2JwOc~w>MBuGg=)1En^nUT80^S%to-f7K*dtZgHy(hbuacgJI)JP|O{mFe{}< zex}r*-ihqM17CykBznUv<#YpITWZ)u%Y>u2X?p=8n^c$$jA=(O5W5*68O)%#ioC1InR zRt@rr{_A(WgW1#O0o+iCm)uIIC_dwDY34MtvojEA#+0rv$sad$BJ`?sTZUWywx#_j z+xpFyK?m;zPmzd{v?IX|sb07V!6sh4ZAXu~L{pWem3Z;5uj1|d--V|gA9}6C7AT1t z#f*7YRscgs_eDWr4s;JDeVT}SWM*aH%AcHsUj2H+m8X&^-&1L?Nn4{i0Jdfa5DEYz z)9zOS4>ezGMzE~C_oJE&O{Y}QR%ejUPoBT&WxT_OT?htE-#J`LDtXk80$Yy70Zw*B zz4LI^wMW3K^{{hGxv6jUVo%zk{{Bafz{F!lL*-a`s%iO=YMYJPD;eG!&XL27jshnz zM0@?=n5*4bj_QxO9m>mido|k?+tEX=?W=d-p>IEiPo{qga3DC0pZZLR<1{h|w1ZR7 zGZ&MN+Z&hv>QsF6+G1>2z7>Y^-uy(d@X=`{qL(+*VDr$ZjW8*37?o$!0up{Zm*rds$@J!W@N? zCW0AD(E=r~%Rid+Cmb~rSO4W)obuJfFymiuW95R-gd;s|lLmOsrc5kb0d)sQi);CNu73)v`Lx(x zEmU6+Yg4H(>F7~7?6iH_E%|l2@*-}sB+9xXZeOtt&;96Gy!yvi%)31{an(0U7K>f>VMf4(c5B8TGYS{{`ZRpy)(ddy z@6W_u`wcYD2=4#kGuXOn2jb26#un#0gXuv|P9{#j>JaEEK(gHqwcl=yMH*u8a*i5- zj3GV2;ST>t0dAK;z{IbZwH$SPqUmoxVVRv3G{0BFLPDjkEht;R6L0?CtJu0^3vvr` zP|&*v3i{-u2ceKVGQ9>Bq2I`24BdYa4!&?AF1+b1e0Tb_xa_azVd9zlqTldhXz4WX znNRS*51z)3bvu!00I8pqd;%r^Wj{I*!^RJYO$XQyFEYIUPJQgvzHOuZFwfrQJwUIY zp8&-9nG%ZWOX^RSMxzcbfhy;&=6GvHTkL_-M?OOJitW^oGPB1Jg#&xylpD{))PG-u zskdKgcAajPh3 zZ93hu=V}&jh1n5epyN7WnRF(DOMu;PEW?KHK7-oLE<|6W<8aaaKZsDgU1~*3|If@Y~<dP1D)?w3!gw%H*Qm9Y#{|4`K$X;+*w_Y44+PwV zRvaT?wi=w`KQvqZ=*K93dLh8hN@U&bk&Wz;eJFOIpI9k>W+68J{#AIq;p+~AK#9L| z%?>>KgQxJ^PoBl*MVrkP(P2c?bB$ugH4tzxbbJXez2S898PXH%de#gb+CAFF8{ON! z^Ejs6z$=P&kb*ET&h2;D2o!J$6w*xXU-_L{i>u2oxT4+Xd=>s9EbM}WsiM~jsNdC)NU$ESTDY{uEX^>E0?V= z#T>53AOGsZSn|pufLB3%#Z-O5?laxi7|zTJU^2H#ul(Ej=-Iyzj-#3k83#rsx0h!< z{622`#$)*G?PbuculOp?S*&-|dHbU+z7Ve-kk7&$0KO&}j7;t_6kc-(oJOt!su2#G zim3&Ts~T=`Zu`wk@T$2hL^`$shcj5-iS;jOsgsP@!NBG0ejipj`1W<-+) zs7f7WsDQ;(9h~VZ-dK+9zkdzE-EsbZDt1ElfrF8KfUW|L`Y{BY4R^o11Z%H+1XZ7H zL}SaSRJq!wb{N*(TgtIu+I&2F&BJ)<`e(3fLm3jz_|$jkkdi41lPDhA3+LZ(22T0j zvB=~=pqe>$Y$(NyzrBIKUU(0d%v}jH>r)~d0=*;9Z*WhXaQQ)S92%mmEe^HS7Is@@ z)5*#}(YKF4fKMLL#5vcC8U0qZmY@=+rm_b=!0s7en9F>umcs{ky?=TfoJ`laC_h{( zDLE?Vt--o$9zzWu|EojE?ox+W7ep0r>OZ_=HXb|YF3jPqyy@e0(112qJD!a>*LaxS z5QzoldQD>N(W7w9Lswv*6UM-C9h&h_&1VGfJUkzNKI>nY^}u_m;4>9#Gr)Q(8=(H= zQ^ula-yY<)-J!PH&U#BNGlE$Y2B7fTDKHD|Ch9v8IinvbCtuJKXd54R?Rt6vJYJ@w zEV9wevHg&9_%LV$kQ6fD@(NPBW+zsidp}CKOXOA8p;LbtHjh`Ys_hk6`Qqo8^V^wt z`uw}`{y*MAHF+Au4krYAj!i9hluQ9GVN3Szhf{xc63+SknaB?B?v--Jf9F5*a1S47 zJLd&p*HnoVJXi+G|VuEw-&0w(#nMk$2J?7R&%*CsU>45Bi~Xy^>yj zm2*dD{vTe(Qzze!x46t%{NE3;Yh5W!;?vvMgx5L7@)QAwf&gc1CI6%gCgRe&F2Vli z?1#+UOjPj(_pN`uja$#X6Epw(D%LJo4}HN#R}|F^+4Eq^Y?G#-tH+mqdpdgdD`nz6Q#3j*vD1Uh|H1j6yQ>+2{7@F0C(5&v&)IrOOsx7;)`R(QSg3Fm% zUw#lXPvk6r)kFB;H!ouOBOjpj^NqfnS{Z?vg&o?D_|LMRHhjxt70Je*wJ*-U<6?|H zZf`7qV+p2RavvVJ_&&V&s~K20{ZmwI+Xd-0NP26>V%n4ajDU+1u09m{K`e^e{7{>1 z-bkw&a2@0wGZIDLJ1XJ3La}xg=n#+53>A|9OH*17ZTaC0ls+&Y!SZTwvVwZmEzCjD zHxC8qt7!1!guw)sbu|b)-dJw<{-W3mF&iVJ^ z^oL5`o9E46Y(!0Y6@qL>=Pn&3zv@KU1`tbUbICOIm#3m{ivKHAHkI&@ z4+(`GF5qlXaOFYBJ$fXg(^$qa8XVUkyJjCo5DhOiM0X7EtTK0bKg*f9g1YDA{V;9`dVgTS(HnZaDk=;WXqNGz8F0+YEaSbp)ZQnna2Y zuc{Vh_s++rs~$!5ryGrVEzx@a_9Vo<)~)s<5KaQkh}Da?VD-h*u zeh}O4ewPp3YUm~v{FPf+4&-NH>~BuSh_8j#&n9VUQthjgMH`@9OB)=Q_RJu%a@kN_ zOB?I~2D83Ha7i1?a$Fpi+d2_UWxWo4+25w3Pct_?*hI<>`H?bXZDxo;Qu6n{;S^*H zE&@oSnV*mBd*F>Z(Uj_TnPcahYY-=i$07LNuJwBOAlNc{nEg`Aig!?FW}vOm#3t z^~q{|pm8O&0hWy(Wx^z>?=?CIWK(seZ_2Es^HSOJ8ek8Im1kxKaO^jZMDg(6aOf}E z`6D0Oxye>G$H?VO8H(b+o`ry(4w7!JrI0G4uLW&$D>bD@b6RKbdTIeSUhxQa{N+t# zPAEaIpBxAM4yx(tunn~@P_UIWf=Ib0h>CYsVeNHKW91bOqwLiskXTUPY@*5^M|^_H^>Qz25SaQNV6b!racGBptJ45e z-^v57gZqA_R4FK&kA~rDl}J_9a_hm&Yict zgLTLL3o7eDp4&4w%7WS#*n}{N7S<5YtEffg$Lq1`YmZ~aCtMvr49H zP7P9)s-OdCeaL^h^GQ{PjYEz$24~e3i^#($a}Xi*I!rVA^uomt&3{ zjgxARW$FTPT^vk%IpFDP1zxHyf@Hy zhPQJ`v+Jo(p_w?IZ}LGw7xpKP;RRv7XRv$jYOK9}I+kBL4Qqe-A}V>~Y<61I4x0I4 z0~!cL2Mol%H=TilAGj2wzJ3^T`}KeuaN$s1Khpywqi$wJ%RgbWMG8dA=}T07(bRD) zkXG3Z(|+typkJ3b?gz*7rdPMjRO+BZesxgONi@q2%sp}hA4Hvm06Wz5UNg-x_RZ96 zg&I3u@Bh^5{~+L1A<^tD9~5|Dn`9~*9&I=rOxkhM6Xb3Gc^`|-(pHQ4_Tr(?iLV^KJ!A9C~!H^$RtRyvMj zmIM)B0AJF3Cd}4f<7-$>6sqS@pM+z9w8{}(^3_gX`f}bsr=oD6?w82yz(c;ZQ}Zcq zhF1dpxK`g=&f|@7Pk^0=W`5KuZE_`OX8Tf;4+_4pO)8ZQkBMV26}5Hh@c~vTpCqs1 zRbeTgBP{u!N3j0pH&Fh;I+!a1Z^}dW46Mi_;lN{(XY%3Qz_a(o#GB8?!Msm6AST9Fpxg?nZk37H(5ZNp2SAdg$ za~cY`S>QMhB^`Un$By+NnbsN6?8$@8jdA9%UO)>1+VP>MyMRdyhu9{S%IcK3G#@tA za_4CG{I%F{>l}Q3;r;k<%0IC9n@?grw^Mh{UWr|wZA9go9jMv58^KC_v4}_X zXgR!3C>qxvCC87(=xYzf0lz&JQ~os-N8W!KCf{%xhMheDg+qG6_(U@Xb95OTp<(3K zU*kJq1_Jnr=u*Gupq@D6=JU|s`~a3DROsMC{&uhnNj9%RaL{1%z3W0~mk6YrYcbRX z>=|}ybL*s9Pd=y<45llQG8?iDyot_*t0Tepo*fM+hk>YHA}7gzrmpK(id zA+Hjj@+u)%Al`SM#-d-mfF-;?S#r~>Sp3IVvFOHEu<%ze;G^$9gZW?mFWw{Q)rq-Z zeH8ByKK%I%tbTd{c5yJNeL?+9PbV0&dKz&Dft71sD&&lR{O?XjUk*5{ z$#}sC>*cxPf1HEdqeq0^9a4`q!)7zpnqjH+D)Pa0gpyum%M-1#!0?%SDhooi$JsUr z4F>Q@b?xR|sNxgmT?;l~=iF7;{`yjEes&?&KlA}s^D43W&Udj&aL;>KL*9nz3$f*` zWuez7c=f1Q$*YH+NANj?2ALa#$yc=}ut{y&jxz}Sa_PA2o1!7TaPrOPV!*_~a9mkI zr}@aoPUB2cjxeseAuhi4JQQ)a11?XEyyn=HnqC12-4`^cM(TCs13eBUn+Mpku#!sH zVY3EPuCkz>_Do$%8|>Hjz=6U)_Ua)_(hM+RpuWv!(hln=Zt_oMwMkaw#@TTk^d4D^ z6aVjQ^wYAZlO@0LRe$y*(C-Ts{qGUz`>*qnq5oZ6J=4q+Y=LSsEr!*aiPU}sn1o8G zc(mdBHxQ@X zej$p-mB5TnbiR-L?R;J(XXcH?! zhh^$Ge-hWzo;g)Zn|iEcX@ld?(z9(@9oaX0>Kp8#-Zl`K=|lRSp;dMn@8Kto#j$@r z8+rYEz*Iu#{K((V=~=Q~u@kcP?T>!Z$_#{N*maB!d)wgl|D*Q(piYX= zbjv2H;fbb;Doc;cazq|niBGv?0?C!s22*A*Ign}AE@%$!V& zyYxUD@y9cfUs4FiaU|}vpmXoE&L!;_gRj?>dtQGuif=gw`cW(PNVCj_x{cY;EbY`Q zseN^hF!RdHt}0ue5>K=Nx@l=67tN6%Sv?K-X_W=Ba>HYNgMm<;7+U_slV=y^;=msr zkBQ$q25tr|O9`F(LoYkGgQ@UNUti2UY9#vIcM85K9|gSZ-vp5kvb2fy;@eXha_z!-r}k zr|yqHP6lgqjYH?xH9Sq$OV@#0l#Ak~UFkP=WstsX3tOo~~F&B8v{9gT^$#{;PqN zsh+^)@F|`=1XJ#~5TmX<2ySM8t#r-9?V4_<$ZOX@=9s?d{mYXuVEX?cd&*Ea>P#oT z{Scek5?|adTy>cIFhEy?BfJ<|i7Th9JkgavHU!c)n3W6;+l!Q0$jr;anC~2kDO~UO zIdCw*D*?LjN3rR`?x(NNRb zfUXEfcrkRtr1s#~w{r0WmXi2C!RJoELHAsYk=GsyH!A>;k1qbP=h?+i zBL(iWGu`|wll*#}qW`o@L))eShR9`V@UX3fik>Aq1k)?X4`G+C*s{Sfv|Y|fHzcP5 z7ytD%j4jCovL~6#^VXMV3di@yf%jjEeQr1nJtq!=wwAGnd~}{|bShoftU5TG9o_%G zm!sdk7orDuIRbt2qfSVP61pc)GDV5njj)|3UzD^4hg>!XIg4m>QiVh75-yq;sFr!Bn27Gxk4tVlPjJfG_^f`JY9Cy!6lRfv$BFUL# z!EvA)<3MpfioSI?2E2F;ivMyJdhmgoZlK-l44A!dC&_k_sII0hxEzbNENp4p(SF)- z9JoyGtUh_@eZpuA``HQD|GBF%{Ab4_XGl+&b-pVr*keC7E_>_=rtHL&2TTSHg#2^& zHUG0^$)j8%J$NY!F5=Tq-Xua-2I@c{{ggG{v>`QT!6tbuZQSA?SBW2RkM<&37P$Rz}4#u zs@P;JdzM0a03_R=N*aR8wR6s)d!gsIk3iploQ?huUxq<1U4y=VKN~%Gh0r(F^b?9s z21D+$fCm=bMAO5moDsdz^U?z_@So>k7@u?O^}r<<^nYid&y}3%NB0G& zk2Y`vUFgH!=tAChc2|{9Tn0J0nb1-xW3OJwKYKii|8Ob>&b%6fXMF?xd6npM!zn2G z+7$HQQY!EGQRcm*tbI$ML6R}3(A0Igq;ffHYirhk%H##OUJnc?K*q3M$Qs`d`k}Dg zV@9%_eNc4mA?WkllTmWtMHn*c>lpmfSJCfxr=Z}Bamb>6KtGPfdzOw^qIK4;OAmmy z?in*ETxiJ@=-&gnN)%i+5xstVEQoX9{Sxi75(qO2qn`lLCK?ELdoNoW5APF zV8GKcKwin?U&a95dz3tMDeGK}{`Xvfe*ZcTegAeAdi~@$6kI+DIY$mRzfo$gHd@ci zhqgY?E^6QHqJHlgZo7`TPtdPQ>xvO5DKx)vlsT-oxf*1SDn=%kDh5;@*$0`!dn03L zPxEz8eFxg*e!Z?ZW>()bqSCHsksbi;8tx_i#_zPEPWDmnoFzw4nf{*U$l|A`^K5(3ApQ+5*)8Tfy{MBsF^6a0U# zHFTp$uqsy9_*VaSRd+mIrNz2;Kz!A_|IecdKApv&b}Zh%ogg^L_5UvZQj%-@j!?fQ zsIvtdGz63NE!0#UU1o4pHYL3|6OLC zdL4pT4@Yzly{G?qRA|t#gxT1&V$Jhk1Yg||WYhk+^5S0vcQ6~v{bX5&(VYD6GQ3Z7 zaxWPF^yzKx`9F_#XrhkWO|g3T)}8)EQ0I;ymiCX8=l>!&dB@>omQUaRaagd+hY`&S z^n+XGe-Tuj$8e}x{&D-ia?NVNfnZN;XjiYu{)^zo9l?zzpTGYlSoR-+$a8q^*?&1y zUAQAy5xf0gy=67r5$w^fTzU2{g6DSx&*yy-s)GM_$xXe+$S~`&>_U3$U&?bvBXJ6h zJzK{AyR+JVc82%Bm;Mo$;SLRNI~szm(*|7rNAs(E6RCGX{2K5Usde*@;?GI+?!LKH zkNx=S|DP3mJ$Pq{FD}F3OQ-*pPU(L>rb+6C_rD7KS{`hQ_4LowuJ=E0n-v-q%-@N}fo~J~ z&o%-0S*3S!RMP&sLh~;`2VwG2=CH$jR43k;^VNxayqVmt%n6Gj850hWs0{^8# zwf`^)Q-Ux3^NAQimO9-@yw2~&?q5un-Z9xbwd39OU-K^bd&lHkzAfm#lwEMgB%b!? z75slOIe*8bLsh2Be~+rr@tqRSP>;@~|L^op@CoJKk=)fAt3B*re)Ybzfwau(GeCI8OOPooT#l7RQsF;5%T4PM2mn~Q+j9amK_g!!s5G%-Mc z;;zOb2`0{`zM4(A8l0HPPecW^Xv0Cik>|(@r^&0|M#Wd%;u^>KOT4YD@T7CdiOS?z zkh4zNW&Sd+tlWxjqD*@;?1; zz8boQMq01WrHC}P8}KBYYaCq{Y{ZkP9=Wz_9_=R$ngFa3(M2K=vunqoCt;>OhH#L7 z)p;OHYxj2cyP=#J_#}j}y0k&pU5l+vC@FiN1_r%5iiWHI!44FTUER`lri38$DquBo z5*fMDJqOPfIqy}mI4H>I5&Zt3Am&{IHRkeTBEML*O=qzEDo zV1=mZ_Tw>4CvN}P`h-+_XPcyt6~w^_T4YD&H|c7jwhX@PQ?!Ng;VyhYJ)XS2{~tpM zc~gclu@UvmKtNo%oViTN)tWY{&){MiIg7mgXnV8D_{Ub?7jTw*%>3Jkh!Zr=E*o?n zC<)67N`)+n4KUM}KK?-QKX6cL9I~r|f%kLAZKJ#)k`0)MZ-GG*`lNaCV!?Dq8%Ep5 z^W4=QrAN+Jb8IY6Qk0Sm65~bYH>$+99hsghMj%euXoX(!a*3T4{|B1nIPZ)p?px)! z37W*La^xy@;G{k;8R~+hpua&)iLCHUbggDU5>I*r1J&TD^@oe)5*#-nHNN$%7eQ`; z%9!s9dCMo1;?s_mp>`3lF!voxph8>5-53spWG`=zdnb%IqdkwFtjO|*<*)FVp!c&y zPB8X*T@?96NaW@Ay8x;G5z)roxpp&=k?w!o5uIc@w~rinaVK!=I^e80qxI?xStHhJ zBrPG@F&kCck<*eU%-DMpc<1w`bwV;f!0|gq_O^}274S(Ep(1Wm7&Gc&7P(vv`I?1T zg1wM&S2!p9h=RSQhadNI2e}P+`Y-+GuoqO`*;CV}DL{oczR%Y9V9=`Q)x+^v_YZ#F z!X>a%K!nQi93O&&omK|AK>l$9aZEH)1{kF1Gcb$SGyB?&c{%g-M7@h4{x$KzWO6kbxMiD2WS zZI@$h;LMY=!xYp-sZjf03DmXu?(OxV$oUtQk1G#KRV?zmF)tPnKNdPOx!ZJR!q!d> zpT{dN@#qSo%|mf%0dtWfJmcne_T7j--$ygkU6b-=P9?LZ@E~vMvRaoV0I|1sw|0qfk>n*ihEa%2qfjaG{Bnyfo)kL)#dp z2pO!#5>CG61tqU~TPp)20zYN1&iZ?owOE^#uagcJ5(etp+n(1UH%DRjs}GCB2NgT& z+8fj!Ot9YUjKYWl5>S7h!01A*`$3QxnbYWnjX~`t0qwv&@;TI%_s~>+qmdV4s`bb1 z8O30mi!6Jj%km?@(F&k-Az|=j4M@KoNr41CapMQ*sCm6`J*6(9!btOjp1zlBC8qYOD5K-b!eV7EGYt?75Do!-P=ok8DtpNR zas2@&{MC%JQyf3q6Vtns(>W7W06d!;*Tv@>u#d}wIp^dF8dvU1lgJIqHKnDi5%6w5 zJ*uM?@Ep69))r-wz>=CmaWq4W>}mDioXu_CoFLO44sijp-?qV{4p0e%8=2$FPo#l? z=`u)fL(%}4*BnZ`en{6)NGAGu4>c<)Nd{#rJ^)P|M|7LO7SmZiaT>CnufHDU4f;M| zDw9!EB?sa+4Z$Gby9X=`GZ8_{tHYE|w2#gfG7{xF2q8+}Nf2~SlZ`RjIOTo< z>sbFL6E%qZ{V7!DgIb`iqx|J6b3Lm87P&2nn*zyX*kCJb`bwm5O#+nEpDOw4g7R~k zis8$>BBC1!Jd8IMI1hVeB3Dik&X)os$xB#V&4^w5+i}vyLhl35IDaqA1~{61q5}5Z z>M&qQYegSp&t2>`TlbqTacH9kuQq>s2Pv}nZO?4j8(Ys_HAZgq95#nLol#4k?}*jJd2K8x+Tf&N4&5VHqRXR!?7$WqnW#;yvUD{kBP4l?2~+hSduVHZ$v5?<(DApQ@<_7umBs~*CTI;Pt(3;HvZf{>TPLq(%_ZTZ0P8E$rN;>)!FukPkNm9!a5NAX07gg1 zgx(MolX;d0CZ@{BvzkkN!2zdk@$G#mlkvbBqUFj#*aPHGN@Oe=AqC{>XD_|iV1+C{ zbs7Dz>qTKu$wBA=Xa@8cNx__mi}uKuK{1*p`M*1*_g!^DKWauDY3)Kzf(2M$mML99 z5j}00-T(0iH8iWIB{j0w2W>c7_}90XEOvoM(KMjPT>HCJbfy#Cj{P7JC?p7Uv=+o_ zdjoRd8?a1Hx0L7}H5XAN;`tp~+LLHPtSg?b+seiuc&#ffANsgY=o6NCjm{t2IyJ52 zX_NL~E`!k2oWFmI>tcEzM`Ec*4H(){8?P2PmKWv|EZhg@ih&T4*E{BO=uH0&VPpCR zv?rQN8+m*@P$&rEz}03=t0}@2k(A)UN2lq08tyh=E0qiUfI-{(JfKk~i7Sj1HRiR8 zp3cpv5SmJjcKYcklad6~yjBS=k^bREakRNYw7Bs22Vw2peSuxY9U+P6N7eUri2tSu zbw`hHN(xVET})xykq*hL1y2U>3{u*-8&|%_O-IBezZChid}A1esSNy2EnrZieN|8` zIVOR&P}kSYj+Mc2U)QbuKvA|(_X*d!!Q8w*)Nz=@>Lo6^qf|%qf6DK3spq;nP5pMF zvt+U~p>wIRp#?xZ2trTqXK6s?QYuJjEV^5Cp0chNI+*?}qyDN(wL!EOE-e7V^9_}H zNtc_5aw6R904?)LqfVrMoZ*5}0~=ns9vEq>qq_;M7lSSmtzm}2G^JMp&}xI@RA7FU z+)tbjgDtVk@Nehn?xp1JOylVV!9P8K0Seul zD1IVm$0$@<3|U~*w#00Q#FRbb0A(Tb&!R(E+Iz^p*~w|Q+qFBz;1TFH=>T<>ZM_0G zPE@RJRLwZJLNg~_oMVCtEqouDEG_AkK7)cLlGWuKUI8e1z~#HlfJ(x2RE4A16fY#| zSjXr!OP4j$UrLFkrx=YVaLn4pw+sv#8;q=OEA8*ZE>e>Ki_|?;p01JsNF_ZoQu6G~ z&yK7qn5LaEF|y2{W&?dtnDzRYGGL|k?nEUs{Jng#dsHecmRm_7e^oG#R&QtK1JS~L z*I868*+`iL1J(`E>=-skM(VtWQu1;oVGz~cs(Xp*9sS6ONoXRxb7iazJ_&iW-;j(b z;P0*AucTaz?}@@ms&R4<$BTTp=qq4mN1$=Yi>4&H4q4=3#(~S>$bjr2N6tCLsy;AS zOE`T8k3Q3?qghPTmX8Ra#9@B%}B=#@n`=8x&lNJeFE?-8fO=gob<+rXlJyt2Lir#a`W# zOuxf^ciHZqa1M2Qf^Ig&_BvE6cMQ3a^Kn5#at>F5qtOt4tjo2urI^ex(?4NE0CF2r zC%UhSe@5T8){pg=l}P-SSw|FRPpHS#(w)`H6Exaa#1rn}O;;xrXE^YnRmmCTGP03W z*ec8Tc88{tJ&j(;TJdHw+yLi2Jg1be*)Qm^;p?a-@c9%^DfKk*9v*EPwTYt0`Nm7h zo9_VYW5OT|2(8m@bf8-elrNzlslzaspH+YpH;XSV_Kc%`yitcQco1@4**bZ)mTiUD zg?8mw!S;p?q2uo+-px6L#FBKTyQD@oVa$*+uY+7PXfh| zP$el7>b7~aEFY!qyJfPHx_o;{9F!1z%F>3@hNDYUZQ1t#`ph(hUavqXZ%X^tyM5a~ zd0{1n`Zdd%ZKBZM)H$EmD+?TMXP+P5z>>e6^!r}RqGT+(iLslS!J$I=r#9I+>-V@* zC;}haifoX4KBYor<|jYkE|jv+U+SXD<$nVcoT9ws9#+YcSd>c{rp(MV7gKkDD| zkJx{Ut98UF(Zy;%N;H1m-q7&e>*oVk{DuN*0@DFk6?pM?-l)D(fmfe3P7KaD>5Syc zcww(5wR{1uosyCzh*1(Xy1yh003}}h`ED83yMT{kER-tQ!8Dhd#YMh5wk`K!#0P~Z zwX;SZ?TPG`HQ{^n4Gxz9I|1v_GQ*@ zbGv6He{>+PE_N~6AF@Kv{KIIvEuZJr;+QQITTeGcW%iPCd(rE>*U60lZoKqf%eaud z*Mir4@2iwoRx(?ryyBkH?>Sf z`RI@*XYZzlg9FG*sW(g#Ib(~oCbq7L>q=fssOSJNSU~oZtgkV-ZLkQ}4Pknmz7%xg zBzb-1tu`JvJ}_Gdd^FM}u0PuzS=C{DSNR+HU~3MH$F@aMYcr>W1fgoqQJddIE+^#6 zWAOhez;=doQ|h1V@f+i4dF!yUy_z{ zgC{gQ8vK+u`e|zq?XdXNVE&ECtb}oy--MuNs1q;o)FV$SFN-wG`({mQI=vqV*Cj(D z#U6+g*a*j~V_gs)>QVTR)ILgi?}mpYm}h zT_sohx~y8R+8kbEv-z+U*g&MWSR&$bi|twi%CnK0TKZ_w%IQp|M?w9(zO zD*mW?I%+t9BrDY!4;GTs&#T*geD774XavUWsKCzceVj@Qx>Vl^LkSWe(5{8H5XEh= zV+(x>E{9Bc^1s(0l*|U3VctqwfT+{cTnmxVsAwOylSb!I*x&D2Q)9AKHKe``@NIv&(&Yc zshJ}>Cke@Q3360!t0%HW9@5XnMF$C~(t&icokWOYG`Y5>)UdD%$zVA0*9T&iLd_}y>Tv1FT(bZJweB#;(>#ShSP zvYdqd=HZV|=3UEasmyN*aUz;e{g$*q;TsB6S4@5pUro1V4s?r{uvOA*f*5S0AsI5l zTwxRk>D~D)x?bqNK^j-y^%~3QF0*f;%Q?q1QLNCdTh#UGObyOW8znvJxPP+;jmYsp zR=EuBw2rH>P=jdkL=SDSs#>xi+HN|=i2R_YM&D29$WY=X8rELIziDvF8Db;!q;+Q8 zOAC0YNRQJvHJ?;3VW7P}qOE2~dVJelw4e3^jKzbR)rR4F@dJ!J8Y%{?(`W} zt+kmem~6L*-wey{4U~1DW(HZ|(SCd@f~nU%IyoS`Qn&pfe&E2Q@;B184n3~%19qPW zvJAD=VtKE>j?JDF-SMl8lM55G#1?T4^uFwZ9;{gGh|<|k`lv&B?HP?1E_(w~DM7>! z!WJLM1(%Q&W^W3Qapis$?el<+ij@Tj%j$o);U!MCpS;K%GrT;USMv++rkjm$8vIXA zK>9N}ig}}2Eg#r-f7&F`eGRVZjjVKrr1|8Yyq;NI^0yYOBsf5Kkcl)kYpjUwT~V~* zoc+YKHH5VD8_FZ%a1G2k00=<`#P*|`Jj%hfNt>gcU`>{tw87RT5!4Pl{mosfcH3OM z@7*q-QiZ0Kys0ovDy)*>K~(4a7kn=t4`*Yl{Qz38B}~5sVH^dkhgA=RCyLNStdI)> z&HsVxqfi+q^-w>iz6vELV~`{*H}D{QKVd)CfE-*%{#;VkxiFq?>Jc}u+spLzbpqi~ z5GKBT&Po%U@+jQ-p`uz>@t&5zuNy2S3p01RPqjUSAW8f)N^HVH#=!E`PLGr<#uldy z6RuOrcqAou=Jcn^r@*6H3D~9?nCD(Mbn@(1ztzk_JFg1*reCD4U4;bNlBq8cb9FU zu4UGF(bw3J`_Q&!D3w?tP}PpgK_ZF4;5E<2FQ!yH4E$i681a1SX>?)=C=d_%I1E+F zIHI!Pk+MNF5AFeY#E1_1{P|s?e!tSgv*Fw;OkJj-twAe$0vWf-<06;k%1O(hW4C|( zo8_zXP?^|;8Pnziqv-+qkD9whHz`(XPif<^+Ml@|PK!qKv&bXlKbMX9@ z(YU*}`wQo{@NNnBwS>!9Sr0j$D-VsleCNK-Z%54joNrVeZ$&jAN zz=e=>Df7BP36+J_zQmXXHDiL?5eaxXK9o2{e2L3f>orCMc%EGz>Y+IyV)6@Io}pqd zKU==yvURl_D2t=RK$!wdlhY8p7WV)K{9?G%qI3r%u^wtqd8EbS>y=OoEphQK*-<;J zhDbRM_Tp+Tk#&j;{!?XRxnXR5AiJ>9}aShPpF9GX`3+l@3`ZhE1gIZ%`Iz~S$^yy>>DX+A32E@uh&?~ zU-@Qxj<7}TEFv3>2<9$))^AmL94E)~;z0MXORr2FyMB%UQ__e$ zg~(iD%3>$|w+L~9R~4Jg?qxU@@A${7JTeC*)_X+n=(#v+J(KI?Ps(jqbz$gAy}ZXQ8}E5D=6+}wN(Y)R$g zsj)CB{t{9bl2g%KcbY1oy<`TQpO!3pDc;lHNv8$Tg%ATwJ52{HO<$CR^TpnC0`LsF zUSi6WKPd|3CTG4O$DsUG$kzlpD!g?P<8|0>){y-VMO!5beg?rpQln1S%Fw+e4RAXJas8{*29SCf$pg<#Gtt(dP+glk}> zR(|Iu)Q>LgZGYg;QEjzsYC3Kwj>;*X9nMl(!RH!QZXANwMO+2t`N`D~m?4T(%T&z6f!kja? zvDFFg^4`=G?l`#~=*=y*VgrrqhNAdD2*n~BHDLdC?XAad{nyHlgM%DQ;IEZiv1e_u zO}q=4sV}~jT}i3Az1pohq;WlNFYhbi-67&Z2tFAaGI8Q)l^`MLKVMhk>;+Af>rgqY zcFLMDYO8g8XY{01m!hZ~_vmLX$An?O$-m|C5A+e6wXjVab-pR+>nsT8og1L`a7ADe zmao}P!as@E-bWXFCdbca#MKo)(`_o4dO!7uh8Gw(4V`RI542~3m2M~o`v8AY_6L-6 zj&bP)qF(g*))gl3o+f$7fy^IxDmVdMuobkJdkNhlI7Bb(0*tkuPsLFe?*BCAP8Ckmv6)Lc1JG!BN2Wp zoDI|H-skkbb7%VOyVn! zd>xDKc(3l3isz}gD3+H@O@ajfgw8gR63*84pTW8?;j$Z>;5h4(f%zOFTD`8f4Wd2d-%D> zhER6$3w5jeP`I%Pl-eRyr9iC|E{l$OK_wJFxkvt1+)YoDXZO8go*5J64B!0NT-z`2 zg$5>Hj3=zS2Q}G+@0Zn5(B~#%8R_gs*ph2K_RsS|l3^lYDjU2wB-5AVNE`lAA`$?sk&1r08^ObbXz& z6^c(50=^LG!-q=L2&%jgU!6pKDe`S3x4lh=9zd80mi7n?CM4&odKAph8U~&lK7HSt z7Irbv$W%`zm6VU{0f=f0g2VlU){;40hsrBIS2{?{DK~u9U>#&k?Z9=4s?M`XaW@b6 zmF(1L>fBOMkx$VHdHE$@C^uP1VET82)XQ~F`i_evlEc(!5S1b))&f0_`Ufying^?p z)_3__>*^!E%kY_r*%Xz{0Zf&zAGPL8KLL=Z)nrh761SoU#kXmCIW?}`ji{&X!=hty?A)@Si+Qy@l=e$1u>^O>kyqa3nfn=UQ`c< zc29io#MAb@sQSSKnL-&B&B$`7`cFPhgqoA_*eLV%E_C+Xw?jhz@f$SZ<@cNKtOEg8 z)#r%*Ft?4O`c;Px(PU_3!2Zgw@f|*$I!qj|QzsJ#p5GYHKXB5dxES>#O-H!&zCkAh zUvr~!!c!);Y_V%e8H0Zi0EsMoI=wOQPj41=(~hkWF1>Q zqY*`}ORBXY>9^iL!DNPbIfXGHE#D$q>FP<|3cmq=3%^jwt)^k|{2;3Q>pM1=6OkiT zSvKql7iJh>{FV;DvS$yuko2`<vXM`&+xoya zRjgMCK%OLU+cCtlO=cl0iBaejH1R3vrv_?~Qrhelz%Dy>M^!z$0ApB__<;n_n^2Rm zXtap&au7BTDn^JB^U3tI-26Y+l5J5jXB>PrtE-{G2KZ6W)-2>WsO#f$Uxk7U0M=sM z0PQpnEVZWIj@*Va)?Z&!4j-8$*3#{5iOO;$0W1hR1-AEc?C2HZqxQ?h9=?1(EQSXq z!gwa>G2z!|u8RV6biu;*L<;AUnlWXcY%A4kQX+4^+^(=D7O^&}!6E0dClwv$YXoeOxmhNLHZ9gq33{#qFCgQYFV zHmGvee!0GKX8=z=o4Vw)aPJbqnZzX1JsmX8ea(~SLPQ_Isd%LWHpWVytD=~Hs&3vN zzkdeU1)E0HKHF2uGh+;ulFu#B(h;BFQD8*Y_6cBO7TOI`jGNxdkM=lwsXg=s;iDCS znP%%p?}PGd@vAhUH{UH7o)QBf1XEtt&zctcJnFjdZ^TBowAe`i(pO{ise_)d9$3cDVf7Ch z-H*``g4_!+!21hsF(l>h(~sfjk&F+!Rc?@S3S~+vw+wYhKBP^O*L<7PM<86zDfE(q zXN>b{K;CExlZ9kL#aJX(Qbw@o%kx6^y3fum#J7p7f`hDU8Ms7U`V%fX96my0R&wJX ztAQ_+rFw-e)*GF7QT2oyCm8hU)@F)sXvO&0W0I2${o@>f_R7Q3Rn@Z5&YjQ$6^-xTRizx%_Kn>smJ#WyS-M(|d8b>*1yV?7z zv$eCP@j=3sdf&3{ze(`tt*EBk>A^{i!R~VfX}aN%c}kR0>v$@7c)Ed0Sp&<%MPK}2 zZFpu|t~rqMpn}Qb5%GR)4serC2^e4(AfqCgeM<#`+RAhWU?NO`^kUJkd8&Ke4L+bn zR+PZNYMk`u32T6XUpxBe2H3M>z4iSO$;jpM4Sol|UWNlgx|2MU9%sGCmT(B!7~C{U1J!~!hR@jmLo5M4&1U!? zoBcGcd>iNzT}2)ugyVx5mae%ZV|z1-hcP*=vFndwCrHYMLIErE#iVVM+U9)?H6&BYNH5n(%m;!RKMw@pGk>FK9S zfXxB_b^ri%0m(dI{vD|0xb15ivU@kd{8jfC!$Pc+G#8YDl?ugS827@Ko5|qYCa?3? zpkKOH(k>1&vlYxgztKBQJ+h|;%Z_fvxN(yQ3wFOj`(|+2i`HR!b?XVFPdY8k=r6&- z6Y+ewPkcn)Tf|KBXYiZEUqsEyllq43dwZT`djx*XP7q0Qhr1(bJhf1&4w&=AyHra3 zl$Usq(b>VR<<`PlDDG?MLy!oV;{%7VJtqj z`@|=7yL2k?^tM%?faP9MiU?`~J9AN5ynQ{aV($l|_;h;E3pMkQ&!RUG+Oze*t z8hHKX+6*|v!Rhx<)Bn)w-8yE){9QnAGxiA*E3kG3P|Ig? zs3YsQsTvF>0ws+;^~sw=Y;=negDXM$+8GFX$7)QYJlE2e**IRR(-0FDV#1I|CT%%m zvzZ0P&NJJWYq04*ky&KRC$UTjq|4AMvp<)3zYC|^BFkCP-Pr8vwmrEA@Tcv)@YB|Lp*o8i#0g_qgjA!*wZoM@c4a8M#pe2_7CJ`95(VP|WVf`C<&Y82qS8ZqGrk7CfTbF|Y)nigq zeEO1zO06t0Op4-OLqffDPx;;thLrqaLA_j0D0AjRP1KW8GFqHB?{Ff%gFc0I2}Mw+ zsn6DBzEGpn&a{L6atJVPa+;1bOs`O<;0fxZ%8DeB%MC6!DlKBw`m9bsaZdU45%C^4 z5wPCkIE5LB`eu&jysCL_=zNrL8J&}Qzl;$X&X{=2@abrLkrNP-bl8vB##vnVY26|j zIhinZ54u;59kddJb9LwjAlROUTtVNoSIKgE<04NvHOV-wb-S`KElIj@+={u>@EgtJ zju6;-P0PDW@iB`Q{@1}Nktd=!7++FR+`^g@`K}`r>&($d%=toe5H6}{H-%+~vFN&K zlL_ra-RSK{(`8sgif$Uy!V=I&n0_I6>HSf~?n7HKZuw<=$*^Hv@poqyth~PjCU(6k z`$E5^$Lr}b-ja5J!!tfc*&WgCtk&bf+b@c9uBUTFe9*vCv_`Og=CQ;xXuqDTgVZ~s z)-8V(E4OW8N~VJCA=IU{sGGL`Ye=jHI!xxrw7uD3`8;qQ#uwP<$JmsN~U`G=RBKA zpfH?_{Q5_l;`HI+_sHf86R6puu{Maac*-y{7BFw`}gGq~&e``%p|`{zr!uuyt>z*XYLt%~D&L$s^b zwYZ1P2Uhs-A5b7{jtRbo{Sk9&l9zGD?MwfN1YIVmPX{?F4r%s%mmyjZ>dbIRP6~Hi zu-b!B2APFJ_+=DiPcmJ7rz8h6^Czzxoa^=hDv47v`Ulj|lmf}sg7DJeK4XC)?q3bb zbO+Q|aj1p_CU#57-J9m-%~e9@69b&}=UHc$WsC)wYXqg2_={6x%(54(C}<{{Lg)q* zK8%W+)t9}-i>CHuYT3~`@2sJ-E#(usX@*QdDirarx18_s1pQ4ndz^(&I%m&P2WQ7U>HaJ4@+{OV%j$%! z7>Zb##?C4r&u!3gw#cP%!pRx?hD-qdnp+`&yF7l(TB6uLcS2W`=}c|A{p^VW3h!j^n&TpY&->cph^N5*v4TN~?k zQ}I-KjIf+Kd*m02fHWObg5NU`PqP0QIoaX}17C8)pMvv;zwG|y`NO*LHJ_LE7*3~= z7$e9QTN0}Xm4BsB?0nvz#9-X_QiEJv>=8GTTV_us&o%JpP0>qe0@EjG5G00l5t)OI z2sUD?QhD3~RSG{*4t(5kytSqMn+8h@9IMiI^et?2DwuLoO&}fwi_+fSSGNwS@IvcY5e)AeDk@LE+ zhgegu0P(tn}85ZRMe!&&Q*dTb3u<2d8sI*ohlyKqI_uw+-YlSldaU+dRGtv|c zC1kJvGRLzkx@5&59&fN3#n>a4K(Al$0)XE5%-}dF^>H>sXFx@QDtNdo-Z1S1?Cv?Q zG>{feW;xOE2QwjMnz^?g3>#+W5OR!&s zsz&gBY-Gkf%nJb3FhH2Psr6jXnw$ljd=jc}D?oqCu_?Jgc6eFQ={igd_-&>&u zNEZ9y0bHkx+oXJlK4(Iv6nm!;mt^pCr0|=I`W;`cM+x+=vA(1}eDE;a=A%Zk8j~-_ zpe+-UU^8bJwUJhYli^IIx3`J>Nms`{oj@GlBrhq7liBn8d0qhvp)dYfa}?oNj~C3r z)Y|RK&0v3an(G0fJUA~Vaj_7%3ty1_vR{z^&nH)oL%jZmaTyQxb*}mqfPx59EKlv6 z#6%Dd`~eSY!4;ZL?VzB#^oYeOs<;Qd{657wTV`PSy>bMS@As48pBJzBOMfJl{Z*sU zyiZ>AI1eXH4O620lVSnQVH{T*Ku|8Ls;1vfQ`CU@N|wtap9)0V^1<+q&3ILLf`zB# z70Nk!1F`vSllTLRL1W3B#z5zVwjSU4nc?j+{R+DdqpF~X*lOUsiZ+}8QRo+) z$qt7XYC;`co?&i23VIBgY=2C9=YnP*NGdM9e5!5M>?cQ)E?iSr2L8*Ns0Q{(8W$=F zDaO6PM5Uf@vww-dN#@!C{YfA=mrs2U20E8K!O18U`jm3i-trVA!sci*vJ#f1*!*PU#&(~u6;{>I?_#ep=_MK3ifr2p)J&RlcEN*5 z3@W)2%UNkG#>A^n$mTH-Ou+|Rl+wh=UIi7khFpaXd6rt~TF|ZNSTXz>tAR;#$#~2o zB`5eP@8x}XXS0;%QmT93w>rPCEbGRLbpxbl_m%OIPnFC*5y6d9@lx5#oovBLdQ5zB zrs0t<$qcAc5{E5FByNw1y}k-8ve_CI z8An9Niu;qx*UoYP>sNjLaSP5H)}5*>;txm0#^&SKNBO7?v7Q!mo3}I{_bT0J zI({{8hlwwlTiTiH8UX0)2kA$=@83>G&c$wu=BKCeO@x+#VrtPo#ee>6rbx`N&SJ7ie0>yCAIG&syanP3vExuJq{}{nBw8T{wB@$e z!0`Fg{%}B2e2wBF1srpznd=1qmFUZ#){&hc-$MQn#e8O z{pcUN8_K!Ny0k=g_ zh~suVsp@#U44W*-=Pis=W|FPL1#mhmss*1qFq(z!a3qyop}B z`wylp5O0g9_qN$E$~a)PA0L9W(8j|KSS4^|2(x8qalF~$SK7_VLvKib64z?~+At*Y zW&#@pN?`TeG<*j?12#xvZbD*Q{1SJo_t7T>a^}Ao!;uC?A&u1A6u;tp1WuM8P2cbZ z6M_?=UxH9Yti>)prF>f+6c0(`7r^n5&;TZTgpwGiwg)`jy5*gknBy!}Heyg%OaNmo zwqn*)DtV=Iv@LSOzbu!DLd7=L4<&BSzM(_nzn!QQ@rcGg#s{f%Ri!RB^TvEoP;MY{ zEqayJ$X=qEknqHLp=0v;X%H97v-nvI+@5b@rcP_F&^iS95=_jU zWOhzj5eNOXAFf=(l#MahD|i=o8eW)U_I3@NCWb`>I=zt$(P*^s&osvVmc|u>FV_Mw z_jo4w36q}Zn8Q52yfT`p@*AR?#RZ)DOJnAMs))5tHXet}Ea!FVhZ?zR6fD=LNGw0q zV=&+boiz!Av+Hq;K^Mt%iE2zct}(YzuR>cT@)l^jwU2f>*9AQfZ0P4>p>zgw^-PU! z`{bI{;Ma6(g^pXd-L?5nP81#4n7b_9W46!`kO-BB#Bg__o8Oe>B$kFb+B!&#w?q!w>IkA+*gY ztM45%d#!GLF?ljO_Po*SPoMtK8a#9Amn3^X1|6a!DNk!lS10R)E;kaH0=C7itg1}$ zivN_;W9~}Y4b&PYcI}LSRJ+4wr@qrS5)#VokdX$&au`(4jRgaHj zhh%2C6iP{4**3mF>QG(vqxe{Hc3A#f@56?7lV3-uM|zLl4XQo6g}}X6wugxGZKu)iPRJnR