Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/publish-nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: dotnet restore

- name: Build
run: dotnet build src/RxSharp/RxSharp.csproj --configuration Release --no-restore
run: dotnet build src/ReactiveExtensionsSharp/ReactiveExtensionsSharp.csproj --configuration Release --no-restore

# Trusted Publishing (https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing): exchanges a
# short-lived GitHub OIDC token for a temporary NuGet API key, valid 1 hour, instead of storing a long-lived
Expand All @@ -40,4 +40,4 @@ jobs:
user: ${{ secrets.NUGET_USER }}

- name: Push to nuget.org
run: dotnet nuget push ./src/RxSharp/bin/Release/*.nupkg --api-key ${{ steps.nuget-login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
run: dotnet nuget push ./src/ReactiveExtensionsSharp/bin/Release/*.nupkg --api-key ${{ steps.nuget-login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
24 changes: 12 additions & 12 deletions CLAUDE.md

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# RxSharp
# ReactiveExtensionsSharp

[![build](https://github.com/hardkoded/ReactiveExtensions-Sharp/actions/workflows/build.yml/badge.svg)](https://github.com/hardkoded/ReactiveExtensions-Sharp/actions/workflows/build.yml)
[![NuGet](https://img.shields.io/nuget/v/ReactiveExtensionsSharp.svg)](https://www.nuget.org/packages/ReactiveExtensionsSharp/)
Expand All @@ -7,7 +7,7 @@

A .NET port of [RxJS](https://rxjs.dev/) — same operators, same semantics, same names you already know, in idiomatic C#.

Why a new Rx library when [Rx.NET](https://github.com/dotnet/reactive) exists? RxSharp isn't trying to replace it — it's a deliberately faithful port of *RxJS specifically*, built to make it painless to bring JS reactive code (and the libraries built on it, like [Puppeteer](https://pptr.dev/)) over to .NET without re-learning a different Rx dialect. If you know `pipe(map(...), filter(...), takeUntil(...))`, you already know RxSharp.
Why a new Rx library when [Rx.NET](https://github.com/dotnet/reactive) exists? ReactiveExtensionsSharp isn't trying to replace it — it's a deliberately faithful port of *RxJS specifically*, built to make it painless to bring JS reactive code (and the libraries built on it, like [Puppeteer](https://pptr.dev/)) over to .NET without re-learning a different Rx dialect. If you know `pipe(map(...), filter(...), takeUntil(...))`, you already know ReactiveExtensionsSharp.

## Quick taste

Expand All @@ -26,7 +26,7 @@ interval(1000)
.subscribe(x => console.log(x));
```

Same pipeline in RxSharp — real, compiling code:
Same pipeline in ReactiveExtensionsSharp — real, compiling code:

<!-- snippet: quick-taste-csharp -->
<a id='snippet-quick-taste-csharp'></a>
Expand All @@ -39,15 +39,15 @@ Observable.Interval(TimeSpan.FromSeconds(1))
.TakeUntil(clicks)
.Subscribe(x => Console.WriteLine(x));
```
<sup><a href='https://github.com/hardkoded/ReactiveExtensions-Sharp/blob/main/test/RxSharp.Tests/Samples/QuickTasteSample.cs#L19-L27' title='Snippet source file'>snippet source</a> | <a href='#snippet-quick-taste-csharp' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/hardkoded/ReactiveExtensions-Sharp/blob/main/test/ReactiveExtensionsSharp.Tests/Samples/QuickTasteSample.cs#L19-L27' title='Snippet source file'>snippet source</a> | <a href='#snippet-quick-taste-csharp' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## What about `pipe`?

RxJS's `pipe(op1, op2, op3)` is mostly just method chaining wearing a different hat — `source.Map(...).Filter(...)`
above *is* the translation. But RxJS's `pipe()` has a second job: called on its own (not as an `Observable`
method), it builds a **reusable** transformation out of several operators, so you can define it once and apply
it to multiple streams. RxSharp covers that with `OperatorFunction<TSource, TResult>` + `Pipe`:
it to multiple streams. ReactiveExtensionsSharp covers that with `OperatorFunction<TSource, TResult>` + `Pipe`:

<!-- snippet: pipe-csharp -->
<a id='snippet-pipe-csharp'></a>
Expand All @@ -62,7 +62,7 @@ public static void Run(Observable<int> numbersA, Observable<int> numbersB)
numbersB.Pipe(squareAndFilterEven).Subscribe(x => Console.WriteLine(x));
}
```
<sup><a href='https://github.com/hardkoded/ReactiveExtensions-Sharp/blob/main/test/RxSharp.Tests/Samples/PipeSample.cs#L11-L21' title='Snippet source file'>snippet source</a> | <a href='#snippet-pipe-csharp' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/hardkoded/ReactiveExtensions-Sharp/blob/main/test/ReactiveExtensionsSharp.Tests/Samples/PipeSample.cs#L11-L21' title='Snippet source file'>snippet source</a> | <a href='#snippet-pipe-csharp' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

There's no hand-written 9-arity `pipe(op1, op2, ..., op9)` overload set, deliberately — it exists in RxJS mainly
Expand All @@ -79,7 +79,7 @@ don't wait forever." Puppeteer's own .NET port, `puppeteer-sharp`, doesn't have
the equivalent logic there is a hand-rolled `while(true)` loop with a linked `CancellationTokenSource` and five
`catch` clauses to tell "timed out" apart from "cancelled" apart from "just retry."

RxSharp ports that exact combinator as `RetryAndRaceWithSignalAndTimer`, proven against a real launched Chrome:
ReactiveExtensionsSharp ports that exact combinator as `RetryAndRaceWithSignalAndTimer`, proven against a real launched Chrome:

<!-- snippet: retry-until-timeout-csharp -->
<a id='snippet-retry-until-timeout-csharp'></a>
Expand All @@ -92,7 +92,7 @@ public static async Task<string> FindElementOnceItRendersAsync(Func<Task<string>
.RetryAndRaceWithSignalAndTimer(TimeSpan.FromSeconds(5), cancellationToken)
.FirstValueFrom().ConfigureAwait(false);
```
<sup><a href='https://github.com/hardkoded/ReactiveExtensions-Sharp/blob/main/test/RxSharp.Tests/Samples/RetryUntilTimeoutSample.cs#L13-L21' title='Snippet source file'>snippet source</a> | <a href='#snippet-retry-until-timeout-csharp' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/hardkoded/ReactiveExtensions-Sharp/blob/main/test/ReactiveExtensionsSharp.Tests/Samples/RetryUntilTimeoutSample.cs#L13-L21' title='Snippet source file'>snippet source</a> | <a href='#snippet-retry-until-timeout-csharp' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Install
Expand Down
4 changes: 2 additions & 2 deletions RxSharp.sln → ReactiveExtensionsSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RxSharp", "src\RxSharp\RxSharp.csproj", "{2BE35833-E385-4FE6-9204-30A6BEF96DB6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveExtensionsSharp", "src\ReactiveExtensionsSharp\ReactiveExtensionsSharp.csproj", "{2BE35833-E385-4FE6-9204-30A6BEF96DB6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0C88DD14-F956-CE84-757C-A364CCF449FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RxSharp.Tests", "test\RxSharp.Tests\RxSharp.Tests.csproj", "{9A08792D-C101-474F-A5E7-9703A46FCAD5}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveExtensionsSharp.Tests", "test\ReactiveExtensionsSharp.Tests\ReactiveExtensionsSharp.Tests.csproj", "{9A08792D-C101-474F-A5E7-9703A46FCAD5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
6 changes: 3 additions & 3 deletions docfx_project/api/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# RxSharp API Documentation
# ReactiveExtensionsSharp API Documentation

This section contains the generated API reference for RxSharp, extracted from the XML documentation comments in `src/RxSharp/`.
This section contains the generated API reference for ReactiveExtensionsSharp, extracted from the XML documentation comments in `src/ReactiveExtensionsSharp/`.

Start with [`Observable<T>`](RxSharp.Observable-1.yml) and the static [`Observable`](RxSharp.Observable.yml) factory class for creation functions (`Of`, `From`, `Timer`, `Merge`, ...), then browse the operator extension methods (`Map`, `Filter`, `MergeMap`, ...) and `RxSharp.Extras` for the Puppeteer-oriented combinators (`Timeout`, `RetryAndRaceWithSignalAndTimer`, ...).
Start with [`Observable<T>`](ReactiveExtensionsSharp.Observable-1.yml) and the static [`Observable`](ReactiveExtensionsSharp.Observable.yml) factory class for creation functions (`Of`, `From`, `Timer`, `Merge`, ...), then browse the operator extension methods (`Map`, `Filter`, `MergeMap`, ...) and `ReactiveExtensionsSharp.Extras` for the Puppeteer-oriented combinators (`Timeout`, `RetryAndRaceWithSignalAndTimer`, ...).
6 changes: 3 additions & 3 deletions docfx_project/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"src": [
{
"files": [
"RxSharp.csproj"
"ReactiveExtensionsSharp.csproj"
],
"src": "../src/RxSharp"
"src": "../src/ReactiveExtensionsSharp"
}
],
"dest": "api",
Expand Down Expand Up @@ -43,7 +43,7 @@
],
"postProcessors": [],
"globalMetadata": {
"_appTitle": "RxSharp",
"_appTitle": "ReactiveExtensionsSharp",
"_appFooter": "<span>Made with love by <a href=\"https://www.hardkoded.com\" rel=\"noreferrer\">Dario Kondratiuk</a></span>"
},
"markdownEngineName": "markdig",
Expand Down
2 changes: 1 addition & 1 deletion docfx_project/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# RxSharp
# ReactiveExtensionsSharp

A .NET port of [RxJS](https://rxjs.dev/) — same operators, same semantics, same names you already know, in idiomatic C#.

Expand Down
8 changes: 4 additions & 4 deletions handoff/blog-post-material.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Blog post material: RxSharp
# Blog post material: ReactiveExtensionsSharp

Raw facts and content for a blog post about this project. No voice/tone direction here on purpose — just what
was built, why, and what's technically interesting about it. The writer should shape this into whatever the
actual post needs.

## The one-sentence pitch

RxSharp is a .NET port of RxJS — not "another Rx library," a *faithful port of RxJS specifically*: same operator
ReactiveExtensionsSharp is a .NET port of RxJS — not "another Rx library," a *faithful port of RxJS specifically*: same operator
names, same semantics, same edge-case behavior, verified test-for-test against RxJS's own upstream spec suite
(tag `7.8.2`). If you know `source$.pipe(map(x => x * 2), filter(x => x > 0))`, you already know how to use it.

Expand All @@ -33,7 +33,7 @@ Rx.NET for any of this — it hand-rolls the equivalent logic with `Cancellation
loops, and manual `catch` clauses to disambiguate "timed out" from "cancelled" from "just retry this attempt."
That code works, but it's bespoke, and every time upstream Puppeteer's JS reactive pipeline changes, someone has
to re-derive the C# equivalent from scratch by reading rxjs semantics and re-implementing them imperatively.
RxSharp exists so that translation becomes mechanical: the same rxjs operator, same name, same behavior, already
ReactiveExtensionsSharp exists so that translation becomes mechanical: the same rxjs operator, same name, same behavior, already
sitting there in C#.

### Concrete before/after: what this replaces
Expand Down Expand Up @@ -88,7 +88,7 @@ private async Task<IJSHandle> RunWithRetryAsync(
}
```

The RxSharp equivalent, proven against a real launched Chrome:
The ReactiveExtensionsSharp equivalent, proven against a real launched Chrome:

```csharp
await Observable.Defer(() => Observable.From(ClickOnceAsync()))
Expand Down
Loading