effect@4.0.0-beta.98
Pre-releasePatch Changes
-
#2587
989603bThanks @gcanti! - ExposeSchemaErroras a public module and re-exportSchema.isSchemaError.This gives consumers a stable import path and guard for schema failures without
depending on the internal schema implementation, while preserving the existing
Schema.SchemaErrorsurface. -
#2592
214c458Thanks @gcanti! - ApplytransformClientwhen building an individual HttpApi endpoint client, preserving the supplied client's error and service channels. -
#2598
a037273Thanks @gcanti! - Preserve__proto__group and endpoint identifiers in HTTP APIs, generated clients, and URL builders. -
#2578
97fdaa9Thanks @tim-smart! - FixAtom.kvsasync mode to retain itsAsyncResultvalue shape after writes. -
#2612
b24d248Thanks @gptguy! - Fix replay of persistedDurableDeferred.raceAllresults. -
#2580
19c222cThanks @gcanti! - Fix HttpApi authorization decoding.Previously,
HttpApiBuilder.securityDecoderemoved the expected scheme length and one following character from theAuthorizationheader without verifying either value. A Bearer decoder could therefore pass credentials from a different scheme such asBasic, accept a malformed header without a separating space, or retain leading spaces when more than one separator was present.The decoder now validates the declared scheme before returning credentials, matches it case-insensitively as required by RFC 9110 section 11.1, and consumes one or more separating spaces. Missing, malformed, or mismatched headers produce the existing empty credential value so security middleware can reject them consistently.
Basic authentication previously split the decoded
user-passvalue at every colon, causing otherwise valid passwords containing:to be discarded. It now uses only the first colon as the separator and preserves the rest of the password, following RFC 7617 section 2. -
#2581
eec85ddThanks @gcanti! - Fix HttpApi client error decoding.Generated clients previously combined every error schema for a status into one union decoder. When schemas used different encodings, their declaration order could determine the decoded error instead of the response
Content-Type; for example, a text decoder could accept a JSON response before the JSON decoder was tried.Error responses are now grouped and selected by normalized content type, matching buffered success responses. Normalization happens before grouping, so declarations that differ only by casing or parameters such as
charsetshare one union decoder instead of making later schemas unreachable.No-content schemas are represented by a headerless alternative, allowing empty error responses without a
Content-Typeheader to decode correctly. Unsupported content types preserve the existing combination ofStatusCodeErrorand the response decoding failure. -
#2605
0082f4fThanks @gcanti! - FixNumber.remainderfor very small and large values formatted in scientific notation. -
#2611
8849052Thanks @tim-smart! - FixPersistedQueueto count schema decoding and malformed SQL payload failures as processing attempts. -
#2500
c15e16aThanks @hsubra89! - Fix Redis-backedPersistedQueuereset and failed-item handling. -
#2602
01d00a3Thanks @gcanti! - Fix a bug where decoding bracket paths from FormData or URLSearchParams could mutate inherited object prototypes. -
#2588
8bd4589Thanks @gcanti! - FixSchemaAST.isJsonto reject class instances and other non-record objects. -
#2605
0082f4fThanks @gcanti! - Fix JSON SchemaallOfimports for tuple intersections and preserve primitive refinements when combining literal constraints. -
#2604
6e08428Thanks @gcanti! - FixSchema.toFormatterandSchema.toEquivalenceindexing for tuples with multiple post-rest elements. -
#2603
388dcf9Thanks @gcanti! - Fix union candidate selection and decoding order so that unions now:- consider matches from every sentinel key instead of dropping valid members after the first match;
- reject ambiguous
oneOfinputs when members with different sentinel keys both match; - preserve declared member order when combining discriminated members with non-discriminated fallbacks;
- commit concurrent decoding results in declaration order instead of completion order.
Reserved SSE failure event names with non-
Causedata are now emitted as application events instead of producing a runtime defect. -
#2609
2b7ce2bThanks @tim-smart! - Fix SQL-backed persisted queues to refresh locks for actively acquired elements. -
#2583
87bea7eThanks @MrGovindan! - Fixed Clock.sleep handling of large durations -
#2582
ce38dc3Thanks @gcanti! - Harden HttpApi documentation HTML rendering.Scalar descriptions and CDN versions were interpolated without attribute-safe escaping. Embedded OpenAPI JSON in Scalar and Swagger also handled only the exact
</script>sequence, not other valid script end-tag forms.Attribute values and CDN versions are now encoded for their contexts, and embedded JSON escapes
<so it cannot close its script element. -
#2591
a807cd1Thanks @gcanti! - Keep HttpApi composition immutable.HttpApi.addHttpApiapplied annotations from the added API by mutating its shared groups. It now creates annotated group copies, keeping the source API and independently annotated variants unchanged while preserving annotation precedence. -
#2584
fd8a356Thanks @gcanti! - Normalize HttpApi payload media types.Payload schemas were stored under their exact declared
Content-Type, but the server lowercased the incoming header and removed its parameters before looking it up. For example, a schema declared asApplication/Vnd.Effect+JSON; profile=declaredwas stored under that value, while the server looked forapplication/vnd.effect+json. This could produce a415response even when the generated client and server used the same API.The same mismatch allowed incompatible encodings for equivalent media types to bypass validation. Generated form-urlencoded requests also ignored custom content types and always used the default one.
Payload maps now use normalized keys for matching and conflict checks, while each encoding keeps its declared content type. Generated requests and OpenAPI use the declared values, including every parameterized variant, and custom form-urlencoded content types are preserved.
-
#2476
c2a5edcThanks @gcanti! - Improve unstableHttpApitype-level performance.The implementation now uses identifier-keyed maps and lighter structural
constraints in several hot type-level paths. Generated group clients consume the
concrete endpoint map directly instead of rebuilding it from the endpoint union.New Features
- Add
HttpApiBuilder.Handlers.handleAll, which registers an identifier-keyed batch of endpoint handlers for a group. Each entry can be either a handler function or{ handler, options }, and the object can be supplied in multiple partial batches. Endpoint identifiers that were already handled are rejected across batches. HttpApi.groupsnow preserves the concrete group type for each group identifier. For example,Api.groups.usersis typed as theusersgroup instead of the full group union.HttpApiGroup.endpointsnow preserves the concrete endpoint type for each endpoint identifier. For example,Group.endpoints.getUseris typed as thegetUserendpoint instead of the full endpoint union.HttpApiEndpointvalues can now be extended as classes, matching the class-like
runtime shape already used byHttpApiandHttpApiGroup.
Measured Type-Level Performance
Main/current comparisons use identical generated fixtures compiled once per
revision with TypeScript 7.0.2. The recorded revisions aremainat
97fdaa9c1f52and the branch source at5798fc5fafcd. The focused pre/post
curves below were captured with the regularhttpapiregression suite during
development. The retained suite uses representative stress points instead of
rerunning every point in those historical curves. All numbers are
type-instantiation deltas over the corresponding shared baseline.Endpoint declaration costs now grow with a lower slope:
endpoints main current 10 4,580 2,808 50 15,500 9,168 100 29,150 17,118 500 138,350 80,718 Class-like endpoint declarations are slightly cheaper than inline endpoint
values in the same 500-endpoint fixture shape:fixture inline class-like 500 endpoints 82,207 71,850 HttpApiBuilderfluent handler registration avoids the previous non-linear
blow-up in the cross-ref comparison:fixture main current 10 endpoints 37,856 11,582 50 endpoints 568,576 63,702 100 endpoints 2,154,476 182,852 500 endpoints 51,741,676 3,296,052 500 raw handlers 51,734,176 3,294,550 In the recorded regular-suite measurements,
handleAllremains the scalable
alternative to the equivalent fluent chain:fixture fluent handleAll10 endpoints 11,579 9,146 50 endpoints 63,699 25,106 100 endpoints 182,849 45,056 500 endpoints 3,296,049 204,656 500 eps, two batches 3,296,049 223,613 Generated-client type production also improves for the hot method-building
paths:fixture main current client methods, 500 endpoints 245,795 176,850 top-level client methods, 500 endpoints 243,651 179,809 client endpoint method, 500 endpoints 56,738 46,294 client groups, 100 groups x 5 endpoints 49,019 25,893 The following focused curves were captured immediately before and after each
isolated type-level change.The focused
Client.Groupcurve shows the improvement from consuming the
identifier-keyed endpoint map directly:endpoints union remapping endpoint map 10 12,448 12,294 50 19,169 18,935 100 27,570 27,236 500 94,770 93,636 The focused
Client.TopLevelMethodscurve improves by reading endpoint
identifiers directly from the endpoint union:endpoints pre-change post-change 10 12,531 12,476 50 19,252 19,197 100 27,653 27,598 500 94,853 94,798 The focused
HttpApiClient.endpointselection curve improves by reading
endpoint identifiers directly from the selected endpoint union:endpoints pre-change post-change 10 7,666 7,588 50 8,707 8,629 100 10,008 9,930 500 20,408 20,330 The focused
HttpApiBuilder.endpointselection curve improves by reading
endpoint identifiers directly from the selected endpoint union:endpoints pre-change post-change 10 12,828 12,745 50 13,869 13,786 100 15,170 15,087 500 25,570 25,487 URL builder types now avoid repeatedly expanding the full API/group shape:
fixture main current URL builder, 500 endpoints 211,356 91,610 top-level URL builder, 500 endpoints 210,724 93,118 builder endpoint, 500 endpoints 62,894 51,952 Breaking Changes
These changes affect unstable
HttpApitype-level APIs and structural API,
group, and endpoint types.Renamed Constraint Types
- Broad structural constraint exports have been renamed to align with
Schema.Constraintterminology:HttpApi.AnytoHttpApi.Constraint,
HttpApi.AnyWithPropstoHttpApi.Top,HttpApiGroup.Anyto
HttpApiGroup.Constraint,HttpApiGroup.AnyWithPropstoHttpApiGroup.Top,
andHttpApiEndpoint.AnytoHttpApiEndpoint.Constraint. HttpApiEndpoint.AnyWithPropshas been replaced byHttpApiEndpoint.Top, whose
schema parameters are constrained toSchema.Top, including success and error
schemas.- Type guards now expose the widened runtime-prop shapes:
HttpApi.isHttpApi
returnsHttpApi.Top,HttpApiGroup.isHttpApiGroupreturns
HttpApiGroup.Top, andHttpApiEndpoint.isHttpApiEndpointreturns
HttpApiEndpoint.Top. HttpApiGroup.ApiGrouphas been renamed toHttpApiGroup.Service.
API, Group, And Endpoint Shapes
HttpApi.groupsis now typed as an identifier-keyed group map instead of
ReadonlyRecord<string, Groups>, andHttpApitracks its group union
invariantly. Dynamic string indexing must refine the key first or cast to a
broad runtime record.HttpApiGroup.endpointsis now typed as an identifier-keyed endpoint map instead of
ReadonlyRecord<string, Endpoints>, andHttpApiGrouptracks its endpoint
union invariantly. Dynamic string indexing must refine the key first or cast to
a broad runtime record.HttpApiEndpointnow exposes its stable key asidentifierinstead ofname,
aligning endpoints with APIs and groups and leavingnameavailable for future
class-based endpoint patterns.HttpApiEndpointvalues are now function objects instead of plain objects.
Runtime checks such astypeof endpointnow return"function", and
endpoint.nameis the native function name. Useendpoint.identifierfor the
stable endpoint key.- Identifier helper types have been renamed from
Name/WithNameto
Identifier/WithIdentifier;HttpApiGroup.Servicenow exposes
identifierinstead ofname.
Builder Handler Types
HttpApiBuilder.Handlersnow tracks endpoints through an identifier-keyed endpoint map and a set of handled endpoint identifiers, instead of tracking the remaining endpoint union. Its public type parameters changed fromHandlers<R, Endpoints>toHandlers<R, EndpointsByIdentifier, HandledIdentifiers>, and its phantom fields changed from_Endpointsto~EndpointsByIdentifier/~HandledIdentifiers.- The unused
HttpApiBuilder.Handlers.Anyhelper type has been removed. - The exported
HttpApiBuilder.HandlersTypeIdsymbol has been removed;Handlers
now uses a private string type id. - Duplicate
handle/handleRawregistrations for the same endpoint are rejected
at the call site, andhandleAllrejects endpoint identifiers that were already
handled by an earlier batch. Missing endpoint handlers are still rejected by
the finalHttpApiBuilder.groupreturn validation.
Client Types
HttpApiClient.Client.Groupnow derives a client from a concrete group type:Client.Group<Group, E, R>. The previous group-union plus group-identifier form is no longer supported.HttpApiClient.Client.TopLevelMethodsnow returns an identifier-keyed method record instead of a union of[identifier, method]tuples.HttpApiClient.makeWithremoves the defaultHttpClientError.HttpClientErrorfrom custom client error types in the returnedClient, while preserving any additional custom client errors.
Endpoint Helper Types
HttpApiEndpoint.HttpApiEndpointnow stores lightweight phantom metadata for middleware and request shapes:~Middleware,~MiddlewareServices,~Request, and~RequestRaw. Its type identifier field is nowreadonly [TypeId]: typeof TypeId.HttpApiEndpoint.Constraintis now a lightweight structural endpoint constraint and does not extendPipeable; values typed only asHttpApiEndpoint.Constraintdo not expose.pipe.HttpApiEndpoint.AddErrorhas been removed; it was not used internally by theHttpApiimplementation.HttpApiEndpoint.JsonandHttpApiEndpoint.StringTreehave been removed in
favor of the canonicalSchema.toCodecJsonandSchema.toCodecStringTree
types.- Omitted request-part metadata now remains
neverinstead of being wrapped as
Schema.toCodecStringTree<never>; codec metadata is applied only when
a params, query, payload, or headers schema is present. - Success metadata now applies
Schema.toCodecJsononly to buffered
success schemas and preserves stream success schemas unchanged, including
mixed buffered and streaming success arrays. - Handler request parts are now flattened with
Struct.Simplify, improving
displayed request types while reducing handler instantiations. - Endpoint helper types now read metadata fields directly instead of re-inferring all type parameters from the full
HttpApiEndpointinterface. This affects helpers such asIdentifier,Success,Error,Params,Query,Payload,Headers,Middleware,MiddlewareServices,Errors,ErrorServicesEncode,ErrorServicesDecode,Request,RequestRaw,ServerServices, andClientServices. HttpApiClient.Client.Methodand related generated-client helpers now require endpoint types that satisfyHttpApiEndpoint.ConstraintRequest. Endpoint-like structural types must include the lightweight request metadata fields to be accepted.
- Add
-
#2585
5946da3Thanks @gcanti! - Reuse HttpApi response schemas.HttpApiBuilderlooked up cached response schemas by their source AST but stored them by the transformed AST, so the cache normally missed. It now uses the source AST consistently. -
#2590
4ae0c5fThanks @IMax153! - Cleanup internals of CLI package -
#2607
5b2a0bcThanks @tim-smart! - ensure WithTransaction wraps entire rpc handler -
#2613
72ac585Thanks @tim-smart! - AddHttpApiError.UnprocessableEntityandHttpApiError.UnprocessableEntityNoContentfor status 422 responses. -
#2594
5e8c1b8Thanks @gcanti! - Reject unknown and duplicate HttpApi handler registrations with descriptive errors. -
#2595
0f9c078Thanks @gcanti! - Reject duplicate OpenAPI operations and operation identifiers, and reject incompatible security schemes that reuse a name.