Rystem 9.0.2
See the version list below for details.
dotnet add package Rystem --version 9.0.2
NuGet\Install-Package Rystem -Version 9.0.2
<PackageReference Include="Rystem" Version="9.0.2" />
paket add Rystem --version 9.0.2
#r "nuget: Rystem, 9.0.2"
// Install Rystem as a Cake Addin #addin nuget:?package=Rystem&version=9.0.2 // Install Rystem as a Cake Tool #tool nuget:?package=Rystem&version=9.0.2
What is Rystem?
Table of Contents
Discriminated Union in C#
This library introduces a AnyOf<T0, T1, ...>
class that implements discriminated unions in C#. Discriminated unions are a powerful type system feature that allows variables to store one of several predefined types, enabling type-safe and concise programming.
This library also includes integration with JSON serialization and deserialization.
What is a Discriminated Union?
A discriminated union is a type that can hold one of several predefined types at a time. It provides a way to represent and operate on data that may take different forms, ensuring type safety and improving code readability.
For example, a union can represent a value that is either an integer, a string, or a boolean:
AnyOf<int, string, bool> value;
The value
can hold an integer, a string, or a boolean, but never more than one type at a time.
AnyOf Classes
The AnyOf
class is implemented as follows:
[JsonConverter(typeof(UnionConverterFactory))]
public class AnyOf<T0, T1> : IAnyOf
{
private Wrapper[]? _wrappers;
public int Index { get; private protected set; } = -1;
public T0? AsT0 => TryGet<T0>(0);
public T1? AsT1 => TryGet<T1>(1);
private protected virtual int MaxIndex => 2;
public AnyOf(object? value)
{
AnyOfInstance(value);
}
private protected void AnyOfInstance(object? value)
{
_wrappers = new Wrapper[MaxIndex];
var check = SetWrappers(value);
if (!check)
throw new ArgumentException($"Invalid value in AnyOf. You're passing an object of type: {value?.GetType().FullName}", nameof(value));
}
private protected Q? TryGet<Q>(int index)
{
if (Index != index)
return default;
var value = _wrappers![index];
if (value?.Entity == null)
return default;
var entity = (Q)value.Entity;
return entity;
}
public bool Is<T>() => Value is T;
private protected virtual bool SetWrappers(object? value)
{
foreach (var wrapper in _wrappers!)
{
if (wrapper?.Entity != null)
wrapper.Entity = null;
}
Index = -1;
if (value == null)
return true;
else if (Set<T0>(0, value))
return true;
else if (Set<T1>(1, value))
return true;
return false;
}
private protected bool Set<T>(int index, object? value)
{
if (value is T v)
{
Index = index;
_wrappers![index] = new(v);
return true;
}
return false;
}
public T? Get<T>() => Value is T value ? value : default;
public object? Value
{
get
{
foreach (var wrapper in _wrappers!)
{
if (wrapper?.Entity != null)
return wrapper.Entity;
}
return null;
}
set
{
SetWrappers(value);
}
}
public static implicit operator AnyOf<T0, T1>(T0 entity)
=> new(entity);
public static implicit operator AnyOf<T0, T1>(T1 entity)
=> new(entity);
public override string? ToString()
=> Value?.ToString();
public override bool Equals(object? obj)
{
if (obj == null && Value == null)
return true;
var dynamicValue = ((dynamic)obj!).Value;
return Value?.Equals(dynamicValue) ?? false;
}
public override int GetHashCode()
=> RuntimeHelpers.GetHashCode(Value);
}
public interface IAnyOf
{
object? Value { get; set; }
int Index { get; }
T? Get<T>();
}
The library defines AnyOf<T0, T1, ..., Tn>
classes, supporting up to 8 types:
AnyOf<T0, T1>
AnyOf<T0, T1, T2>
- ...
AnyOf<T0, T1, ..., T7>
Each union class contains methods and properties for:
- Accessing the current type value.
- Performing safe operations based on the active type.
- Ensuring type safety during assignments.
JSON Integration
This library supports seamless JSON serialization and deserialization for discriminated unions. It uses a mechanism called "Signature" to identify the correct class during deserialization. The "Signature" is constructed based on the names of all properties that define each class in the union.
How "Signature" Works
- During deserialization, the library analyzes the properties present in the JSON.
- The "Signature" matches the property names in the JSON to a predefined signature for each class in the union.
- Once a match is found, the correct class is instantiated and populated with the data.
Usage Examples
Defining Unions
Here’s how to define and use a discriminated union:
var testClass = new CurrentTestClass
{
OneClass_String = new FirstClass { FirstProperty = "OneClass_String.FirstProperty", SecondProperty = "OneClass_String.SecondProperty" },
SecondClass_OneClass = new SecondClass
{
FirstProperty = "SecondClass_OneClass.FirstProperty",
SecondProperty = "SecondClass_OneClass.SecondProperty"
},
OneClass_string__2 = "ExampleString",
Bool_Int = 3,
Decimal_Bool = true,
OneCLass_SecondClass_Int = 42,
FirstClass_SecondClass_Int_ThirdClass = new ThirdClass
{
Stringable = "StringContent",
SecondClass = new SecondClass { FirstProperty = "Nested.FirstProperty", SecondProperty = "Nested.SecondProperty" },
ListOfSecondClasses = new List<SecondClass>
{
new SecondClass { FirstProperty = "List.Item1.FirstProperty", SecondProperty = "List.Item1.SecondProperty" },
new SecondClass { FirstProperty = "List.Item2.FirstProperty", SecondProperty = "List.Item2.SecondProperty" }
},
DictionaryItems = new Dictionary<string, string>
{
{ "Key1", "Value1" },
{ "Key2", "Value2" }
},
ArrayOfStrings = new[] { "ArrayElement1", "ArrayElement2" },
ObjectDictionary = new Dictionary<string, SecondClass>
{
{ "DictKey1", new SecondClass { FirstProperty = "Dict.Value1.FirstProperty", SecondProperty = "Dict.Value1.SecondProperty" } },
{ "DictKey2", new SecondClass { FirstProperty = "Dict.Value2.FirstProperty", SecondProperty = "Dict.Value2.SecondProperty" } }
}
}
};
Notice that the implicit conversion allows you to directly assign values of compatible types (e.g., FirstClass
and SecondClass
) without explicitly constructing a AnyOf<T0, T1>
instance.
Serializing and Deserializing JSON
Here is an example that demonstrates JSON integration:
Classes
public class FirstClass {
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
}
public class SecondClass {
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
}
public class ThirdClass {
public string Stringable { get; set; }
public SecondClass SecondClass { get; set; }
public List<SecondClass> ListOfSecondClasses { get; set; }
public Dictionary<string, string> DictionaryItems { get; set; }
public string[] ArrayOfStrings { get; set; }
public Dictionary<string, SecondClass> ObjectDictionary { get; set; }
}
Example JSON
{
"OneClass_String": {
"FirstProperty": "OneClass_String.FirstProperty",
"SecondProperty": "OneClass_String.SecondProperty"
},
"SecondClass_OneClass": {
"FirstProperty": "SecondClass_OneClass.FirstProperty",
"SecondProperty": "SecondClass_OneClass.SecondProperty"
}
}
Deserialization
var json = "{\"OneClass_String\":{\"FirstProperty\":\"OneClass_String.FirstProperty\",\"SecondProperty\":\"OneClass_String.SecondProperty\"},\"SecondClass_OneClass\":{\"FirstProperty\":\"SecondClass_OneClass.FirstProperty\",\"SecondProperty\":\"SecondClass_OneClass.SecondProperty\"}}";
var deserialized = json.FromJson<CurrentTestClass>();
Console.WriteLine(deserialized.OneClass_String.AsT0.FirstProperty); // Outputs: OneClass_String.FirstProperty
Serialization
var serializedJson = testClass.ToJson();
Console.WriteLine(serializedJson); // Outputs the JSON representation of testClass
Signature-Based Deserialization
This library employs a "signature method" for deserialization, allowing it to determine the correct type based on property names. Here’s an example demonstrating how it works:
[Fact]
public void DeserializationSignature()
{
var testClass = new SignatureTestClass
{
Test = new SignatureClassTwo
{
FirstProperty = "FirstProperty",
SecondProperty = "SecondProperty"
}
};
var json = testClass.ToJson();
var deserialized = json.FromJson<SignatureTestClass>();
// It's correct that the class during deserialization is not the same as the original one,
// because the deserialization is based on the name of the properties (signature method),
// and both classes are the same in terms of properties.
Assert.True(deserialized.Test!.Is<SignatureClassOne>());
Assert.False(deserialized.Test!.Is<SignatureClassTwo>());
}
private sealed class SignatureTestClass
{
public AnyOf<SignatureClassOne, SignatureClassTwo>? Test { get; set; }
}
private sealed class SignatureClassOne
{
public string? FirstProperty { get; set; }
public string? SecondProperty { get; set; }
}
private sealed class SignatureClassTwo
{
public string? FirstProperty { get; set; }
public string? SecondProperty { get; set; }
}
This approach ensures flexibility and correctness when deserializing objects that share similar property structures, with a think to performance.
Benefits of Using It
- Type Safety: Ensures only predefined types are used.
- JSON Support: Automatically identifies and deserializes the correct type using "Signature".
- Code Clarity: Reduces boilerplate code for type management and error handling.
Extension methods
Stopwatch
You can monitor the time spent on an action, task or in a method. Some examples from Unit test.
var started = Stopwatch.Start();
//do something
await Task.Delay(2000);
var result = started.Stop();
or
var result = await Stopwatch.MonitorAsync(async () =>
{
await Task.Delay(2000);
});
or with a return value
var result = await Stopwatch.MonitorAsync(async () =>
{
await Task.Delay(2000);
return 3;
});
Linq expression serializer
Usually a linq expression is not serializable as string. With this method you can serialize your expression with some limits. Only primitives are allowed in the expression body. An example from Unit test.
Expression<Func<MakeIt, bool>> expression = ƒ => ƒ.X == q && ƒ.Samules.Any(x => x == k) && ƒ.Sol && (ƒ.X.Contains(q) || ƒ.Sol.Equals(IsOk)) && (ƒ.E == id | ƒ.Id == V) && (ƒ.Type == MakeType.Yes || ƒ.Type == qq);
var serialized = expression.Serialize();
with result
"ƒ => ((((((ƒ.X == \"dasda\") AndAlso ƒ.Samules.Any(x => (x == \"ccccde\"))) AndAlso ƒ.Sol) AndAlso (ƒ.X.Contains(\"dasda\") OrElse ƒ.Sol.Equals(True))) AndAlso ((ƒ.E == Guid.Parse(\"bf46510b-b7e6-4ba2-88da-cef208aa81f2\")) Or (ƒ.Id == 32))) AndAlso ((ƒ.Type == 1) OrElse (ƒ.Type == 2)))"
with deserialization
var newExpression = expressionAsString.Deserialize<MakeIt, bool>();
and usage, for instance, with Linq
var result = makes.Where(newExpression.Compile()).ToList();
you can deserialize and compile at the same time with
var newExpression = expressionAsString.DeserializeAndCompile<MakeIt, bool>();
you can deserialize as dynamic and use the linq dynamic methods
Expression<Func<MakeIt, int>> expression = x => x.Id;
string value = expression.Serialize();
LambdaExpression newLambda = value.DeserializeAsDynamic<MakeIt>();
var got = makes.AsQueryable();
var cut = got.OrderByDescending(newLambda).ThenByDescending(newLambda).ToList();
please see the unit test here to understand better how it works You may deal with return type of your lambda expression:
LambdaExpression newLambda = value.DeserializeAsDynamic<MakeIt>();
newLambda = newLambda.ChangeReturnType<bool>();
or
newLambda = newLambda.ChangeReturnType(typeof(bool));
Reflection helper
Name of calling class
You can find the name of the calling class from your method, with deep = 1 the calling class of your method, with deep = 2 the calling class that calls the class that calls your method, and so on, with fullName set to true you obtain the complete name of the discovered class.
ReflectionHelper.NameOfCallingClass(deep, fullName);
Extensions for Type class
You can get the properties, fields and constructors for your class (and singleton them to save time during new requests)
Type.FetchProperties();
Type.FecthConstructors();
Type.FetchFields();
You can check if a Type is a son or a father or both of other type (in the example Zalo and Folli are Sulo). You may find more information in unit test here
Zalo zalo = new();
Zalo zalo2 = new();
Folli folli = new();
Sulo sulo = new();
object quo = new();
int x = 2;
decimal y = 3;
Assert.True(zalo.IsTheSameTypeOrASon(sulo));
Assert.True(folli.IsTheSameTypeOrASon(sulo));
Assert.True(zalo.IsTheSameTypeOrASon(zalo2));
Assert.True(zalo.IsTheSameTypeOrASon(quo));
Assert.False(sulo.IsTheSameTypeOrASon(zalo));
Assert.True(sulo.IsTheSameTypeOrAParent(zalo));
Assert.False(y.IsTheSameTypeOrAParent(x));
Mock a Type
If you need to create a type over an abstract class or interface you may use the mocking system of Rystem. For example, if you have an abstract class like this one down below.
public abstract class Alzio
{
private protected string X { get; }
public string O => X;
public string A { get; set; }
public Alzio(string x)
{
X = x;
}
}
you can create an instace of it or simply mock it with
var mocked = typeof(Alzio).CreateInstance("AAA") as Alzio;
mocked.A = "rrrr";
and you can use the class like a real class. You also may do it with
Alzio alzio = null!;
var mocked = alzio.CreateInstance("AAA");
mocked.A = "rrrr";
or
Mocking.CreateInstance<Alzio>("AAA");
you may see "AAA" as argument for your constructor in abstract class.
Check nullability for properties, fields and parameters.
Following an example from unit test.
private sealed class InModel
{
public string? A { get; set; }
public string B { get; set; }
public string? C;
public string D;
public InModel(string? b, string c)
{
A = b;
B = c;
}
public void SetSomething(string? b, string c)
{
A = b;
B = c;
}
}
[Fact]
public void Test1()
{
var type = typeof(InModel);
var constructorParameters = type.GetConstructors().First().GetParameters().ToList();
Assert.True(constructorParameters[0].IsNullable());
Assert.False(constructorParameters[1].IsNullable());
var methodParameters = type.GetMethod(nameof(InModel.SetSomething)).GetParameters().ToList();
Assert.True(methodParameters[0].IsNullable());
Assert.False(methodParameters[1].IsNullable());
var properties = type.GetProperties().ToList();
Assert.True(properties[0].IsNullable());
Assert.False(properties[1].IsNullable());
var fields = type.GetFields().ToList();
Assert.True(fields[0].IsNullable());
Assert.False(fields[1].IsNullable());
}
Text extensions
You may convert as fast as possible byte[] to string or stream to byte[] or byte[] to stream or stream to string or string to stream. For example, string to byte array and viceversa.
string olfa = "daskemnlandxioasndslam dasmdpoasmdnasndaslkdmlasmv asmdsa";
var bytes = olfa.ToByteArray();
string value = bytes.ConvertToString();
For example, string to stream and viceversa.
string olfa = "daskemnlandxioasndslam dasmdpoasmdnasndaslkdmlasmv asmdsa";
var stream = olfa.ToStream();
string value = stream.ConvertToString();
You may read a string with break lines as an enumerable of string
string olfa = "daskemnlandxioasndslam\ndasmdpoasmdnasndaslkdmlasmv\nasmdsa";
var stream = olfa.ToStream();
var strings = new List<string>();
await foreach (var x in stream.ReadLinesAsync())
{
strings.Add(x);
}
A simple method to make uppercase the first character.
string olfa = "dasda";
var olfa2 = olfa.ToUpperCaseFirst();
A simple method to check if a char is contained at least X times.
string value = "abcderfa";
bool containsAtLeastTwoAChar = value.ContainsAtLeast(2, 'a');
Character separated-value (CSV)
Transform any kind of IEnumerable data in a CSV string.
string value = _models.ToCsv();
Minimization of a model (based on CSV concept)
It's a brand new idea to serialize any kind of objects (with lesser occupied space of json), the idea comes from Command separated-value standard. To serialize
string value = _models.ToMinimize();
To deserialize (for instance in a List of a class named CsvModel)
value.FromMinimization<List<CsvModel>>();
Extensions for json
I don't know if you are fed up to write JsonSerializer.Serialize, I do, and so, you may use the extension method to serialize faster. To serialize
var text = value.ToJson();
To deserialize in a class (for instance a class named User)
var value = text.FromJson<User>();
Extensions for Task
I don't know if you still are fed up to write .ConfigureAwait(false) to eliminate the context waiting for a task. I do. Why should I set the configure await to false? To set configure await to false
await {your async task}.NoContext();
Instead, to get the result as synchronous result but with a configure await set to false.
{your async task}.ToResult();
You may change the behavior of your NoContext() or ToResult(), setting (in the bootstrap of your application for example)
RystemTask.WaitYourStartingThread = true;
When do I need a true? In windows application for example you have to return after a button clicked to the same thread that started the request.
TaskManager
When you need to run a list of tasks concurrently you may use this static method.
In the next example with TaskManager.WhenAll you may run a method ExecuteAsync {times} times with {concurrentTasks} times in concurrency, and running them when a time slot is free. For example if you run this function with 8 times and 3 concurrentsTasks and true in runEverytimeASlotIsFree You will have this behavior: first 3 tasks starts and since the fourth the implementation waits the end of one of the 3 started before. As soon as one of the 3 started is finished the implementation starts to run the fourth.
var bag = new ConcurrentBag<int>();
await TaskManager.WhenAll(ExecuteAsync, times, concurrentTasks, runEverytimeASlotIsFree).NoContext();
Assert.Equal(times, bag.Count);
async Task ExecuteAsync(int i, CancellationToken cancellationToken)
{
await Task.Delay(i * 20, cancellationToken).NoContext();
bag.Add(i);
}
You may run a {atLeast} times of tasks and stopping to wait the remaining tasks with TaskManager.WhenAtLeast
var bag = new ConcurrentBag<int>();
await TaskManager.WhenAtLeast(ExecuteAsync, times, atLeast, concurrentTasks).NoContext();
Assert.True(bag.Count < times);
Assert.True(bag.Count >= atLeast);
async Task ExecuteAsync(int i, CancellationToken cancellationToken)
{
await Task.Delay(i * 20, cancellationToken).NoContext();
bag.Add(i);
}
Concurrency
ConcurrentList
You can use the ConcurrentList implementation to have the List behavior with lock operations.
var items = new ConcurrentList<ItemClass>();
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. |
-
net9.0
- Fare (>= 2.2.1)
- System.Interactive (>= 6.0.1)
- System.Interactive.Async (>= 6.0.1)
- System.Linq.Dynamic.Core (>= 1.5.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Rystem:
Package | Downloads |
---|---|
Rystem.DependencyInjection
Rystem is a open-source framework to improve the System namespace in .Net |
|
Rystem.Authentication.Social.Blazor
Rystem.Authentication.Social helps you to integrate with new .Net Identity system and social logins. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
9.0.3 | 0 | 12/22/2024 |
9.0.2 | 67 | 12/21/2024 |
9.0.1 | 587 | 12/21/2024 |
9.0.0 | 173,268 | 11/16/2024 |
9.0.0-rc.1 | 237 | 10/18/2024 |
6.2.0 | 219,691 | 10/9/2024 |
6.1.2 | 106 | 10/9/2024 |
6.1.1 | 399 | 10/9/2024 |
6.1.0 | 48,271 | 9/29/2024 |
6.0.24 | 654 | 9/11/2024 |
6.0.23 | 340,606 | 7/18/2024 |
6.0.22 | 114 | 7/18/2024 |
6.0.21 | 502 | 6/18/2024 |
6.0.20 | 728,103 | 6/16/2024 |
6.0.19 | 31,040 | 6/14/2024 |
6.0.18 | 460 | 6/14/2024 |
6.0.17 | 462 | 6/14/2024 |
6.0.16 | 50,332 | 6/10/2024 |
6.0.15 | 460 | 6/9/2024 |
6.0.14 | 94,820 | 5/24/2024 |
6.0.13 | 470 | 5/23/2024 |
6.0.12 | 385 | 5/23/2024 |
6.0.11 | 477 | 5/20/2024 |
6.0.9 | 498 | 5/19/2024 |
6.0.7 | 482 | 5/18/2024 |
6.0.6 | 434 | 5/10/2024 |
6.0.5 | 385 | 5/10/2024 |
6.0.4 | 550,399 | 4/3/2024 |
6.0.3 | 1,890 | 3/25/2024 |
6.0.2 | 380,527 | 3/11/2024 |
6.0.1 | 51,106 | 3/8/2024 |
6.0.0 | 1,171,488 | 11/21/2023 |
6.0.0-rc.6 | 190 | 10/25/2023 |
6.0.0-rc.5 | 140 | 10/25/2023 |
6.0.0-rc.4 | 109 | 10/23/2023 |
6.0.0-rc.3 | 113 | 10/19/2023 |
6.0.0-rc.2 | 179 | 10/18/2023 |
6.0.0-rc.1 | 205 | 10/16/2023 |
5.0.20 | 640,772 | 9/25/2023 |
5.0.19 | 2,912 | 9/10/2023 |
5.0.18 | 2,215 | 9/6/2023 |
5.0.17 | 2,040 | 9/6/2023 |
5.0.16 | 2,128 | 9/5/2023 |
5.0.15 | 2,091 | 9/5/2023 |
5.0.14 | 2,095 | 9/5/2023 |
5.0.13 | 2,182 | 9/1/2023 |
5.0.12 | 2,035 | 8/31/2023 |
5.0.11 | 2,034 | 8/30/2023 |
5.0.10 | 2,061 | 8/29/2023 |
5.0.9 | 2,107 | 8/24/2023 |
5.0.8 | 2,110 | 8/24/2023 |
5.0.7 | 451,592 | 8/23/2023 |
5.0.6 | 19,617 | 8/21/2023 |
5.0.5 | 6,281 | 8/21/2023 |
5.0.4 | 2,165 | 8/16/2023 |
5.0.3 | 214,869 | 8/2/2023 |
5.0.2 | 3,914 | 8/2/2023 |
5.0.1 | 13,713 | 8/1/2023 |
5.0.0 | 14,021 | 7/31/2023 |
4.1.26 | 143,209 | 7/20/2023 |
4.1.25 | 24,688 | 7/16/2023 |
4.1.24 | 402,172 | 6/13/2023 |
4.1.23 | 47,793 | 6/13/2023 |
4.1.22 | 131,791 | 5/30/2023 |
4.1.21 | 57,885 | 5/20/2023 |
4.1.20 | 407,155 | 4/19/2023 |
4.1.19 | 97,849 | 3/20/2023 |
4.1.18 | 2,498 | 3/20/2023 |
4.1.17 | 2,791 | 3/16/2023 |
4.1.16 | 2,543 | 3/16/2023 |
4.1.15 | 2,939 | 3/15/2023 |
4.1.14 | 12,353 | 3/9/2023 |
4.1.13 | 2,658 | 3/7/2023 |
4.1.12 | 3,024 | 2/9/2023 |
4.1.11 | 2,667 | 1/26/2023 |
4.1.10 | 2,915 | 1/22/2023 |
4.1.9 | 2,524 | 1/20/2023 |
4.1.8 | 2,721 | 1/18/2023 |
4.1.7 | 2,771 | 1/18/2023 |
4.1.6 | 2,618 | 1/17/2023 |
4.1.5 | 368 | 1/15/2023 |
4.1.4 | 370 | 1/15/2023 |
4.1.3 | 362 | 1/15/2023 |
4.1.2 | 379 | 1/15/2023 |
4.1.1 | 2,611 | 1/4/2023 |
4.1.0 | 2,799 | 12/30/2022 |
4.0.102 | 2,803 | 12/21/2022 |
4.0.101 | 803 | 12/20/2022 |
4.0.100 | 359 | 12/19/2022 |
4.0.99 | 377 | 12/18/2022 |
4.0.98 | 6,457 | 12/7/2022 |
4.0.97 | 370 | 12/7/2022 |
4.0.96 | 365 | 12/7/2022 |
4.0.95 | 365 | 12/7/2022 |
4.0.94 | 4,358 | 12/1/2022 |
4.0.93 | 398 | 12/1/2022 |
4.0.92 | 3,349 | 11/29/2022 |
4.0.91 | 8,576 | 11/22/2022 |
4.0.90 | 4,584 | 11/18/2022 |
4.0.89 | 4,313 | 11/18/2022 |
4.0.88 | 4,462 | 11/15/2022 |
4.0.87 | 4,400 | 11/14/2022 |
4.0.86 | 4,531 | 11/13/2022 |
4.0.85 | 4,825 | 11/2/2022 |
4.0.84 | 4,539 | 11/2/2022 |
4.0.83 | 4,696 | 10/29/2022 |
4.0.82 | 4,799 | 10/29/2022 |
4.0.81 | 1,739 | 10/29/2022 |
4.0.80 | 14,119 | 10/16/2022 |
4.0.79 | 4,314 | 10/9/2022 |
4.0.78 | 459 | 10/9/2022 |
4.0.77 | 451 | 10/8/2022 |
4.0.76 | 21,499 | 10/5/2022 |
4.0.75 | 43,741 | 9/20/2022 |
4.0.74 | 4,875 | 9/20/2022 |
4.0.73 | 4,499 | 9/20/2022 |
4.0.72 | 684 | 9/20/2022 |
4.0.71 | 3,976 | 9/20/2022 |
4.0.70 | 557 | 9/14/2022 |
4.0.69 | 526 | 9/14/2022 |
4.0.68 | 4,729 | 9/13/2022 |
4.0.67 | 493 | 9/1/2022 |
4.0.66 | 4,484 | 8/19/2022 |
4.0.65 | 471 | 8/19/2022 |
4.0.64 | 483 | 8/19/2022 |
4.0.63 | 506 | 8/16/2022 |
4.0.62 | 519 | 8/16/2022 |
4.0.61 | 462 | 8/16/2022 |
4.0.60 | 497 | 8/14/2022 |
4.0.59 | 516 | 8/13/2022 |
4.0.58 | 488 | 8/13/2022 |
4.0.57 | 519 | 8/13/2022 |
4.0.56 | 493 | 8/13/2022 |
4.0.54 | 518 | 8/13/2022 |
4.0.53 | 509 | 8/12/2022 |
4.0.52 | 478 | 8/11/2022 |
4.0.51 | 503 | 8/9/2022 |
4.0.50 | 513 | 8/8/2022 |
4.0.49 | 496 | 8/8/2022 |
4.0.48 | 493 | 8/8/2022 |
4.0.47 | 499 | 8/6/2022 |
4.0.46 | 480 | 8/6/2022 |
4.0.45 | 491 | 8/5/2022 |
4.0.44 | 475 | 8/5/2022 |
4.0.43 | 499 | 8/4/2022 |
4.0.42 | 506 | 8/4/2022 |
4.0.41 | 503 | 8/4/2022 |
4.0.40 | 491 | 8/3/2022 |
4.0.39 | 500 | 8/3/2022 |
4.0.38 | 4,837 | 7/30/2022 |
4.0.37 | 4,547 | 7/29/2022 |
4.0.36 | 4,012 | 7/29/2022 |
4.0.35 | 496 | 7/29/2022 |
4.0.34 | 793 | 7/29/2022 |
4.0.33 | 4,753 | 7/29/2022 |
4.0.32 | 4,555 | 7/29/2022 |
4.0.31 | 505 | 7/29/2022 |
4.0.30 | 509 | 7/29/2022 |
4.0.29 | 4,625 | 7/27/2022 |
4.0.28 | 550 | 7/27/2022 |
4.0.26 | 4,528 | 7/27/2022 |
4.0.25 | 4,170 | 7/26/2022 |
4.0.24 | 4,674 | 7/25/2022 |
4.0.23 | 4,583 | 7/25/2022 |
4.0.22 | 4,477 | 7/22/2022 |
4.0.21 | 4,525 | 7/19/2022 |
4.0.20 | 4,564 | 7/19/2022 |
4.0.19 | 518 | 7/19/2022 |
4.0.18 | 4,584 | 7/19/2022 |
4.0.17 | 4,595 | 7/19/2022 |
4.0.16 | 4,661 | 7/18/2022 |
4.0.15 | 4,517 | 7/18/2022 |
4.0.14 | 527 | 7/18/2022 |
4.0.13 | 4,587 | 7/18/2022 |
4.0.12 | 4,095 | 7/17/2022 |
4.0.11 | 1,493 | 7/17/2022 |
4.0.10 | 532 | 7/17/2022 |
4.0.9 | 1,179 | 7/17/2022 |
4.0.8 | 13,814 | 7/15/2022 |
4.0.7 | 5,780 | 7/15/2022 |
4.0.6 | 6,518 | 7/8/2022 |
4.0.5 | 6,227 | 7/6/2022 |
4.0.4 | 11,416 | 7/1/2022 |
4.0.3 | 1,340 | 6/29/2022 |
4.0.1 | 1,825 | 6/29/2022 |
4.0.0 | 1,317 | 6/27/2022 |
3.0.2 | 1,173 | 5/5/2022 |
3.0.1 | 1,450 | 11/30/2021 |
3.0.0 | 1,402 | 11/12/2021 |
2.0.12 | 2,466 | 10/14/2021 |
2.0.11 | 1,545 | 10/14/2021 |
2.0.8 | 2,347 | 10/6/2021 |
2.0.7 | 1,730 | 10/2/2021 |
2.0.6 | 476 | 9/30/2021 |
2.0.5 | 431 | 9/30/2021 |
2.0.4 | 1,545 | 9/29/2021 |
2.0.3 | 1,648 | 9/28/2021 |
2.0.2 | 1,356 | 9/26/2021 |
2.0.1 | 1,233 | 9/24/2021 |
2.0.0 | 1,312 | 9/22/2021 |
1.4.0 | 1,397 | 9/14/2021 |
1.3.18 | 1,307 | 9/10/2021 |
1.3.17 | 1,122 | 9/9/2021 |
1.3.16 | 1,438 | 8/28/2021 |
1.3.15 | 1,197 | 8/26/2021 |
1.3.14 | 1,173 | 8/19/2021 |
1.3.13 | 1,184 | 8/13/2021 |
1.3.12 | 1,236 | 8/12/2021 |
1.3.11 | 508 | 7/29/2021 |
1.3.10 | 1,282 | 7/27/2021 |
1.3.9 | 1,235 | 7/27/2021 |
1.3.1 | 1,310 | 7/25/2021 |
1.3.0 | 1,973 | 7/17/2021 |
1.2.4 | 1,256 | 7/14/2021 |
1.2.3 | 458 | 7/14/2021 |
1.2.2 | 434 | 7/14/2021 |
1.2.1 | 1,305 | 7/14/2021 |
1.2.0 | 2,562 | 7/13/2021 |
1.1.0 | 660 | 4/14/2021 |
1.0.3 | 423 | 4/13/2021 |
1.0.2 | 426 | 4/13/2021 |
1.0.1 | 426 | 4/13/2021 |
1.0.0 | 442 | 4/12/2021 |