HelpersKit 1.0.0
dotnet add package HelpersKit --version 1.0.0
NuGet\Install-Package HelpersKit -Version 1.0.0
<PackageReference Include="HelpersKit" Version="1.0.0" />
<PackageVersion Include="HelpersKit" Version="1.0.0" />
<PackageReference Include="HelpersKit" />
paket add HelpersKit --version 1.0.0
#r "nuget: HelpersKit, 1.0.0"
#:package HelpersKit@1.0.0
#addin nuget:?package=HelpersKit&version=1.0.0
#tool nuget:?package=HelpersKit&version=1.0.0
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 invalidsignature— 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
- All parameters (including
expires) are sorted by key (ordinal). - The payload is
{basePath}?{key1}={val1}&{key2}={val2}&.... - HMAC-SHA256 is computed over the UTF-8 payload using the UTF-8 secret key.
- 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
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 | Versions 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. |
-
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 |