Repository navigation
Release v22.26.0
Added
-
Select a tenant with
ITenantScope.Beginfor commands, validation, queries, and tenant-scoped reads in background work. The selection takes precedence over request tenant resolution inside the current flow. Dispose to restore the previous tenant; disposal is idempotent, and disposing an outer scope early leaves an active inner tenant selected until it ends (#2747)- Custom
ITenantIdAccessorregistrations are preserved whether they are registered before or after Arc. Explicit tenant scopes require Arc'sTenantIdAccessoras the effective accessor; resolvingITenantScopewith a custom accessor throwsExplicitTenantScopeRequiresArcAccessorinstead of silently selecting a tenant that downstream services cannot see. - Independent HTTP requests reset inherited tenant selection when they enter Arc's request pipeline, before authentication and tenant-bound resolution. On ASP.NET Core this covers all application middleware, but not middleware added by another
IStartupFilterregistered before Arc's. Observable emissions use the subscriber's tenant, not a producer's explicit tenant, on every streaming transport. Tasks and timers started inside a tenant scope keep its tenant after the scope is disposed: do not carry that captured context into unrelated work.
- Custom
-
Configurable access to the command and query introspection catalogs (
/.cratis/commandsand/.cratis/queries) throughCratis:Arc:Introspection(ArcOptions.Introspection) (#2764):Enabled(defaulttrue): set tofalseto leave both routes unmapped.RequireAuthentication(defaultfalse): deny anonymous callers. Arc.Core returns 401; ASP.NET Core uses the configured scheme's challenge behavior.Roles(comma-separated, any one grants access, requiresRequireAuthentication): deny callers without a listed role. Arc.Core returns 403; ASP.NET Core uses the configured scheme's forbid behavior.TrustForwardedIdentityHeaders(defaultfalse): lets the Microsoft Identity Platform header handler authenticate protected catalog requests; enable it only behind a trusted ingress that replaces those headers.
Defaults are unchanged: both catalogs stay mapped and anonymous.
/.cratis/identity-details/schemais not affected. The JVM backend has no equivalent options.
Changed
- Plain minimal API responses that return concepts change from objects such as
{"value":"..."}to primitive values such as"...", matching Arc-generated endpoints and the TypeScript proxies. To keep the object shape for a concept, register your ownJsonConverter<YourConcept>withConfigureHttpJsonOptions; it takes precedence for individual concepts, concept collections, and, when it implements property-name conversion, dictionary keys, whether registered before or afterAddCratisArc(#2755) - Only concept-related converters are added to ASP.NET Core's HTTP JSON options. Your converters keep precedence and the naming policy is unchanged; enums (including
long-backed values),DateOnly,TimeOnly,Uri, and other non-concept types keep ASP.NET Core's defaults on plain minimal APIs (#2755) - A concept query parameter is always supplied by the caller. If you injected a value into a query by registering a concept type in dependency injection, inject a service or accessor instead (#2757)
- Each JavaScript package's
package.jsonnow declares"type": "module".importstill targetsdist/esm;requirestill targetsdist/cjs, which has its own"type": "commonjs"marker. Applications do not need to change their import syntax (#2775) - Custom single-input CommandForm fields receive the field id; put it on your input so the label is associated with it. For multi-input fields, set
groupRoleinasCommandFormFieldand label each option individually (#2661) - Proxy generation looks for concept validators in dependencies that reference Arc Core directly or through another assembly, including validators derived from a shared validator base. It rejects multiple discoverable validators for a requested type, matching Arc's runtime discovery; unrelated validators no longer affect generation. A partial type-load failure in the generated assembly or the requested model's assembly fails generation; other dependencies contribute the types that load and produce a warning naming the dependency. Failures to load executable counterparts or to construct validators can still leave rules ungenerated (#2766)
Fixed
- Concept-typed (
ConceptAs<T>) query arguments, including nullable ones, are bound from the request on model-bound and controller-based queries. Previously they were resolved from the dependency injection container whenever it reported the type as a service, which could fail with HTTP 500. An explicit[FromServices]still resolves from the container on controllers. Missing or invalid required concept arguments on controller-based queries are rejected instead of reaching the action asnull(#2757) - Collection query arguments sent with GET or QUERY no longer run the query with silently changed values: an invalid element, or
nullin a collection of a non-nullable element type, fails validation with HTTP 400 instead of becoming the element's default value. Nested collections are rejected the same way, including on controller-based queries, and raw collection arguments rejected by the query pipeline report a validation failure naming the argument (#2789) - QUERY bodies keep array element boundaries and empty string elements, so
["", "x"]binds as two strings rather than one comma-joined value ornull(#2789) - Collections of
DateOnly,TimeOnly,Uri, nullable value types, andHashSet<T>are bound from the request instead of being resolved from dependency injection or failing with HTTP 500. JSON node collections bind from QUERY bodies; GET rejectsJsonObjectandJsonArraycollections with HTTP 400 because comma-delimited values cannot represent them. Generated proxies classify the same element types as arguments (#2789) - Concepts on plain minimal APIs bind from and serialize to primitive JSON values, like Arc-generated endpoints: individual concepts, concept collections, and dictionaries keyed by concepts (#2755)
- OpenAPI describes concept values and collections as primitives and arrays, and resolves recursive dictionary and polymorphic references. A concept with your own converter keeps an unspecified schema unless you add a matching schema transformer. Enum schemas follow the effective numeric or string converter (#2755)
- Named imports from
@cratis/arc,@cratis/arc.react,@cratis/arc.react.mvvmand@cratis/arc.viteresolve for TypeScript ESM (.mts) and CommonJS (.cts) consumers usingmoduleResolution: Node16orNodeNext, as well as Bundler consumers. Published declarations use fully specified.jsrelative imports, and each export selects declarations matching itsimportorrequireentry (#2775) - Clicking a CommandForm field's auto-rendered label focuses its input, and the label gives the input its accessible name. Radio-group titles name the group without selecting its first option or firing a change event; untitled groups are rendered without an unnamed group role (#2661)
DbSet.Observe(),ObserveSingle(), andObserveById()support primary keys mapped as EF Core indexer properties, including shared-type entities backed by property bags.ObserveById()keeps filtering by a mapped public CLRIdwhen there is one and otherwise uses a single, non-shadow primary key; composite keys and shadow-only keys without a CLRIdare rejected when observation is set up. Entities with a shadow primary key stay observable through a mapped CLRId, and query-context sorting works for mapped indexer properties (#2741)- Validation rules from a
ConceptValidatordeclared in a referenced project or package appear in the generated TypeScript proxies of commands and queries that use the concept, even when the command, concept and validator live in three different assemblies (#2766)
Security
- Identity responses can no longer be stored by browser or intermediary caches and served to another user.
/.cratis/me,/.cratis/usersand/.cratis/tenantsresponses, including 401 and 403, and any response that writes the identity cookie carryCache-Control: no-store, privateandVary: Cookieon both the ASP.NET Core and Arc.Core hosts. An existingVaryvalue, such asOriginfrom CORS, is kept. Responses a cache stored before you upgrade are not invalidated; evict cached identity responses if a cache in front of your application may have stored them (#2665) - Protected introspection catalogs require an authenticated user even when the ASP.NET Core default authorization policy permits anonymous callers or an unauthenticated principal carries a listed role (#2764)
- A protected introspection catalog cannot be opened with forged
x-ms-client-principalheaders. On ASP.NET Core, startup fails withInvalidIntrospectionConfigurationwhen protection is enabled and the header handler, including subclasses, schemes that forward to it, or schemes on the default authorization policy, could authenticate these endpoints, unlessTrustForwardedIdentityHeadersis set; this also applies when mapping directly withAspNetCoreEndpointMapper. Without the opt-in, the header handler does not authenticate protected catalog requests, including when a route group or named policy supplies its scheme, and rebuilding the principal's identity cannot bypass this. If header authentication succeeds before endpoint routing, the catalog returns 401. On the Arc.Core host the handler ignores those headers for protected catalogs unless trust is enabled (#2764)
Deprecated
IEndpointMapper.MapIntrospectionEndpoints()without options is obsolete; use the overload that takes the configured introspection options. Builds that treat warnings as errors report CS0618 (#2764)