Skip to content

Release v22.26.0

Choose a tag to compare

@github-actions github-actions released this 26 Sep 12:34
1b5df0b

Added

  • Select a tenant with ITenantScope.Begin for 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 ITenantIdAccessor registrations are preserved whether they are registered before or after Arc. Explicit tenant scopes require Arc's TenantIdAccessor as the effective accessor; resolving ITenantScope with a custom accessor throws ExplicitTenantScopeRequiresArcAccessor instead 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 IStartupFilter registered 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.
  • Configurable access to the command and query introspection catalogs (/.cratis/commands and /.cratis/queries) through Cratis:Arc:Introspection (ArcOptions.Introspection) (#2764):

    • Enabled (default true): set to false to leave both routes unmapped.
    • RequireAuthentication (default false): deny anonymous callers. Arc.Core returns 401; ASP.NET Core uses the configured scheme's challenge behavior.
    • Roles (comma-separated, any one grants access, requires RequireAuthentication): deny callers without a listed role. Arc.Core returns 403; ASP.NET Core uses the configured scheme's forbid behavior.
    • TrustForwardedIdentityHeaders (default false): 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/schema is 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 own JsonConverter<YourConcept> with ConfigureHttpJsonOptions; it takes precedence for individual concepts, concept collections, and, when it implements property-name conversion, dictionary keys, whether registered before or after AddCratisArc (#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.json now declares "type": "module". import still targets dist/esm; require still targets dist/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 groupRole in asCommandFormField and 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 as null (#2757)
  • Collection query arguments sent with GET or QUERY no longer run the query with silently changed values: an invalid element, or null in 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 or null (#2789)
  • Collections of DateOnly, TimeOnly, Uri, nullable value types, and HashSet<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 rejects JsonObject and JsonArray collections 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.mvvm and @cratis/arc.vite resolve for TypeScript ESM (.mts) and CommonJS (.cts) consumers using moduleResolution: Node16 or NodeNext, as well as Bundler consumers. Published declarations use fully specified .js relative imports, and each export selects declarations matching its import or require entry (#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(), and ObserveById() support primary keys mapped as EF Core indexer properties, including shared-type entities backed by property bags. ObserveById() keeps filtering by a mapped public CLR Id when there is one and otherwise uses a single, non-shadow primary key; composite keys and shadow-only keys without a CLR Id are rejected when observation is set up. Entities with a shadow primary key stay observable through a mapped CLR Id, and query-context sorting works for mapped indexer properties (#2741)
  • Validation rules from a ConceptValidator declared 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/users and /.cratis/tenants responses, including 401 and 403, and any response that writes the identity cookie carry Cache-Control: no-store, private and Vary: Cookie on both the ASP.NET Core and Arc.Core hosts. An existing Vary value, such as Origin from 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-principal headers. On ASP.NET Core, startup fails with InvalidIntrospectionConfiguration when 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, unless TrustForwardedIdentityHeaders is set; this also applies when mapping directly with AspNetCoreEndpointMapper. 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)