micro-elements - MicroElements.Metadata 2.0.0-beta.6
Metadata model, parsing and reporting.
PM> Install-Package MicroElements.Metadata -Version 2.0.0-beta.6 -Source https://www.myget.org/F/micro-elements/api/v3/index.json
> nuget.exe install MicroElements.Metadata -Version 2.0.0-beta.6 -Source https://www.myget.org/F/micro-elements/api/v3/index.json
> dotnet add package MicroElements.Metadata --version 2.0.0-beta.6 --source https://www.myget.org/F/micro-elements/api/v3/index.json
source https://www.myget.org/F/micro-elements/api/v3/index.json
nuget MicroElements.Metadata ~> 2.0.0-beta.6
Copy to clipboard
> choco install MicroElements.Metadata --version 2.0.0-beta.6 --source https://www.myget.org/F/micro-elements/api/v2
Import-Module PowerShellGet
Register-PSRepository -Name "micro-elements" -SourceLocation "https://www.myget.org/F/micro-elements/api/v2"
Install-Module -Name "MicroElements.Metadata" -RequiredVersion "2.0.0-beta.6" -Repository "micro-elements" -AllowPreRelease
Copy to clipboard
MicroElements.Metadata
Provides metadata model, parsing and reporting.
Statuses
Table of Contents
Installation
dotnet add package MicroElements.Metadata
Build
Windows: Run build.ps1
Linux: Run build.sh
License
This project is licensed under the MIT license. See the LICENSE file for more info.
Getting started
Property
Represents property for describing metadata model.
There are two main interfaces: untyped IProperty and generic IProperty<T>.
IProperty provides Name and Type and optional Description and Alias.
IProperty<T> extends IProperty with DefaultValue, Calculator and Examples
Source: IProperty.cs
PropertyValue
Represents property and its value.
Has untyped form: IPropertyValue and strong typed: IPropertyValue<T>.
Source: IPropertyValue.cs
PropertyContainer
PropertyContainer represents collection that contains properties and values for these properties. IPropertyContainer is an immutable collection of IPropertyValue
IPropertyContainer provides Properties, ParentSource and SearchOptions
IMutablePropertyContainer extends IPropertyContainer with Add* and Set* methods.
Sample
public class PropertyContainerUsage
{
public class EntityMeta
{
public static readonly IProperty<DateTime> CreatedAt = new Property<DateTime>("CreatedAt");
public static readonly IProperty<string> Description = new Property<string>("Description");
}
[Fact]
public void simple_set_and_get_value()
{
IPropertyContainer propertyContainer = new MutablePropertyContainer()
.WithValue(EntityMeta.CreatedAt, DateTime.Today)
.WithValue(EntityMeta.Description, "description");
propertyContainer.GetValue(EntityMeta.CreatedAt).Should().Be(DateTime.Today);
propertyContainer.GetValue(EntityMeta.Description).Should().Be("description");
}
[Fact]
public void get_property_value()
{
IPropertyContainer propertyContainer = new MutablePropertyContainer()
.WithValue(EntityMeta.CreatedAt, DateTime.Today)
.WithValue(EntityMeta.Description, "description");
IPropertyValue<string>? propertyValue = propertyContainer.GetPropertyValue(EntityMeta.Description);
propertyValue.Should().NotBeNull();
propertyValue.Property.Should().BeSameAs(EntityMeta.Description);
propertyValue.Value.Should().Be("description");
propertyValue.Source.Should().Be(ValueSource.Defined);
}
}
MetadataProvider
IMetadataProvider represents object that has metadata where Metadata is IPropertyContainer.
MetadataProvider allows to extend any object with additional properties. IMetadataProvider default implementation uses MetadataGlobalCache.GetInstanceMetadata that creates MutablePropertyContainer. If you want mutable metadata - implement Metadata with IMutablePropertyContainer.
Metadata default search mode: ByTypeAndName
Methods
/// <summary>
/// Gets metadata of required type.
/// </summary>
/// <typeparam name="TMetadata">Metadata type.</typeparam>
/// <param name="metadataProvider">Metadata provider.</param>
/// <param name="metadataName">Optional metadata name.</param>
/// <param name="defaultValue">Default value to return if not metadata found.</param>
/// <returns>Metadata or default value if not found.</returns>
[return: MaybeNull]
public static TMetadata GetMetadata<TMetadata>(
this IMetadataProvider metadataProvider,
string? metadataName = null,
[AllowNull] TMetadata defaultValue = default)
/// <summary>
/// Sets metadata for target object and returns the same metadataProvider for chaining.
/// </summary>
/// <typeparam name="TMetadataProvider">Metadata provider type.</typeparam>
/// <typeparam name="TMetadata">Metadata type.</typeparam>
/// <param name="metadataProvider">Target metadata provider.</param>
/// <param name="metadataName">Metadata name.</param>
/// <param name="data">Metadata to set.</param>
/// <returns>The same metadataProvider.</returns>
public static TMetadataProvider SetMetadata<TMetadataProvider, TMetadata>(
this TMetadataProvider metadataProvider,
string? metadataName,
TMetadata data)
where TMetadataProvider : IMetadataProvider
/// <summary>
/// Configures metadata with action. Can be called many times.
/// If metadata is not exists then it creates with default constructor.
/// </summary>
/// <typeparam name="TMetadataProvider">Metadata provider type.</typeparam>
/// <typeparam name="TMetadata">Metadata type.</typeparam>
/// <param name="metadataProvider">Target metadata provider.</param>
/// <param name="configureMetadata">Configure action.</param>
/// <param name="metadataName">Optional metadata name.</param>
/// <returns>The same metadataProvider.</returns>
public static TMetadataProvider ConfigureMetadata<TMetadataProvider, TMetadata>(
this TMetadataProvider metadataProvider,
Action<TMetadata> configureMetadata,
string? metadataName = null)
where TMetadataProvider : IMetadataProvider
where TMetadata : new()
other methods can be found in MetadataProviderExtensions
UseCases
Searching
Searching is one of the important concept of working with metadata. Most search methods accepts SearchOptions
SearchOptions
| Property | DefaultValue | Description |
|---|---|---|
| PropertyComparer | ByTypeAndNameEqualityComparer |
Equality comparer for comparing properties. |
| SearchInParent | true |
Do search in parent if no PropertyValue was found. |
| CalculateValue | true |
Calculate value if value was not found. |
| UseDefaultValue | true |
Use default value from property is property value was not found. |
| ReturnNotDefined | true |
Return fake PropertyValue with |
Search methods
- Main search methods provided by
ISearchAlgorithm - Main search algorithm can be get or set in
Search.Algorithm - All search methods from
SearchExtensionsuseSearch.Algorithm SearchOptionscan be composed bySearchclass
ISearchAlgorithm
/// <summary>
/// Represents search algorithm.
/// </summary>
public interface ISearchAlgorithm
{
/// <summary>
/// Searches <see cref="IPropertyValue{T}"/> by <see cref="IProperty{T}"/> and <see cref="SearchOptions"/>.
/// </summary>
/// <param name="propertyContainer">Property container.</param>
/// <param name="property">Property to search.</param>
/// <param name="searchOptions">Search options.</param>
/// <returns><see cref="IPropertyValue"/> or null.</returns>
IPropertyValue? SearchPropertyValueUntyped(
IPropertyContainer propertyContainer,
IProperty property,
SearchOptions? searchOptions = default);
/// <summary>
/// Gets <see cref="IPropertyValue{T}"/> by <see cref="IProperty{T}"/> and <see cref="SearchOptions"/>.
/// </summary>
/// <typeparam name="T">Property type.</typeparam>
/// <param name="propertyContainer">Property container.</param>
/// <param name="property">Property to search.</param>
/// <param name="searchOptions">Search options.</param>
/// <returns><see cref="IPropertyValue"/> or null.</returns>
IPropertyValue<T>? GetPropertyValue<T>(
IPropertyContainer propertyContainer,
IProperty<T> property,
SearchOptions? searchOptions = null);
}
SearchExtensions
| Method | Description |
|---|---|
| GetPropertyValue | Gets or calculates typed property and value for property using search conditions. It's a full search that uses all search options: SearchOptions.SearchInParent, SearchOptions.CalculateValue, SearchOptions.UseDefaultValue, SearchOptions.ReturnNotDefined. |
| SearchPropertyValueUntyped | Searches property and value for untyped property using search conditions. Search does not use SearchOptions.UseDefaultValue and SearchOptions.CalculateValue. Search uses only SearchOptions.SearchInParent and SearchOptions.ReturnNotDefined. |
| GetPropertyValueUntyped | Gets property and value for untyped property using search conditions. Uses simple untyped search SearchPropertyValueUntyped if CanUseSimpleUntypedSearch or property has type Search.UntypedSearch. Uses full GetPropertyValue{T} based on property.Type in other cases. |
| GetValue | Gets or calculates value for property. |
| GetValueAsOption | Gets or calculates optional not null value. |
| GetValueUntyped | Gets or calculates untyped value for property. |
| GetValueByName | Gets or calculates value by name. |
EntityParseValidateReport example
Source: EntityParseValidateReport.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using FluentAssertions;
using MicroElements.Functional;
using MicroElements.Parsing;
using MicroElements.Reporting.Excel;
using MicroElements.Validation;
using MicroElements.Validation.Rules;
using Xunit;
namespace MicroElements.Metadata.Tests.examples
{
public class EntityParseValidateReport
{
public class Entity
{
public DateTime CreatedAt { get; }
public string Name { get; }
public Entity(DateTime createdAt, string name)
{
CreatedAt = createdAt;
Name = name;
}
}
public class EntityMeta : IPropertySet, IPropertyContainerMapper<Entity>
{
public static readonly EntityMeta Instance = new EntityMeta();
public static readonly IProperty<DateTime> CreatedAt = new Property<DateTime>("CreatedAt");
public static readonly IProperty<string> Name = new Property<string>("Name");
/// <inheritdoc />
public IEnumerable<IProperty> GetProperties()
{
yield return CreatedAt;
yield return Name;
}
/// <inheritdoc />
public IPropertyContainer ToContainer(Entity model)
{
return new MutablePropertyContainer()
.WithValue(CreatedAt, model.CreatedAt)
.WithValue(Name, model.Name);
}
/// <inheritdoc />
public Entity ToModel(IPropertyContainer container)
{
return new Entity(
createdAt: container.GetValue(CreatedAt),
name: container.GetValue(Name));
}
}
public class EntityParser : ParserProvider
{
protected Option<DateTime> ParseDate(string value) => Prelude.ParseDateTime(value);
/// <inheritdoc />
public EntityParser()
{
Source("CreatedAt", ParseDate).Target(EntityMeta.CreatedAt);
Source("Name").Target(EntityMeta.Name);
}
}
public class EntityValidator : IValidator
{
/// <inheritdoc />
public IEnumerable<IValidationRule> GetRules()
{
yield return EntityMeta.CreatedAt.NotDefault();
yield return EntityMeta.Name.Required();
}
}
public class EntityReport : ReportProvider
{
public EntityReport(string reportName = "Entities")
: base(reportName)
{
Add(EntityMeta.CreatedAt);
Add(EntityMeta.Name);
}
}
public Stream ReportToExcel(Entity[] entities)
{
var reportRows = entities.Select(entity => EntityMeta.Instance.ToContainer(entity));
var excelStream = new MemoryStream();
ExcelReportBuilder.Create(excelStream)
.AddReportSheet(new EntityReport("Entities"), reportRows)
.SaveAndClose();
return excelStream;
}
public Entity[] ParseExcel(Stream stream)
{
var document = SpreadsheetDocument.Open(stream, false);
var messages = new List<Message>();
var entities = document
.GetSheet("Entities")
.GetRowsAs(new EntityParser(), list => new PropertyContainer(list))
.ValidateAndFilter(new EntityValidator(), result => messages.AddRange(result.ValidationMessages))
.Select(container => EntityMeta.Instance.ToModel(container))
.ToArray();
return entities;
}
[Fact]
public void UseCase()
{
// Trim DateTime to milliseconds because default DateTime render trimmed to milliseconds
DateTime NowTrimmed()
{
DateTime now = DateTime.Now;
return now.AddTicks(-(now.Ticks % TimeSpan.TicksPerMillisecond));
}
Entity[] entities = {
new Entity(NowTrimmed(), "Name1"),
new Entity(NowTrimmed(), "Name2"),
new Entity(NowTrimmed(), "Name3"),
};
Stream excelStream = ReportToExcel(entities);
Entity[] fromExcel = ParseExcel(excelStream);
fromExcel.Should().HaveCount(3);
fromExcel.Should().BeEquivalentTo(entities);
}
}
}
Calculation, Mapping TBD
- IPropertySet
- IPropertyContainerMapper
Dynamic
IPropertyContainer can be casted to dynamic object with AsDynamic extension.
[Fact]
public void DynamicContainer()
{
var propertyContainer = new MutablePropertyContainer();
propertyContainer.SetValue("PropertyA", "ValueA");
propertyContainer.SetValue(new Property<int>("PropertyB"), 42);
dynamic dynamicContainer = propertyContainer.AsDynamic();
object valueA = dynamicContainer.PropertyA;
valueA.Should().Be("ValueA");
object valueB = dynamicContainer.PropertyB;
valueB.Should().Be(42);
object notFoundProperty = dynamicContainer.NotFoundProperty;
notFoundProperty.Should().BeNull();
}
Parsing TBD
See: EntityParseValidateReport example
Validation TBD
See: EntityParseValidateReport example
Reporting TBD
See: EntityParseValidateReport example
- ReportProvider
- Renderer
- IPropertyParser
$# 2.0.0-beta.4
- ExcelReportBuilder support for SharedStrings
- Fixed broken copy and paste in generated excel
2.0.0-beta.3
- Validation customization for last rule in chain
- NotNull for Nullable struct
- Added predefined search modes: Default, ExistingOnly, ExistingOnlyWithParent
- Search clean up.
2.0.0-beta.2
- Property search redesigned and unified
- Search API became more consistent and customizable
- PropertyContainer and MutablePropertyContainer can accept custom SearchOptions
2.0.0-beta.1
- Property search allows to know whether property exists, not exists or exists with undefined value
- New validation rules: Exists, Required
- IValidationRule now implements IMetadataProvider
- Added generic IValidationRule
- Rich rule building with combine support
- IConfigurableValidationRule removed
- Validation message customization moved to ValidationMessageOptions and linked to IValidationRule as metadata
1.3.0
- IParserProvider: convert Parsers property to GetParsers method to be more functional
- Added PropertyParser extensions to create property parsers in fluent parser providers
- Added CachedParserProvider and Cached extension
Full release notes can be found at: https://github.com/micro-elements/MicroElements.Metadata.git/blob/master/CHANGELOG.md
-
.NETStandard 2.1
- DocumentFormat.OpenXml (>= 2.10.0)
- MicroElements.Functional (>= 0.17.0)
- NodaTime (>= 2.4.7)
- .NETStandard 2.1: 2.1.0.0
OwnersAlexey Petryashev |
Authorsmicro-elements |
Project URLhttps://github.com/micro-elements/MicroElements.Metadata.git |
LicenseMIT |
Tagsmetadata parsing parse reporting report etl |
Info10539 total downloads |
| 74 downloads for version 2.0.0-beta.6 |
| Download (75.15 KB) |
| Found on the current feed only |
Package history
| Version | Size | Last updated | Downloads | Mirrored? | |||
|---|---|---|---|---|---|---|---|
|
8.0.0-beta.3 | 292.14 KB | Fri, 13 Dec 2024 16:33:09 GMT | 79 |
|
||
|
8.0.0-beta.2 | 281.57 KB | Fri, 13 Dec 2024 14:47:25 GMT | 58 |
|
||
|
8.0.0-beta.1 | 266.76 KB | Fri, 19 Apr 2024 20:09:41 GMT | 68 |
|
||
|
7.14.0 | 240.94 KB | Sun, 06 Nov 2022 15:43:53 GMT | 69 |
|
||
|
7.13.0 | 222.66 KB | Mon, 16 May 2022 05:20:17 GMT | 70 |
|
||
|
7.12.0 | 189.82 KB | Sun, 05 Sep 2021 18:37:35 GMT | 83 |
|
||
|
7.11.0 | 185.68 KB | Thu, 02 Sep 2021 14:39:33 GMT | 81 |
|
||
|
7.10.0 | 185.56 KB | Wed, 01 Sep 2021 20:35:47 GMT | 65 |
|
||
|
7.9.2 | 183 KB | Tue, 31 Aug 2021 12:26:52 GMT | 67 |
|
||
|
7.9.1 | 182.93 KB | Tue, 31 Aug 2021 08:39:43 GMT | 76 |
|
||
|
7.9.0 | 182.7 KB | Fri, 27 Aug 2021 16:05:54 GMT | 66 |
|
||
|
7.8.0 | 182.55 KB | Thu, 15 Jul 2021 07:49:16 GMT | 76 |
|
||
|
7.7.1 | 182.34 KB | Wed, 14 Jul 2021 22:27:26 GMT | 76 |
|
||
|
7.7.0 | 180.23 KB | Mon, 12 Jul 2021 14:17:11 GMT | 75 |
|
||
|
7.6.4 | 177.42 KB | Thu, 08 Jul 2021 18:31:33 GMT | 73 |
|
||
|
7.6.3 | 176.49 KB | Tue, 06 Jul 2021 08:41:26 GMT | 60 |
|
||
|
7.6.2 | 176.36 KB | Tue, 06 Jul 2021 07:30:53 GMT | 69 |
|
||
|
7.6.1 | 176.55 KB | Mon, 05 Jul 2021 20:21:32 GMT | 72 |
|
||
|
7.6.0 | 173.74 KB | Sun, 04 Jul 2021 20:10:37 GMT | 70 |
|
||
|
7.5.0 | 164.46 KB | Sat, 26 Jun 2021 19:51:33 GMT | 67 |
|
||
|
7.4.3 | 164.04 KB | Wed, 09 Jun 2021 17:31:21 GMT | 65 |
|
||
|
7.4.2 | 160.27 KB | Tue, 08 Jun 2021 15:51:59 GMT | 70 |
|
||
|
7.4.1 | 160.28 KB | Tue, 08 Jun 2021 12:05:31 GMT | 79 |
|
||
|
7.4.0 | 159.66 KB | Mon, 07 Jun 2021 11:34:39 GMT | 73 |
|
||
|
7.3.4 | 156.68 KB | Mon, 31 May 2021 12:52:54 GMT | 74 |
|
||
|
7.3.3 | 152.27 KB | Wed, 26 May 2021 18:32:25 GMT | 74 |
|
||
|
7.3.2 | 152.22 KB | Wed, 26 May 2021 07:52:37 GMT | 71 |
|
||
|
7.3.1 | 151.67 KB | Tue, 25 May 2021 20:22:57 GMT | 86 |
|
||
|
7.3.0 | 151.5 KB | Tue, 25 May 2021 17:43:05 GMT | 69 |
|
||
|
7.2.0 | 150.12 KB | Sat, 22 May 2021 08:57:13 GMT | 67 |
|
||
|
7.1.0 | 149 KB | Fri, 23 Apr 2021 12:39:53 GMT | 71 |
|
||
|
7.0.0 | 148.69 KB | Thu, 22 Apr 2021 11:32:14 GMT | 80 |
|
||
|
7.0.0-rc.32 | 148.77 KB | Mon, 08 Mar 2021 17:54:23 GMT | 85 |
|
||
|
7.0.0-rc.31 | 141.39 KB | Sun, 28 Feb 2021 19:40:50 GMT | 70 |
|
||
|
7.0.0-rc.30 | 141.41 KB | Sun, 28 Feb 2021 19:40:14 GMT | 86 |
|
||
|
7.0.0-rc.28 | 130.04 KB | Mon, 15 Feb 2021 17:32:42 GMT | 72 |
|
||
|
7.0.0-rc.27 | 128.89 KB | Sun, 14 Feb 2021 19:45:59 GMT | 71 |
|
||
|
7.0.0-rc.26 | 125.56 KB | Sat, 13 Feb 2021 13:54:23 GMT | 73 |
|
||
|
7.0.0-rc.25 | 124.28 KB | Thu, 11 Feb 2021 17:46:24 GMT | 75 |
|
||
|
7.0.0-rc.24 | 124.23 KB | Thu, 11 Feb 2021 10:56:00 GMT | 75 |
|
||
|
7.0.0-rc.23 | 124.08 KB | Wed, 10 Feb 2021 21:57:47 GMT | 77 |
|
||
|
7.0.0-rc.22 | 106.53 KB | Tue, 09 Feb 2021 15:17:28 GMT | 68 |
|
||
|
7.0.0-rc.21 | 104.92 KB | Fri, 05 Feb 2021 16:39:58 GMT | 79 |
|
||
|
7.0.0-rc.20 | 104.9 KB | Fri, 05 Feb 2021 15:44:54 GMT | 69 |
|
||
|
7.0.0-rc.19 | 104.94 KB | Wed, 20 Jan 2021 19:50:01 GMT | 79 |
|
||
|
7.0.0-rc.18 | 102.73 KB | Tue, 19 Jan 2021 14:26:44 GMT | 86 |
|
||
|
7.0.0-rc.17 | 99.99 KB | Mon, 18 Jan 2021 20:22:34 GMT | 66 |
|
||
|
7.0.0-rc.16 | 98.35 KB | Mon, 18 Jan 2021 10:59:15 GMT | 68 |
|
||
|
7.0.0-rc.15 | 97.72 KB | Sun, 17 Jan 2021 17:38:09 GMT | 76 |
|
||
|
7.0.0-rc.14 | 95.96 KB | Sat, 16 Jan 2021 10:36:37 GMT | 88 |
|
||
|
7.0.0-rc.13 | 95.75 KB | Fri, 15 Jan 2021 15:50:42 GMT | 76 |
|
||
|
7.0.0-rc.12 | 91.46 KB | Wed, 13 Jan 2021 16:38:20 GMT | 68 |
|
||
|
7.0.0-rc.11 | 91.39 KB | Wed, 13 Jan 2021 13:51:23 GMT | 63 |
|
||
|
7.0.0-rc.10 | 88.16 KB | Mon, 11 Jan 2021 14:45:06 GMT | 85 |
|
||
|
7.0.0-rc.9 | 88.55 KB | Sun, 27 Dec 2020 19:30:48 GMT | 67 |
|
||
|
7.0.0-rc.8 | 87.79 KB | Sat, 26 Dec 2020 18:51:40 GMT | 81 |
|
||
|
7.0.0-rc.7 | 85.55 KB | Thu, 24 Dec 2020 15:56:02 GMT | 74 |
|
||
|
7.0.0-rc.6 | 85.53 KB | Thu, 24 Dec 2020 10:38:07 GMT | 80 |
|
||
|
7.0.0-rc.5 | 85.32 KB | Wed, 23 Dec 2020 21:54:24 GMT | 71 |
|
||
|
7.0.0-rc.4 | 85.31 KB | Wed, 23 Dec 2020 16:21:25 GMT | 75 |
|
||
|
7.0.0-rc.3 | 84.88 KB | Thu, 17 Dec 2020 16:33:14 GMT | 67 |
|
||
|
7.0.0-rc.2 | 84.11 KB | Wed, 16 Dec 2020 15:11:52 GMT | 68 |
|
||
|
7.0.0-rc.1 | 83.92 KB | Mon, 14 Dec 2020 21:28:48 GMT | 62 |
|
||
|
6.0.0 | 75.74 KB | Tue, 08 Dec 2020 14:58:50 GMT | 68 |
|
||
|
5.3.0 | 75.72 KB | Thu, 03 Dec 2020 20:05:08 GMT | 69 |
|
||
|
5.2.0 | 79.2 KB | Wed, 02 Dec 2020 22:44:36 GMT | 75 |
|
||
|
5.1.0 | 72.96 KB | Thu, 19 Nov 2020 15:42:38 GMT | 72 |
|
||
|
5.0.0 | 73.28 KB | Thu, 19 Nov 2020 08:51:59 GMT | 83 |
|
||
|
4.9.0 | 73.94 KB | Wed, 18 Nov 2020 09:50:30 GMT | 76 |
|
||
|
4.8.1 | 73.72 KB | Tue, 17 Nov 2020 09:04:03 GMT | 67 |
|
||
|
4.8.0 | 73.68 KB | Mon, 16 Nov 2020 21:26:42 GMT | 68 |
|
||
|
4.7.0 | 73.97 KB | Mon, 16 Nov 2020 13:46:07 GMT | 84 |
|
||
|
4.6.0 | 74.09 KB | Thu, 12 Nov 2020 10:50:47 GMT | 72 |
|
||
|
4.5.0 | 74.13 KB | Wed, 11 Nov 2020 20:26:59 GMT | 69 |
|
||
|
4.4.0 | 74.9 KB | Thu, 05 Nov 2020 17:25:20 GMT | 79 |
|
||
|
4.3.0 | 74.89 KB | Sun, 01 Nov 2020 20:51:03 GMT | 72 |
|
||
|
4.2.0 | 74.5 KB | Thu, 29 Oct 2020 17:52:41 GMT | 74 |
|
||
|
4.1.1 | 74.12 KB | Thu, 29 Oct 2020 08:18:57 GMT | 83 |
|
||
|
4.1.0 | 74.08 KB | Wed, 28 Oct 2020 21:43:17 GMT | 70 |
|
||
|
4.0.0 | 72.7 KB | Tue, 27 Oct 2020 17:09:07 GMT | 80 |
|
||
|
4.0.0-beta.1 | 72.32 KB | Sun, 25 Oct 2020 20:05:14 GMT | 70 |
|
||
|
3.9.0 | 69.11 KB | Fri, 23 Oct 2020 07:33:26 GMT | 70 |
|
||
|
3.8.0 | 68.23 KB | Tue, 20 Oct 2020 13:20:46 GMT | 80 |
|
||
|
3.7.0 | 67.39 KB | Mon, 19 Oct 2020 19:36:09 GMT | 72 |
|
||
|
3.6.0 | 65.54 KB | Thu, 15 Oct 2020 16:28:01 GMT | 71 |
|
||
|
3.5.0 | 65.13 KB | Mon, 12 Oct 2020 12:10:24 GMT | 74 |
|
||
|
3.4.0 | 65.14 KB | Thu, 03 Sep 2020 15:00:42 GMT | 77 |
|
||
|
3.3.0 | 61.42 KB | Tue, 04 Aug 2020 20:28:41 GMT | 79 |
|
||
|
3.2.0 | 59.8 KB | Fri, 10 Jul 2020 10:01:48 GMT | 70 |
|
||
|
3.1.0 | 59.5 KB | Thu, 09 Jul 2020 16:25:41 GMT | 81 |
|
||
|
3.0.0 | 58.49 KB | Tue, 02 Jun 2020 10:32:50 GMT | 76 |
|
||
|
2.0.0 | 76.53 KB | Wed, 20 May 2020 14:32:54 GMT | 75 |
|
||
|
2.0.0-beta.7 | 75.15 KB | Thu, 14 May 2020 12:50:50 GMT | 77 |
|
||
|
2.0.0-beta.6 | 75.15 KB | Thu, 14 May 2020 11:58:19 GMT | 74 |
|
||
|
2.0.0-beta.5 | 74.85 KB | Thu, 14 May 2020 10:22:22 GMT | 68 |
|
||
|
2.0.0-beta.4 | 74.81 KB | Thu, 14 May 2020 09:44:29 GMT | 76 |
|
||
|
2.0.0-beta.3 | 73.71 KB | Mon, 11 May 2020 16:18:59 GMT | 76 |
|
||
|
2.0.0-beta.2 | 73.08 KB | Mon, 11 May 2020 09:15:47 GMT | 83 |
|
||
|
2.0.0-beta.1 | 68.49 KB | Fri, 08 May 2020 18:08:12 GMT | 68 |
|
||
|
1.3.0 | 64.38 KB | Thu, 07 May 2020 16:03:50 GMT | 76 |
|
||
|
1.2.0 | 63.75 KB | Thu, 07 May 2020 13:43:06 GMT | 64 |
|
||
|
1.1.0 | 63.36 KB | Thu, 07 May 2020 10:23:30 GMT | 75 |
|
||
|
1.0.0 | 62.25 KB | Wed, 06 May 2020 15:58:44 GMT | 74 |
|
||
|
0.14.0 | 50.42 KB | Tue, 28 Apr 2020 18:55:43 GMT | 76 |
|
||
|
0.13.0 | 48.81 KB | Tue, 28 Apr 2020 10:11:16 GMT | 76 |
|
||
|
0.12.0 | 48.78 KB | Tue, 28 Apr 2020 07:24:59 GMT | 67 |
|
||
|
0.11.1 | 48.49 KB | Sat, 25 Apr 2020 13:12:20 GMT | 67 |
|
||
|
0.11.0 | 48.46 KB | Fri, 24 Apr 2020 15:30:50 GMT | 74 |
|
||
|
0.10.0 | 47.87 KB | Fri, 24 Apr 2020 12:45:39 GMT | 77 |
|
||
|
0.9.4 | 47.38 KB | Sun, 12 Apr 2020 09:41:29 GMT | 62 |
|
||
|
0.9.3 | 47.11 KB | Thu, 09 Apr 2020 15:40:11 GMT | 68 |
|
||
|
0.9.2 | 46.9 KB | Thu, 09 Apr 2020 06:50:14 GMT | 70 |
|
||
|
0.9.1 | 45.02 KB | Wed, 08 Apr 2020 14:47:53 GMT | 76 |
|
||
|
0.9.0 | 42.35 KB | Tue, 07 Apr 2020 16:24:37 GMT | 79 |
|
||
|
0.8.1 | 36.18 KB | Thu, 02 Apr 2020 14:16:24 GMT | 74 |
|
||
|
0.8.0 | 36.17 KB | Mon, 02 Mar 2020 11:55:27 GMT | 82 |
|
||
|
0.7.0 | 35.53 KB | Wed, 26 Feb 2020 14:53:59 GMT | 69 |
|
||
|
0.6.1 | 35.51 KB | Wed, 26 Feb 2020 09:33:01 GMT | 67 |
|
||
|
0.6.0 | 35.19 KB | Wed, 26 Feb 2020 08:48:47 GMT | 73 |
|
||
|
0.5.0 | 34.32 KB | Mon, 24 Feb 2020 19:47:06 GMT | 82 |
|
||
|
0.4.3 | 33.72 KB | Mon, 24 Feb 2020 18:17:11 GMT | 62 |
|
||
|
0.4.2 | 33.79 KB | Mon, 24 Feb 2020 18:06:01 GMT | 69 |
|
||
|
0.4.1 | 33.75 KB | Mon, 24 Feb 2020 17:50:28 GMT | 79 |
|
||
|
0.4.0 | 32.91 KB | Mon, 24 Feb 2020 11:25:32 GMT | 64 |
|
||
|
0.3.3 | 31.76 KB | Mon, 24 Feb 2020 06:16:57 GMT | 76 |
|
||
|
0.3.2 | 31.76 KB | Sun, 23 Feb 2020 20:57:20 GMT | 84 |
|
||
|
0.3.1 | 31.04 KB | Sun, 23 Feb 2020 19:57:42 GMT | 67 |
|
||
|
0.3.0 | 30.81 KB | Mon, 17 Feb 2020 14:31:18 GMT | 70 |
|
||
|
0.2.14 | 27.96 KB | Fri, 14 Feb 2020 19:55:38 GMT | 76 |
|
||
|
0.2.13 | 27.87 KB | Fri, 14 Feb 2020 19:42:52 GMT | 80 |
|
||
|
0.2.12 | 27.57 KB | Mon, 10 Feb 2020 19:36:34 GMT | 71 |
|
||
|
0.2.11 | 26.5 KB | Wed, 05 Feb 2020 09:22:07 GMT | 70 |
|
||
|
0.2.10 | 26.56 KB | Tue, 04 Feb 2020 19:19:51 GMT | 71 |
|
||
|
0.2.9 | 26.69 KB | Tue, 04 Feb 2020 14:35:33 GMT | 75 |
|
||
|
0.2.8 | 26.68 KB | Tue, 04 Feb 2020 14:28:26 GMT | 64 |
|
||
|
0.2.7 | 26.16 KB | Wed, 29 Jan 2020 19:06:58 GMT | 81 |
|
||
|
0.2.6 | 25.87 KB | Sun, 26 Jan 2020 09:30:38 GMT | 82 |
|
||
|
0.2.5 | 25.84 KB | Sat, 25 Jan 2020 20:31:34 GMT | 68 |
|
||
|
0.2.4 | 25.77 KB | Sat, 25 Jan 2020 19:48:03 GMT | 69 |
|
||
|
0.2.3 | 25.82 KB | Sat, 25 Jan 2020 18:29:07 GMT | 56 |
|
||
|
0.2.2 | 25.69 KB | Sat, 25 Jan 2020 09:30:46 GMT | 75 |
|
||
|
0.2.1 | 25.22 KB | Fri, 24 Jan 2020 18:33:32 GMT | 67 |
|
||
|
0.2.0 | 24.84 KB | Fri, 24 Jan 2020 17:50:00 GMT | 86 |
|
||
|
0.1.0 | 21.6 KB | Fri, 24 Jan 2020 13:23:12 GMT | 72 |
|