Skip to content

Zodrules update - #51

Merged
kieronlanning merged 27 commits into
mainfrom
zodrules-update
Oct 7, 2026
Merged

kieronlanning merged 27 commits into
mainfrom
zodrules-update

Conversation

@kieronlanning

Copy link
Copy Markdown
Collaborator

No description provided.

…ntime codegen

JSON Schema export was silently dropping most of the schema. The converter reached into the
library's own types by reflecting on type and field names, and several of those names matched
nothing; GetField returns null rather than throwing, so every miss failed quietly:

- object properties were always empty (looked for _shape; ZodObject.Shape is public)
- property types were empty even once the shape was found (the builders wrap each field schema)
- array Min/Max never exported (MinItemsRule/_minItems do not exist anywhere)
- array element schemas never exported (_elementSchema does not exist)
- optional properties exported as an empty "any" schema (_innerSchema does not exist)
- unions exported with an empty anyOf (_options does not exist, and the cast expected an array)
- optional properties were still listed as required (the check matched the wrapper's type name)

Replaced with type-checked internal seams, which also removed all 17 trim warnings. Both were
symptoms of the same root cause.

Also fixed:

- ZodArray built corrupt error paths for nested element failures: it used the two-argument
  ImmutableArray.CopyTo, which takes a destination index, so the field name was destroyed and a
  null segment introduced. ProblemDetails reported "[0]" instead of "[0].email".
- ZodArray now enforces length constraints before validating elements. A million-element array
  against .Max(10) previously ran a million validations and allocations first.
- CompiledValidator no longer emits a DynamicMethod per call. The expression tree compiled to a
  single Validate call, so it inlined nothing, and the emitted method is never reclaimed.
- ZodTransform no longer puts the exception message into the validation error, which flowed into
  ProblemDetails and out to the client. Cancellation now propagates instead of being swallowed.

Security:

- Regex match timeouts on every pattern the library compiles. RegexRule(string) and the generated
  [RegularExpression] support had none, and the generated path dropped the 2s default that
  RegularExpressionAttribute provides. JSON Schema import was worse: pattern and input are both
  external. The shared budget is 2s; the previous 100ms on Email/Url/Duration threw under load.

Trimming and Native AOT:

- Purview.ZodSharp, .SystemTextJson and .AspNetCore are verified clean and marked IsAotCompatible.
- RegisterFromAssembly is now genuinely trim-safe rather than annotated: the generator records the
  validator type as a typeof in the attribute, so no type is resolved from a string.
- System.Text.Json gained JsonTypeInfo<T> overloads, with the options overloads annotated, and the
  converter resolves a JsonTypeInfo instead of calling reflection-based overloads.
- Purview.ZodSharp.NewtonsoftJson cannot be AOT-compatible and is documented as such.

Packaging and tooling:

- Microsoft.Extensions.* versions now track the target framework; net8.0 was being dragged onto
  .NET 10 assemblies.
- New packed-generator smoke test: the in-repo tests run against the unmerged generator, and pack
  validation only checks the merged assembly is present. That merge has regressed twice.
- The vitest cross-platform suite now runs in CI, and no longer passes when the C# fixtures are
  absent. Its flat directory read never matched a file, so the handshake had never been asserted.

Docs:

- Removed "10x faster than reflection-based validation" (no comparative benchmark exists) and the
  ArrayPool claim (ZeroAllocationHelpers is internal and has no callers).
- Fixed a thread-safety contradiction: the caching page claimed schemas are immutable while the
  guarantees page correctly documents that fluent rule methods mutate the receiver.

ZODSGEN037-043 moved into AnalyzerReleases.Shipped.md under Release 2.1.0.

BREAKING CHANGE: ZodSchemaGeneratedAttribute now takes (Type targetType, Type validatorType). It is
generator-emitted, so it regenerates on rebuild; only hand-written usages need updating.
…rk 1.0.0

Moves from Purview.BuildSdk 1.0.2.2 and Purview.SourceGeneratorFramework
1.0.0-prerelease.54 to the first stable releases of both.

Target frameworks are now selected rather than listed. The repository-local
ZodSharpNetTargetFrameworks property held net8.0;net9.0;net10.0;net11.0, which
is exactly the SDK's 'All' set, so it is replaced by
<PurviewTargetFrameworkSet>All</PurviewTargetFrameworkSet> and the two
remaining use sites reference $(PurviewTargetFrameworksAll). The resolved list
is identical - verified before testing - so no package targets change; a
runtime lifecycle change is now an SDK bump instead of an edit here.

Dropped the NamespaceRemoveSuffix opt-outs for 'SourceGenerators' and
'CodeFixes'. The SDK no longer strips Roslyn component names from
RootNamespace, so those removals are dead configuration - and keeping a Remove
for an item that no longer exists invites confusion about whether the SDK still
strips them.

Fixed warnings the previous SDK was hiding, all in code added earlier on this
branch: an unnecessary using in IJsonSchemaArrayInfo and in
CompiledValidatorTests, an unresolved XML doc cref for
JsonPropertyNameAttribute, and five 'var with target-typed new' violations in
RegexRuleTimeoutTests. ZodJsonConverter now resolves its contract through the
generic JsonSerializerOptions.GetTypeInfo<T>() on net11.0, which returns
JsonTypeInfo<T> with no cast; that overload does not exist before .NET 11, so
the older targets keep the non-generic lookup.

Tests: 5004 passing, 0 failed. Build is clean with no warnings.
The shared purview-build/purview-release workflows default to the 10.0.x SDK,
and this repository never overrode it - so the runner only ever had .NET 10.
This repository targets net11.0, so the job could not succeed either way:

  - before global.json pinned an SDK version, the 10.0.x SDK was selected and
    the net11.0 target failed with NETSDK1045 ("does not support targeting
    .NET 11.0");
  - after the pin, resolution fails earlier and louder - "A compatible .NET SDK
    was not found" - and takes down the first dotnet command in the job,
    `dotnet tool install Purview.Build`, with exit code 155.

Both are the same gap: CI was not installing a .NET 11 SDK for the build job.
The standalone cross-platform and packed-generator jobs already requested
11.0.x with preview quality, which is why only the shared jobs failed.

Passing dotnet-version to the shared workflows fixes it. The exact RC version
is used rather than 11.0.x because the shared workflows expose no
dotnet-quality input, so a floating 11.0.x would not resolve a prerelease.
This matches what purview-dev/results already does.
The cross-platform and packed-generator jobs asked for `11.0.x` with quality
`preview`, which resolves the latest *preview*-channel build. A preview sorts
below the rc that global.json pins, so global.json was left unsatisfiable and
both jobs failed the same way the shared build job did: "A compatible .NET SDK
was not found".

They now install exactly what global.json pins, which is the only thing the
first dotnet command in each job can resolve against. The comment claiming
global.json pins no sdk.version was stale - it has pinned one since the net11
SDK was fixed in place.
…packs

The smoke test failed on a clean checkout with NETSDK1004, "Assets file
.../src/src/CodeFixes/obj/project.assets.json not found".

PackZodSharpCodeFixes reaches CodeFixes.csproj through the MSBuild *task*
rather than a ProjectReference, so nothing in ZodSharp.csproj's reference
graph points at it and `dotnet pack` never restores it. The shared pipeline
restores the whole solution before packing, so the release path was unaffected
- only this script, which packs a single project, hit it.

It passed locally for the wrong reason: an earlier solution build had already
written CodeFixes' assets, so the missing restore was invisible on a warm tree
and showed up only on CI, which starts cold. Reproduced locally by deleting
every bin/obj first, which fails identically, and verified the fix the same way.

The script now restores the solution before packing, matching the pipeline.
…ed workflows

The cross-platform and packed-generator jobs were still on actions/checkout@v4
and actions/setup-dotnet@v4 while the shared purview-build and purview-release
workflows use @v7 and @v6. Both were raising the Node 20 deprecation warning -
"forced to run on Node.js 24" - which becomes a failure once the runners drop
the Node 20 shim.

Every workflow across the purview-dev repositories now resolves to one version
per action: actions/checkout@v7, actions/setup-dotnet@v6, oven-sh/setup-bun@v2.
zodsharp was the only repository out of step.

setup-dotnet@v6 takes global-json-file exactly as v4 did, so the SDK still
comes from global.json.
@kieronlanning
kieronlanning merged commit a530d6b into main Oct 7, 2026
3 checks passed
@kieronlanning
kieronlanning deleted the zodrules-update branch October 7, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant