Cursor 0.0.4

dotnet add package Cursor --version 0.0.4
                    
NuGet\Install-Package Cursor -Version 0.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cursor" Version="0.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cursor" Version="0.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Cursor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cursor --version 0.0.4
                    
#r "nuget: Cursor, 0.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cursor@0.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cursor&version=0.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Cursor&version=0.0.4
                    
Install as a Cake Tool

Cursor Pagination Library

A C# source generator library that automatically creates async enumeration methods for cursor and offset-based paginated APIs, making it effortless to iterate through paginated results.

Features

  • Automatic Code Generation: Uses C# source generators to create enumeration methods at compile-time
  • 🚀 Zero Runtime Overhead: All code is generated during compilation
  • 🔄 IAsyncEnumerable Support: Modern async streaming with await foreach
  • 🎯 Type-Safe: Full IntelliSense support and compile-time checking
  • 🔌 API Agnostic: Works with any HTTP client library (Refit, HttpClient, etc.)
  • ⚙️ Customizable: Configure parameter names to match your API conventions
  • 📦 AOT Compatible: Fully compatible with Native AOT compilation

Installation

dotnet add package Cursor

Quick Start

1. Define Your Cursor Page Model

Implement the ICursorPage<T> interface for your paginated response:

using Cursor;

public class CursorPage<T> : ICursorPage<T>
{
    public List<T> Items { get; set; } = new();
    public string? NextCursor { get; set; }
}

2. Decorate Your API Methods

Add the [GenerateEnumerator] attribute to methods that return paginated results:

using Cursor;
using Refit;

public interface IExampleApi
{
    [Get("/items")]
    [GenerateEnumerator]
    Task<CursorPage<Item>> ListItemsAsync(
        int? limit = null,
        string? cursor = null,
        CancellationToken cancellationToken = default
    );
}

3. Use the Generated Enumerators

The source generator automatically creates extension methods for you:

var api = RestService.For<IExampleApi>("https://api.example.com");

// Enumerate all items across all pages
await foreach (var item in api.EnumerateItemsAsync())
{
    Console.WriteLine(item);
}

// Or collect all items at once
var allItems = await api.EnumerateItemsAsync().ToListAsync();

// Enumerate pages instead of individual items
await foreach (var page in api.EnumerateItemsPagesAsync())
{
    Console.WriteLine($"Page with {page.Items.Count} items");
}

Requirements

Your API methods must:

  • Return Task<TPage> where TPage implements ICursorPage<T>
  • Follow the naming pattern List*Async (e.g., ListItemsAsync, ListUsersAsync)
  • Have parameters for:
    • limit (or custom name) - page size
    • cursor (or custom name) - pagination token
    • cancellationToken - for async cancellation

Advanced Usage

Custom Parameter Names

If your API uses different parameter names, you can customize them:

[Get("/items")]
[GenerateEnumerator(
    LimitParameterName = "pageSize", 
    CursorParameterName = "nextToken"
)]
Task<CursorPage<Item>> ListItemsAsync(
    int? pageSize,
    string? nextToken,
    CancellationToken cancellationToken
);

[Get("/items")]
[GenerateEnumerator(
    CursorParameterName = "offset"
)]
Task<CursorPage<Item>> ListItemsAsync(
    int? offset,
    int? limit,
    CancellationToken cancellationToken
);

Custom Cursor Page Implementation

You can use any class that implements ICursorPage<T>:

public record CustomCursorPage<T> : ICursorPage<T>
{
    public required List<T> Data { get; init; }
    public string? Cursor { get; init; }

    List<T> ICursorPage<T>.Items => Data;
    string? ICursorPage<T>.NextCursor => Cursor;
}

[Get("/items")]
[GenerateEnumerator]
Task<CustomCursorPage<Item>> ListItemsAsync(
    int? limit,
    string? cursor = null,
    CancellationToken cancellationToken = default
);

Passing Additional Parameters

The generator preserves any additional parameters in the generated methods:

[Get("/items")]
[GenerateEnumerator]
Task<CursorPage<Item>> ListItemsAsync(
    string category,  // Additional parameter
    int? limit = null,
    string? cursor = null,
    CancellationToken cancellationToken = default
);

// Usage
await foreach (var item in api.EnumerateItemsAsync("electronics"))
{
    // Process items from "electronics" category
}

Custom Attributes

You can create your own attribute that inherits from GenerateEnumeratorAttribute:

[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class MyCustomEnumeratorAttribute : GenerateEnumeratorAttribute
{
    public MyCustomEnumeratorAttribute()
    {
        LimitParameterName = "pageSize";
        CursorParameterName = "nextToken";
    }
}

// Usage
[Get("/items")]
[MyCustomEnumerator]
Task<CursorPage<Item>> ListItemsAsync(
    int? pageSize,
    string? nextToken,
    CancellationToken cancellationToken
);

Generated Code

For a method named ListItemsAsync, the generator creates:

  1. EnumerateItemsAsync() - Returns IAsyncEnumerable<T> for iterating individual items
  2. EnumerateItemsPagesAsync() - Returns IAsyncEnumerable<ICursorPage<T>> for iterating pages

Both methods:

  • Automatically handle pagination by following the NextCursor
  • Support CancellationToken for graceful cancellation
  • Allow configuring the page size via an optional pageSize parameter

How It Works

The library uses Roslyn source generators to analyze your code at compile-time:

  1. Finds methods decorated with [GenerateEnumerator] or derived attributes
  2. Validates the method signature and return type
  3. Generates extension methods that handle the pagination loop
  4. The generated code is added to your compilation automatically

No reflection or runtime code generation is involved, making it AOT-compatible and performant.

Examples

Example 1: Basic Usage with Refit

using Cursor;
using Refit;

public interface IGitHubApi
{
    [Get("/users/{user}/repos")]
    [GenerateEnumerator]
    Task<CursorPage<Repository>> ListRepositoriesAsync(
        string user,
        int? limit = null,
        string? cursor = null,
        CancellationToken cancellationToken = default
    );
}

// Usage
var api = RestService.For<IGitHubApi>("https://api.github.com");
await foreach (var repo in api.EnumerateRepositoriesAsync("octocat"))
{
    Console.WriteLine($"{repo.Name}: {repo.Description}");
}

Example 2: Processing Pages

await foreach (var page in api.EnumerateRepositoriesPagesAsync("octocat", limit: 50))
{
    Console.WriteLine($"Processing page with {page.Items.Count} repositories");
    
    // Process entire page at once
    foreach (var repo in page.Items)
    {
        // ...
    }
}

Example 3: With Cancellation

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

try
{
    await foreach (var item in api.EnumerateItemsAsync(cancellationToken: cts.Token))
    {
        // Process item
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation timed out");
}

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Cursor:

Package Downloads
Akiles.ApiClient

Unofficial .NET API Client for Akiles.

Cursor.EntityFrameworkCore

Entity Framework Core integration for cursor-based pagination. Provides extension methods to convert IQueryable queries into cursor-paginated results with support for single and compound keys.

Onomondo.ApiClient

Unofficial .NET API Client for Onomondo.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.4 63 2/17/2026
0.0.3 74 2/10/2026
0.0.2 70 1/27/2026
0.0.1 250 12/16/2025