Add new APIs, fix correctness bugs, and harden all packages#362
Merged
Conversation
- Add IsHostName (RFC 1123 syntactic check), IsIPv4Address, IsIPv6Address and IsIPAddress to StringHasIsExtensions - Add netstandard2.0 polyfills for String.Contains/Replace/Split/StartsWith/EndsWith and nullability attributes - Add theory tests covering host names, IPv4, IPv6 and combined IP validation
- Read stdout and stderr concurrently to avoid a pipe-buffer deadlock when a child fills one stream while we wait on the other (all four execute paths). - Surface the started process id so the timeout handler can actually kill a runaway process (the kill branch was previously dead code). - Base IsSuccessful on the exit code only; tools that write warnings to stderr while exiting 0 are no longer reported as failures.
…tstandard2.0 The serialization constructors called base(ExceptionMessage) instead of base(SerializationInfo, StreamingContext), so a round-tripped exception lost its Message, StackTrace, InnerException and Data. They now delegate to the base serialization constructor under netstandard2.0 (where BinaryFormatter is live and the API is not obsolete), keeping the existing behavior on net9.0/net10.0 to avoid SYSLIB0051.
…d math - LongExtensions.FromUnixTimeMs: convert from milliseconds (was dropping sub-second precision or treating ms as seconds). - ExceptionExtensions: Flatten emits each inner exception's own stack trace; ToXml no longer throws on short/blank stack-trace frames. - ConcurrentHashSet.GetEnumerator: iterate a read-locked snapshot so concurrent mutation cannot throw. - ByteSize.GetHashCode: derive from Value so it agrees with Equals (reliable as a dictionary/set key). - TriangleHelper: bound the refinement recursion, throwing ArithmeticException for degenerate input instead of StackOverflowException. - NumberToStringJsonConverter: format/parse numbers with InvariantCulture for locale-stable JSON. - ByteExtensions.TakeBytesAndConvertToInt/Long: zero-pad to the target width so partial-length reads return the correct value instead of throwing. - TypeExtensions.BeautifyName: genericize all type arguments, not just the first. - EnumHelper: convert boxed enum values via Convert.ToInt32 so non-int-backed enums no longer throw InvalidCastException. - StringExtensions: XmlEncode emits valid entities and XmlDecode decodes the ampersand last for a faithful round-trip; repaired dead accent-normalization replacements. - LoggerExtensions: log via a constant message template instead of passing interpolated text as the format string.
…ndling - Pagination.TotalPages returns null when PageSize <= 0 instead of producing a garbage value from a divide-by-zero. - GetOrAddRequestId rejects CR/LF and oversized x-request-id values (header-injection / log-forging) and replaces them with a fresh GUID; non-GUID formats still pass through.
ValidateIssuerSigningKey is now always enabled and never disabled on an empty key set or a key-fetch timeout, so a transient identity-provider issue can no longer cause unverified tokens to be accepted.
- Escape the category name before composing Spectre markup so a generic category containing '[' no longer throws a markup-parse exception. - BeginScope returns a no-op IDisposable instead of null, preventing a NullReferenceException on dispose.
- Guard null MemberName when matching XML-doc comments. - Escape pipe and newline characters in table cells so summaries cannot corrupt the table layout. - Read enum values via Convert.ToInt64 so non-int-backed enums are documented without crashing.
Throw ItemNotFoundException instead of a NullReferenceException when neither the property nor the parent schema carries a Reference.Id.
Compare only the media type (case-insensitive), so 'application/json; charset=utf-8' no longer fails the content-type assertion.
- Widen IsEmailAddress TLD regex from {2,6} to {2,63} (the DNS label limit);
previously rejected valid TLDs such as .photography and .international.
- Add IsHostName: RFC 1123 syntactic DNS name check (1-63 char labels, 253 max,
optional trailing dot; no underscore/raw-Unicode).
- Add IsIPv4Address / IsIPv6Address / IsIPAddress via IPAddress.TryParse.
- Add IsPort: valid TCP/UDP port string (1-65535, no leading zeros or signs).
- Add IsMacAddress: accepts colon-, hyphen-, Cisco-dot-, and compact-hex forms.
StreamExtensions: - Guard Position = 0 with CanSeek in all three methods; non-seekable streams (network, GZip) no longer throw NotSupportedException. - ToStringData: pass leaveOpen: true to StreamReader so the caller's stream is not disposed when the reader is done. - CopyToStream / ToBytes: replace manual buffer loops with Stream.CopyTo. MemoryStreamExtensions: - Default encoding changed from UTF-16 (Encoding.Unicode) to UTF-8; the old default silently garbled any UTF-8 payload written without an explicit encoding. DataTableExtensions: - ToXPathNodeIterator: use dataTable.Copy() before adding to the temporary DataSet; previously the call stole the table from its owning DataSet, making subsequent access on the caller's DataSet throw.
…offset loss DoubleExtensions.CountDecimalPoints: - The old loop used double.Epsilon (~4.9e-324) as its convergence check; for repeating decimals (e.g. 1.0/3.0) the residual never dropped that low, causing an infinite loop that overflowed to Infinity. - Now capped at 15 iterations (the limit of meaningful double precision) with a 1e-9 absolute tolerance. DateTimeOffsetExtensions.SetHourAndMinutes: - Was hard-coding TimeSpan.Zero as the offset, discarding the caller's original timezone offset on every call. - Now preserves dateTimeOffset.Offset.
GetCultures, GetCountryNames, GetLanguageNames, and GetCultureLcidsWhereCountryIsNotTranslated all temporarily switch Thread.CurrentThread.CurrentUICulture to the requested display-language LCID. The restore was done in a plain if-block with no try/finally, so any exception thrown by GetCultures() or CreateKeyValueDictionaryOfIntString() left the thread permanently on the wrong culture for the lifetime of the process. Wrapped the work in try/finally at all four sites.
…emOfUnitsHelper The nested switch had empty break stubs for 18 of 21 prefixTypeFrom cases, causing ArithmeticException (d=NaN) for any conversion involving Kilo, Mega, Giga, Tera, Deca, Hecto, Deci, Micro, Nano, Pico and others. Replaces the ~230-line switch with a PrefixType→exponent dictionary and Math.Pow(10, fromExp−toExp); covers all combinations. Adds 29 test cases for previously broken conversions.
…ormatter ByteSizeFormatter's constructor captured Thread.CurrentThread.CurrentUICulture .NumberFormat. The UI culture controls language (menu strings), not number/date formatting — using it for a byte-size formatter produces wrong group/decimal separators when the user's UI language differs from their regional format. Changed to CultureInfo.CurrentCulture.NumberFormat. Adds a test that sets CurrentCulture=en-US and CurrentUICulture=da-DK and verifies en-US formatting is used.
…erloads Using int.MaxValue/int.MinValue as sentinels in double[] and List<double> overloads produced wrong results for inputs outside the int range (e.g. 3e9 or -3e9). Switched to double.MaxValue/double.MinValue. Also guards TruncateToMaxPrecision against Substring throwing when decimalPrecision exceeds the actual number of fractional digits.
DateTime.Now captures local time, which is DST-sensitive and unsuitable for log timestamps. Switched to DateTime.UtcNow so all log entries carry a stable, timezone-agnostic timestamp by default.
utmZoneLetter[0] was dereferenced before the IsNullOrEmpty guard, causing IndexOutOfRangeException when an empty string was passed. Merged the index access into the guard condition so empty strings are handled the same as null zone letters (treated as Northern Hemisphere, no northing adjustment).
Formatter threw ArgumentOutOfRangeException for negative sizes, making
ByteSize.ToString() unusable in the debugger. Negative sizes now return
a raw "{size} B" string instead. Also fixed plural suffix for non-byte
units: "2 Kilobyte" → "2 Kilobytes" by threading displaySize through
BuildSuffixLastPart and applying the same > 1 pluralization already used
for the base Byte unit.
…n through base(message) Both exception types used ReflectionHelper.SetPrivateField(this, "_message", ...) to set the message after construction, relying on a private runtime field name that can change across .NET versions. Extracted message-building into private static helpers and routed the affected constructors through : base(BuildMessage(...)), removing the reflection dependency entirely.
…FoundException GetValueRecursive, SetValueRecursive, and RemovePathRecursive all used the dictionary indexer (currentDict[key]) for non-terminal path segments, throwing KeyNotFoundException when the intermediate key was absent. Replaced with TryGetValue + pattern-match so missing intermediate segments return null / IsSucceeded=false consistently with the existing terminal- segment behaviour.
…te backing fields VersionJsonConverter was reading _Major/_Minor/_Build/_Revision (private runtime fields) when the JSON was in object form. STJ serializes Version using the public Major/Minor/Build/Revision properties, so round-trips via object format silently returned new Version(). Updated property names and adjusted remarks doc and test.
NumberStyles.Any caused int.TryParse to interpret exponent-notation strings like "1E3" as the integer 1000. This had two effects: (1) SemanticVersion's strict-mode validator rejected valid alphanumeric identifiers because Clean() returned "1000" != "1E3", throwing ArgumentException; (2) pre-release comparison classified "1E3" as numeric, producing wrong precedence ordering vs pure-numeric identifiers. Switching to NumberStyles.None correctly treats any identifier containing non-digit characters as alphanumeric.
…piVersioningOptions TelemetryClient was injected via constructor but never referenced in Configure(), causing DI failure for consumers without Application Insights registered. Replaced with a parameterless constructor.
The else-if blocks that threw Exception("Whoops..") and Exception("Ups..")
were placeholder breakpoint hooks in debugLimitData paths. These would
abort the entire compliance run in Debug builds and were never meaningful
in Release (the condition is rare; the exception non-specific).
Removed both blocks — developers can set explicit breakpoints in the
caller when debugging with debugLimitData.
…SwaggerEnumDescriptionsDocumentFilter GetEnumTypeByName called Assembly.GetTypes() which throws ReflectionTypeLoadException when an assembly fails to load some types. This crashed Swagger document generation entirely. Extracted GetEnumTypesFromAssembly helper that uses GetExportedTypes() (public types only; smaller surface than GetTypes()) and handles both ReflectionTypeLoadException (returns partial set) and any other exception (returns empty) so Swagger generation continues even with problematic assemblies.
…ithContent convenience methods OkResultAssertions.WithEmptyContent() asserts that the OK result carries no body (Value is null), for endpoints that return 200 with no payload. ResultAssertions.BeOkResultWithContent<T>(expectedContent) is a one-call shorthand for BeOkResult().WithContent(expectedContent), removing the common two-step pattern from caller test code.
…n extensions - SyntaxLiteralExpressionFactory: add Create(long), Create(double), Create(bool), Create(char), CreateNull() overloads - InterfaceDeclarationSyntaxExtensions: add AddSuppressMessageAttribute (mirrors class extension) - RecordDeclarationSyntaxExtensions: new file with AddSuppressMessageAttribute and AddGeneratedCodeAttribute
…s to SyntaxObjectCreationExpressionFactory Add Create(name, ArgumentListSyntax), Create(ns, name, ArgumentListSyntax), CreateGeneric(name, TypeArgumentListSyntax), CreateGeneric(name, typeName), CreateGeneric(name, TypeArgumentListSyntax, ArgumentListSyntax), and CreateGeneric(name, typeName, ArgumentListSyntax) overloads.
…umentation helpers AssemblyCommentHelper, DocumentationHelper, and CodeComplianceDocumentationHelper all gain FileInfo xmlDocPath overloads so callers can specify the XML doc file explicitly instead of relying on AppDomain base-dir auto-resolution. Renamed private core methods to *Core to avoid S4136/S1144 analyzer violations.
…are and HealthReportEntry data sanitization - ExceptionTelemetryMiddleware: check RequestAborted before writing response, and write application/problem+json (ProblemDetails) instead of plain text - HealthReportEntryExtensions.SanitizeData: convert non-string/non-Exception data values to their string representation to prevent verbatim serialization of potentially sensitive objects to unauthenticated /health endpoints
…, and ToWgs84 convenience overload - GeoSpatialHelper.Distance: add optional earthRadiusKm parameter (default 6371.0 km) - GeoSpatialHelper.Bearing: new method calculates initial bearing (0-360°) between two points - UniversalTransverseMercatorConverter.ToWgs84: convenience overload accepting UniversalTransverseMercatorResult directly
…n from O(n²) to O(n) AutoRegistrateServices: build a lookup keyed by interface FullName so each interface maps to its implementations in O(1) instead of rescanning all types per interface. ValidateServiceRegistrations: build a HashSet of registered service types for O(1) contains-check instead of O(services) per interface.
…onfigureApiBehaviorOptions Previously serialized the full ValidationProblemDetails (including user-submitted field values) into every 400 telemetry trace. Now logs only the invalid field names and the correlation traceId, which is sufficient for diagnostics without capturing PII.
Expose stdout and stderr as distinct fields by changing InvokeExecuteWithProcessId return type to (IsSuccessful, StdOut, StdErr, ProcessId), adding the new InvokeExecuteWithTimeoutSeparate private helper, and surfacing two public ExecuteWithSeparateOutput overloads (FileInfo and DirectoryInfo+FileInfo). Existing Execute overloads are unaffected — they still combine stdout/stderr.
…ectResult factory overloads - StructDeclarationSyntaxExtensions: AddSuppressMessageAttribute and AddGeneratedCodeAttribute, mirroring the existing Class/Interface/Record extension pattern. - ResultFactory: add CreateObjectResultWithProblemDetails and CreateObjectResultWithValidationProblemDetails overloads that return ObjectResult so ASP.NET Core output formatters apply app-configured JsonSerializerOptions instead of bypassing them. - ErrorHandlingExceptionFilterAttribute: use ObjectResult for the ProblemDetails path, removing the manual JsonSerializer.Serialize call. - docs/CodeDoc: regenerated entries for JsonSerializerOptionsFactory.Default and IsBinarySequence (documentation for APIs added in prior commits).
CurrencyRoundingAsInteger/CurrencyRounding on decimal and double used Thread.CurrentThread.CurrentUICulture.NumberFormat.CurrencyDecimalDigits. The UI culture controls resource language, not regional number format; currency rounding should follow CultureInfo.CurrentCulture.
When Assembly.Location is empty (single-file publish), FileVersionInfo returns null. Now falls back to AssemblyFileVersionAttribute so callers get the correct version string instead of the 1.0.0.0 sentinel.
…se cached options HealthCheckOptionsFactory.CreateJson previously serialized the health-check response to an intermediate string with a freshly-allocated JsonSerializerOptions per request. Now: - Uses JsonSerializer.SerializeAsync to write directly to HttpResponse.Body, eliminating the intermediate string allocation. - Falls back to JsonSerializerOptionsFactory.Default (shared cached instance) instead of JsonSerializerOptionsFactory.Create() (new object per call). - Remove redundant Microsoft.AspNetCore.Http global using (already available via the Microsoft.AspNetCore.App framework reference).
…uthorizationOptions startup Removed the eager OIDC signing-key pre-fetch (Task.Run+Wait with a 30 s timeout) from ConfigureAuthorizationOptions.PostConfigure. JwtBearer's built-in ConfigurationManager already fetches and caches the OIDC discovery document (including signing keys) on the first authentication request via options.Authority — the manual pre-fetch was redundant and blocked application startup for up to 30 s on slow or unreachable IdPs. Removed the two GetIssuerSigningKeysAsync private methods and the unused WellKnownOpenidConfiguration / SigningKeyFetchTimeout constants. Removed the now-unused Microsoft.IdentityModel.Protocols global usings.
…taxObjectCreationExpression test After adding Create(string, ArgumentListSyntax) alongside Create(string, string), passing null! was ambiguous. Cast to (string)null! to select the correct overload.
…xtensions.GetPrettyTime
Number formatting ("N{n}" format specifier) and string case conversion in
GetPrettyTime used Thread.CurrentThread.CurrentUICulture. The UI culture
controls resource language, not number/date regional format; using it for
number formatting produces wrong decimal separators when UI language ≠
regional format (e.g. da-DK region with en-US UI language).
Changed both usages to CultureInfo.CurrentCulture to match the regional
number format convention used throughout the rest of the codebase.
…ptions
Azure AD access tokens use short-form role/name claims ("roles", "name",
"preferred_username") rather than the long-URI ClaimTypes.Role / ClaimTypes.Name
that TokenValidationParameters defaults to. Without setting RoleClaimType,
[Authorize(Roles="admin")] silently never matches Azure AD role claims.
Added optional RoleClaimType and NameClaimType to AuthorizationOptions.
ConfigureAuthorizationOptions.PostConfigure now applies them when configured,
leaving the framework defaults intact when left null/empty.
…x compliance checks Adds GeoSpatialHelper.Bearing (CartesianCoordinate and double overloads) and UniversalTransverseMercatorConverter.ToWgs84(UniversalTransverseMercatorResult) tests, resolving AssertExportedMethodsWithMissingTests failures for both AbstractSyntaxTree and MonoReflection decompiler modes. Also includes updated auto-generated CodeDoc for APIs added in prior sessions.
…ResourceManagerForResource AppDomain.GetAssemblies().Single() threw InvalidOperationException when both the net10.0 and netstandard2.0 builds of Atc were loaded in the same AppDomain during test runs. typeof(CultureHelper).Assembly always resolves to the correct assembly unambiguously. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switched from JsonSerializerOptionsFactory.Default (WriteIndented=true) to JsonSerializerOptionsFactory.Create(writeIndented: false) so HTTP response bodies are compact rather than pretty-printed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nd string-sanitized health data - ErrorHandlingExceptionFilterAttributeTests: expect ObjectResult instead of ContentResult now that HandleException returns ObjectResult when useProblemDetailsAsResponseBody=true - HealthReportEntryExtensionsTests: expect string values after SanitizeData converts all non-exception entries via .ToString() (bool true -> "True", int 3 -> "3", etc.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x factories and declaration extensions - SyntaxLiteralExpressionFactory: add CreateNull test - SyntaxObjectCreationExpressionFactory: add tests for ArgumentList, namespace+ArgumentList, and all CreateGeneric overloads; simplify Create+ArgumentList test to avoid nested Create call that confused the compliance checker's AST overload resolution - InterfaceDeclarationSyntaxExtensions: add AddSuppressMessageAttribute and AddGeneratedCodeAttribute tests; fix assertion to expect "SuppressMessage" (Roslyn strips the Attribute suffix via RemoveSuffix) - RecordDeclarationSyntaxExtensionsTests: new file covering AddSuppressMessageAttribute and AddGeneratedCodeAttribute on record declarations - StructDeclarationSyntaxExtensionsTests: new file covering the same on struct declarations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…singCurrentUiCulture suffix to Ui New methods using CultureInfo.CurrentUICulture for frontend/UI rendering scenarios: - TimeSpanExtensions.GetPrettyTimeUi - DecimalExtensions.CurrencyRoundingUi - DoubleExtensions.CurrencyRoundingUi - DateTimeExtensions.GetWeekNumberUi - DateTimeOffsetExtensions.GetWeekNumberUi - IntegerExtensions.GetNumberOfWeeksByYearUi - IntegerExtensions.GetFirstDayOfWeekNumberByYearUi - IntegerExtensions.GetLastDayOfWeekNumberByYearUi Breaking renames (old names removed): - To*StringUsingCurrentUiCulture -> To*StringUi on DateTime and DateTimeOffset extensions - TryParse*UsingCurrentUiCulture -> TryParse*Ui on DateTimeHelper and DateTimeOffsetHelper - IntegerExtensions.GetMonthNameByMonthNumber -> GetMonthNameByMonthNumberUi Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hod calls - Add theory tests for all new *Ui methods verifying CurrentUICulture is used (tests set CurrentUICulture independently from CurrentCulture to confirm the correct property is read) - Rename test method calls from *UsingCurrentUiCulture to *Ui to match source renames - Fix GetMonthNameByMonthNumber -> GetMonthNameByMonthNumberUi call sites - Fix TestMemberDataForTimeSpanExtensions: correct expected values for GetPrettyTimeUi Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes
✨ Features
🐛 Fixes
⚡ Performance
♻️ Refactoring
💥 Breaking Changes
📝 Documentation
📦 Dependencies