HelpersKit 1.0.0

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

HelpersKit

Lightweight .NET utility library — HMAC-signed URLs, prefixed IDs, date range periods, and calendar link generation.

HelpersKit provides four small, focused building blocks that appear in almost every production application. Each is a pure function or immutable value object with no database interaction, no EF Core, no Dapper, and no DI registration. Zero framework dependencies. Consumed by other Kit packages and any .NET project that needs these primitives.

Table of Contents

Packages

Package Description Target
HelpersKit HMAC-signed URLs, prefixed IDs, date range periods, and calendar link generation. Zero framework dependencies. net8.0, net9.0, net10.0

Installation

dotnet add package HelpersKit

Quick Start

using HelpersKit;

// HMAC-signed URL with 7-day expiry
var url = SignedUrl.Create("/api/v1/invitations/accept", new Dictionary<string, string>
{
    ["token"] = invitation.Token,
    ["email"] = invitation.Email
}, secret: signingKey, expiresAt: DateTime.UtcNow.AddDays(7));

bool isValid = SignedUrl.Verify(incomingUrl, secret: signingKey);

// Crypto-safe prefixed ID
var apiKey = PrefixedId.Generate("sk_live");
// → "sk_live_a1b2c3d4e5f6..."

// Date range
var q1 = new Period(new DateTime(2026, 1, 1), new DateTime(2026, 3, 31));
bool containsFeb = q1.Contains(new Period(new DateTime(2026, 2, 1), new DateTime(2026, 2, 28)));

// Add to Calendar links
var link = CalendarLink.Create(new CalendarEvent
{
    Title = "SLA Deadline: ACME-042",
    StartsAt = DateTime.UtcNow.AddHours(2),
    Duration = TimeSpan.FromMinutes(15)
});
string googleUrl = link.Google();
string icalUri   = link.Ical();

SignedUrl

SignedUrl generates and verifies tamper-proof URLs signed with HMAC-SHA256. Use this for invitation links, password-reset links, one-time download links, or any URL that must not be forged or replayed.

Generate a signed URL

var url = SignedUrl.Create(
    baseUrl: "/api/v1/invitations/accept",
    parameters: new Dictionary<string, string>
    {
        ["token"] = invitation.Token,
        ["email"] = invitation.Email
    },
    secret: configuration["SigningKey"]!,
    expiresAt: DateTime.UtcNow.AddDays(7));

// → "/api/v1/invitations/accept?email=x%40y.com&token=abc&expires=1735689600&signature=sha256hex..."

Parameters in the resulting URL are:

  • All provided key/value pairs (sorted alphabetically)
  • expires — Unix timestamp (UTC) at which the URL becomes invalid
  • signature — lowercase hex HMAC-SHA256 over the entire payload

Verify a signed URL

// Returns false if: expired, signature mismatch, missing params, or empty inputs
bool isValid = SignedUrl.Verify(incomingUrl, secret: configuration["SigningKey"]!);

if (!isValid)
    return Results.Forbid();

Replay protection with maxAge

maxAge rejects URLs that were issued too long ago, even if they have not technically expired yet. Useful when you issue short-lived tokens with a generous expiry and want to prevent them from being used repeatedly over a long window.

// Accept the URL only if it was issued within the last 5 minutes
bool isValid = SignedUrl.Verify(
    incomingUrl,
    secret: signingKey,
    maxAge: TimeSpan.FromMinutes(5));

How signing works

  1. All parameters (including expires) are sorted by key (ordinal).
  2. The payload is {basePath}?{key1}={val1}&{key2}={val2}&....
  3. HMAC-SHA256 is computed over the UTF-8 payload using the UTF-8 secret key.
  4. The digest is appended as lowercase hex signature=....

Verification reverses the process: strips signature, re-sorts the remaining parameters, recomputes the HMAC, and compares using ordinal equality.

PrefixedId

PrefixedId generates crypto-safe, human-readable identifiers with a typed prefix. Use this for API keys, webhook secrets, invitation tokens, or any ID that benefits from being identifiable at a glance.

The random portion is produced by RandomNumberGenerator (CSPRNG) and encoded as lowercase hex. The separator between prefix and value is always _.

Generate an ID

// Default: 32 random bytes → 64 hex characters
var apiKey = PrefixedId.Generate("sk_live");
// → "sk_live_a1b2c3d4e5f6789..."

// Custom length (bytes)
var shortToken = PrefixedId.Generate("tok", length: 16);
// → "tok_a1b2c3d4e5f67890a1b2c3d4e5f67890" (32 hex chars)

Prefixes may contain lowercase letters, digits, and underscores only.

Parse an ID

var parsed = PrefixedId.Parse("sk_live_a1b2c3d4");
// parsed.Prefix → "sk_live"
// parsed.Value  → "a1b2c3d4"

Parse throws FormatException if the string has no separator or an empty value.

Validate an ID

bool isValid = PrefixedId.IsValid("sk_live_a1b2c3d4", expectedPrefix: "sk_live");
// → true

bool isValid = PrefixedId.IsValid("sk_test_xyz", expectedPrefix: "sk_live");
// → false

IsValid never throws — returns false for null, empty, or malformed inputs.

Period

Period is an immutable, closed date range value object. Start must be ≤ End. It implements IEquatable<Period> and IComparable<Period> (ordered by Start, then End).

Construction

var q1 = new Period(new DateTime(2026, 1, 1), new DateTime(2026, 3, 31));

q1.Start;    // 2026-01-01
q1.End;      // 2026-03-31
q1.Length;   // TimeSpan (89 days)

Throws ArgumentException if End < Start.

Factory methods

// Full calendar month (Start = first tick of month, End = last tick of month)
var march = Period.Month(2026, 3);

// ISO 8601 week (Monday–Sunday) containing the given date
var thisWeek = Period.Week(DateTime.UtcNow);

Query methods

var q1  = new Period(new DateTime(2026, 1, 1),  new DateTime(2026, 3, 31));
var feb = new Period(new DateTime(2026, 2, 1),  new DateTime(2026, 2, 28));
var q2  = new Period(new DateTime(2026, 4, 1),  new DateTime(2026, 6, 30));

// Containment
q1.Contains(feb);                    // true — feb is entirely within q1
q1.Contains(new DateTime(2026, 2, 15)); // true

// Overlap
q1.OverlapsWith(feb);               // true
q1.OverlapsWith(q2);                // false

// Overlap region
Period? overlap = q1.Overlap(feb);  // Period(Feb 1, Feb 28) — or null

// Gap between two disjoint periods
Period? gap = q1.Gap(q2);           // Period(Mar 31, Apr 1) — or null if overlapping

// Merge (bounding box)
Period merged = q1.Merge(q2);       // Period(Jan 1, Jun 30)

Transformation methods

All transformations return a new Period; the original is unchanged.

var q1 = new Period(new DateTime(2026, 1, 1), new DateTime(2026, 3, 31));

// Extend the end by a grace period
var withGrace = q1.ExtendEnd(TimeSpan.FromDays(7));
// → Period(Jan 1, Apr 7)

// Extend the start backward
var extended = q1.ExtendStart(TimeSpan.FromDays(7));
// → Period(Dec 25 2025, Mar 31)

// Shift the entire period forward or backward
var nextQuarter = q1.Shift(TimeSpan.FromDays(90));
// → Period(Apr 1, Jun 29)  (preserves Length)

Equality and ordering

var p1 = new Period(new DateTime(2026, 1, 1), new DateTime(2026, 3, 31));
var p2 = new Period(new DateTime(2026, 1, 1), new DateTime(2026, 3, 31));

p1 == p2;        // true
p1.Equals(p2);   // true

// Sorting: ordered by Start, then End
var periods = new[] { q2, q1, feb }.Order().ToList();
// → [feb, q1, q2]

CalendarLink generates "Add to Calendar" links for Google Calendar, Outlook Live, and iCalendar (RFC 5545 data URI). Use this for SLA deadlines, meeting invites, subscription renewal reminders, or any event you want users to save.

var link = CalendarLink.Create(new CalendarEvent
{
    Title       = "SLA Deadline: ACME-042",
    Description = "First response due for critical ticket",
    Location    = "Remote",
    StartsAt    = slaDeadline,              // UTC DateTime
    Duration    = TimeSpan.FromMinutes(15)  // defaults to 1 hour
});

Google Calendar

string url = link.Google();
// → "https://calendar.google.com/calendar/render?action=TEMPLATE&text=SLA%20Deadline%3A%20ACME-042&dates=...&details=...&location=..."

Open this URL in a browser and the user lands directly on the Google Calendar event creation page with all fields pre-filled.

Outlook Live

string url = link.Outlook();
// → "https://outlook.live.com/calendar/0/deeplink/compose?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&subject=...&startdt=...&enddt=...&body=...&location=..."

All date/times are formatted as ISO 8601 with a Z suffix.

iCalendar data URI

string dataUri = link.Ical();
// → "data:text/calendar;charset=utf-8,BEGIN%3AVCALENDAR..."

Decode the URI and you get a standards-compliant RFC 5545 .ics file:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//HelpersKit//CalendarLink//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
UID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@helperskit
DTSTAMP:20260324T120000Z
DTSTART:20260615T140000Z
DTEND:20260615T141500Z
SUMMARY:SLA Deadline: ACME-042
DESCRIPTION:First response due for critical ticket
LOCATION:Remote
END:VEVENT
END:VCALENDAR

Use this as an href on an anchor tag for native calendar app support:

<a href="{{ icalUri }}" download="event.ics">Add to Calendar</a>

Special characters in Title, Description, and Location are escaped per RFC 5545 (backslashes, semicolons, and commas are escaped; newlines become \n).

License

MIT — Copyright (c) 2026 HelpersKit Contributors

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 193 3/26/2026