Wiaoj.Pagination 0.2.0-alpha.3

This is a prerelease version of Wiaoj.Pagination.
dotnet add package Wiaoj.Pagination --version 0.2.0-alpha.3
                    
NuGet\Install-Package Wiaoj.Pagination -Version 0.2.0-alpha.3
                    
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="Wiaoj.Pagination" Version="0.2.0-alpha.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Wiaoj.Pagination" Version="0.2.0-alpha.3" />
                    
Directory.Packages.props
<PackageReference Include="Wiaoj.Pagination" />
                    
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 Wiaoj.Pagination --version 0.2.0-alpha.3
                    
#r "nuget: Wiaoj.Pagination, 0.2.0-alpha.3"
                    
#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 Wiaoj.Pagination@0.2.0-alpha.3
                    
#: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=Wiaoj.Pagination&version=0.2.0-alpha.3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Wiaoj.Pagination&version=0.2.0-alpha.3&prerelease
                    
Install as a Cake Tool

Wiaoj.Pagination

A foundational .NET library that provides domain primitives, request models, and response structures for offset-based and keyset (cursor-based) pagination.

Wiaoj.Pagination contains no dependencies on ASP.NET Core or Entity Framework Core, making its contracts safe for use in Domain, Application, and Infrastructure layers.


Features

  • Offset Pagination: Lightweight PageRequest, PageMetadata, and PagedResult<T> structures with long total record support.
  • Keyset / Cursor Pagination: Forward and backward navigation models (CursorRequest, CursorToken, CursorMetadata, CursorResult<T>).
  • Cryptographic Cursor Signing: HMAC-SHA256 signed cursor tokens (SignedCursorToken) to prevent parameter tampering.
  • Framework-Agnostic Binding: Implements ISpanParsable<T> and IUtf8SpanParsable<T> for route and query string binding in Minimal APIs without framework dependencies.
  • Collection Value Equality: Powered by EquatableArray<T> for content-based equality comparisons.
  • System.Text.Json Support: Built-in converters for all metadata, token, and generic result types.

Installation

dotnet add package Wiaoj.Pagination

Core Types Overview

Category Model Description
Offset PageRequest Represents an input request containing PageNumber and PageSize. Clamps limits and calculates query offsets (CalculateSkip).
PageMetadata Contains pagination state: TotalCount (long), TotalPages (long), PageNumber, PageSize, HasPrevious, and HasNext.
PagedResult<T> Wraps EquatableArray<T> and PageMetadata. Supports projections via .Select(...).
Keyset CursorToken An opaque Base64Url-encoded token representing a sequence position.
CursorRequest Input request containing CursorToken, Limit, and CursorDirection (Forward / Backward).
CursorMetadata Stores window boundaries: StartCursor, EndCursor, HasPrevious, and HasNext.
CursorResult<T> Wraps EquatableArray<T> and CursorMetadata. Supports projections via .Select(...).
Security SignedCursorToken Encapsulates a CursorToken and its 32-byte HMAC-SHA256 signature (Payload.Signature).

Usage Examples

1. Offset-Based Pagination

using Wiaoj.Pagination;
using Wiaoj.Primitives.Collections;

// 1. Create or bind a request (clamped to MaxPageSize: 100)
var request = new PageRequest(pageNumber: 2, pageSize: 20);
int skip = request.CalculateSkip(); // 20

// 2. Fetch data and construct metadata (TotalCount supports 64-bit integers)
long totalRecords = 150;
var metadata = new PageMetadata(totalRecords, request.PageNumber, request.PageSize);

// 3. Wrap items into PagedResult
EquatableArray<string> items = new[] { "Product A", "Product B" };
var result = new PagedResult<string>(items, metadata);

// 4. Project elements while preserving metadata
PagedResult<string> mapped = result.Select(p => p.ToUpperInvariant());

2. Keyset / Cursor-Based Pagination

using Wiaoj.Pagination;
using Wiaoj.Primitives.Collections;

// 1. Parse a cursor from client input
var token = CursorToken.FromUtf8("order_id_10920");
var request = new CursorRequest(token, limit: 25, CursorDirection.Forward);

// 2. Build metadata with window boundaries
var metadata = new CursorMetadata(
    startCursor: CursorToken.FromUtf8("order_id_10900"),
    endCursor: CursorToken.FromUtf8("order_id_10925"),
    hasPrevious: true,
    hasNext: true
);

// 3. Return results
EquatableArray<int> ids = new[] { 10901, 10902, 10903 };
var cursorResult = new CursorResult<int>(ids, metadata);

3. Cryptographically Signed Cursors (HMAC-SHA256)

Use SignedCursorToken to prevent clients from forging pagination keys:

using Wiaoj.Pagination;

ReadOnlySpan<byte> secretKey = "your-32-byte-secret-key-here-!!!"u8;

// 1. Sign a cursor token on emission
var rawToken = CursorToken.FromUtf8("user_id_4401");
SignedCursorToken signed = SignedCursorToken.Sign(rawToken, secretKey);

string clientPayload = signed.ToString(); 
// Output format: "Payload.Signature" (e.g. "dXNlcl9pZF80NDAx.f8A2..._k")

// 2. Verify and extract upon receiving a request
if (SignedCursorToken.TryParse(clientPayload, out var incoming) && 
    incoming.TryUnsign(secretKey, out var verifiedToken))
{
    // Token is authentic and unmodified
    ReadOnlySpan<char> cursorValue = verifiedToken.Value;
}

4. String Parsing & Span Formatting

All request and token types implement standard BCL formatting and parsing interfaces (ISpanParsable<T>, ISpanFormattable, IUtf8SpanFormattable).

// Parsing from strings / spans
PageRequest parsedRequest = PageRequest.Parse("2:50");
CursorRequest parsedCursor = CursorRequest.Parse("dG9rZW4:25:Backward");

// Zero-allocation span formatting
Span<char> charBuffer = stackalloc char[64];
if (parsedRequest.TryFormat(charBuffer, out int charsWritten))
{
    ReadOnlySpan<char> output = charBuffer[..charsWritten]; // "2:50"
}

5. Alternate Span Lookups in Collections

CursorToken supports .NET alternate equality comparisons, allowing lookups in Dictionary<CursorToken, TValue> and HashSet<CursorToken> directly using ReadOnlySpan<char> or ReadOnlySpan<byte>:

var cache = new Dictionary<CursorToken, string>(CursorToken.OrdinalComparer);
cache[CursorToken.FromUtf8("item_99")] = "CachedData";

var lookup = cache.GetAlternateLookup<ReadOnlySpan<char>>();
bool exists = lookup.TryGetValue("item_99".AsSpan(), out string? cached);

6. Standard Query Parameters (PaginationParameters)

Central constants defining query string parameter names for offset and keyset pagination:

using Wiaoj.Pagination;

// Available constants:
// PaginationParameters.Page      => "page"
// PaginationParameters.Size      => "size"
// PaginationParameters.Cursor    => "cursor"
// PaginationParameters.Direction => "direction"
// PaginationParameters.Limit     => "limit"
// PaginationParameters.All       => ["page", "size", "cursor", "direction", "limit"]

JSON Serialization

Pre-configured System.Text.Json converters are registered directly on types.

PagedResult<T> JSON Payload

{
  "items": [
    "Item 1",
    "Item 2"
  ],
  "metadata": {
    "totalCount": 150,
    "pageNumber": 1,
    "pageSize": 2,
    "totalPages": 75,
    "hasPrevious": false,
    "hasNext": true
  }
}

CursorResult<T> JSON Payload

{
  "items": [
    "Item 1",
    "Item 2"
  ],
  "metadata": {
    "startCursor": "bXlfY3Vyc29yXzE",
    "endCursor": "bXlfY3Vyc29yXzI",
    "hasPrevious": false,
    "hasNext": true
  }
}

License

This project is licensed under the MIT License.

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.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Wiaoj.Pagination:

Package Downloads
Wiaoj.Pagination.AspNetCore

Package Description

Wiaoj.Pagination.EntityFrameworkCore

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0-alpha.3 64 9/24/2026
0.2.0-alpha.2 62 9/24/2026
0.2.0-alpha.1 63 9/24/2026
0.1.0-alpha.9 155 9/21/2026
0.1.0-alpha.8 65 9/21/2026
0.1.0-alpha.7 71 9/18/2026
0.1.0-alpha.6 67 9/16/2026
0.1.0-alpha.5 70 9/16/2026
0.1.0-alpha.4 76 9/16/2026
0.1.0-alpha.3 65 9/15/2026
0.1.0-alpha.2 107 9/15/2026
0.1.0-alpha.1 89 9/14/2026
0.0.1-alpha.112-preview 69 9/13/2026
0.0.1-alpha.111-preview 71 9/13/2026
0.0.1-alpha.110-preview 67 9/12/2026
0.0.1-alpha.109-preview 86 9/11/2026
0.0.1-alpha.108-preview 90 9/8/2026
0.0.1-alpha.107-preview 91 9/8/2026
0.0.1-alpha.106-preview 84 9/8/2026
0.0.1-alpha.105-preview 81 9/8/2026
Loading failed