Skip to content

Releases: xshaheen/headless-framework

0.10.0

24 Jun 08:25
b95013c

Choose a tag to compare

This release consolidates everything since 0.5.3 — a large span covering the messaging consumer model, the distributed-locks and coordination substrates, the caching resilience program, the new commit-coordination primitive, and a broad hot-path performance pass. The headline change in 0.10.0 itself is the buffer-first ISerializer redesign.

⚠️ Pre-1.0 software: breaking changes land in minor versions. Review the Breaking Changes section before upgrading.

⚠️ Breaking Changes

Serializer is now buffer-first (#534)

ISerializer no longer takes or returns Stream. The core contract is buffer-based:

// before (Stream-first)
void Serialize<T>(T value, Stream output);
T? Deserialize<T>(Stream data);

// after (buffer-first)
void Serialize<T>(T value, IBufferWriter<byte> output);
T? Deserialize<T>(ReadOnlyMemory<byte> data);
T? Deserialize<T>(in ReadOnlySequence<byte> data);

Migration: most call sites are unaffected — byte[], string, and Stream helpers (SerializeToBytes, SerializeToString, Deserialize<T>(byte[]), Deserialize<T>(string?), Serialize<T>(T, Stream), Deserialize<T>(Stream)) live on as SerializerExtensions (C# 14 extension members). Only custom ISerializer implementations must be rewritten to the buffer overloads. Bonus: SerializeToBytes no longer pays a MemoryStream + ToArray() copy, and byte-array reads happen in place.

Emails: unified provider builder + named senders (#504, #516)

The four bespoke registration extensions are removed in favor of the framework's unified setup-builder grammar (matching Caching / Coordination / Settings / …), and a new Azure Communication Services provider is added:

// before — one bespoke extension per provider
services.AddMailKitEmailSender(config);
services.AddAwsSesEmailSender(awsOptions);
services.AddDevEmailSender("emails.txt");

// after — one entry, exactly one default provider per call
services.AddHeadlessEmails(setup => setup.UseMailkit(config));
services.AddHeadlessEmails(setup => setup.UseAwsSes(awsOptions));
services.AddHeadlessEmails(setup => setup.UseDevelopment("emails.txt"));
services.AddHeadlessEmails(setup => setup.UseAzure(o => o.ConnectionString = "…")); // new ACS provider
Removed Replacement
AddMailKitEmailSender setup.UseMailkit
AddAwsSesEmailSender setup.UseAwsSes
AddDevEmailSender setup.UseDevelopment
AddNoopEmailSender setup.UseNoop

Zero, multiple, or repeated AddHeadlessEmails on the same IServiceCollection now throws InvalidOperationException at registration (a host resolves exactly one default IEmailSender). Named senders are supported alongside the required default and resolved via the new IEmailSenderProvider:

services.AddHeadlessEmails(setup =>
{
    setup.UseAwsSes(awsOptions);                                  // default (required)
    setup.AddNamed("marketing", i => i.UseMailkit(smtpSection));  // named, keyed
});
// resolve: IEmailSenderProvider.GetSender("marketing")  or  [FromKeyedServices("marketing")] IEmailSender

The IEmailSender send contract is unchanged. Custom-provider authors: IEmailProviderOptionsExtension is dropped (use the captured-Action<IServiceCollection> contribution model); MailkitSmtpOptions properties changed initset.

Captcha: provider abstraction + package rename (#503)

The single hard-wired Headless.ReCaptcha package is retired and split into an abstraction + providers:

// before  (package: Headless.ReCaptcha)
services.AddReCaptchaV3(o => {});
// inject IReCaptchaSiteVerifyV3

// after  (packages: Headless.Captcha.Abstractions + Headless.Captcha.ReCaptcha [+ .Turnstile])
services.AddHeadlessCaptcha(b => b.UseReCaptchaV3(o => {}));
// inject IReCaptchaV3Verifier (score) or ICaptchaVerifier (pass/fail)
  • Package id Headless.ReCaptchaHeadless.Captcha.ReCaptcha; new Headless.Captcha.Turnstile (Cloudflare).
  • Removed (no shim): AddReCaptchaV2 / AddReCaptchaV3, IReCaptchaSiteVerifyV2 / IReCaptchaSiteVerifyV3.
  • Call sites should depend on ICaptchaVerifier (pass/fail); the reCAPTCHA v3 numeric score stays on IReCaptchaV3Verifier. At most one default provider plus any number of named providers resolved via ICaptchaProvider.GetVerifier(name).

Domain: non-boxing equality hooks on EqualityBase (#517)

The boxing EqualityComponents() contract is replaced by two typed hooks — removing per-probe boxing and enumerator allocations on every dictionary / HashSet / EF change-tracker key compare:

// before
protected abstract IEnumerable<object?> EqualityComponents();

// after
protected abstract bool EqualityComponentsEqual(T other);   // typed compare, early-exit, no box
protected abstract void BuildHashCode(ref HashCode hash);   // typed hash, no box
  • Value objects now derive from ValueObject<TSelf> (CRTP — no cast in overrides): sealed class Money : ValueObject<Money>. The non-generic ValueObject base is replaced by the IValueObject marker for heterogeneous references.
  • Entities that overrode EqualityComponents() move to the two hooks. Entity<TId> / GetKeys() and EF key mapping are unchanged (the composite-key Entity<TId> de-box is a tracked follow-up).

✨ Features

Caching

  • Resilience program: timeouts, sliding expiration, eager + conditional refresh, tagging, distributed stampede protection, named caches, auto-recovery (#437)
  • Distributed, tag-aware IOutputCacheStore adapter (#451)
  • BCL IDistributedCache adapter (#446)
  • Serve stale values on factory failure (#408)
  • Redis cache entry envelope (#398) and cache entry options envelope (#389)

Messaging

  • Split bus and queue delivery intents (#340)
  • ForMessage registration API (#362) and configure scanned consumers (#387)
  • Universal provider knobs — Cluster 0.4 (#410)
  • Monitored retry lock leases (#430); recover retry rows from dead coordination owners (#421); dead-only reclaim + shared coordination recovery bridge (#449)

Distributed Locks

  • Fencing tokens + Redis semaphores (#368) and Redis reader/writer locks (#342)
  • Providers: PostgreSQL advisory (#391), in-memory + Postgres with shared conformance harness (#393), SQL Server (#406)
  • Distinguish safety-deadline stall from contention in non-blocking acquire (#444)

Coordination & Commit Coordination

  • Provider-agnostic commit coordinator, replacing the ambient-transaction approach (#428)
  • Membership substrate (#416); migrate Jobs node membership & dead-node recovery onto Headless.Coordination (#422)

Jobs

  • Atomic job enqueue via commit coordination (#455)
  • OnNodeDeath consumer input API + per-job policy and lease/status vocabulary alignment (#454, #456)
  • Optional IDistributedLock guard for cron-seed migration (#468)

Blobs & Storage

  • Multiple named blob storages in one DI container (#495)
  • Cloudflare R2 backend + presigned URLs (#476)
  • Unified storage provider setup (#354)

⚡ Performance

  • Buffer-first serializer removes intermediate MemoryStream/ToArray() copies on serialize and reads byte arrays in place (#534)
  • Replace dynamic SQL IN-lists with TVPs + messaging hot-path allocations (#352 via #448)
  • Coordination: targeted single-node liveness via ReadNodeLivenessAsync SPI (#447); co-locate incarnation to drop O(N) Redis GETs (#425)
  • Messaging: drop per-publish payload copy (ASB) and LINQ attribute dict (SQS/SNS) (#529)
  • serializer-json: cache derived options in CollectionItemJsonConverter (#531)
  • Assorted hot-path allocation fixes across packages (#513, #518, #530, #533)

🐛 Fixes

  • Caching: review-driven reliability, perf & correctness hardening (#512)
  • Jobs: honest renewal-loop signals — membership skip + fenced-success flag (#470)
  • Messaging: reject more than one storage provider at bootstrap (#450); publish callback response values (#395); hourly monitoring bugs (#423); integration-test reliability (#363)
  • Coordination: retry SQL Server membership deadlocks (#435)
  • Postgres: harden advisory savepoint detection (#432)
  • Blobs/TUS: Azure blob & TUS review fixes (#496)
  • Dashboards: harden SPA build automation (#494); vue-tsc 3 import fix (#497)

♻️ Refactoring

  • ORM/EF: internalize context runtime (#433); two-tier event dispatch — domain-event bus + integration-event outbox bridge (#407)
  • Messaging: broker-neutral message names (#361); order-independent consumer registration (#420); MessageOptions rename (#394)
  • Core: backend-keyed storage GUIDs (#414)
  • Distributed locks: builder-only setup API (#415)
  • Review hardening pass across Blobs, NATS, PostgreSQL messaging storage, Emails (#506)

🧪 Tests & 📚 Docs

  • Distributed-locks provider test plan + fencing-token / race-condition regression coverage (#413, #412, #452, #409)
  • Messaging consume-dispatch benchmark harness (#514)
  • Conform docs/llms + package READMEs to the authoring shape; add provider tables + end-to-end examples (#532)

🔧 Build & CI

  • Route hooks and CI through Make (#457); pre-push build/format guardrails + CI format gate (#434)
  • Automate dashboard SPA build + npm dependency updates (#479)
  • Prune prerelease packages workflow; CodeQL on PRs only

⬆️ Dependencies & Security

Contributors: @xshaheen, with dependency updates from @dependabot.

Full Changelog: 0.5.3...0.10.0

0.5.3

23 May 23:32
a007173

Choose a tag to compare

What's Changed

  • fix(caching-memory): align InMemoryCache set key-type predicate by @xshaheen in #325
  • docs(orm): document HeadlessDbContext is intentionally not poolable by @xshaheen in #326
  • feat(distributed-locks): improve lock acquisition ergonomics by @xshaheen in #322
  • feat(management): configure EF storage schema by @xshaheen in #327
  • feat(distributed-locks): add lease monitoring by @xshaheen in #330

Full Changelog: 0.5.2...0.5.3

0.4.18

20 May 00:45
10ab060

Choose a tag to compare

  • feat(idempotency): rewrite as Stripe-style replay in new Headless.Api.Idempotency package by @xshaheen (#286)
  • feat(messaging): replace filters with typed middleware pipeline (+ distributed-locks docs) by @xshaheen (#285)
  • refactor(api): move tenant enforcement to authorization by @xshaheen (#281)
  • docs(messaging): refresh Phase 1 LLM doc + provider capability matrix by @xshaheen (#277)
  • feat(messaging-otel): add IActivityTagEnricher tag-enrichment pipeline by @xshaheen (#275)

🧰 Maintenance

Contributors

@dependabot[bot], @xshaheen and dependabot[bot]

0.4.17

18 May 00:40

Choose a tag to compare

  • No changes

Contributors

No contributors

0.4.16

18 May 00:27

Choose a tag to compare

Full Changelog: 0.4.15...0.4.16

0.4.15

18 May 00:09

Choose a tag to compare

  • No changes

Contributors

No contributors

0.4.14

17 May 23:46

Choose a tag to compare

  • feat(messaging): add retry policy contract by @xshaheen (#254)
  • refactor(api): split Headless.Api into Core + ServiceDefaults by @xshaheen (#261)
  • feat: add headless tenancy configuration by @xshaheen (#245)

Contributors

@xshaheen

0.4.13

12 May 15:53

Choose a tag to compare

What's Changed

  • ci(deps): bump release-drafter/release-drafter from 7.2.1 to 7.3.0 in the actions-minor-patch group by @dependabot[bot] in #247
  • build(deps): bump dotnet-sdk from 10.0.100 to 10.0.203 by @dependabot[bot] in #246
  • refactor(orm): simplify headless save pipeline by @xshaheen in #248

Full Changelog: 0.4.12...0.4.13

0.0.90

06 Oct 11:30

Choose a tag to compare

🧰 Maintenance

👨🏼‍💻 Contributors

@xshaheen

0.0.7

05 Mar 09:07
a0a863f

Choose a tag to compare

🧰 Maintenance

👨🏼‍💻 Contributors

@dependabot[bot], @xshaheen and dependabot[bot]