rm.Extensions
3.0.2
.NET 6.0
This package targets .NET 6.0. The package is compatible with this framework or higher.
.NET Standard 2.0
This package targets .NET Standard 2.0. The package is compatible with this framework or higher.
This package has a SemVer 2.0.0 package version: 3.0.2+0f900fb3b4c4ffad1ab0f295520a137cd2e61dfd.
There is a newer version of this package available.
See the version list below for details.
See the version list below for details.
dotnet add package rm.Extensions --version 3.0.2
NuGet\Install-Package rm.Extensions -Version 3.0.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="rm.Extensions" Version="3.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add rm.Extensions --version 3.0.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: rm.Extensions, 3.0.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install rm.Extensions as a Cake Addin #addin nuget:?package=rm.Extensions&version=3.0.2 // Install rm.Extensions as a Cake Tool #tool nuget:?package=rm.Extensions&version=3.0.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
csharp-extensions
A collection of utility C# extension methods.
main: dev:
nuget:
Install-Package rm.Extensions
string extensions:
var s = "";
if (s.IsNullOrEmpty()) { /**/ }
if (s.IsNullOrWhiteSpace()) { /**/ }
// some string that could be null/empty/whitespace
string s = null; // or "value"
string text = "default";
if (!s.IsNullOrWhiteSpace()) text = s.Trim();
// fluent code by avoiding comparison
string text = s.OrEmpty().Trim(); // "" when s is null/empty/whitespace
string text = s.Or("default").Trim(); // "default" when s is null/empty/whitespace
// or using null-conditional and null-coalesce operators
string text = s.NullIfEmpty()?.Trim() ?? ""
string text = s.NullIfWhiteSpace()?.Trim() ?? "default"
// html-en/decode, url-en/decode
string s = "";
string htmlencoded = s.HtmlEncode();
string htmldecoded = s.HtmlDecode();
string urlencoded = s.UrlEncode();
string urldecoded = s.UrlDecode();
// "".Format() instead of string.Format()
"{0} is a {1}".Format("this", "test");
// parameter index is optional
"{} is a {}".Format("this", "test");
"{} is a {1}".Format("this", "test"); // mixing is ok
// parameter meta is allowed
"The name is {0}. {first} {last}.".Format(lastName, firstName, lastName); // adding arg meta is ok
"The name is {last}. {first} {last}.".Format(lastName, firstName); // bit intelligent about repeating arg meta
// bool try-parse string with default value
bool b = "".ToBool(defaultValue: true);
// b: true
// munge a password, up to two chars
string[] munged = "pass".Munge().ToArray();
// munged: { "pa$$", "pa55", "p@ss", "p@$$", "p@55" }
string[] munged = "ai".Munge().ToArray();
// munged: { "a1", "a!", "@i", "@1", "@!" }
string[] munged = "pw".Munge().ToArray();
// munged: { "puu", "p2u" }
// unmunge a password
string[] unmunged = "h@x0r".Unmunge().ToArray();
// unmunged: { "haxor" }
string[] unmunged = "puu".Unmunge().ToArray();
string[] unmunged = "p2u".Unmunge().ToArray();
// unmunged: { "pw" }
// scrabble characters of word (like the game)
var word = "on";
var scrabbled = word.Scrabble();
// scrabbled: { "o", "on", "n", "no" }
// parse a string in UTC format as DateTime
DateTime date = "2013-04-01T03:42:14-04:00".ParseAsUtc();
// date: 4/1/2013 7:42:14 AM, Kind: Utc
// convert a string to title case
string result = "war and peace".ToTitleCase();
// result: "War And Peace"
// split a csv string
string[] result = "a,b;c|d".SplitCsv().ToArray();
// result: [ "a", "b", "c", "d" ]
// substring from start
string result = "this is a test".SubstringFromStart(4);
// result: "this"
// substring till end
string result = "this is a test".SubstringTillEnd(4);
// result: "test"
// substring by specifying start index and end index
string result = "this".SubstringByIndex(1, 3);
// result: "hi"
ThrowIf extensions:
public void SomeMethod(object obj1, object obj2)
{
// throws ArgumentNullException if object is null
obj1.ThrowIfArgumentNull("obj1");
obj2.ThrowIfArgumentNull("obj2");
// OR
new[] { obj1, obj2 }.ThrowIfAnyArgumentNull();
// ...
object obj = DoSomething();
// throws NullReferenceException if object is null
obj.ThrowIfNull("obj");
// OR
new[] { obj1, obj2 }.ThrowIfAnyNull();
}
public void SomeMethod(string s1, string s2)
{
// throws ArgumentNullException or EmptyException if string is null or empty
s1.ThrowIfNullOrEmptyArgument("s1"); // or s1.ThrowIfNullOrWhiteSpaceArgument("s1")
s2.ThrowIfNullOrEmptyArgument("s2");
// OR
new[] { s1, s2 }.ThrowIfNullOrEmptyArgument();
// ...
string s = DoSomething();
// throws NullReferenceException or EmptyException if string is null or empty.
s.ThrowIfNullOrEmpty("s"); // or s1.ThrowIfNullOrWhiteSpace("s")
}
DateTime extensions:
// gives date in UTC format string
string dateUtc = date.ToUtcFormatString();
// dateUtc: "1994-11-05T13:15:30.000Z"
// gives min date that can be inserted in sql database without exception (SqlDateTime.MinValue)
DateTime date = new DateTime().ToSqlDateTimeMinUtc();
// date: 1/1/1753 12:00:00 AM
// date read from db or parsed from string has its Kind as Unspecified.
// specifying its kind as UTC is needed if date is expected to be UTC.
// ToUniversalTime() assumes that the kind is local while converting it and is undesirable.
DateTime date = DateTime.Parse("4/1/2014 12:00:00 AM").AsUtcKind();
// date: 4/1/2014 12:00:00 AM, Kind: Utc
IEnumerable extensions:
// creates chunks of given collection of specified size
IEnumerable<IEnumerable<int>> chunks = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }.Chunk(3);
// chunks: { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10 } }
// if a collection is null or empty
var collection = new[] { 1, 2 };
if (collection.IsNullOrEmpty()) { /**/ }
// split a collection into n parts
var collection = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<IEnumerable<int>> splits = collection.Split(3);
// splits: { { 1, 4, 7, 10 }, { 2, 5, 8 }, { 3, 6, 9 } }
// if a collection is sorted
var collection1 = new[] { 1, 2, 3, 4 };
bool isSorted1 = collection1.IsSorted();
var collection2 = new[] { 7, 5, 3 };
bool isSorted2 = collection2.IsSorted();
// isSorted1, isSorted2: true
// Double(), DoubleOrDefault() as Single(), SingleOrDefault()
IEnumerable<int> doubleitems = new[] { 1, 2 }.Double();
// doubleitems: { 1, 2 }
IEnumerable<int> doubleitems = new[] { 1, 2, 3 }.Double(x => x > 1);
// doubleitems: { 2, 3 }
IEnumerable<int> doubleordefaultitems = new int[0].DoubleOrDefault();
// doubleordefaultitems: null
IEnumerable<int> doubleordefaultitems = new[] { 1, 2, 3 }.DoubleOrDefault(x => x > 1);
// doubleordefaultitems: { 2, 3 }
// throws InvalidOperationException
new[] { 1 }.Double();
new[] { 1 }.Double(x => x > 0);
new[] { 1 }.DoubleOrDefault();
new[] { 1 }.DoubleOrDefault(x => x > 0);
// shuffle collection in O(n) time (Fisher-Yates shuffle, revised by Knuth)
var shuffled = new[] { 0, 1, 2, 3 }.Shuffle();
// shuffled: { 2, 3, 1, 0 }
// slice a collection as Python (http://docs.python.org/2/tutorial/introduction.html#strings)
var a = new[] { 0, 1, 2, 3, 4 }
var slice = a.Slice(step: 2);
// slice: { 0, 2, 4 }
a.Slice(start, end); // items start through end-1
a.Slice(start); // items start through the rest of the array
a.Slice(0, end); // items from the beginning through end-1
a.Slice(); // a copy of the whole array
a.Slice(start, end, step); // start through not past end, by step
a.Slice(-1); // last item in the array
a.Slice(-2); // last two items in the array
a.Slice(-3, -2); // third last item in the array
a.Slice(0, -2); // everything except the last two items
a.Slice(step: -1); // copy with array reversed
// help
a.Slice(end: 2) // 1st 2
a.Slice(2) // except 1st 2
a.Slice(-2) // last 2
a.Slice(0, -2) // except last 2
a.Slice(1, 1 + 1) // 2nd char
a.Slice(-2, -2 + 1) // 2nd last char
// scrabble a list of words (like the game)
var words = { "this", "test" };
var scrabbled = words.Scrabble();
// scrabbled: { "this", "thistest", "test", "testthis" }
// check an enumerable's count efficiently
if (enumerable.Count() == 2) { ... } // inefficient for large enumerable
if (enumerable.HasCount(2)) { ... }
if (enumerable.HasCountOfAtLeast(2)) { ... } // count >= 2
// get permutations or combinations for particular r
var result = new[] { 1, 2 }.Permutation(2);
// result: { { 1, 2 }, { 2, 1 } }
var result = new[] { 1, 2 }.Combination(2);
// result: { { 1, 2 } }
// if a collection is empty instead of !collection.Any()
var collection = new[] { 1, 2 };
if (collection.IsEmpty()) { /**/ }
// get top n or bottom n efficiently (using min/max-heap)
IEnumerable<int> top_n = { 2, 3, 1, 4, 5 }.Top(3);
IEnumerable<int> bottom_n = { 2, 3, 1, 4, 5 }.Bottom(3);
// top_n: { 3, 5, 4 }
// bottom_n: { 3, 1, 2 }
// get top n or bottom n from IEnumerable
IEnumerable<Person> top_n = persons.Top(3);
IEnumerable<Person> bottom_n = persons.Bottom(3);
// get top n or bottom n by using a key selector or/and comparer
IEnumerable<Person> oldest_3 = persons.Top(3, x => x.Age);
IEnumerable<Person> youngest_3 = persons.Bottom(3, x => x.Age);
IEnumerable<Person> oldest_3 = persons.Top(3, personByAgeComparer);
IEnumerable<Person> youngest_3 = persons.Bottom(3, personByAgeComparer);
IEnumerable<Person> oldest_3 = persons.Top(3, x => x.Age, ageComparer);
IEnumerable<Person> youngest_3 = persons.Bottom(3, x => x.Age, ageComparer);
// source.Except(second, comparer) linqified instead of a full-blown class for comparer
source.ExceptBy(second, x => x.Member);
// same for source.Distinct(comparer)
source.DistinctBy(x => x.Member);
// same for source.OrderBy(keySelector, comparer)
source.OrderBy(x => x.Property,
(p1, p2) => p1.CompareTo(p2) // where p1, p2 are of same type as x.Property
);
// source.OrEmpty() or source.EmptyIfDefault() to avoid a null check
foreach (var item in source.OrEmpty()) { /**/ }
foreach (var item in source.EmptyIfDefault()) { /**/ }
// instead of
if (source != null) { foreach (var item in source) { /**/ } }
// TrySingle() to get single without exception
if (source.TrySingle(out singleT)) { ... }
// OneOrDefault() to get the only one element or default
// input firstOrDefault singleOrDefault oneOrDefault
// { 1, 2 } 1 throws default
// { 1 } 1 1 1
// { } default default default
var oneT = source.OneOrDefault();
// calls IEnumerable.Contains()
bool result = value.In(source);
bool result = value.In(source, comparer);
IList extensions:
// RemoveLast() to remove last item(s) in list
list.RemoveLast();
list.RemoveLast(2);
Enum extensions:
enum Color { Red = 1, Green, Blue };
Color color = "Red".Parse<Color>();
// OR
Color color;
"Red".TryParse<Color>(out color);
// color: Color.Red
enum Color
{
[Description("Red color")] Red = 1,
Green,
[Description("Blue color")] Blue
}
string redDesc = Color.Red.GetDescription();
string greenDesc = Color.Green.GetDescription();
// redDesc: "Red color"
// greenDesc: "Green"
enum Color { Red = 1, Green, Blue };
Color color = "Red".GetEnumValue<Color>();
// color: Color.Red
// enumValue.GetEnumName() is fastest of all
// fastest, dictionary lookup after 1st call
if (Color.Red.GetEnumName() == "Red") { /**/ }
// slightly slow, dictionary lookup after 1st call
if ("Red".GetEnumValue<Color>() == Color.Red) { /**/ }
// slow, due to reflection
if ("Red".Parse<Color>() == Color.Red) { /**/ }
// slowest, due to reflection
if (Color.Red.ToString() == "Red") { /**/ }
enum Color
{
[Description("Red color")] Red = 1,
Green,
[Description("Blue color")] Blue
}
IDictionary<string, string> colorsMap = EnumExtension.GetEnumNameToDescriptionMap<Color>();
// build a select list
IEnumerable<ListItem> selectOptions = colorsMap
.Select(x => new ListItem() { text: x.Value, value: x.Key });
// <select>
// <option value="Red">Red color</option>
// <option value="Green">Green</option>
// <option value="Blue">Blue color</option>
// </select>
enum Color
{
[Description("Red color")] Red = 1,
Green,
[Description("Blue color")] Blue
}
string redName = "Red color".GetEnumNameFromDescription<Color>()
// redName: "Red"
bool hasValue = 0.IsDefined<Color>();
// hasValue: false
bool hasValue = 1.IsDefined<Color>();
// hasValue: true
enum Color
{
[Description("Red color")] Red = 1,
Green,
[Description("Blue color")] Blue
}
// compile error: cannot have int as enum values or hyphen sign in enum values
enum Grade { Toddler, Pre-K, Kindergarten, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, College }
// work-around: use Description attribute
enum Grade
{
Toddler = 1,
[Description("Pre-K")] PreK,
Kindergarten,
[Description("1")] One,
[Description("2")] Two,
[Description("3")] Three,
[Description("4")] Four,
[Description("5")] Five,
[Description("6")] Six,
[Description("7")] Seven,
[Description("8")] Eight,
[Description("9")] Nine,
[Description("10")] Ten,
[Description("11")] Eleven,
[Description("12")] Twelve,
College
}
// to sort gradesUnsorted, use GetEnumValueFromDescription<T>() and GetDescription<T>() methods
string[] gradesUnsorted = new[] { "Pre-K", "1", "College", "2", "Toddler" };
Grade[] grades = gradesUnsorted
.Select(x => x.GetEnumValueFromDescription<Grade>()).ToArray();
Array.Sort(grades);
string[] gradesSorted = grades.Select(x => x.GetDescription());
// gradesSorted: { "Toddler", "Pre-K", "1", "2", "College" }
NameValueCollection extensions:
// get query string for name-value collection
NameValueCollection nvc = new NameValueCollection { {"k1,", "v1"}, {"k2", "v2"} };
string query = nvc.ToQueryString(); // OR nvc.ToQueryString(prefixQuestionMark: false);
// query: "?k1%2C=v1&k2=v2" // OR "k1%2C=v1&k2=v2"
TimeSpan extensions:
// round timespan as ms, s, m, h, d, wk, mth, y.
string round = TimeSpan.FromDays(10).Round();
// round: "1wk"
// n Days, Hours, Minutes, Seconds, Milliseconds, etc.
TimeSpan ts = 10.Days();
Uri extensions:
// calculate uri's checksum (sha1, md5)
var uri = new Uri(@"https://www.google.com/images/srpr/logo11w.png") // url
// OR new Uri(@"D:\temp\images\logo.png"); // local file
// OR new Uri(new Uri(@"D:\temp\"), @".\images\logo.png"); // dir and relative path
string sha1 = uri.Checksum(Hasher.sha1);
string md5 = uri.Checksum(Hasher.md5);
// sha1: 349841408d1aa1f5a8892686fbdf54777afc0b2c
// md5: 57e396baedfe1a034590339082b9abce
Helper methods:
// swap two values or references
Helper.Swap(ref a, ref b);
decimal extensions:
// truncate decimal to specified digits
12.349m.TruncateTo(2); // 12.34m
int extensions:
// round int as k, m, g
1000.Round(); // "1k"
1000000.Round(); // "1m"
1500.Round(); // "1k"
1500.Round(1); // "1.5k"
Graph extensions:
// if graph is cyclic (used for deadlock detection)
bool isCyclic = graph.IsCyclic();
StringBuilder extensions:
// instead of buffer.AppendLine(string.Format(format, args))
buffer.AppendLine(format, args);
// reverse StringBuilder in-place
buffer.Reverse();
Dictionary extensions:
// for key in dictionary, get value if exists or default / specified value
var value = dictionary.GetValueOrDefault(key);
var value = dictionary.GetValueOrDefault(key, other);
// get dictionary as readonly
var dictionaryReadonly = dictionary.AsReadOnly();
Wrapped extensions:
// wrap (box) any type to avoid using pass by ref parameters
var intw = new Wrapped<int>(1); // or 1.Wrap();
// intw.Value = 1
BitSet:
BitSet bitset = new BitSet(10); // 0 to 10 inclusive
bitset.Add(5); // add 5
bitset.Add(6);
bitset.Remove(5); // remove 5
bitset.Remove(3);
bitset.Toggle(3); // toggle 3
bool has2 = bitset.Has(2); // if has 2
bitset.Clear(); // remove all
foreach(int item in bitset) { /**/ }
Circular Queue:
CircularQueue<int> cq = new CircularQueue<int>(capacity: 2);
cq.Enqueue(1);
cq.Enqueue(2);
cq.Enqueue(3);
cq.Enqueue(4);
int head;
head = cq.Dequeue(); // returns 3
head = cq.Dequeue(); // returns 4
Circular Stack:
CircularStack<int> cq = new CircularStack<int>(capacity: 2);
cq.Push(1);
cq.Push(2);
cq.Push(3);
cq.Push(4);
int top;
top = cq.Pop(); // returns 4
top = cq.Pop(); // returns 3
Deque:
Deque<int> dq = new Deque<int>();
Node<int> node = dq.Enqueue(1);
dq.Enqueue(2);
dq.Delete(node); // delete in O(1) time
int i = dq.Dequeue(); // returns 2
LRU cache:
LruCache<int, int> cache = new LruCache<int, int>(5);
cache.Insert(key: 2, value: 2);
cache.Get(2); // returns value 2
var count = cache.Count(); // return 1
cache.IsEmpty(); // returns false
cache.Remove(2); // removes 2 from cache
cache.IsFull(); // returns false
cache.Capacity(); // returns 5
cache.Clear(); // clears cache
Guid:
// guid.ToByteArray() is sensitive to endianness, but
// guid.ToByteArrayMatchingStringRepresentation() is not and matches guid.ToString()
// see https://stackoverflow.com/questions/9195551/why-does-guid-tobytearray-order-the-bytes-the-way-it-does
var bytes = Guid.Parse(someGuid).ToByteArrayMatchingStringRepresentation();
// similar roundtrip method
var guid = bytes.ToGuidMatchingStringRepresentation(); // same as someGuid
Base64, Base64Url:
var base64 = s.ToUtf8Bytes().Base64Encode();
var s = base64.Base64Decode().ToUtf8String();
var base64Url = s.ToUtf8Bytes().Base64UrlEncode();
var s = base64Url.Base64UrlDecode().ToUtf8String();
Random:
var gaussian = rng.NextGaussian(mu: mu, sigma: sigma);
var s = rng.NextString(length: 10, charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
var doubleN = rng.NextDouble(minValue: 5.0d, maxValue: 10.0d);
var decimalN = rng.NextDecimal(minValue: 5.0m, maxValue: 10.0m);
Base16 (Hex):
note: net5.0
+ has Convert.ToHexString(bytes)
, Convert.FromHexString(hex)
which are faster than below.
var base16 = s.ToUtf8Bytes().Base16Encode();
var s = base16.Base16Decode().ToUtf8String();
// or
var hex = s.ToUtf8Bytes().ToHexString();
var s = hex.FromHexString().ToUtf8String();
char extensions:
// all/most System.Char static utility functions
var c = 'a'.ToUpper(); // returns 'A'
var isLower = c.IsLower(); // returns false
var isLetter = c.IsLetter(); // returns true
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
-
.NETStandard 2.1
- No dependencies.
-
net6.0
- No dependencies.
-
net7.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on rm.Extensions:
Package | Downloads |
---|---|
rm.DelegatingHandlers
Provides DelegatingHandlers. |
|
rm.Masking
Provides masking methods. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.1.1 | 2,983 | 11/19/2023 |
3.1.0 | 193 | 10/15/2023 |
3.1.0-alpha3 | 48 | 10/14/2023 |
3.1.0-alpha2 | 63 | 9/24/2023 |
3.1.0-alpha1 | 76 | 3/12/2023 |
3.1.0-alpha0 | 78 | 2/12/2023 |
3.0.2 | 159 | 9/24/2023 |
3.0.1 | 2,834 | 2/12/2023 |
3.0.0-alpha0 | 93 | 2/2/2023 |
2.10.1-alpha | 89 | 1/29/2023 |
2.10.0-alpha1 | 85 | 1/3/2023 |
2.9.2-alpha | 89 | 1/29/2023 |
2.9.1-alpha1 | 96 | 8/14/2022 |
2.9.0-alpha0 | 122 | 1/2/2022 |
2.8.2 | 13,797 | 1/29/2023 |
2.8.2-alpha | 90 | 1/29/2023 |
2.8.1 | 2,686 | 2/14/2022 |
2.8.1-alpha0 | 97 | 2/14/2022 |
2.8.0 | 1,252 | 1/2/2022 |
2.8.0-alpha0 | 120 | 1/2/2022 |
2.7.1 | 4,910 | 11/28/2021 |
2.7.1-alpha0 | 1,269 | 11/28/2021 |
2.7.0 | 161 | 11/14/2021 |
2.7.0-alpha0 | 145 | 11/14/2021 |
2.6.0 | 175 | 11/14/2021 |
2.6.0-alpha0 | 255 | 11/14/2021 |
2.5.4 | 1,719 | 11/2/2021 |
2.5.4-alpha0 | 144 | 11/2/2021 |
2.5.3-alpha0 | 161 | 11/2/2021 |
2.5.2 | 200 | 11/1/2021 |
2.5.2-alpha0 | 199 | 11/1/2021 |
2.5.1 | 220 | 10/31/2021 |
2.5.0 | 212 | 10/31/2021 |
2.5.0-alpha1 | 204 | 10/31/2021 |
2.5.0-alpha0 | 127 | 10/22/2021 |
2.4.0 | 211 | 10/30/2021 |
2.4.0-alpha2 | 212 | 10/30/2021 |
2.4.0-alpha1 | 204 | 10/2/2021 |
2.4.0-alpha0 | 132 | 9/23/2021 |
2.3.0 | 269 | 10/22/2021 |
2.3.0-alpha1 | 150 | 10/22/2021 |
2.3.0-alpha0 | 145 | 9/23/2021 |
2.2.0 | 200 | 9/12/2021 |
2.2.0-alpha1 | 183 | 9/9/2021 |
2.2.0-alpha0 | 170 | 9/9/2021 |
2.1.0 | 182 | 9/6/2021 |
2.1.0-alpha3 | 131 | 9/6/2021 |
2.1.0-alpha2 | 135 | 9/6/2021 |
2.1.0-alpha1 | 193 | 9/5/2021 |
2.1.0-alpha0 | 134 | 9/5/2021 |
2.0.0 | 155 | 9/6/2021 |
2.0.0-alpha2 | 132 | 8/24/2021 |
2.0.0-alpha1 | 139 | 8/23/2021 |
2.0.0-alpha | 338 | 11/28/2020 |
1.5.1-alpha | 239 | 11/3/2020 |
1.5.0 | 402 | 9/5/2021 |
1.5.0-alpha | 404 | 9/13/2020 |
1.4.0 | 988 | 8/24/2020 |
1.4.0-alpha | 414 | 8/23/2020 |
1.3.6-alpha | 463 | 5/20/2019 |
1.3.5-alpha | 524 | 1/8/2019 |
1.3.4-alpha | 550 | 12/23/2018 |
1.3.1-alpha | 522 | 12/22/2018 |
1.3.0-alpha | 608 | 12/3/2018 |
1.2.2 | 791 | 12/3/2018 |
1.2.2-alpha | 655 | 7/29/2018 |
1.2.1-alpha | 692 | 3/6/2018 |
1.1.2-alpha | 795 | 3/4/2018 |
1.1.1-alpha | 704 | 2/17/2018 |
1.1.0-alpha | 763 | 2/17/2018 |
1.0.0-alpha | 963 | 2/11/2018 |
tag: v3.0.2