![]() |
VOOZH | about |
dotnet add package Aiursoft.AiurEventSyncer.WebExtends --version 10.0.22
NuGet\Install-Package Aiursoft.AiurEventSyncer.WebExtends -Version 10.0.22
<PackageReference Include="Aiursoft.AiurEventSyncer.WebExtends" Version="10.0.22" />
<PackageVersion Include="Aiursoft.AiurEventSyncer.WebExtends" Version="10.0.22" />Directory.Packages.props
<PackageReference Include="Aiursoft.AiurEventSyncer.WebExtends" />Project file
paket add Aiursoft.AiurEventSyncer.WebExtends --version 10.0.22
#r "nuget: Aiursoft.AiurEventSyncer.WebExtends, 10.0.22"
#:package Aiursoft.AiurEventSyncer.WebExtends@10.0.22
#addin nuget:?package=Aiursoft.AiurEventSyncer.WebExtends&version=10.0.22Install as a Cake Addin
#tool nuget:?package=Aiursoft.AiurEventSyncer.WebExtends&version=10.0.22Install as a Cake Tool
๐ MIT licensed
๐ NuGet version
A powerful, elegant distributed synchronization framework that brings Git-like version control to any .NET application. Powers Kahla real-time messaging with eventual consistency, automatic conflict resolution, and offline-first capabilities.
Imagine building a multiplayer Snake game where all players see the exact same state in real-time, or a collaborative chat app that works perfectly even when offline. AiurVersionControl makes this trivially easy by treating all data changes as immutable commits that can be synced, merged, and replayed.
Key advantages:
| Package | Description | Use Case |
|---|---|---|
| Aiursoft.AiurEventSyncer | Core event synchronization engine | Building distributed apps with auto-sync |
| Aiursoft.AiurVersionControl | Event replay & workspace reconstruction | State management with commit history |
| Aiursoft.AiurVersionControl.Crud | High-level CRUD operations | Managing collections with Add/Update/Delete |
| Aiursoft.AiurEventSyncer.WebExtends | WebSocket transport support | Real-time web applications |
| Aiursoft.AiurStore | Immutable storage abstraction | Custom database backends |
dotnet add package Aiursoft.AiurEventSyncer
Create two repositories that automatically stay in sync:
using Aiursoft.AiurEventSyncer.Models;
using Aiursoft.AiurEventSyncer.Remotes;
// Create two repositories
var clientRepo = new Repository<string>();
var serverRepo = new Repository<string>();
// Subscribe to observe commits
clientRepo.AppendCommitsHappened.Subscribe(commits =>
{
Console.WriteLine($"[Client] Received: {commits.Last().Item}");
return Task.CompletedTask;
});
serverRepo.AppendCommitsHappened.Subscribe(commits =>
{
Console.WriteLine($"[Server] Received: {commits.Last().Item}");
return Task.CompletedTask;
});
// Attach client to server with bi-directional auto-sync
await new ObjectRemote<string>(serverRepo, autoPush: true, autoPull: true)
.AttachAsync(clientRepo);
// Now they sync automatically!
clientRepo.Commit("Hello from client");
// Output: [Client] Received: Hello from client
// [Server] Received: Hello from client
serverRepo.Commit("Hello from server");
// Output: [Server] Received: Hello from server
// [Client] Received: Hello from server
// Both repositories now have identical commit history
Console.WriteLine($"Client commits: {clientRepo.Commits.Count()}"); // 2
Console.WriteLine($"Server commits: {serverRepo.Commits.Count()}"); // 2
Work with collections using familiar operations:
using Aiursoft.AiurVersionControl.Crud;
using Aiursoft.AiurVersionControl.Remotes;
// Define your model
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
}
// Create a collection repository
var library = new CollectionRepository<Book>
{
new() { Id = 1, Title = "1984" },
new() { Id = 2, Title = "Brave New World" }
};
// CRUD operations
library.Add(new Book { Id = 3, Title = "Fahrenheit 451" });
library.Patch(nameof(Book.Id), 2, nameof(Book.Title), "Updated Title");
library.Drop(nameof(Book.Id), 1);
// Sync with remote
var remoteLibrary = new CollectionRepository<Book>();
var remote = new ObjectRemoteWithWorkSpace<CollectionWorkSpace<Book>>(
library, autoPush: true, autoPull: true);
await remote.AttachAsync(remoteLibrary);
// All changes automatically sync to remoteLibrary!
Build stateful applications with full commit history:
using Aiursoft.AiurVersionControl.Models;
using Aiursoft.AiurVersionControl.Remotes;
// Your state model
public class GameState : WorkSpace
{
public int Score { get; set; }
public override object Clone() => new GameState { Score = Score };
}
// Modification actions
public class UpdateScore : IModification<GameState>
{
public int Points { get; set; }
public void Apply(GameState workspace) => workspace.Score += Points;
}
// Create controlled repository
var game = new ControlledRepository<GameState>();
// Apply changes
game.ApplyChange(new UpdateScore { Points = 10 });
game.ApplyChange(new UpdateScore { Points = 5 });
Console.WriteLine(game.WorkSpace.Score); // 15
// Sync with other players
var player2 = new ControlledRepository<GameState>();
await new ObjectRemoteWithWorkSpace<GameState>(player2, true, true)
.AttachAsync(game);
// player2.WorkSpace.Score is now 15!
Check out the /demos folder for complete working examples:
A fully functional multiplayer Snake game demonstrating:
cd demos/Aiursoft.SnakeGame
dotnet run
Windows desktop app showcasing offline-first data binding.
AiurVersionControl implements a layered architecture inspired by distributed version control systems:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your Application (CRUD, WorkSpace) โ โ High-level business logic
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ AiurVersionControl (Event Replay) โ โ State reconstruction
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ AiurEventSyncer (Sync Engine) โ โ Distributed syncing
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ AiurStore (Immutable Storage) โ โ Persistence layer
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Publish-Subscribe: Hub-and-Spoke: Peer-to-Peer:
sender โ server clientโ โ server repoโ โ repoโ
โ clientโ โ server โ โ
subscriberโ clientโ โ server repoโ โ repoโ
subscriberโ
All patterns supported with autoPush and autoPull configurations!
Instead of storing current state, AiurVersionControl stores all changes as immutable commits:
// Traditional approach
var user = dbContext.Users.Find(123);
user.Name = "Alice"; // Lost: what was the old name? when did it change?
dbContext.SaveChanges();
// AiurVersionControl approach
userRepo.Commit(new UpdateName { UserId = 123, NewName = "Alice" });
// Full history preserved! Can replay, debug, and sync.
When two repositories diverge and then sync, commits are automatically merged in deterministic order:
// Repo A and B start synchronized
await new ObjectRemote<int>(repoB, autoPush: true, autoPull: true)
.AttachAsync(repoA);
// Both add commits while temporarily disconnected
repoA.Commit(100); // A's timeline: [100]
repoB.Commit(200); // B's timeline: [200]
// When reconnected, both converge to same order
await Task.Delay(100); // Allow sync
// Both now have: [100, 200] or [200, 100] - deterministic based on commit IDs
All operations work without network connection - commits, queries, and CRUD operations are purely local:
// At home with WiFi
var todoApp = new CollectionRepository<Todo>();
todoApp.Add(new Todo { Id = 1, Task = "Buy milk" });
var remote = await ConnectToServer(todoApp);
// Airplane mode! Disconnect from server
await remote.DetachAsync();
// Everything still works - completely offline!
todoApp.Add(new Todo { Id = 2, Task = "Read book" });
todoApp.Add(new Todo { Id = 3, Task = "Exercise" });
todoApp.Patch(nameof(Todo.Id), 2, nameof(Todo.Task), "Read 50 pages");
todoApp.Drop(nameof(Todo.Id), 1);
// Query your local repository - no network needed
var allTodos = todoApp.WorkSpace.ToList();
Console.WriteLine($"Offline todos count: {allTodos.Count}"); // 2
// Back online - reconnect to server
await remote.AttachAsync(todoApp);
// All offline commits automatically sync to server!
// Server now has the same state: todos 2 and 3
This makes AiurVersionControl perfect for building offline-capable CRUD applications - notes apps, todo lists, inventory managers, or any app that needs to work without internet!
| Feature | AiurVersionControl | SignalR | Firebase | Event Store |
|---|---|---|---|---|
| Offline Support | โ Full | โ | โ ๏ธ Limited | โ |
| Automatic Sync | โ Bi-directional | โ ๏ธ Manual | โ | โ |
| Conflict Resolution | โ Automatic | โ | โ ๏ธ Manual | โ |
| Type Safety | โ Generic | โ ๏ธ Partial | โ | โ ๏ธ Partial |
| Self-Hosted | โ | โ | โ | โ |
| CRUD Abstraction | โ | โ | โ | โ |
The framework includes comprehensive test coverage demonstrating all features:
# Run all tests
dotnet test
# Run specific test suite
dotnet test --filter "FullyQualifiedName~AiurEventSyncer.Tests"
Key test scenarios:
// Implement your own storage (SQL, Redis, etc.)
public class SqlCommitStore : InOutDatabase<Commit<MyData>>
{
public override void Add(Commit<MyData> item) => /* SQL INSERT */;
public override IEnumerable<Commit<MyData>> GetAll() => /* SQL SELECT */;
// ... implement other methods
}
var repo = new Repository<MyData>(new SqlCommitStore());
// Server side
services.AddAiurEventSyncer();
app.MapWebSocketSyncer<ChatMessage>("/sync");
// Client side
var wsRemote = new WebSocketRemote<ChatMessage>(
"wss://myapp.com/sync", autoPush: true, autoPull: true);
await wsRemote.AttachAsync(localRepo);
var game = new ControlledRepository<GameState>();
// React to state changes
game.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == nameof(game.WorkSpace))
{
UI.UpdateDisplay(game.WorkSpace);
}
};
game.ApplyChange(new UpdateScore { Points = 10 });
// UI automatically updates!
# Restore dependencies
dotnet restore
# Build solution
dotnet build
# Run tests
dotnet test
# Pack NuGet packages
dotnet pack
AiurVersionControl/
โโโ src/
โ โโโ Aiursoft.AiurEventSyncer/ # Core sync engine
โ โโโ Aiursoft.AiurVersionControl/ # Version control layer
โ โโโ Aiursoft.AiurVersionControl.Crud/ # CRUD operations
โ โโโ Aiursoft.AiurEventSyncer.WebExtends/ # WebSocket support
โ โโโ Aiursoft.AiurStore/ # Storage abstraction
โโโ demos/
โ โโโ Aiursoft.SnakeGame/ # Multiplayer game demo
โ โโโ SampleWPF/ # Desktop app demo
โโโ tests/ # Comprehensive tests
We welcome contributions! This project follows a fork-and-pull-request workflow:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the file for details.
Made with ๐ฏ precision and โจ elegance by Aiursoft
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.22 | 93 | 6/10/2026 |
| 10.0.21 | 101 | 5/26/2026 |
| 10.0.20 | 108 | 5/15/2026 |
| 10.0.19 | 107 | 5/13/2026 |
| 10.0.18 | 107 | 5/4/2026 |
| 10.0.17 | 125 | 4/22/2026 |
| 10.0.16 | 132 | 4/15/2026 |
| 10.0.15 | 113 | 4/11/2026 |
| 10.0.14 | 118 | 3/27/2026 |
| 10.0.13 | 135 | 3/13/2026 |
| 10.0.12 | 128 | 3/11/2026 |
| 10.0.11 | 132 | 2/11/2026 |
| 10.0.10 | 135 | 1/28/2026 |
| 10.0.9 | 143 | 1/18/2026 |
| 10.0.8 | 135 | 1/14/2026 |
| 10.0.7 | 140 | 1/12/2026 |
| 10.0.6 | 146 | 1/1/2026 |
| 10.0.5 | 133 | 12/28/2025 |
| 10.0.3 | 474 | 12/11/2025 |
| 10.0.2 | 308 | 11/30/2025 |