micro-elements - MicroElements.Metadata 8.0.0-beta.3

Metadata model, parsing, validation and reporting.

PM> Install-Package MicroElements.Metadata -Version 8.0.0-beta.3 -Source https://www.myget.org/F/micro-elements/api/v3/index.json

Copy to clipboard

> nuget.exe install MicroElements.Metadata -Version 8.0.0-beta.3 -Source https://www.myget.org/F/micro-elements/api/v3/index.json

Copy to clipboard

> dotnet add package MicroElements.Metadata --version 8.0.0-beta.3 --source https://www.myget.org/F/micro-elements/api/v3/index.json

Copy to clipboard
<PackageReference Include="MicroElements.Metadata" Version="8.0.0-beta.3" />
Copy to clipboard
source https://www.myget.org/F/micro-elements/api/v3/index.json

nuget MicroElements.Metadata  ~> 8.0.0-beta.3
Copy to clipboard

> choco install MicroElements.Metadata --version 8.0.0-beta.3 --source https://www.myget.org/F/micro-elements/api/v2

Copy to clipboard
Import-Module PowerShellGet
Register-PSRepository -Name "micro-elements" -SourceLocation "https://www.myget.org/F/micro-elements/api/v2"
Install-Module -Name "MicroElements.Metadata" -RequiredVersion "8.0.0-beta.3" -Repository "micro-elements" -AllowPreRelease
Copy to clipboard

MicroElements.Metadata

Provides metadata model, parsing and reporting.

Statuses

License NuGetVersion NuGetDownloads MyGetVersion

Travis

Gitter

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 SearchExtensions use Search.Algorithm
  • SearchOptions can be composed by Search class
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

$# 8.0.0-beta.2

  • switched all projects to dotnet 8, netstandard removed
  • removed reflection based dDI configuration
  • removed obsolete MicroElements.Functional dependency

8.0.0-beta.1

  • MicroElements.Functional dependency removed
  • MicroElements.Diagnostics extracted
  • ISchema
  • OneOf initial support
  • Base58 hash for SchemaDigest
  • Added copyMetadata to WithComponent

Metadata.Parsing:

  • IParserRuleProvider: search methods added, search implementation moved to ParserRuleProvider
  • Added CachedParserRuleProvider
  • XmlParserContext: ParserCache changed to ParserRuleProvider (cached)
  • CollectionParser supports recursing with IParserRuleProvider

Metadata.NewtonsoftJson:

  • WriteSchemaToPropertyName removed from MetadataJsonSerializationOptions
  • PropertyContainerWithMetadataTypesConverter removed

7.15.0

  • Change: IComposite.Components renamed to GetComponents. Added extension GetComponents, GetComponentsAndMetadata
  • Change: BuildAs for IComposite - removed new constraint. It can use default constructor or constructor with all optional parameters
  • Added: IStaticComponentProvider, IStaticMetadataProvider that bound components and metadata to Type and can be accessed without type instantiation
  • GetFriendlyName method replaced with ME.Reflection.Sources implementation

7.14.0

  • Added ICloneable
  • Added: IComposite, ICompositeBuilder, IConfigurableBuilder
  • Added: FormatterBuilder that can create non static recursive formatters
  • Change: CollectionFormatter can use optional configuration
  • Change: CollectionFormatter can be configured with Configure
  • Change: KeyValuePairFormatter, ValueTuplePairFormatter can use optional format string

7.13.0

  • SchemaBuilder functionality allows to use varios WithXXX methods in generic way

  • MessageBuilder used for reducing memory allocations

  • IPropertyValue.ValueUntyped should return 'null' if Source is NotDefined

  • NotDefined fix for DynamicContainer, Json serialization

  • Added 'defaultValue' param to 'PropertyContainer.GetValue' and 'MutablePropertyContainer.GetValue' to have the same signature as common 'GetValue'

  • Added 'allowMapUndefined' param to PropertyExtensions.Map

  • Added: SetValueIfNotDefault, WithValueIfNotDefault for IMutablePropertyContainer

  • Change: Property.WithDefaultValue methods replaced with SchemaBuilder methods

  • BREAKING CHANGE: Property

  • Added: IExamples component. Property

  • Change: IDefaultValue become covariant

  • Change: 'IStaticSchema' now implements 'IStaticPropertySet'

  • Change: ISchema become covariant

Full release notes can be found at: https://github.com/micro-elements/MicroElements.Metadata/blob/master/CHANGELOG.md

  • .NETFramework 8.0
    • MicroElements.Diagnostics (>= 8.0.0-beta.3)
  • .NETFramework 8.0: 8.0.0.0

Owners

Alexey Petryashev

Authors

alexey.petriashev, MicroElements

Project URL

https://github.com/micro-elements/MicroElements.Metadata

License

MIT

Tags

metadata schema property parsing validation parse reporting report etl

Info

11420 total downloads
82 downloads for version 8.0.0-beta.3
Download (292.14 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 82
8.0.0-beta.2 281.57 KB Fri, 13 Dec 2024 14:47:25 GMT 65
8.0.0-beta.1 266.76 KB Fri, 19 Apr 2024 20:09:41 GMT 71
7.14.0 240.94 KB Sun, 06 Nov 2022 15:43:53 GMT 76
7.13.0 222.66 KB Mon, 16 May 2022 05:20:17 GMT 74
7.12.0 189.82 KB Sun, 05 Sep 2021 18:37:35 GMT 92
7.11.0 185.68 KB Thu, 02 Sep 2021 14:39:33 GMT 86
7.10.0 185.56 KB Wed, 01 Sep 2021 20:35:47 GMT 73
7.9.2 183 KB Tue, 31 Aug 2021 12:26:52 GMT 72
7.9.1 182.93 KB Tue, 31 Aug 2021 08:39:43 GMT 88
7.9.0 182.7 KB Fri, 27 Aug 2021 16:05:54 GMT 70
7.8.0 182.55 KB Thu, 15 Jul 2021 07:49:16 GMT 81
7.7.1 182.34 KB Wed, 14 Jul 2021 22:27:26 GMT 82
7.7.0 180.23 KB Mon, 12 Jul 2021 14:17:11 GMT 79
7.6.4 177.42 KB Thu, 08 Jul 2021 18:31:33 GMT 80
7.6.3 176.49 KB Tue, 06 Jul 2021 08:41:26 GMT 66
7.6.2 176.36 KB Tue, 06 Jul 2021 07:30:53 GMT 77
7.6.1 176.55 KB Mon, 05 Jul 2021 20:21:32 GMT 80
7.6.0 173.74 KB Sun, 04 Jul 2021 20:10:37 GMT 75
7.5.0 164.46 KB Sat, 26 Jun 2021 19:51:33 GMT 75
7.4.3 164.04 KB Wed, 09 Jun 2021 17:31:21 GMT 70
7.4.2 160.27 KB Tue, 08 Jun 2021 15:51:59 GMT 75
7.4.1 160.28 KB Tue, 08 Jun 2021 12:05:31 GMT 86
7.4.0 159.66 KB Mon, 07 Jun 2021 11:34:39 GMT 79
7.3.4 156.68 KB Mon, 31 May 2021 12:52:54 GMT 80
7.3.3 152.27 KB Wed, 26 May 2021 18:32:25 GMT 81
7.3.2 152.22 KB Wed, 26 May 2021 07:52:37 GMT 75
7.3.1 151.67 KB Tue, 25 May 2021 20:22:57 GMT 93
7.3.0 151.5 KB Tue, 25 May 2021 17:43:05 GMT 72
7.2.0 150.12 KB Sat, 22 May 2021 08:57:13 GMT 73
7.1.0 149 KB Fri, 23 Apr 2021 12:39:53 GMT 81
7.0.0 148.69 KB Thu, 22 Apr 2021 11:32:14 GMT 92
7.0.0-rc.32 148.77 KB Mon, 08 Mar 2021 17:54:23 GMT 95
7.0.0-rc.31 141.39 KB Sun, 28 Feb 2021 19:40:50 GMT 75
7.0.0-rc.30 141.41 KB Sun, 28 Feb 2021 19:40:14 GMT 92
7.0.0-rc.28 130.04 KB Mon, 15 Feb 2021 17:32:42 GMT 77
7.0.0-rc.27 128.89 KB Sun, 14 Feb 2021 19:45:59 GMT 78
7.0.0-rc.26 125.56 KB Sat, 13 Feb 2021 13:54:23 GMT 84
7.0.0-rc.25 124.28 KB Thu, 11 Feb 2021 17:46:24 GMT 79
7.0.0-rc.24 124.23 KB Thu, 11 Feb 2021 10:56:00 GMT 78
7.0.0-rc.23 124.08 KB Wed, 10 Feb 2021 21:57:47 GMT 83
7.0.0-rc.22 106.53 KB Tue, 09 Feb 2021 15:17:28 GMT 72
7.0.0-rc.21 104.92 KB Fri, 05 Feb 2021 16:39:58 GMT 86
7.0.0-rc.20 104.9 KB Fri, 05 Feb 2021 15:44:54 GMT 82
7.0.0-rc.19 104.94 KB Wed, 20 Jan 2021 19:50:01 GMT 82
7.0.0-rc.18 102.73 KB Tue, 19 Jan 2021 14:26:44 GMT 90
7.0.0-rc.17 99.99 KB Mon, 18 Jan 2021 20:22:34 GMT 72
7.0.0-rc.16 98.35 KB Mon, 18 Jan 2021 10:59:15 GMT 72
7.0.0-rc.15 97.72 KB Sun, 17 Jan 2021 17:38:09 GMT 82
7.0.0-rc.14 95.96 KB Sat, 16 Jan 2021 10:36:37 GMT 94
7.0.0-rc.13 95.75 KB Fri, 15 Jan 2021 15:50:42 GMT 81
7.0.0-rc.12 91.46 KB Wed, 13 Jan 2021 16:38:20 GMT 70
7.0.0-rc.11 91.39 KB Wed, 13 Jan 2021 13:51:23 GMT 69
7.0.0-rc.10 88.16 KB Mon, 11 Jan 2021 14:45:06 GMT 91
7.0.0-rc.9 88.55 KB Sun, 27 Dec 2020 19:30:48 GMT 72
7.0.0-rc.8 87.79 KB Sat, 26 Dec 2020 18:51:40 GMT 87
7.0.0-rc.7 85.55 KB Thu, 24 Dec 2020 15:56:02 GMT 76
7.0.0-rc.6 85.53 KB Thu, 24 Dec 2020 10:38:07 GMT 82
7.0.0-rc.5 85.32 KB Wed, 23 Dec 2020 21:54:24 GMT 75
7.0.0-rc.4 85.31 KB Wed, 23 Dec 2020 16:21:25 GMT 82
7.0.0-rc.3 84.88 KB Thu, 17 Dec 2020 16:33:14 GMT 72
7.0.0-rc.2 84.11 KB Wed, 16 Dec 2020 15:11:52 GMT 72
7.0.0-rc.1 83.92 KB Mon, 14 Dec 2020 21:28:48 GMT 66
6.0.0 75.74 KB Tue, 08 Dec 2020 14:58:50 GMT 74
5.3.0 75.72 KB Thu, 03 Dec 2020 20:05:08 GMT 75
5.2.0 79.2 KB Wed, 02 Dec 2020 22:44:36 GMT 83
5.1.0 72.96 KB Thu, 19 Nov 2020 15:42:38 GMT 75
5.0.0 73.28 KB Thu, 19 Nov 2020 08:51:59 GMT 89
4.9.0 73.94 KB Wed, 18 Nov 2020 09:50:30 GMT 81
4.8.1 73.72 KB Tue, 17 Nov 2020 09:04:03 GMT 71
4.8.0 73.68 KB Mon, 16 Nov 2020 21:26:42 GMT 72
4.7.0 73.97 KB Mon, 16 Nov 2020 13:46:07 GMT 88
4.6.0 74.09 KB Thu, 12 Nov 2020 10:50:47 GMT 77
4.5.0 74.13 KB Wed, 11 Nov 2020 20:26:59 GMT 77
4.4.0 74.9 KB Thu, 05 Nov 2020 17:25:20 GMT 84
4.3.0 74.89 KB Sun, 01 Nov 2020 20:51:03 GMT 75
4.2.0 74.5 KB Thu, 29 Oct 2020 17:52:41 GMT 80
4.1.1 74.12 KB Thu, 29 Oct 2020 08:18:57 GMT 90
4.1.0 74.08 KB Wed, 28 Oct 2020 21:43:17 GMT 80
4.0.0 72.7 KB Tue, 27 Oct 2020 17:09:07 GMT 89
4.0.0-beta.1 72.32 KB Sun, 25 Oct 2020 20:05:14 GMT 74
3.9.0 69.11 KB Fri, 23 Oct 2020 07:33:26 GMT 76
3.8.0 68.23 KB Tue, 20 Oct 2020 13:20:46 GMT 84
3.7.0 67.39 KB Mon, 19 Oct 2020 19:36:09 GMT 77
3.6.0 65.54 KB Thu, 15 Oct 2020 16:28:01 GMT 78
3.5.0 65.13 KB Mon, 12 Oct 2020 12:10:24 GMT 79
3.4.0 65.14 KB Thu, 03 Sep 2020 15:00:42 GMT 84
3.3.0 61.42 KB Tue, 04 Aug 2020 20:28:41 GMT 90
3.2.0 59.8 KB Fri, 10 Jul 2020 10:01:48 GMT 76
3.1.0 59.5 KB Thu, 09 Jul 2020 16:25:41 GMT 85
3.0.0 58.49 KB Tue, 02 Jun 2020 10:32:50 GMT 82
2.0.0 76.53 KB Wed, 20 May 2020 14:32:54 GMT 80
2.0.0-beta.7 75.15 KB Thu, 14 May 2020 12:50:50 GMT 83
2.0.0-beta.6 75.15 KB Thu, 14 May 2020 11:58:19 GMT 82
2.0.0-beta.5 74.85 KB Thu, 14 May 2020 10:22:22 GMT 78
2.0.0-beta.4 74.81 KB Thu, 14 May 2020 09:44:29 GMT 87
2.0.0-beta.3 73.71 KB Mon, 11 May 2020 16:18:59 GMT 82
2.0.0-beta.2 73.08 KB Mon, 11 May 2020 09:15:47 GMT 89
2.0.0-beta.1 68.49 KB Fri, 08 May 2020 18:08:12 GMT 72
1.3.0 64.38 KB Thu, 07 May 2020 16:03:50 GMT 78
1.2.0 63.75 KB Thu, 07 May 2020 13:43:06 GMT 68
1.1.0 63.36 KB Thu, 07 May 2020 10:23:30 GMT 78
1.0.0 62.25 KB Wed, 06 May 2020 15:58:44 GMT 78
0.14.0 50.42 KB Tue, 28 Apr 2020 18:55:43 GMT 85
0.13.0 48.81 KB Tue, 28 Apr 2020 10:11:16 GMT 80
0.12.0 48.78 KB Tue, 28 Apr 2020 07:24:59 GMT 73
0.11.1 48.49 KB Sat, 25 Apr 2020 13:12:20 GMT 72
0.11.0 48.46 KB Fri, 24 Apr 2020 15:30:50 GMT 82
0.10.0 47.87 KB Fri, 24 Apr 2020 12:45:39 GMT 81
0.9.4 47.38 KB Sun, 12 Apr 2020 09:41:29 GMT 69
0.9.3 47.11 KB Thu, 09 Apr 2020 15:40:11 GMT 79
0.9.2 46.9 KB Thu, 09 Apr 2020 06:50:14 GMT 79
0.9.1 45.02 KB Wed, 08 Apr 2020 14:47:53 GMT 83
0.9.0 42.35 KB Tue, 07 Apr 2020 16:24:37 GMT 90
0.8.1 36.18 KB Thu, 02 Apr 2020 14:16:24 GMT 81
0.8.0 36.17 KB Mon, 02 Mar 2020 11:55:27 GMT 88
0.7.0 35.53 KB Wed, 26 Feb 2020 14:53:59 GMT 77
0.6.1 35.51 KB Wed, 26 Feb 2020 09:33:01 GMT 76
0.6.0 35.19 KB Wed, 26 Feb 2020 08:48:47 GMT 83
0.5.0 34.32 KB Mon, 24 Feb 2020 19:47:06 GMT 87
0.4.3 33.72 KB Mon, 24 Feb 2020 18:17:11 GMT 71
0.4.2 33.79 KB Mon, 24 Feb 2020 18:06:01 GMT 75
0.4.1 33.75 KB Mon, 24 Feb 2020 17:50:28 GMT 87
0.4.0 32.91 KB Mon, 24 Feb 2020 11:25:32 GMT 67
0.3.3 31.76 KB Mon, 24 Feb 2020 06:16:57 GMT 82
0.3.2 31.76 KB Sun, 23 Feb 2020 20:57:20 GMT 90
0.3.1 31.04 KB Sun, 23 Feb 2020 19:57:42 GMT 77
0.3.0 30.81 KB Mon, 17 Feb 2020 14:31:18 GMT 74
0.2.14 27.96 KB Fri, 14 Feb 2020 19:55:38 GMT 84
0.2.13 27.87 KB Fri, 14 Feb 2020 19:42:52 GMT 86
0.2.12 27.57 KB Mon, 10 Feb 2020 19:36:34 GMT 78
0.2.11 26.5 KB Wed, 05 Feb 2020 09:22:07 GMT 76
0.2.10 26.56 KB Tue, 04 Feb 2020 19:19:51 GMT 75
0.2.9 26.69 KB Tue, 04 Feb 2020 14:35:33 GMT 82
0.2.8 26.68 KB Tue, 04 Feb 2020 14:28:26 GMT 72
0.2.7 26.16 KB Wed, 29 Jan 2020 19:06:58 GMT 90
0.2.6 25.87 KB Sun, 26 Jan 2020 09:30:38 GMT 85
0.2.5 25.84 KB Sat, 25 Jan 2020 20:31:34 GMT 73
0.2.4 25.77 KB Sat, 25 Jan 2020 19:48:03 GMT 74
0.2.3 25.82 KB Sat, 25 Jan 2020 18:29:07 GMT 66
0.2.2 25.69 KB Sat, 25 Jan 2020 09:30:46 GMT 79
0.2.1 25.22 KB Fri, 24 Jan 2020 18:33:32 GMT 71
0.2.0 24.84 KB Fri, 24 Jan 2020 17:50:00 GMT 93
0.1.0 21.6 KB Fri, 24 Jan 2020 13:23:12 GMT 79