You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
P2.1 — Extract ISagaExecutor interface; register via TryAddSingleton<ISagaExecutor, SagaExecutor>; add SagaExecutorFactory on SharkOption
P2.2 — Expose CronLockTtl on ICronScheduler interface (was only on concrete CronScheduler)
P2.3 — Normalize ConfigureJwt to ConfigureJwt(Action<JwtOptions>) pattern (consistent with every other ConfigureXxx method). Old ConfigureJwt(string, string[], Action<JwtBearerOptions>?) marked [Obsolete].
P2.4 — Normalize ConfigureAuthorization from property to method ConfigureAuthorization(Action<AuthorizationOptions>) (consistent with ConfigureRateLimiter, ConfigureOutputCache, etc.)
P3.3 — Startup warning when UnifiedResultFactory is set without WrapSchemaFactory — prevents OpenAPI schema mismatch
P3.4 — IUnifiedResult XML docs cross-reference UnifiedResult<T> and IUnifiedResultFactory with usage guidance and WrapSchemaFactory tip
feat
FEAT-01 — Security headers middleware (ConfigureSecurityHeaders()) — opt-in support for X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Content-Security-Policy, Permissions-Policy, Strict-Transport-Security, Cross-Origin-Resource-Policy, Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy
FEAT-02 — Framework metrics via System.Diagnostics.Metrics — ISharkMetrics interface with MetricsFactory on SharkOption (consistent with existing IdempotencyStoreFactory/RateLimitStoreFactory patterns); default SharkMetrics uses Meter("Sharkable") with counters for requests, rate-limit rejections, idempotency hit/miss/conflict, auth failures, audit drops, cron runs/failures, saga completed/compensated. Configurable via ConfigureMetrics(). Zero dependencies, AOT-compatible.
FEAT-03 — IAuditSink abstraction — pluggable audit sink for shipping entries to Seq/Elastic/Kafka. AuditSinkFactory on SharkOption, default LoggingAuditSink preserves the current structured-logging behavior. AuditLogEntry record made public.
FEAT-05 — Per-endpoint distributed rate-limit policies — [SharkRateLimit(limit, windowSeconds)] attribute on ISharkEndpoint classes, .SharkRateLimit(limit, windowSeconds) DSL, SharkRateLimitMetadata read by SharkRateLimiterMiddleware overriding global defaults.
FEAT-06 — Per-endpoint idempotency via [SharkIdempotent(ttlSeconds)] attribute — opt-in idempotency per endpoint when global is disabled. [SharkNoIdempotency] attribute unscoped (already supported per-class). Middleware now checks SharkIdempotentMetadata.
FEAT-07 — Response cache profile via [SharkCacheProfile(durationSeconds, varyByHeader, privateOnly)] attribute — emits Cache-Control and optional Vary headers per endpoint group via SharkCacheProfileFilter. Composes with ETag and Output Cache.
FEAT-08 — Request-timeout DSL — opt.RequestTimeoutsConfigure + .SharkRequestTimeout(policyName) + .SharkRequestTimeout(ms) DSL. Thin wrapper over ASP.NET Core AddRequestTimeouts. DefaultRequestTimeoutPolicy for global fallback.
FEAT-09 — Unified validation error shape — ValidationErrorMode.Messages (default, joins with "; ") and ValidationErrorMode.ProblemDetails (RFC 7807 with errors object mapping field → messages). Configurable via SharkOption.ValidationErrorMode.
FEAT-10 — Parallel warmup services — ConfigureWarmup<T>() now callable multiple times; all registered IWarmupService instances run in parallel with individual timeouts during startup. Aggregate failures propagated via AggregateException.
FEAT-11 — Sharkable.Testing NuGet package — SharkTestFactory<TEntryPoint> base WebApplicationFactory, FakeIdempotencyStore, FakeRateLimitStore, UnifiedResultAssertions for fluent test assertions. Separate package.
FEAT-12 — SharkOption.GroupConvention (per-group) + EndpointConvention (per-endpoint) — single place for auth policies, filters, ProducesResponseType conventions.
FEAT-13 — OpenAPI ergonomics — AddOpenApiOperationTransformer() / AddOpenApiSchemaTransformer() methods on SharkOption (no need to touch OpenApiOptions directly). OpenApiExampleFactory hook for example generation.
FEAT-15 — IApiKeyValidator interface with ApiKeyValidationResult — carries claims/roles + optional per-key RateLimitMultiplier. DefaultApiKeyValidator wraps the existing static-array behavior. ApiKeyFilter now accepts the custom validator via DI.
FEAT-14 — Source generator for discovery (tracked as separate RFC; not yet implemented)
Add ISingleton, IScoped, ITransient marker interfaces — classes implementing these are auto-registered in DI (same as [SingletonService] etc.)
PERF-02 — Source-generated JSON context defaults to WriteIndented=false for compact production output (P1)
PERF-04 — Audit middleware caches _redactQueryParams HashSet in ctor instead of allocating per request (P2)
PERF-05 — Audit middleware checks _logger.IsEnabled() before building log strings, avoiding allocations when the category is filtered out (P2)
PERF-06 — Shared ApiKeyValidator service caches stored-key hashes and invalidates via IOptionsMonitor.OnChange, replacing O(keys) SHA-256 per request (P2)
PERF-07 — Validation filter caches ConcurrentDictionary<Type, Func<IServiceProvider, IValidator?>> per argument type, avoiding MakeGenericType + DI lookup per request (P2)
PERF-08 — ETag middleware uses Convert.ToHexString() instead of per-byte StringBuilder + ToString("x2") — one allocation instead of 33 (P3)
PERF-09 — Idempotency middleware uses HashSet<string> for method check and TryGetBuffer() to avoid double body copy (P3)
MEM-04 — MemoryRateLimitStore and MemoryIdempotencyStore implement IDisposable so MemoryCache expiration-scan timers are properly disposed (P2)
MEM-07 — ETag CountingResponseBody disposes its internal MemoryStream spool; using var counting in middleware (P3)
security
SEC-01 — HealthCheckDetailLevel enum (StatusOnly/Description/Full); /healthz defaults to StatusOnly in non-Development, never leaking Exception.Message (P1)
SEC-03 — Startup warning when OpenAPI/Scalar is enabled outside Development (P2)
SEC-04 — Admin endpoints (/_sharkable/jobs, profiler gate) unified on shared ApiKeyValidator service with hot-reload support (P3)
SEC-05 — Configuration validator warns when rate limiting is enabled without proxy ForwardedHeaders configuration (P3)
SEC-06 — JwtHealthCheck caches result for 30 seconds to avoid hammering OIDC discovery endpoint on k8s probes (P3)
fix
UnifiedResultExtension and UnifiedResult static methods now respect UnifiedResultFactory — custom factory implementations are used for all .AsNotFound(), UnifiedResult.Ok(), etc.
BUG-01 — Cron shutdown cancellation token wired into ExecuteJobAsync so in-flight jobs are cancelled on app.StopAsync() (P1)
BUG-02 — Exception handler no longer logs client disconnects as errors and avoids writing to already-started responses (P1)
BUG-03 — Saga compensation now runs with its own CTS (CompensationTimeout, default 60s) — never cancelled by the execution token (P1)
BUG-04 — Cron job timeouts are recorded as "Job timed out" with zero retries; retry delay now respects shutdown via linked token (P2)
BUG-06 — Auto-wrap docs fixed; UseSharkOptions.EnableAutoWrap is now nullable tri-state overriding SharkOption.EnableAutoWrap; [SharkDontWrap] evaluated per endpoint class instead of per merged group (P2)
BUG-07 — MemoryRateLimitStore now supports MaxEntries = -1 to disable the cap (uncapped MemoryCache) (P2)