Skip to content

Releases: SharkableIO/Sharkable

v0.7.0

Choose a tag to compare

@charleypeng charleypeng released this 18 Jul 07:46

feat

  • Plugin system: ISharkPlugin interface with three lifecycle hooks — ConfigureServices (AddShark), ConfigurePipeline (UseShark), ConfigureOpenApi. Three discovery paths: NuGet/assembly auto-scanning, hot-plug folder scanning (opt.ConfigurePlugins(p => { p.ScanOnStartup = true; })), and manual opt.RegisterPlugin(). Per-folder AssemblyLoadContext isolation. opt.DisablePlugin("name") for opt-out. AOT-safe for NuGet/manual paths; folder scanning is JIT-only.

fix

  • P1.1 — Dispose AuditLogBuffer after FlushRemaining() during shutdown — CTS leak eliminated
  • P1.2 — Dispose CronScheduler renewCts after lock renewal task completes
  • P1.3 — Unify ResponseSizeExceededException — single shared type instead of two duplicate inner-class definitions in ETag and Idempotency middleware
  • P1.4 — Move exception handler to end of pipeline (just before MapEndpoints) — catches exceptions from ALL middleware, not just auth and beyond
  • P1.5 — Guard AutoCrudExtension dynamic assembly loading behind AotMode check — prevents reflection-based plugin discovery in AOT

refactor

  • 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.)
  • P2.5 — ICronJobStore.RenewJobLockAsync has default implementation => Task.CompletedTask (matching ISagaStore.RenewLockAsync pattern)
  • P2.6 — Seal SqlSugarOptions, Shark, AssemblyContext, UnifiedResult<T> — public classes not designed for inheritance
  • P2.7 — Remove dead code: Utils.SetupModules() (never called), obsolete .SharkRequestTimeout(int, string?) DSL overload

feat

  • P3.1 — Health checks enabled by default (EnableHealthChecks = true) — every deployed app gets /healthz out of the box
  • P3.2 — Cron job concurrency default changed to SkipIfRunning (was AllowConcurrent) — safer default, prevents overlapping runs
  • 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.MetricsISharkMetrics 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-03IAuditSink 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-11Sharkable.Testing NuGet package — SharkTestFactory<TEntryPoint> base WebApplicationFactory, FakeIdempotencyStore, FakeRateLimitStore, UnifiedResultAssertions for fluent test assertions. Separate package.
  • FEAT-12SharkOption.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-15IApiKeyValidator 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

  • PERF-01DefaultUnifiedResultFactory.Instance singleton; UnifiedResultFactoryHelper.ResolveFactory() caches configured factory at 5 hot-path call sites (P1)
  • 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)
  • PERF-10 — Profiler TrackMemory option (default false) gates per-request GC.GetTotalMemory(false) calls (P3)
  • PERF-11HttpResponseExtension.WriteJsonAsync caches static JsonSerializerOptions instead of allocating per call (P3)

fix (memory & lifetime)

  • MEM-01MemoryIdempotencyStore now owns a private MemoryCache instead of hijacking the app-wide IMemoryCache registration (P1)
  • MEM-02AdaptiveLimitMonitor registered as singleton in DI with autoStart:true; DI handles disposal of the timer + process handle (P2)
  • MEM-03AuditLogBuffer consumer survives flush failures with try/catch + Interlocked-tracked dropped-entry counter (P2)
  • MEM-04MemoryRateLimitStore 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-01HealthCheckDetailLevel enum (StatusOnly/Description/Full); /healthz defaults to StatusOnly in non-Development, never leaking Exception.Message (P1)
  • SEC-02ExceptionHandlerOptions.IncludeExceptionMessage (default false); production error responses return generic "An error occurred." (P2)
  • 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-06JwtHealthCheck 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 writin...
Read more

v0.6.1

Choose a tag to compare

@charleypeng charleypeng released this 18 Jul 07:46

feat

  • EnableAutoWrap defaults to true — all plain return values wrapped in UnifiedResult out of the box
  • Custom IResult (UnifiedResultResult) eliminates one allocation per wrapped response and syncs HTTP status code
  • Per-endpoint opt-out via [SharkDontWrap] attribute (class-level) and .DisableAutoWrap() fluent API (route-level)

refactor

  • UnifiedResultWrapFilter checks endpoint metadata for DisableAutoWrapMetadata before wrapping
  • ProblemDetailsResult reuses UnifiedResultResult for consistent error response writing

Full Changelog: v0.6.0...v0.6.1

v0.6.0

Choose a tag to compare

@charleypeng charleypeng released this 15 Jul 03:36

feat

  • Add unified result extension methods and static factories for 20 HTTP status codes (202, 405, 406, 410, 415, 422, 429, 500–504)
  • Add generic AsStatus() extension and UnifiedResult.Status<T>() static factory for custom HttpStatusCode — covers any status code not explicitly enumerated
  • Add parameterless ConfigureAuditTrail() overload — enables audit trail with default options without requiring a callback
  • Add AuditTrailFormat enum with Default, DotnetLogger, JsonStyle, and Compact presets — configurable via AuditTrailOptions.LogFormat
  • Add SharkOption.ShowStartupBanner (default true) — allows disabling the startup banner

feat (Phase 5 — Lifecycle & startup integrity)

  • #25 Startup banner: Sharkable now prints a formatted banner (version, environment, UTC timestamp) to the console at the end of UseShark()
  • #20 Lifecycle hooks: SharkOption.ConfigureOnStarted() / ConfigureOnStopped() — register callbacks that fire on IHostApplicationLifetime.ApplicationStarted / ApplicationStopping
  • #22 Readiness gate: InternalShark.StartupCompleted opens after all wiring completes; /healthz returns 503 until then
  • #23 Warmup: IWarmupService interface + SharkOption.ConfigureWarmup<T>() — resolves and runs a warmup task synchronously before the readiness gate opens (30s timeout)
  • #27 Eager singleton: [SingletonService(Eager = true)] — eagerly resolves the singleton from DI during UseShark(), before the server accepts requests
  • #26 Pipeline injection points: UseSharkOptions.AddBeforeAuth() / AddAfterAuth() / AddAfterEndpoints() — inject custom middleware at specific pipeline positions
  • #21 Liveness probe: /livez endpoint mapped alongside /healthz — returns {"status":"alive"} unconditionally, not blocked by readiness/shutdown gates
  • #24 DI validation: SharkOption.ValidateOnStart<T>() — calls GetRequiredService<T>() during UseShark(), fails fast on missing registrations

docs

  • Document all unified result extension methods and static factories in EN + ZH
  • Add service registration guide covering [ScopedService], [TransientService], [SingletonService] attributes (EN + ZH)
  • Add lifecycle hooks, startup banner, warmup, eager singleton, pipeline injection, DI validation guide (EN + ZH)
  • Update health checks guide with readiness gate and liveness probe details (EN + ZH)

Full Changelog: v0.5.7...v0.6.0

v0.5.5

Choose a tag to compare

@charleypeng charleypeng released this 02 Jul 16:48

security

  • Add MaxFingerprintBodySize (default 64 KiB) to SharkIdempotencyOptions — prevent OOM via attacker-controlled Content-Length header in idempotency middleware fingerprinting. Fixed chunked-transfer bypass where all Transfer-Encoding: chunked requests hashed to the same fingerprint (now hashes body incrementally with -1 sentinel for unknown length); setter rejects <= 0
  • Replace Thread.Sleep polling with await Task.Delay in graceful shutdown drain — prevent ApplicationStopping thread block (SHARK-SEC-003). Initial fix used .GetAwaiter().GetResult() on the drain task which still blocked the callback; corrected to true fire-and-forget so ApplicationStopping returns immediately.
  • Make SagaExecutor.LockTtl configurable; add LockRenewalInterval with periodic lock extension — prevent split-brain sagas when step duration exceeds lock TTL (SHARK-SEC-004, cross-repo with Sharkable.Cache.Redis). ISagaStore.RenewLockAsync converted from a required interface member to a default interface method (DIM) so existing third-party implementations keep compiling unchanged.
  • Replace unconditional KeyDelete in RedisSagaStore.ReleaseLockAsync and RedisCronJobStore.ReleaseJobLockAsync with check-and-delete Lua script — prevent split-brain when LockTtl expires mid-work (SHARK-SEC-005, cross-repo with Sharkable.Cache.Redis)
  • Add ICronJobStore.RenewJobLockAsync plus CronScheduler.CronLockTtl (default 10 min) and a background renewal task mirroring SagaExecutor — prevent split-brain cron jobs when job duration exceeds lock TTL (SHARK-SEC-005 follow-up, cross-repo with Sharkable.Cache.Redis)
  • BREAKING: Add [CrudAllow] attribute for explicit field allowlist on AutoCrud insertable/updateable — endpoints with Create | Update enabled and zero [CrudAllow] properties now throw InvalidOperationException at startup. Existing entities must mark every writable field with [CrudAllow]. Prevents mass-assignment privilege escalation (SHARK-SEC-006)
  • BREAKING (SHARK-SEC-006 follow-up): Exclude the configured soft-delete column (SqlSugarOptions.SoftDeleteFieldName, default "IsDeleted") from the [CrudAllow] allow-list even when explicitly marked — otherwise an attacker can revive soft-deleted rows by sending the field in a PUT body. Honors [SugarColumn(ColumnName = "...")] renames
  • BREAKING (SHARK-SEC-006 follow-up): AutoCrud POST / and PUT /{id} now return the persisted row re-read from the database instead of the user-controlled request body — previous behavior silently hid server-side defaults (timestamps, identity-generated PK, server-set soft-delete state) from the client
  • Require API key on profiler endpoint /_sharkable/profiler by default; return 404 if no API keys configured (SHARK-SEC-015). Adds SharkOption.ProfilerRequireApiKey (default true); also caps the top slow-requests surface at 50 to bound data exposure
  • Require API key on cron admin endpoint /_sharkable/jobs; redact LastError field to its first 100 characters + ... to prevent business-logic leakage (SHARK-SEC-016). Adds SharkOption.CronAdminRequireApiKey (default true); returns 404 if no API keys are configured
  • BREAKING: Make CronScheduler.Register async — eliminate sync-over-async deadlock risk with distributed stores (SHARK-SEC-017). ICronScheduler.Register is replaced by RegisterAsync returning Task; SharkOption.ConfigureCronJobs callback type changes from Action<ICronScheduler> to Func<ICronScheduler, Task> so the hosted service can await it without blocking on startup. Internal await _store.LoadStateAsync(...) replaces .GetAwaiter().GetResult().
  • Add SharkOption.RequireAuthenticatedByDefault opt-in flag — enforce auth on framework endpoints via fallback policy (SHARK-SEC-011, closes both H-4 and H-6)
  • Add ETagOptions.MaxResponseSize (default 10 MiB) + counting stream + incremental hashing — prevent OOM via huge response bodies (SHARK-SEC-012)
  • Add IMemoryCache SizeLimit (100k) + periodic eviction sweep to MemoryRateLimitStore — prevent slow-loris DoS via unique path explosion (SHARK-SEC-013)
  • Add IMemoryCache SizeLimit (10k) + entry.Size tracking to MemoryIdempotencyStore — prevent TB-DoS via unique idempotency keys (SHARK-SEC-014)
  • Add JWT algorithm allowlist + RequireSignedTokens + RequireExpirationTime + reduce ClockSkew to 30s — prevent algorithm confusion attacks (SHARK-SEC-007)
  • Use CryptographicOperations.FixedTimeEquals for API key comparison — prevent timing oracle (SHARK-SEC-008)
  • Gate ScalarJwtToken / ScalarApiKeyValue to IHostEnvironment.IsDevelopment() — prevent token leakage to public /scalar/v1 UI (SHARK-SEC-009)
  • Implement AuditTrailMiddleware header redaction per RedactHeaders list — credential-bearing headers (Authorization, X-Api-Key, Cookie by default) have their values replaced with *** in audit log output. Header names preserved so reviewers see which credentials were presented (SHARK-SEC-010)
  • Redact RedisHealthCheck description — never expose topology or exception messages on public /healthz (SHARK-SEC-018)
  • Validate connection string at AddSharkableRedis — null-check + default abortConnect=false + optional TLS enforcement (SHARK-SEC-019)
  • Replace JsonSerializer.Serialize<T> with source-generated JsonSerializerContext in RedisIdempotencyStore — Cache.Redis is now AOT-compatible (SHARK-SEC-020 follow-up, cross-repo with Sharkable.Cache.Redis)
  • Remove auto-registration of RedisHealthCheck as IHealthCheck in AddSharkableRedisUseSharkableRedisHealthCheck() is the only way to wire it (SHARK-SEC-021 follow-up, cross-repo with Sharkable.Cache.Redis)
  • Connection string: only override abortConnect=false when the key is absent — respect an explicit abortConnect=true (SHARK-SEC-019 follow-up, cross-repo with Sharkable.Cache.Redis)
  • Replace empty catch in RedisIdempotencyStore deserialization with typed exception handling + tombstone record — prevent silent double-execution on corruption (SHARK-SEC-020)
  • Add UseSharkableRedisHealthCheck() extension — explicit opt-in to wire health check into /healthz (SHARK-SEC-021)
  • Set TTL on RedisSagaStore progress records via RedisStoreOptions.SagaProgressTtl (default 7d) — prevent unbounded memory growth (SHARK-SEC-022)
  • Add AutoCrudSqlSugar.AutoCrudRequireAuthorization opt-in flag — auto-attach .RequireAuthorization() to generated CRUD endpoints. Default false for backward compat; production deployments MUST enable. (SHARK-SEC-023, cross-repo with Sharkable.AutoCrud.SqlSugar)
  • Defense-in-depth: also exclude SafeSoftDeleteField from [CrudAllow] allow-list by case-insensitive name match — protects against entities whose C# property name matches the configured soft-delete column but lacks [SugarColumn] rename (SHARK-SEC-024, cross-repo with Sharkable.AutoCrud.SqlSugar)
  • Add AutoCrudSqlSugar.MaxPageNumber (default 1M) + overflow check on (page - 1) * pageSize — prevent pagination DoS via page=int.MaxValue producing a negative OFFSET (SHARK-SEC-025, cross-repo with Sharkable.AutoCrud.SqlSugar)
  • Redact SqlSugarHealthCheck description — never expose dbType or ex.Message on public /healthz; full diagnostic detail logged at LogWarning for operators only (SHARK-SEC-026, cross-repo with Sharkable.AutoCrud.SqlSugar)
  • Escape SQL LIKE wildcards + cap filter value length (200) + cap IN array size (100) in AutoCrud search — prevent LIKE wildcard DoS and large-IN clause DoS (SHARK-SEC-027, cross-repo with Sharkable.AutoCrud.SqlSugar)
  • Fix ETag CountingResponseBody.FlushAsync duplicate body write on over-cap responses (SHARK-SEC-012 follow-up)
  • Fix MemoryRateLimitStore.IncrementAsync non-atomic increment allowing concurrent bypass (SHARK-SEC-013 follow-up)
  • Replace AuditTrailMiddleware.CaptureHeaders JsonSerializer.Serialize with hand-rolled formatter — AOT-compatible (SHARK-SEC-010 follow-up)
  • Make JWT audience validation mandatory when ConfigureJwt is called — reject empty audience list at config time (SHARK-SEC-007 follow-up)
  • Strip assembly-qualified type name from ExceptionHandlerOptions.GetErrorMessage dev output — prevent fingerprinting of Sharkable.X.Y, Version=1.0.0.0 over the wire (SHARK-SEC-M003)
  • Redact JwtHealthCheck description on /healthz — replace authority URL + ex.Message echoes with generic descriptions; full URL + exception now surface via ILogger.LogWarning for operators only (SHARK-SEC-M004)
  • Pin CultureInfo.InvariantCulture for string.Format in HttpContext.Localize(key, args) — prevent culture-dependent format-string growth attacks via malicious translations (SHARK-SEC-M005)
  • Implement IDisposable on AdaptiveLimitMonitor + wrap Adjust() in try/catch — prevent timer-callback exception tearing the process down + close the Process handle leak (SHARK-SEC-M010)
  • Cap CronExpression.GetNext iteration count at 2.2 M — bound CPU on non-matching patterns (e.g. 0 0 30 2 *) that previously looped 2.1 M times per hosted-service tick (SHARK-SEC-M011)
  • Link SharkCronHostedService host stoppingToken to a per-job CancellationTokenSource — prevent orphaned long-running cron jobs surviving app.StopAsync() and tripping k8s termination grace periods (SHARK-SEC-M012)
  • Collapse CronScheduler three correlated dictionaries (_jobs / _states / _expressions) into one Dictionary<string, JobEntry> under a single lock — eliminate TOCTOU between Register and GetDueJobsAsync (SHARK-SEC-M013)
  • Bound /healthz aggregate HealthCheckService.CheckHealthAsync with a 10 s CancellationTokenSource — prevent a single hung check from keeping the endpoint open and tripping k8s probes (SHARK-SEC-M015)
  • JwtHealthCheck now accepts IHttpClientFactory via DI — reuse a pooled HttpMessageHandler instead of opening a new socket per probe (SHARK-SEC-M016)
  • Default rate-...
Read more

v0.5.4

Choose a tag to compare

@charleypeng charleypeng released this 02 Jul 14:36

security

  • Add 100ms regex timeout to FormatAsGroupName / GetVersionFormat — prevent ReDoS via malicious GroupNameSuffixPattern / VersionFormatPattern
  • Add SafeSoftDeleteField validation in AutoCrudGenerator — reject non-alphanumeric field names to prevent SQL injection

Full Changelog: v0.5.3...v0.5.4

v0.5.3

Choose a tag to compare

@charleypeng charleypeng released this 30 Jun 15:34

feat

  • Add HttpContext.Localize(string key, params object[] args) overload — format args in localized strings
  • Add HttpContext.GetCulture() extension — resolve client culture from Accept-Language header
  • ApiKeyFilter automatically skipped when AuthorizationInterceptorFactory is set — interceptor owns auth entirely
  • Add SqlSugarOptions.MaxPageSize / DefaultPageSize — configurable AutoCrud pagination limits (defaults: 100 / 20)
  • Add SqlSugarOptions.SoftDeleteFieldName — configurable soft delete field (default: "IsDeleted")
  • Add SharkOption.ETagOptions.CacheableMethods / CacheControlHeader / ShouldSkipStatus — ETag method set, cache header, and cacheable status logic are now configurable
  • Add SharkOption.ApiKeyHeaderName — configurable API key request header
  • Add GracefulShutdownOptions.ShutdownStatusCode / DrainPollingInterval — configurable 503 status code and drain polling interval
  • Add SharkOption.ProblemDetailsTypeFactory / ProblemDetailsTitleFactory — delegates for customizing RFC 7807 type URI and title
  • Add SharkOption.DefaultCulture — configurable default locale for error localizer
  • Add SharkRateLimiterOptions.AdaptiveGcHighThreshold / AdaptiveGcLowThreshold / AdaptiveReductionDivisor — GC pressure thresholds and reduction factor for adaptive rate limiting
  • Add ProfilerOptions.MaxEntries — configurable profiler ring buffer size
  • Add SharkIdempotencyOptions.RetryAfterSeconds / ShouldCacheStatus — configurable Retry-After and cacheable status predicate
  • Add SharkOption.HealthCheckPath — configurable health check endpoint path
  • Add TracingOptions.ActivitySourceName — configurable ActivitySource name (separate from ServiceName)
  • Add SharkOption.GroupNameSuffixPattern / VersionFormatPattern / VersionFormatReplacement — customizable endpoint URL naming regexes
  • Remove hardcoded "Sharkable" ActivitySourceName in TracingMiddleware — now uses TracingOptions.ActivitySourceName
  • Add HttpContext.Localize(string key) extension method — easy localization in user endpoints
  • Add LocalizationExtensions — public scaffolding for endpoint-level error translation
  • Add SharkOption.EnableAuthorization (default true) — allows users to opt out of authorization service registration
  • Add SharkOption.ConfigureAuthorization — delegate for customizing AuthorizationOptions (policies, fallback, etc.)
  • Add SharkOption.ScalarJwtToken / SharkOption.ScalarApiKeyValue — configure pre-filled credentials in Scalar UI

docs

  • Add complete error localization guide with IErrorLocalizer implementation example, HttpContext.Localize() usage, and middleware integration pattern (EN + ZH)

fix

  • Fix InvalidOperationException when UseAuthorization is called without AddAuthorization — register AddAuthorization by default (configurable via EnableAuthorization), with factory support via ConfigureAuthorization delegate
  • Fix UnifiedResultWrapFilter always wrapping with status 200 — now uses actual HttpContext.Response.StatusCode
  • Fix AuditTrailMiddleware response header hardcoded to "X-Correlation-Id" — now respects AuditTrailOptions.CorrelationIdHeader

v0.5.1

Choose a tag to compare

@charleypeng charleypeng released this 29 Jun 14:47

Full Changelog: v0.5.0...v0.5.1

v0.3.2

Choose a tag to compare

@charleypeng charleypeng released this 27 Jun 11:00

Full Changelog: v0.3.1...v0.3.2

v0.3.1

Choose a tag to compare

@charleypeng charleypeng released this 27 Jun 10:10

Full Changelog: v0.3.0...v0.3.1

v0.3.0

Choose a tag to compare

@charleypeng charleypeng released this 30 Jun 15:35

What's Changed

Full Changelog: https://github.com/SharkableIO/Sharkable/blob/main/CHANGELOG.md