NETReporter 1.5.0

NETReporter

Knihovna pro generování reportů (PDF, SVG, HTML, JSON) z deklarativních JSON šablon a JSON dat.

Šablona popisuje stránku, sekce, rozvržení, styly a bindingy na data; knihovna ji zvaliduje, vyhodnotí nad daty a vykreslí. PDF a SVG kreslí QuestPDF, grafy OxyPlot.

Instalace

Balíček je v privátním NuGet feedu Advisor Software:

dotnet nuget add source https://nuget.advisor-soft.com/v3/index.json --name AdvisorSoft
dotnet add package NETReporter

Požadavky: .NET 10.

Rychlý start

using NetReporter;

var renderer = new ReportRenderer();

await using var template = File.OpenRead("template.json");
await using var data = File.OpenRead("data.json");
await using var pdf = await renderer.RenderAsync(template, data);   // výchozí formát je PDF

await using var file = File.Create("report.pdf");
await pdf.CopyToAsync(file);

S DI kontejnerem (ReportRenderer se registruje jako singleton):

services.AddNetReporter();                        // ReportRenderer, TemplateSchemaParser, TemplateAnalyzer, VisualTreeBuilder
services.AddNetReporter<MyPipelineObserver>();    // + vlastní IReportPipelineObserver

var renderer = serviceProvider.GetRequiredService<ReportRenderer>();

RenderAsync bere šablonu a data jako Stream a vrací Stream s výstupem (pozice 0). Při neplatné šabloně vyhodí InvalidOperationException s diagnostikami (zpráva + JSON cesta).

Šablona v kostce

{
  "version": "1.0",
  "meta": { "name": "Faktura", "culture": "cs-CZ", "palette": { "primary": "#1A73E8" } },
  "page": { "size": "A4", "orientation": "Portrait", "marginsMm": { "top": 15, "right": 12, "bottom": 15, "left": 12 } },
  "styles": {
    "base": { "font": "Arial", "fontSize": 9 },
    "h1": { "fontSize": 16, "bold": true, "color": "primary" }
  },
  "content": {
    "root": {
      "type": "Stack",
      "gapMm": 3,
      "items": [
        { "type": "Text", "text": "Faktura {{$.Number}}", "styleRef": "h1" },
        { "type": "Text", "text": "Vystaveno: {{$.Issued:dd.MM.yyyy}}, celkem {{$.Total:C2}}" },
        {
          "type": "Table",
          "columns": [
            { "key": "name", "header": "Položka", "width": "3*" },
            { "key": "price", "header": "Cena", "width": "1*", "align": "Right" }
          ],
          "rows": {
            "source": "$.Items",
            "as": "item",
            "cells": {
              "name": { "text": "{{item.Name}}" },
              "price": { "text": "{{item.Price:N2}}" }
            }
          }
        }
      ]
    }
  },
  "pageFooter": {
    "root": { "type": "PageNumbers", "mode": "Current", "showTotal": true, "separator": " / " }
  }
}
  • Sekce: pageBackground, pageHeader, reportHeader, content, reportFooter, firstPageFooter, pageFooter, pageOverlay.
  • Vodoznak a razítko: kořenové watermark (text nebo obrázek, podmínka visibleIf, průhlednost v barvě) na každé stránce PDF, SVG, HTML i JSON; z kódu RenderOptions.Watermark.
  • Rozvržení: Stack, Row, Box, Absolute, Spacer, PageBreak; libovolný uzel lze opakovat nad kolekcí přes "repeat": { "source": "$.Items", "as": "item" }; "keepTogether": true (u Stack, Row, Box, Text, Paragraph, Table) zabrání zalomení uzlu přes stránku – nevejde-li se na zbytek stránky, přesune se celý na další.
  • Obsah: Text, Paragraph, HttpLink, Image, Line, PageNumbers, Barcode, Code2D (QR, DataMatrix…), PaymentQr (QR platba SPAYD a EPC s kontrolou IBAN), Checkbox, Table (skupiny, souhrny, stránkování), Chart.
  • Binding: {{$.Cesta.K.Hodnote}} (kořen dat), {{alias.Pole}} (položka repeat/tabulky/grafu), formát za dvojtečkou ({{$.Total:N2}}, {{$.Date:dd.MM.yyyy}}), C# výrazy s prefixem # ({{#item.Qty * item.Price}}), systémové proměnné now, page.number, page.total. Formátuje se podle meta.culture.
  • Styly: pojmenované styly v styles (base platí pro vše), odkaz styleRef a inline style; písmo, barvy (#RRGGBB nebo název z meta.palette), okraje a výplně v mm, rámečky, zarovnání. Styl lze vybrat podle dat – styleRef může obsahovat binding, jehož výsledkem je jméno stylu ("styleRef": "{{# item.Overdue ? \"overdue\" : \"\" }}"); tabulky mají styl řádku rows.styleRef a buňky rows.cells.<key>.styleRef / style.

Kompletní popis formátu je v přibalené dokumentaci (viz níže) – kapitola 02_template_format.md.

Grafy

{
  "type": "Chart",
  "chartType": "Column",
  "source": "$.Months",
  "as": "m",
  "category": "{{m.Month}}",
  "series": [
    { "name": "2025", "value": "m.Revenue", "color": "primary" },
    { "name": "Plán", "value": "#m.Revenue * 1.1", "dashed": true }
  ],
  "valueAxis": { "format": "N0" },
  "legend": { "position": "Top" }
}

Typy Column, Bar, Line, Area, Pie, Donut; více sérií, paleta, legenda, osy, popisky hodnot, skládání. Kombinovaný graf: série může přepsat typ ("type": "Line") a čáry mohou mít druhou osu vpravo ("axis": "Secondary" + secondaryValueAxis). Grafy jsou vektorové ve všech výstupech. Podrobnosti: 02_template_format.md §7.11 a 17-charts.md.

Výstupní formáty

await using var output = await renderer.RenderAsync(template, data, new RenderOptions { Format = RenderFormat.Svg });
Formát Popis
RenderFormat.Pdf výchozí; plnohodnotné PDF se stránkováním
RenderFormat.Svg stejný layout a stránkování jako PDF; jeden SVG se stránkami pod sebou (data-pages, <svg data-page="N">)
RenderFormat.Html HTML náhled bez stránkování (vhodný pro editor/prohlížeč)
RenderFormat.Json strukturovaný výsledek renderu – vyhodnocené texty, data grafů, metadata obrázků a kódů; pro automatické a AI kontroly

Každou SVG stránku zvlášť vrací RenderSvgPagesAsync:

IReadOnlyList<string> pages = await renderer.RenderSvgPagesAsync(template, data);

Do SVG se nevkládají fonty – prohlížeč použije písmo nainstalované v systému (pozice znaků zůstanou zachované).

Ladění šablon: new RenderOptions { Format = RenderFormat.Html, RenderBindingsAsExpressions = true } zobrazí místo hodnot samotné binding výrazy. RenderOptions.DebugMode přidá k HTML výstupu ladicí atributy (data-nr-type s typem uzlu u prvků s id), ladicí CSS a skript pro vizuální editor šablon; výstup bez něj se nemění.

Limit stránek: PDF/SVG render skončí RenderPageLimitExceededException po překročení RenderOptions.MaxPages (výchozí 10 000, 0 = bez limitu) – ochrana před šablonou se zacykleným layoutem. CancellationToken se kontroluje i během generování stránek. Generování PDF/SVG (QuestPDF) se v rámci procesu serializuje (souběžné generování QuestPDF tiše poškozuje výstup), takže souběžné požadavky na PDF/SVG čekají na řadu; HTML a JSON se renderují souběžně.

Metadata PDF

Vlastnosti dokumentu (Název, Autor, Předmět, Klíčová slova, Aplikace, Jazyk) se nastavují v meta.document. Hodnoty jsou texty s bindingy vyhodnocené nad daty; prázdný výsledek se nezapíše, klíčová slova mohou být pole a bez language se použije meta.culture. Metadata se zapisují jen do PDF.

"meta": {
  "name": "Faktura",
  "culture": "cs-CZ",
  "document": { "title": "Faktura {{$.Number}}", "author": "{{$.Supplier}}", "keywords": [ "faktura", "{{$.Number}}" ] }
}

Podrobnosti: 02_template_format.md §1.4.

Validace šablony

Šablonu lze zkontrolovat bez renderu. Parser ji ověří proti vestavěnému schématu a vrátí diagnostiky s JSON cestou k chybě:

using NetReporter.Common;
using NetReporter.Template.Parsing;

var parser = new TemplateSchemaParser();                  // výchozí ParserValidationMode.Strict
var result = parser.Parse(templateJson);

if (!result.Success)
{
    foreach (var d in result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error))
        Console.WriteLine($"{d.JsonPath}: {d.Message} ({d.Code})");
}
  • Strict hlásí neznámé vlastnosti a chybný obsah stylů (neznámá vlastnost, špatný typ, neplatný enum) jako chybu, Compat jako varování (new TemplateSchemaParser(validationMode: ParserValidationMode.Compat); při renderu new RenderOptions { ValidationMode = ParserValidationMode.Compat }, varování pak dostane pozorovatel v ParseCompletedContext.Diagnostics).
  • Diagnostiky mají stabilní Code a JsonPath (např. $.content.root.items[2].rows.source).
  • Schéma šablon (typy uzlů, vlastnosti, enumy) vrací TemplateSchemaProvider.Create().

Podrobnosti: kapitola 03_parser_and_validation.md.

Sledování pipeline

IReportPipelineObserver dostává události OnPipelineStarted, OnParseStarted/Completed, OnRenderStarted/Completed (doba, velikost výstupu) a OnError (fáze, kategorie, stabilní kód chyby) – vhodné pro metriky a logování. Registruje se přes AddNetReporter<TObserver>() nebo konstruktor ReportRenderer. Knihovna loguje přes ILogger, pokud předáte ILoggerFactory.

RenderCompletedContext v OnRenderCompleted nese také BindingDiagnostics a OmittedBindingDiagnostics: chyby v bindingech (nedokončené {{, nečíselná hodnota grafu…) render nezastaví, takže je jinak neuvidíte. Shodné diagnostiky se slučují a počet omezuje RenderOptions.MaxBindingDiagnostics (výchozí 200, 0 sběr vypne). Při selhání renderu je (do chyby nasbírané) nese i PipelineError v OnError.

Dokumentace v balíčku

Balíček obsahuje dokumentaci knihovny a šablon ve složce docs/ (po instalaci v %USERPROFILE%\.nuget\packages\netreporter\<verze>\docs\, na Linuxu ~/.nuget/packages/netreporter/<verze>/docs/). Rozcestník je docs/README.md:

Soubor Obsah
docs/user/pouziti-knihovny.md průvodce použitím knihovny krok za krokem
docs/dev/02_template_format.md formát šablony – sekce, uzly, vlastnosti, repeat, tabulky, grafy
docs/dev/03_parser_and_validation.md parser a validace šablon, diagnostiky
docs/dev/04_style_system.md stylovací systém
docs/dev/05_binding_system.md binding a výrazy
docs/dev/17-charts.md grafy – chování a omezení
docs/dev/01_introduction.md, 06_runtime_pipeline.md, 07_renderer_questpdf.md, 08_program_flow_example.md, 09_extensibility.md architektura knihovny, pipeline, renderer, rozšiřitelnost

Seznam změn všech verzí je v souboru CHANGELOG.md v kořeni balíčku; změny konkrétní verze ukazuje NuGet v release notes balíčku.

Licence

MIT, © Advisor Software s.r.o.

Zdrojový kód, ukázkové šablony a změny verzí: repozitář projektu.

No packages depend on NETReporter.

- Templates: new node property `visibleIf` (a condition, on every node type). It is a text with a binding such as `"{{$.Customer.Note}}"` or `"{{# root.Total > 1000 }}"`; a leading `!` outside the binding negates it (`"!{{$.Invoice.Paid}}"`). The node is rendered only when the condition is true, a hidden node takes no space and its children are not rendered. Falsy values are an empty text, `false`, `0` and any number equal to zero (`0,00`), `ne`, `no`, `n`, `off`, `null`, `[]` and `{}`, anything else is true (unlike `Checkbox`, whose behavior is unchanged), so a non-empty note shows its node. With `repeat` the condition is evaluated for every item (in the item's context); a hidden child of a `Row` with `columns` is replaced by an empty cell so that the column weights do not shift. A condition without a binding gives the warning `NRP031` (`DiagnosticCodes.ConditionWithoutBinding`, both modes) unless it is `true`/`false`/`1`/`0`/`ano`/`ne`/`yes`/`no`. In the debug mode (`RenderBindingsAsExpressions`) nothing is hidden: the condition is evaluated with the real evaluator and the node is only marked: the HTML classes `nr-conditional` / `nr-hidden`, the attribute `data-nr-visible-if` and the debug CSS, the JSON `attributes.visibleIf` and `attributes.hidden`. `IRenderSink` has the new default methods `BeginConditional(string, bool)` and `EndConditional()` (custom sinks keep compiling; they receive the calls in the debug mode only). `Node.VisibleIf` is a new init property of the AST. - Templates: new properties `filter` and `sortBy` of `repeat` (on any node) and of the table rows (`rows`). `filter` is a text with a binding evaluated in the context of the item (alias `as`); a leading `!` negates it and the truthiness rules are the same as for `visibleIf`, so `"filter": "{{item.Discount}}"` renders only items with a non-zero discount. `sortBy` is a list of keys separated by commas, each with an optional `asc` (default) or `desc`, e.g. `"item.DueDate desc, item.Number"`; a key is a path (`item.X`, `$.X`), a C# expression with `#` (`#item.Qty * item.Price desc`) or a text with `{{...}}`. The key is evaluated once per item; a text is tried as a number (the template culture, then invariant), then as a date (ISO 8601, then the culture), so `"10"` sorts after `"9"`; mixed values sort as number < date < text < boolean, text is compared by the template culture ignoring case (in Czech `Ch` follows `H`), empty, `null` and missing values are always last (ascending and descending) and the sort is stable. In a table the order is filter -> sort -> group -> summaries, so `totalCount`, `totalSums`, `groupCount`, `groupSums` and `groupSubtotal` count only the filtered rows; grouping still groups only consecutive keys, put the group key first in `sortBy` to get contiguous groups. The index in the paths of diagnostics (`repeat[i]`) and in the element ids (`-rN`) is the position after filtering and sorting. In the debug mode (`RenderBindingsAsExpressions`) the filter is not applied (all items stay visible), the sort is. A filter without a binding gives the warning `NRP031`, an invalid `sortBy` (an empty key, an unknown direction such as `item.X up`, an invalid path) the error `NRP032` (`DiagnosticCodes.InvalidSortBy`, both modes). `RepeatNode.Filter`/`SortBy` and `TableRowsNode.Filter`/`SortBy` are new init properties of the AST. Filtering and sorting of `Chart.source` and the `columns[].visibleIf` of tables are not part of this change. - Bindings: new operator of fallback values `??` outside C# expressions, `{{$.Invoice.OrderNumber ?? '—'}}`, `{{item.Alias ?? item.Name ?? $.DefaultName}}`. The result is the first non-empty operand; an operand is a path, a system variable, a text in single or double quotes (escapes `\'`, `\"` and `\\`) or a number (with a dot). Empty are a missing field, `null`, a text of spaces, `[]` and `{}`; `0` and `false` are not empty. The format after the colon applies to the selected value (a number literal is formatted, a text literal is not): `{{$.Discount ?? 0:N2}}` gives `0,00` or `12,50`. An empty operand, a C# expression as an operand and an unterminated string are binding errors (before, `a ?? b` was an "unexpected character" error, so no existing template changes). `BindingExpression` has the new public records `CoalesceExpr` and `LiteralExpr`; the echo evaluator of the debug mode prints the original text. `??` in `source` (`repeat`, `rows`, `Chart`) gives a warning, in `sortBy` it works as a text with a binding. - Bindings: fixed the format separator in C# expressions: `?` of `??`, `?.` and `?[` was counted as the start of a ternary operator, so the colon of the format was not recognized and the format got lost: `{{# root.A ?? 0 :N2}}` and `{{# root.A?.B :N2}}` now format the value (a ternary, including `?.5`, is unchanged). - Bindings: helper functions in C# expressions (public methods of `CSharpBindingGlobals`, which is now `partial`, callable without qualification): `Slovy(amount, currency = "CZK")` writes an amount in Czech words ("dvanáct tisíc tři sta čtyřicet pět korun českých šedesát sedm haléřů"; CZK and EUR, rounded to hellers away from zero, a negative amount starts with "minus", an empty value gives an empty text), `Plural(n, one, few, many, fraction = null)` picks a Czech form (1, 2-4, other), `Sum`, `Avg`, `Min` and `Max` (array, optional field or dotted path) aggregate the numbers of an array (non-numeric values are skipped, an empty array gives 0 or an empty text), `IfEmpty(value, fallback)` and `Iban(account, grouped = false)` converts a Czech account `[prefix-]number/bank` (modulo 11 check) or normalizes an IBAN (modulo 97 check). A failing call (an invalid IBAN, an unsupported currency, a non-numeric amount) is reported as a warning with the JSON path and gives an empty text. The functions are part of the reference of the public API (`get_library_api`). - Templates: new node `PaymentQr`, a QR code for a payment: the Czech SPAYD 1.0 (QR Platba, CZK, `standard: "Spayd"`, the default) or the European EPC069-12 version 002 (SEPA transfer, EUR, `standard: "Epc"`). Properties: `iban` (required; an IBAN or a Czech account `[prefix-]number/bank`, which is converted with the modulo 11 check), `bic`, `amount`, `currency`, `vs`, `ss`, `ks`, `message`, `recipientName`, `dueDate`, `reference`, `caption` (default "QR Platba" for SPAYD, none for EPC; an empty text means none) and `sizeMm` (default 30), all with bindings. SPAYD keys are sorted alphabetically (`ACC`, `AM`, `CC`, `DT`, `MSG`, `RF`, `RN`, `X-KS`, `X-SS`, `X-VS`), text is ASCII without diacritics and `*` is written as `%2A`; EPC lines are separated by LF, written in UTF-8 without ECI and limited to 331 bytes. The code is generated with error correction M and the quiet zone of 4 modules, in its natural size, and scaled as a vector to `sizeMm` (the generic `Code2D` is unchanged: error correction L, no hints). At run time an invalid IBAN or account, an out-of-range or non-numeric amount, an EPC without a recipient name, an EPC with a currency other than EUR, an EPC with both reference and message and an EPC over 331 bytes mean that **no code is rendered** (the JSON has `valid: false`) and a binding warning with the path of the property is reported (for example `$.content.root.items[0].repeat[3].iban`); an invalid symbol, BIC, date or reference is omitted and a too long text is shortened, with a warning. The validation checks the static values of the node (without a binding) with the same rules as the error `NRP033` (`DiagnosticCodes.InvalidPaymentData`, both modes): an invalid IBAN, amount, currency, symbol, BIC, date or reference, a too long message or name, an EPC without `recipientName` and an EPC with a static reference and message together. In the debug mode (`RenderBindingsAsExpressions`) the code is made from the unevaluated texts without checks and warnings (`preview: true` in the JSON). The node has no `keepTogether`. `IRenderSink` has the new default method `PaymentQr(ResolvedStyle, ResolvedPaymentQr, string?)` that composes a stack of `Code2D` and `Text`, so custom sinks keep working; `PaymentQrNode` and `ResolvedPaymentQr` are new public types. The template editor has a button, an icon and a ready skeleton (SPAYD with the fields of a Czech invoice) for the node. - Templates: new root property `watermark`, an object or an array of objects (drawn in the given order), a text or an image stamp on **every page of all formats**. Properties (section `watermarkProperties` of the schema): `text` or `image` (exactly one; bindings are allowed, the image source is like `Image.src`), `visibleIf`, `color` (default `#40000000`), `font`, `fontSize` (72), `bold` (true), `rotateDeg` (-30, around the centre), `widthMm`, `heightMm`, `align`, `alignVertical` (both `Center`) and `marginMm`. PDF and SVG draw each watermark as its own layer of the page foreground (above `pageOverlay`, which stays PDF and SVG only), HTML as a `div.nr-watermark` with `position:fixed` (the browser repeats it on every printed page; the layer is omitted when `visibleIf` hides the watermark) and JSON as `sections.watermark` plus a `Watermark` node of `documentTree`, present only for templates with a watermark, so the JSON of other templates does not change. The debug mode (`RenderBindingsAsExpressions`) draws a conditional watermark always and marks it. Validation: the new error `NRP034` (`DiagnosticCodes.InvalidWatermark`, both modes) for a missing text and image, both of them, or an empty one; unknown properties `NRP013`, wrong types `NRP014`, enums `NRP015`, colours `NRP029` (also from `meta.palette`), a condition without a binding the warning `NRP031`. `TemplateSchema.WatermarkProperties`, `TemplateDocument.Watermarks` and the public `WatermarkNode` are new, `IRenderSink` is unchanged. - `RenderOptions.Watermark` (`string?`): a literal watermark text (for example `"KONCEPT"`) added after the watermarks of the template on every page of every format with the default look. A text containing `{{` throws `ArgumentException` at the start of the render. The MCP tool `render_report` has the new optional parameter `watermark` for it, and its description (like `validate_data`) says that the root `watermark` is rendered in all formats, unlike `pageOverlay`. - HTML output: colours with transparency written as `#AARRGGBB` or `#ARGB` (the form used by templates and QuestPDF) are converted to CSS `rgba(...)`, and hex without `#` gets the `#`. Browsers read `#AARRGGBB` as `#RRGGBBAA` (a different colour) or ignore it, so text, background, border, line, shadow and checkbox colours with transparency were wrong in HTML before. - Template editor: the Page tab has the group "Vodoznak" (text, `visibleIf`, colour, font size and rotation of a single text watermark; an array or an image watermark is left for the JSON tab and kept unchanged on save), and the JSON editor knows the root property `watermark`. - Samples: new `ceska-faktura` (an anonymised Czech tax invoice in a frame, A4, six items with VAT 12 % and 21 %, a logo as an SVG asset) with matching sample data. It uses the new features: the bank account block and the `PaymentQr` code only while `Invoice.Paid` is false (`visibleIf`), the VAT recapitulation without the zero rate (`repeat.filter`), `{{$.Invoice.OrderNumber ?? '—'}}`, the total in words (`Slovy`) and the watermark ZAPLACENO for a paid invoice. It replaces the `advisor-faktura` sample, which contained real data. - Template editor: the table editor has the new fields *Filtr řádků (filter)* and *Řazení řádků (sortBy)* (`rows.filter`, `rows.sortBy`; `TableEditorModel.RowsFilter` / `RowsSortBy`), and a node with `visibleIf` shows the icon of an eye with a slash in the tree with the condition in its tooltip (`EditableTreeNode.VisibleIf`). The property grid offers `visibleIf` on every node and `filter` / `sortBy` in `repeat` from the schema. Celý seznam změn: CHANGELOG.md v balíčku a https://dev.azure.com/advsoft/Knihovny/_git/NetReporter?path=/src/NetReporter/CHANGELOG.md

Version Downloads Last updated
3.9.2 3 10/28/2026
3.9.1 1 10/27/2026
3.9.0 6 10/20/2026
3.8.1 6 10/19/2026
3.8.0 4 10/19/2026
3.7.0 4 10/19/2026
3.6.0 6 10/17/2026
3.5.1 62 10/15/2026
3.5.0 4 10/15/2026
3.4.2 5 10/15/2026
3.4.0 4 10/12/2026
3.3.0 5 10/11/2026
3.2.2 5 10/10/2026
3.2.1 4 10/10/2026
3.2.0 4 10/10/2026
3.1.1 4 10/09/2026
3.1.0 5 10/09/2026
3.0.1 8 10/08/2026
3.0.0 5 10/08/2026
2.2.1 5 10/05/2026
2.2.0 4 10/05/2026
2.1.0 9 10/04/2026
2.0.1 8 10/04/2026
2.0.0 5 10/03/2026
1.7.0 6 10/03/2026
1.6.0 5 10/02/2026
1.5.0 12 10/02/2026
1.4.0 6 10/01/2026
1.3.0 4 10/01/2026
1.2.0 5 10/01/2026
1.1.1 6 09/30/2026
1.1.0 8 09/30/2026
1.0.10 4 09/29/2026
1.0.9 4 09/29/2026
1.0.8 16 09/29/2026
1.0.7 115 09/24/2026
1.0.6 9 09/24/2026
1.0.5 7 09/24/2026
1.0.4 11 09/24/2026
1.0.3 8 09/24/2026
1.0.2 13 09/23/2026
1.0.0 7 09/23/2026
0.9.1 13 09/23/2026
0.9.0 87 09/14/2026
0.8.1 25 02/20/2026
0.8.1-preview.1 34 02/13/2026
0.7.1-preview.2 32 02/10/2026
0.7.1-preview.1 33 02/10/2026
0.7.0 30 02/10/2026
0.0.6 28 02/01/2026
0.0.5 19 02/01/2026
0.0.0-alpha.0.17 35 02/01/2026