Refitter 0.8.2
See the version list below for details.
dotnet tool install --global Refitter --version 0.8.2
dotnet new tool-manifest # if you are setting up this repo dotnet tool install --local Refitter --version 0.8.2
#tool dotnet:?package=Refitter&version=0.8.2
nuke :add-package Refitter --version 0.8.2
Refitter
Refitter is a CLI tool for generating a C# REST API Client using the Refit library. Refitter can generate the Refit interface from OpenAPI specifications
Installation:
The tool is packaged as a .NET Tool and is published to nuget.org. You can install the latest version of this tool like this:
dotnet tool install --global Refitter
Usage:
$ refitter --help
USAGE:
refitter [URL or input file] [OPTIONS]
EXAMPLES:
refitter ./openapi.json
refitter https://petstore3.swagger.io/api/v3/openapi.yaml
refitter ./openapi.json --settings-file ./openapi.refitter --output ./GeneratedCode.cs
refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --output ./GeneratedCode.cs
refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --internal
refitter ./openapi.json --output ./IGeneratedCode.cs --interface-only
refitter ./openapi.json --use-api-response
refitter ./openapi.json --cancellation-tokens
refitter ./openapi.json --no-operation-headers
refitter ./openapi.json --no-accept-headers
refitter ./openapi.json --use-iso-date-format
refitter ./openapi.json --additional-namespace "Your.Additional.Namespace" --additional-namespace "Your.Other.Additional.Namespace"
refitter ./openapi.json --multiple-interfaces ByEndpoint
refitter ./openapi.json --tag Pet --tag Store --tag User
refitter ./openapi.json --match-path '^/pet/.*'
refitter ./openapi.json --no-deprecated-operations
refitter ./openapi.json --operation-name-template '{operationName}Async'
refitter ./openapi.json --optional-nullable-parameters
ARGUMENTS:
[URL or input file] URL or file path to OpenAPI Specification file
OPTIONS:
DEFAULT
-h, --help Prints help information
-s, --settings-file Path to .refitter settings file. Specifying this will ignore all other settings (except for --output)
-n, --namespace GeneratedCode Default namespace to use for generated types
-o, --output Output.cs Path to Output file
--no-auto-generated-header Don't add <auto-generated> header to output file
--no-accept-headers Don't add <Accept> header to output file
--interface-only Don't generate contract types
--use-api-response Return Task<IApiResponse<T>> instead of Task<T>
--internal Set the accessibility of the generated types to 'internal'
--cancellation-tokens Use cancellation tokens
--no-operation-headers Don't generate operation headers
--no-logging Don't log errors or collect telemetry
--additional-namespace Add additional namespace to generated types
--use-iso-date-format Explicitly format date query string parameters in ISO 8601 standard date format using delimiters (2023-06-15)
--multiple-interfaces Generate a Refit interface for each endpoint. May be one of ByEndpoint, ByTag
--match-path Only include Paths that match the provided regular expression. May be set multiple times
--tag Only include Endpoints that contain this tag. May be set multiple times and result in OR'ed evaluation
--skip-validation Skip validation of the OpenAPI specification
--no-deprecated-operations Don't generate deprecated operations
--operation-name-template Generate operation names using pattern. When using --multiple-interfaces ByEndpoint, this is name of the Execute() method in the interface
--optional-nullable-parameters Generate nullable parameters as optional parameters
.Refitter File format
The following is an example .refitter
file
{
"openApiPath": "/path/to/your/openAPI", // Required
"namespace": "Org.System.Service.Api.GeneratedCode", // Optional. Default=GeneratedCode
"naming": {
"useOpenApiTitle": false, // Optional. Default=true
"interfaceName": "MyApiClient" // Optional. Default=ApiClient
},
"generateContracts": true, // Optional. Default=true
"generateXmlDocCodeComments": true, // Optional. Default=true
"addAutoGeneratedHeader": true, // Optional. Default=true
"addAcceptHeaders": true, // Optional. Default=true
"returnIApiResponse": false, // Optional. Default=false
"generateOperationHeaders": true, // Optional. Default=true
"typeAccessibility": "Public", // Optional. Values=Public|Internal. Default=Public
"useCancellationTokens": false, // Optional. Default=false
"useIsoDateFormat": false, // Optional. Default=false
"multipleInterfaces": "ByEndpoint", // Optional. May be one of "ByEndpoint" or "ByTag"
"generateDeprecatedOperations": false, // Optional. Default=true
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName} when multipleInterfaces != ByEndpoint
"optionalParameters": false, // Optional. Default=false
"outputFolder": "../CustomOutput" // Optional. Default=./Generated
"additionalNamespaces": [ // Optional
"Namespace1",
"Namespace2"
],
"includeTags": [ // Optional. OpenAPI Tag to include when generating code
"Pet",
"Store",
"User"
],
"includePathMatches": [ // Optional. Only include Paths that match the provided regular expression
"^/pet/.*",
"^/store/.*"
],
"dependencyInjectionSettings": {
"baseUrl": "https://petstore3.swagger.io/api/v3", // Optional. Leave this blank to set the base address manually
"httpMessageHandlers": [ // Optional
"AuthorizationMessageHandler",
"TelemetryMessageHandler"
],
"usePolly": true, // Optional. Set this to true, to configure Polly with a retry policy that uses a jittered backoff. Default=false
"pollyMaxRetryCount": 3, // Optional. Default=6
"firstBackoffRetryInSeconds": 0.5 // Optional. Default=1.0
}
}
openApiPath
- points to the OpenAPI Specifications file. This can be the path to a file stored on disk, relative to the.refitter
file. This can also be a URL to a remote file that will be downloaded over HTTP/HTTPSnamespace
- the namespace used in the generated code. If not specified, this defaults toGeneratedCode
naming.useOpenApiTitle
- a boolean indicating whether the OpenApi title should be used. Default istrue
naming.interfaceName
- the name of the generated interface. The generated code will automatically prefix this withI
so if this set toMyApiClient
then the generated interface is calledIMyApiClient
. Default isApiClient
generateContracts
- a boolean indicating whether contracts should be generated. A use case for this is several API clients use the same contracts. Default istrue
generateXmlDocCodeComments
- a boolean indicating whether XML doc comments should be generated. Default istrue
addAutoGeneratedHeader
- a boolean indicating whether XML doc comments should be generated. Default istrue
addAcceptHeaders
- a boolean indicating whether to add accept headers [Headers("Accept: application/json")]. Default istrue
returnIApiResponse
- a boolean indicating whether to returnIApiResponse<T>
objects. Default isfalse
generateOperationHeaders
- a boolean indicating whether to use operation headers in the generated methods. Default istrue
typeAccessibility
- the generated type accessibility. Possible values arePublic
andInternal
. Default isPublic
useCancellationTokens
- Use cancellation tokens in the generated methods. Default isfalse
useIsoDateFormat
- Set totrue
to explicitly format date query string parameters in ISO 8601 standard date format using delimiters (for example: 2023-06-15). Default isfalse
multipleInterfaces
- Set toByEndpoint
to generate an interface for each endpoint, orByTag
to group Endpoints by their Tag (like SwaggerUI groups them).outputFolder
- a string describing a relative path to a desired output folder. Default is./Generated
additionalNamespaces
- A collection of additional namespaces to include in the generated file. A use case for this is when you want to reuse contracts from a different namespace than the generated code. Default is emptyincludeTags
- A collection of tags to use a filter for including endpoints that contain this tag.includePathMatches
- A collection of regular expressions used to filter paths.generateDeprecatedOperations
- a boolean indicating whether deprecated operations should be generated or skipped. Default istrue
operationNameTemplate
- Generate operation names using pattern. This must contain the string {operationName}. An example usage of this could be{operationName}Async
to suffix all method names with Async. When using multipleIinterfaces=ByEndpoint, This is name of the Execute() method in the interfaceoptionalParameters
- Generate non-required parameters as nullable optional parametersdependencyInjectionSettings
- Setting this will generated extension methods toIServiceCollection
for configuring Refit clientsbaseUrl
- Used as the HttpClient base address. Leave this blank to manually set the base URLhttpMessageHandlers
- A collection ofHttpMessageHandler
that is added to the HttpClient pipelineusePolly
- Set this to true to configure the HttpClient to use Polly using a retry policy with a jittered backoffpollyMaxRetryCount
- This is the max retry count used in the Polly retry policy. Default is 6firstBackoffRetryInSeconds
- This is the duration of the initial retry backoff. Default is 1 second
To generate code from an OpenAPI specifications file, run the following:
$ refitter [path to OpenAPI spec file] --namespace "[Your.Namespace.Of.Choice.GeneratedCode]"
This will generate a file called Output.cs
which contains the Refit interface and contract classes generated using NSwag
Using the generated code
Here's an example generated output from the Swagger Petstore example using the default settings
$ refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode"
using Refit;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Your.Namespace.Of.Choice.GeneratedCode
{
public partial interface ISwaggerPetstore
{
/// <summary>
/// Update an existing pet by Id
/// </summary>
[Put("/pet")]
Task<Pet> UpdatePet([Body] Pet body);
/// <summary>
/// Add a new pet to the store
/// </summary>
[Post("/pet")]
Task<Pet> AddPet([Body] Pet body);
/// <summary>
/// Multiple status values can be provided with comma separated strings
/// </summary>
[Get("/pet/findByStatus")]
Task<ICollection<Pet>> FindPetsByStatus([Query] Status? status);
/// <summary>
/// Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
/// </summary>
[Get("/pet/findByTags")]
Task<ICollection<Pet>> FindPetsByTags([Query(CollectionFormat.Multi)] IEnumerable<string> tags);
/// <summary>
/// Returns a single pet
/// </summary>
[Get("/pet/{petId}")]
Task<Pet> GetPetById(long petId);
[Post("/pet/{petId}")]
Task UpdatePetWithForm(long petId, [Query] string name, [Query] string status);
[Delete("/pet/{petId}")]
Task DeletePet(long petId, [Header("api_key")] string api_key);
[Post("/pet/{petId}/uploadImage")]
Task<ApiResponse> UploadFile(long petId, [Query] string additionalMetadata, StreamPart body);
/// <summary>
/// Returns a map of status codes to quantities
/// </summary>
[Get("/store/inventory")]
Task<IDictionary<string, int>> GetInventory();
/// <summary>
/// Place a new order in the store
/// </summary>
[Post("/store/order")]
Task<Order> PlaceOrder([Body] Order body);
/// <summary>
/// For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
/// </summary>
[Get("/store/order/{orderId}")]
Task<Order> GetOrderById(long orderId);
/// <summary>
/// For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
/// </summary>
[Delete("/store/order/{orderId}")]
Task DeleteOrder(long orderId);
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Post("/user")]
Task CreateUser([Body] User body);
/// <summary>
/// Creates list of users with given input array
/// </summary>
[Post("/user/createWithList")]
Task<User> CreateUsersWithListInput([Body] IEnumerable<User> body);
[Get("/user/login")]
Task<string> LoginUser([Query] string username, [Query] string password);
[Get("/user/logout")]
Task LogoutUser();
[Get("/user/{username}")]
Task<User> GetUserByName(string username);
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Put("/user/{username}")]
Task UpdateUser(string username, [Body] User body);
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Delete("/user/{username}")]
Task DeleteUser(string username);
}
}
Here's an example generated output from the Swagger Petstore example configured to wrap the return type in IApiResponse<T>
$ refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --use-api-response
using Refit;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Your.Namespace.Of.Choice.GeneratedCode.WithApiResponse
{
public partial interface ISwaggerPetstore
{
/// <summary>
/// Update an existing pet by Id
/// </summary>
[Put("/pet")]
Task<IApiResponse<Pet>> UpdatePet([Body] Pet body);
/// <summary>
/// Add a new pet to the store
/// </summary>
[Post("/pet")]
Task<IApiResponse<Pet>> AddPet([Body] Pet body);
/// <summary>
/// Multiple status values can be provided with comma separated strings
/// </summary>
[Get("/pet/findByStatus")]
Task<IApiResponse<ICollection<Pet>>> FindPetsByStatus([Query] Status? status);
/// <summary>
/// Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
/// </summary>
[System.Obsolete]
[Get("/pet/findByTags")]
Task<IApiResponse<ICollection<Pet>>> FindPetsByTags([Query(CollectionFormat.Multi)] IEnumerable<string> tags);
/// <summary>
/// Returns a single pet
/// </summary>
[Get("/pet/{petId}")]
Task<IApiResponse<Pet>> GetPetById(long petId);
[Post("/pet/{petId}")]
Task UpdatePetWithForm(long petId, [Query] string name, [Query] string status);
[Delete("/pet/{petId}")]
Task DeletePet(long petId, [Header("api_key")] string api_key);
[Post("/pet/{petId}/uploadImage")]
Task<IApiResponse<ApiResponse>> UploadFile(long petId, [Query] string additionalMetadata, StreamPart body);
/// <summary>
/// Returns a map of status codes to quantities
/// </summary>
[Get("/store/inventory")]
Task<IApiResponse<IDictionary<string, int>>> GetInventory();
/// <summary>
/// Place a new order in the store
/// </summary>
[Post("/store/order")]
Task<IApiResponse<Order>> PlaceOrder([Body] Order body);
/// <summary>
/// For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
/// </summary>
[Get("/store/order/{orderId}")]
Task<IApiResponse<Order>> GetOrderById(long orderId);
/// <summary>
/// For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
/// </summary>
[Delete("/store/order/{orderId}")]
Task DeleteOrder(long orderId);
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Post("/user")]
Task CreateUser([Body] User body);
/// <summary>
/// Creates list of users with given input array
/// </summary>
[Post("/user/createWithList")]
Task<IApiResponse<User>> CreateUsersWithListInput([Body] IEnumerable<User> body);
[Get("/user/login")]
Task<IApiResponse<string>> LoginUser([Query] string username, [Query] string password);
[Get("/user/logout")]
Task LogoutUser();
[Get("/user/{username}")]
Task<IApiResponse<User>> GetUserByName(string username);
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Put("/user/{username}")]
Task UpdateUser(string username, [Body] User body);
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Delete("/user/{username}")]
Task DeleteUser(string username);
}
}
Here's an example generated output from the Swagger Petstore example configured to generate an interface for each endpoint
CLI Tool
$ refitter ./openapi.json --namespace "Your.Namespace.Of.Choice.GeneratedCode" --multiple-interfaces ByEndpoint
Output
/// <summary>
/// Update an existing pet
/// </summary>
public partial interface IUpdatePetEndpoint
{
/// <summary>
/// Update an existing pet by Id
/// </summary>
[Put("/pet")]
Task<Pet> Execute([Body] Pet body);
}
/// <summary>
/// Add a new pet to the store
/// </summary>
public partial interface IAddPetEndpoint
{
/// <summary>
/// Add a new pet to the store
/// </summary>
[Post("/pet")]
Task<Pet> Execute([Body] Pet body);
}
/// <summary>
/// Finds Pets by status
/// </summary>
public partial interface IFindPetsByStatusEndpoint
{
/// <summary>
/// Multiple status values can be provided with comma separated strings
/// </summary>
[Get("/pet/findByStatus")]
Task<ICollection<Pet>> Execute([Query] Status? status);
}
/// <summary>
/// Finds Pets by tags
/// </summary>
public partial interface IFindPetsByTagsEndpoint
{
/// <summary>
/// Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
/// </summary>
[Get("/pet/findByTags")]
Task<ICollection<Pet>> Execute([Query(CollectionFormat.Multi)] IEnumerable<string> tags);
}
/// <summary>
/// Find pet by ID
/// </summary>
public partial interface IGetPetByIdEndpoint
{
/// <summary>
/// Returns a single pet
/// </summary>
[Get("/pet/{petId}")]
Task<Pet> Execute(long petId);
}
/// <summary>
/// Updates a pet in the store with form data
/// </summary>
public partial interface IUpdatePetWithFormEndpoint
{
[Post("/pet/{petId}")]
Task Execute(long petId, [Query] string name, [Query] string status);
}
/// <summary>
/// Deletes a pet
/// </summary>
public partial interface IDeletePetEndpoint
{
[Delete("/pet/{petId}")]
Task Execute(long petId, [Header("api_key")] string api_key);
}
/// <summary>
/// uploads an image
/// </summary>
public partial interface IUploadFileEndpoint
{
[Post("/pet/{petId}/uploadImage")]
Task<ApiResponse> Execute(long petId, [Query] string additionalMetadata, StreamPart body);
}
/// <summary>
/// Returns pet inventories by status
/// </summary>
public partial interface IGetInventoryEndpoint
{
/// <summary>
/// Returns a map of status codes to quantities
/// </summary>
[Get("/store/inventory")]
Task<IDictionary<string, int>> Execute();
}
/// <summary>
/// Place an order for a pet
/// </summary>
public partial interface IPlaceOrderEndpoint
{
/// <summary>
/// Place a new order in the store
/// </summary>
[Post("/store/order")]
Task<Order> Execute([Body] Order body);
}
/// <summary>
/// Find purchase order by ID
/// </summary>
public partial interface IGetOrderByIdEndpoint
{
/// <summary>
/// For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
/// </summary>
[Get("/store/order/{orderId}")]
Task<Order> Execute(long orderId);
}
/// <summary>
/// Delete purchase order by ID
/// </summary>
public partial interface IDeleteOrderEndpoint
{
/// <summary>
/// For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
/// </summary>
[Delete("/store/order/{orderId}")]
Task Execute(long orderId);
}
/// <summary>
/// Create user
/// </summary>
public partial interface ICreateUserEndpoint
{
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Post("/user")]
Task Execute([Body] User body);
}
/// <summary>
/// Creates list of users with given input array
/// </summary>
public partial interface ICreateUsersWithListInputEndpoint
{
/// <summary>
/// Creates list of users with given input array
/// </summary>
[Post("/user/createWithList")]
Task<User> Execute([Body] IEnumerable<User> body);
}
/// <summary>
/// Logs user into the system
/// </summary>
public partial interface ILoginUserEndpoint
{
[Get("/user/login")]
Task<string> Execute([Query] string username, [Query] string password);
}
/// <summary>
/// Logs out current logged in user session
/// </summary>
public partial interface ILogoutUserEndpoint
{
[Get("/user/logout")]
Task Execute();
}
/// <summary>
/// Get user by user name
/// </summary>
public partial interface IGetUserByNameEndpoint
{
[Get("/user/{username}")]
Task<User> Execute(string username);
}
/// <summary>
/// Update user
/// </summary>
public partial interface IUpdateUserEndpoint
{
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Put("/user/{username}")]
Task Execute(string username, [Body] User body);
}
/// <summary>
/// Delete user
/// </summary>
public partial interface IDeleteUserEndpoint
{
/// <summary>
/// This can only be done by the logged in user.
/// </summary>
[Delete("/user/{username}")]
Task Execute(string username);
}
RestService
Here's an example usage of the generated code above
using Refit;
using System;
using System.Threading.Tasks;
namespace Your.Namespace.Of.Choice.GeneratedCode;
internal class Program
{
private static async Task Main(string[] args)
{
var client = RestService.For<ISwaggerPetstore>("https://petstore3.swagger.io/api/v3");
var pet = await client.GetPetById(1);
Console.WriteLine("## Using Task<T> as return type ##");
Console.WriteLine($"Name: {pet.Name}");
Console.WriteLine($"Category: {pet.Category.Name}");
Console.WriteLine($"Status: {pet.Status}");
Console.WriteLine();
var client2 = RestService.For<WithApiResponse.ISwaggerPetstore>("https://petstore3.swagger.io/api/v3");
var response = await client2.GetPetById(2);
Console.WriteLine("## Using Task<IApiResponse<T>> as return type ##");
Console.WriteLine($"HTTP Status Code: {response.StatusCode}");
Console.WriteLine($"Name: {response.Content.Name}");
Console.WriteLine($"Category: {response.Content.Category.Name}");
Console.WriteLine($"Status: {response.Content.Status}");
}
}
The RestService
class generates an implementation of ISwaggerPetstore
that uses HttpClient
to make its calls.
The code above when run will output something like this:
## Using Task<T> as return type ##
Name: Gatitotototo
Category: Chaucito
Status: Sold
## Using Task<IApiResponse<T>> as return type ##
HTTP Status Code: OK
Name: Gatitotototo
Category: Chaucito
Status: Sold
ASP.NET Core and HttpClientFactory
Here's an example Minimal API with the Refit.HttpClientFactory
library:
using Refit;
using Your.Namespace.Of.Choice.GeneratedCode;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services
.AddRefitClient<ISwaggerPetstore>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://petstore3.swagger.io/api/v3"));
var app = builder.Build();
app.MapGet(
"/pet/{id:long}",
async (ISwaggerPetstore petstore, long id) =>
{
try
{
return Results.Ok(await petstore.GetPetById(id));
}
catch (Refit.ApiException e)
{
return Results.StatusCode((int)e.StatusCode);
}
})
.WithName("GetPetById")
.WithOpenApi();
app.UseHttpsRedirection();
app.UseSwaggerUI();
app.UseSwagger();
app.Run();
.NET Core supports registering the generated ISwaggerPetstore
interface via HttpClientFactory
The following request to the API above
$ curl -X 'GET' 'https://localhost:5001/pet/1' -H 'accept: application/json'
Returns a response that looks something like this:
{
"id": 1,
"name": "Special_char_owner_!@#$^&()`.testing",
"photoUrls": [
"https://petstore3.swagger.io/resources/photos/623389095.jpg"
],
"tags": [],
"status": "Sold"
}
System requirements
.NET 6.0 (LTS)
For tips and tricks on software development, check out my blog
If you find this useful and feel a bit generous then feel free to buy me a coffee ☕
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | 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 was computed. 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. |
This package has no dependencies.
Version | Downloads | Last updated |
---|---|---|
1.4.1 | 55,042 | 11/20/2024 |
1.4.1-preview.62 | 150 | 11/4/2024 |
1.4.0 | 354,672 | 10/14/2024 |
1.4.0-preview.61 | 101 | 10/7/2024 |
1.3.2 | 325,928 | 9/23/2024 |
1.3.2-preview.60 | 48 | 9/23/2024 |
1.3.1 | 48,961 | 9/20/2024 |
1.3.0 | 96,805 | 9/14/2024 |
1.2.1-preview.59 | 60 | 9/13/2024 |
1.2.1-preview.58 | 71 | 9/11/2024 |
1.2.1-preview.57 | 54 | 9/11/2024 |
1.2.1-preview.56 | 68 | 9/9/2024 |
1.2.1-preview.55 | 3,228 | 9/2/2024 |
1.2.1-preview.54 | 4,710 | 8/29/2024 |
1.2.0 | 559,865 | 8/12/2024 |
1.2.0-preview.53 | 5,178 | 8/4/2024 |
1.2.0-preview.52 | 1,840 | 7/29/2024 |
1.1.3 | 359,831 | 7/19/2024 |
1.1.3-preview.51 | 50 | 7/19/2024 |
1.1.2 | 25,452 | 7/17/2024 |
1.1.2-preview.50 | 47 | 7/16/2024 |
1.1.2-preview.49 | 62 | 7/11/2024 |
1.1.1 | 188,752 | 7/6/2024 |
1.1.1-preview.48 | 67 | 7/4/2024 |
1.1.1-preview.47 | 62 | 7/1/2024 |
1.1.1-preview.46 | 59 | 6/28/2024 |
1.1.0.45-preview | 98 | 6/25/2024 |
1.0.2 | 238,547 | 6/13/2024 |
1.0.1 | 48,339 | 6/7/2024 |
1.0.0 | 244,769 | 5/3/2024 |
0.9.9.44-preview | 88 | 4/29/2024 |
0.9.9 | 8,093 | 3/7/2024 |
0.9.8 | 3,688 | 2/27/2024 |
0.9.7 | 71,988 | 2/7/2024 |
0.9.6 | 254 | 1/29/2024 |
0.9.5 | 38,843 | 1/15/2024 |
0.9.4.43-preview | 122 | 1/15/2024 |
0.9.4 | 32,081 | 1/12/2024 |
0.9.3.42-preview | 154 | 1/10/2024 |
0.9.2 | 20,112 | 1/10/2024 |
0.9.1 | 3,895 | 1/9/2024 |
0.9.0 | 3,098 | 1/9/2024 |
0.8.7.41-preview | 158 | 1/3/2024 |
0.8.7.40-preview | 134 | 12/20/2023 |
0.8.7 | 55,031 | 12/18/2023 |
0.8.6.39-preview | 151 | 12/14/2023 |
0.8.6.38-preview | 167 | 12/14/2023 |
0.8.6 | 3,544 | 12/11/2023 |
0.8.5 | 54,747 | 11/23/2023 |
0.8.4 | 390 | 11/7/2023 |
0.8.3 | 249 | 10/31/2023 |
0.8.2 | 436 | 10/9/2023 |
0.8.1 | 893 | 10/4/2023 |
0.8.0 | 1,746 | 9/23/2023 |
0.7.5 | 2,727 | 9/7/2023 |
0.7.4 | 347 | 9/6/2023 |
0.7.3.37-preview | 217 | 8/25/2023 |
0.7.3.36-preview | 239 | 8/25/2023 |
0.7.3.35-preview | 253 | 8/21/2023 |
0.7.3.34-preview | 255 | 8/15/2023 |
0.7.3.33-preview | 236 | 8/12/2023 |
0.7.3 | 2,025 | 8/26/2023 |
0.7.2.32-preview | 215 | 8/7/2023 |
0.7.2 | 3,819 | 8/7/2023 |
0.7.1.31-preview | 250 | 8/2/2023 |
0.7.1.30-preview | 186 | 8/2/2023 |
0.7.1.29-preview | 252 | 8/1/2023 |
0.7.1 | 700 | 8/3/2023 |
0.7.0.28-preview | 177 | 7/28/2023 |
0.7.0.27-preview | 281 | 7/28/2023 |
0.7.0.26-preview | 259 | 7/27/2023 |
0.7.0.23-preview | 242 | 7/27/2023 |
0.7.0.22-preview | 295 | 7/27/2023 |
0.7.0.21-preview | 233 | 7/27/2023 |
0.7.0.20-preview | 277 | 7/27/2023 |
0.7.0 | 418 | 7/31/2023 |
0.6.3 | 1,160 | 7/22/2023 |
0.6.2 | 12,159 | 6/22/2023 |
0.6.1 | 334 | 6/20/2023 |
0.6.0 | 707 | 6/15/2023 |
0.5.30 | 491 | 6/12/2023 |
0.5.29 | 147 | 6/12/2023 |
0.5.28 | 158 | 6/10/2023 |
0.5.27 | 213 | 5/24/2023 |
0.5.26 | 278 | 5/11/2023 |
0.5.25 | 197 | 5/10/2023 |
0.5.3 | 186 | 5/5/2023 |
0.5.2 | 151 | 5/2/2023 |
0.5.1 | 166 | 5/2/2023 |
0.5.0 | 211 | 4/28/2023 |
0.4.2 | 204 | 4/24/2023 |
0.4.1 | 430 | 4/3/2023 |
0.4.0 | 310 | 3/24/2023 |
0.3.17 | 7,427 | 3/24/2023 |
0.3.16 | 255 | 3/22/2023 |
0.3.4 | 215 | 3/22/2023 |
0.3.3 | 302 | 3/17/2023 |
0.3.2 | 269 | 3/16/2023 |
0.3.1 | 244 | 3/14/2023 |
0.3.0 | 236 | 3/14/2023 |
0.2.4-alpha | 270 | 3/1/2023 |
0.2.3-alpha | 179 | 2/27/2023 |
0.2.2-alpha | 221 | 2/25/2023 |
0.2.1-alpha | 247 | 2/25/2023 |
0.2.0-alpha | 196 | 2/24/2023 |
0.1.5-alpha | 251 | 2/18/2023 |
0.1.4-alpha | 292 | 2/17/2023 |
0.1.3-alpha | 272 | 2/17/2023 |
0.1.2-alpha | 241 | 2/17/2023 |