From 13779fe8bc310f0a36aa2383f2f826144b412beb Mon Sep 17 00:00:00 2001 From: Kieron Lanning Date: Thu, 12 Nov 2026 10:33:27 +0000 Subject: [PATCH] fix(build): stop compiling with Runtime Async, which Mono cannot run 3.0.0-prerelease.5 shipped every net11.0 assembly built with Runtime Async, which Purview.BuildSdk 2.0 turns on by default. Mono does not support it (dotnet/aspnetcore#68067), and Mono is the runtime behind Blazor WebAssembly, so a wasm consumer threw an empty InvalidProgramException from AsyncHelpers.Await the first time it validated a form through ZodValidator. Republishing the same consumer untrimmed reproduced it, so this was not ILLink. Almost every package here can be consumed from the browser - the core package, SystemTextJson, Http and Blazor - so the repository opts out instead of carrying a per-project split that would still leave the core library unusable on WebAssembly. The C# 15 updated memory-safety rules are independent of this and stay on. Proven with purview-dev/value-objects' packed-consumer gate: its Blazor WebAssembly consumer failed on 3.0.0-prerelease.5 and passes on the packages packed from this commit. --- AGENTS.md | 12 +++++++++++- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- src/Directory.Build.props | 12 ++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index acf844d..37d6969 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,17 @@ The build is driven by `Purview.BuildSdk`, imported via `src/Directory.Build.pro - Package versions are managed centrally in `Directory.Packages.props` (Central Package Management). - `global.json` pins the `Purview.BuildSdk` MSBuild SDK version. - Pack layout for all packages is validated by `.config/purview-build.json` (`PackValidation.RequiredContent`). -- **.NET 11 opt-ins.** `Purview.BuildSdk` 2.0 compiles every `net11.0` target with Runtime Async (`runtime-async=on`) and the C# 15 updated memory-safety rules (`MemorySafetyRules=2`). The GA compiler still classifies the latter as a preview feature, so `LangVersion` is `preview`, and `AllowUnsafeBlocks` is on: the STJ generator emits unsafe fast paths, and the two `Unsafe.As` boundaries in the sentinel/required rules use `unsafe` blocks so the audit boundary is visible at the call site. Do not remove either without removing the tier-2 opt-in. +- **.NET 11 opt-ins.** `Purview.BuildSdk` 2.0 compiles every `net11.0` target with Runtime Async (`runtime-async=on`) + unless told otherwise; this repository sets `PurviewRuntimeAsync=false`, because Runtime Async is not supported by + Mono, and Mono is what Blazor WebAssembly runs on + ([dotnet/aspnetcore#68067](https://github.com/dotnet/aspnetcore/issues/68067)). A package built with it throws an + empty `InvalidProgramException` at the first async method a wasm consumer runs (`ZodValidator` validation, for + example). Almost every package here is browser-facing — the core package, SystemTextJson, Http and Blazor — so the + opt-out is repository-wide rather than per project; re-enable it only for a project that can never be consumed from + WebAssembly. The C# 15 updated memory-safety rules (`MemorySafetyRules=2`) stay on, and the GA compiler still + classifies them as a preview feature, so `LangVersion` is `preview`, and `AllowUnsafeBlocks` is on: the STJ generator + emits unsafe fast paths, and the two `Unsafe.As` boundaries in the sentinel/required rules use `unsafe` blocks so the + audit boundary is visible at the call site. Do not remove either without removing the tier-2 opt-in. - **Multithreaded MSBuild is off** (`PurviewMultiThreadedBuild=false`). The .NET 11 GA WebAssembly SDK declares `ReadWasmProps` with a custom `TaskFactory`, which `-mt` refuses to execute out of process, so `Examples.Blazor.Client` cannot build in that mode. Re-enable it once `dotnet/runtime` annotates the task as multithreadable. Before changing build/SDK configuration or adding/moving projects, consult the SDK's own skills, which are committed under `.agents/skills/` (see [Installed skills](#installed-skills-agentsskills)): `purview-buildsdk-configuration`, `purview-buildsdk-project-detection`, `purview-buildsdk-project-layout`. diff --git a/CHANGELOG.md b/CHANGELOG.md index dbce09c..b7f4b73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and Released versions correspond to `v` GitHub releases. +## 3.0.0-prerelease.6 + +### Fixed + +- **Blazor WebAssembly consumers no longer fail with `InvalidProgramException`.** The `net11.0` assemblies were + compiled with Runtime Async, which Mono does not support, so the first async method a WebAssembly consumer ran + (validating a form through `ZodValidator`, for example) threw an empty `InvalidProgramException` before any of its + own code executed. The repository now builds with `PurviewRuntimeAsync=false`; the C# 15 updated memory-safety rules + are unaffected. See [dotnet/aspnetcore#68067](https://github.com/dotnet/aspnetcore/issues/68067). + ## 3.0.0-prerelease.5 ### Added diff --git a/package.json b/package.json index ed4c4fe..8240e94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zodsharp", - "version": "3.0.0-prerelease.5", + "version": "3.0.0-prerelease.6", "private": true, "license": "MIT", "author": { diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 4471c43..30aff3a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,6 +17,18 @@ sentinel/required rules use it deliberately (see ZodSharp.csproj). --> true + + false ZodSharp false