Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3147e79
Add Moltar Schema runtime benchmarks
gcanti Sep 2, 2026
b8bf183
Add opt-in Schema decoder compiler
gcanti Sep 2, 2026
aa16840
Capture Schema Union parser defects
gcanti Sep 2, 2026
b4f8dac
Compile structural Schema unions
gcanti Sep 2, 2026
f8627e9
Compile Schema graphs around runtime islands
gcanti Sep 2, 2026
f53f96e
Compile Schema decoding transformations
gcanti Sep 2, 2026
e78875f
Compile recursive and symbol-keyed Schemas
gcanti Sep 2, 2026
322bb7d
Compile Schema record key patterns
gcanti Sep 2, 2026
8b8dead
Compile Schema structs with index signatures
gcanti Sep 2, 2026
90f1f95
Compile transformed Schema record keys
gcanti Sep 2, 2026
ea115fc
Compile Schema encoding checks
gcanti Sep 2, 2026
0b010c5
Simplify compiled custom object handling
gcanti Sep 2, 2026
ba1e1e0
Reuse compiled Schema decoders for type guards
gcanti Sep 2, 2026
aa4f769
Document compiled Schema type guards
gcanti Sep 2, 2026
f8c8c2f
Accept inherited Schema struct fields
gcanti Sep 3, 2026
d6938c1
Simplify compiled Schema struct access
gcanti Sep 3, 2026
b3cfd8e
Compare compiled Effect runtime benchmarks
gcanti Sep 3, 2026
61d904c
Optimize inherited Schema field reads
gcanti Sep 3, 2026
8d46a48
Revert speculative Schema field reads
gcanti Sep 3, 2026
b2a71f1
Specialize inherited Schema field lookup
gcanti Sep 3, 2026
d12f6bb
Move SchemaCompiler to unstable schema
gcanti Sep 3, 2026
925710e
Optimize Schema object property parsing
gcanti Sep 3, 2026
1140b26
Simplify Schema property lookup
gcanti Sep 3, 2026
24d3b8d
Enable Schema compiler with side-effect import
gcanti Sep 3, 2026
c18bce4
Optimize compiled Schema decode and validation
gcanti Sep 3, 2026
ae6e973
Simplify Schema compiler implementation
gcanti Sep 3, 2026
c7e7e7e
Document Schema compiler fallback behavior
gcanti Sep 3, 2026
35580ab
Update Schema benchmark results
gcanti Sep 3, 2026
ce10016
Compile Schema encoding pipelines
gcanti Sep 4, 2026
f5addf2
wip
gcanti Sep 4, 2026
1311f05
Redesign Schema compiler registry
gcanti Sep 4, 2026
c0b5e3a
Simplify Schema parsing and remove local parse options
gcanti Sep 5, 2026
cbf422e
Refine Schema compiler contracts and parsing semantics
gcanti Sep 5, 2026
9d3e742
Fix Schema excess-key handling and consolidate changesets
gcanti Sep 5, 2026
867a1bb
Fix Schema compiler inlining, oneOf, and signed-zero semantics
gcanti Sep 5, 2026
01391f3
Remove Schema propertyOrder parsing option
gcanti Sep 5, 2026
e497071
Remove Schema decoder default variants and sameExit
gcanti Sep 5, 2026
d53a007
Add multi-root Schema AOT compilation with shared JIT runtime
gcanti Sep 6, 2026
01b23c6
Unify Schema compiler entries and shared compilation rules
gcanti Sep 6, 2026
4853d62
Fall back to interpreted parsing on JIT compilation failures
gcanti Sep 6, 2026
37f6bfe
Restore default-only schema type guards
gcanti Sep 6, 2026
d8d9654
Improve schema compiler composition and synchronous encoding
gcanti Sep 6, 2026
559d88c
Align compiled template literals with updated Schema AST
gcanti Sep 7, 2026
dae1263
Align Schema class make with constructor parsers
gcanti Sep 7, 2026
b4c007f
handle make
gcanti Sep 7, 2026
ab2c95b
Restore JSON Schema additional properties default
gcanti Sep 7, 2026
6c6ea71
Fix compiled schema type guards
gcanti Sep 7, 2026
0e54322
Remove Schema union parser cycle
gcanti Sep 7, 2026
a3b0d57
Enforce exhaustive schema constructor compilation
gcanti Sep 7, 2026
b1554d9
Add schema parser traversal regressions
gcanti Sep 8, 2026
10965ee
Finalize schema compiler release notes
gcanti Sep 8, 2026
23ef385
Remove obsolete barrel codegen workaround
gcanti Sep 8, 2026
d66b531
Regenerate migration guidance
gcanti Sep 8, 2026
b0d6c9f
Restore zero-allocation Schema parser successes
gcanti Sep 8, 2026
ae1a082
Update schema compiler performance snapshot
gcanti Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .changeset/add-schema-compiler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
"effect": patch
---

Add experimental JIT and AOT compilation through the existing `SchemaParser` parsing and construction APIs:

- Import `effect/unstable/schema/SchemaJITCompiler/enable` for global lazy JIT.
- Use `SchemaJITCompiler.enable(ast)` for one AST and its dependencies.
- Use `SchemaCompiler.set(ast, decoder)` to install a trusted decoder.
- Use `SchemaAOTCompiler.compile(asts)` at build time to generate a module exporting `install(asts)`. Supply runtime ASTs in the same order; regenerate after schema or Effect upgrades.

Install before parsers' first execution to accelerate them. JIT falls back to the interpreter when dynamic code generation is unavailable or compilation fails; parsing errors keep their normal behavior. AOT runs without dynamic code generation.

For replay-safe ASTs, a failed fast validation can evaluate checks and property getters twice before producing detailed issues. Checks and Declaration recognizers must have no observable side effects, and getters must be deterministic.

Construction shares the same cache, initializes independently from decoding, and does not replay defaults or constructors. Installed bundles require `decodeEffect`; optional `makeEffect` supplies construction, otherwise the interpreter handles it. Target `SchemaAST.toType(schema.ast)` for selective JIT or AOT construction. Global JIT must also precede the first maker call to optimize its entry.

### Breaking changes

- Schema class `make` methods now use the same constructor parser as `makeOption` and `makeEffect`, preserving existing instances without rerunning their constructors. Use `new MyClass(input)` when a new instance is required.
- `Literal(0)` and `Literal(-0)` preserve the input's zero sign. Normalize explicitly if you relied on canonicalization.
- Structs accept inherited declared fields, except `__proto__`. Record index signatures remain own-only. Check ownership before parsing if required.
- `parseOptions` annotations no longer affect parsing. Pass options to parser APIs instead.
- Remove `propertyOrder`. Output key order is unspecified, including inside checks. Remove order-dependent checks and handle presentation order explicitly.
- Remove `concurrency` from `ParseOptions`; remove it from parser option objects. Composite children parse sequentially. Parallelize independent parse calls explicitly with Effect concurrency combinators.
- Remove `onExcessProperty: "preserve"`. Use `Record` or `StructWithRest` with an explicit value schema. `"error"` rejects keys outside the combined declared-field and index-signature coverage, including on records.
- `SchemaAST.Union` takes `{ mode }` instead of a mode string. Read `ast.options?.mode ?? "anyOf"` instead of `ast.mode`; regenerate persisted representations. Public `Schema.Union` calls are unchanged.
30 changes: 15 additions & 15 deletions migration/annotations/effect__SchemaAST.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
note: "The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model."
"effect/SchemaAST#BatchingAnnotation":
replacement: "none"
note: "Per-schema batching annotations were removed; control asynchronous parsing with ParseOptions.concurrency."
note: "Per-schema batching annotations were removed. Composite schemas parse children sequentially; use Effect combinators to coordinate independent parsing operations."
"effect/SchemaAST#BatchingAnnotationId":
replacement: "none"
note: "Symbol annotation IDs were removed and batching is no longer a schema annotation."
Expand Down Expand Up @@ -53,11 +53,11 @@
replacement: "SchemaAST.Encoding"
note: "The marker transformation was replaced by explicit SchemaAST.Link encoding chains."
"effect/SchemaAST#ConcurrencyAnnotation":
replacement: "SchemaAST.ParseOptions[\"concurrency\"]"
note: "Concurrency is now a parse option rather than its own annotation type."
replacement: "none"
note: "Schema parsing concurrency was removed. Composite schemas parse children sequentially; use Effect concurrency combinators around independent parsing operations."
"effect/SchemaAST#ConcurrencyAnnotationId":
replacement: "Schema.Annotations.Bottom[\"parseOptions\"]"
note: "Symbol annotation IDs were removed; put concurrency inside the parseOptions annotation."
replacement: "none"
note: "Schema parsing concurrency was removed. Composite schemas parse children sequentially; use Effect concurrency combinators around independent parsing operations."
"effect/SchemaAST#Declaration":
replacement: "SchemaAST.Declaration"
note: "The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model."
Expand Down Expand Up @@ -117,16 +117,16 @@
note: "Resolve string-keyed annotations with resolveAt, or use resolveIdentifier, resolveTitle, and resolveDescription."
"effect/SchemaAST#getBatchingAnnotation":
replacement: "none"
note: "Batching annotations were removed; read ParseOptions.concurrency when controlling asynchronous parsing."
note: "Batching annotations were removed. Composite schemas parse children sequentially; use Effect combinators to coordinate independent parsing operations."
"effect/SchemaAST#getBrandAnnotation":
replacement: "SchemaAST.resolveAt(\"brands\")"
note: "Resolve the string-keyed brands annotation."
"effect/SchemaAST#getCompiler":
replacement: "none"
note: "The Match-based compiler was removed; traverse SchemaAST.AST directly or use the relevant Schema derivation API."
"effect/SchemaAST#getConcurrencyAnnotation":
replacement: "SchemaAST.resolveAt(\"parseOptions\")"
note: "Resolve parseOptions and read concurrency from it."
replacement: "none"
note: "Schema parsing concurrency and its annotations were removed. Use Effect concurrency combinators around independent parsing operations."
"effect/SchemaAST#getDecodingFallbackAnnotation":
replacement: "none"
note: "Fallbacks are encoding middleware in v4, not readable annotations; attach them with Schema.catchDecoding."
Expand Down Expand Up @@ -164,8 +164,8 @@
replacement: "none"
note: "Issue-title callbacks were removed; use message or expected annotations and SchemaIssue formatters."
"effect/SchemaAST#getParseOptionsAnnotation":
replacement: "SchemaAST.resolveAt(\"parseOptions\")"
note: "Resolve the string-keyed parseOptions annotation."
replacement: "none"
note: "Parse options are no longer schema annotations. Pass options when creating or calling a decoder or encoder; there is no annotation-based override for nested schemas."
"effect/SchemaAST#getPropertySignatures":
replacement: "SchemaAST.Objects.propertySignatures"
note: "Narrow to Objects and read propertySignatures directly."
Expand Down Expand Up @@ -330,10 +330,10 @@
note: "Use the built-in JSON string codec instead of checking the old schema ID."
"effect/SchemaAST#ParseOptions":
replacement: "SchemaAST.ParseOptions"
note: "The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model."
note: "Pass parsing options at runtime. onExcessProperty supports ignore or error, not preserve; model extra values with an explicit Record or StructWithRest. The concurrency and propertyOrder options were removed. Output key order is unspecified, including in values passed to checks. Handle required presentation or serialization order explicitly outside the parser."
"effect/SchemaAST#ParseOptionsAnnotationId":
replacement: "Schema.Annotations.Bottom[\"parseOptions\"]"
note: "Symbol annotation IDs were removed; use the parseOptions key."
replacement: "none"
note: "Parse options are no longer schema annotations. Pass options when creating or calling a decoder or encoder; there is no annotation-based override for nested schemas."
"effect/SchemaAST#partial":
replacement: "Schema.mapFields + Struct.map(Schema.optional)"
note: "Partial object transforms moved to schema field transforms."
Expand Down Expand Up @@ -414,7 +414,7 @@
note: "Symbol annotation IDs were removed; use declaration codec annotation keys."
"effect/SchemaAST#TypeLiteral":
replacement: "SchemaAST.Objects"
note: "The v4 SchemaAST redesign renamed this primitive, collection, or guard while preserving its role."
note: "Use Objects(propertySignatures, indexSignatures, annotations?, checks?, encoding?, context?, encodingChecks?). Output key order is unspecified; there is no property-order option."
"effect/SchemaAST#TypeLiteralTransformation":
replacement: "SchemaAST.Encoding"
note: "Object transformations are encoding links; use Schema.encodeKeys for key mappings."
Expand All @@ -426,7 +426,7 @@
note: "The v4 SchemaAST redesign renamed this primitive, collection, or guard while preserving its role."
"effect/SchemaAST#Union":
replacement: "SchemaAST.Union"
note: "The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model."
note: "Pass member ASTs and an optional options object: new SchemaAST.Union(types, { mode: 'oneOf' }). Read options.mode, defaulting to 'anyOf', instead of a direct mode field."
"effect/SchemaAST#UniqueSymbol":
replacement: "SchemaAST.UniqueSymbol"
note: "The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model."
Expand Down
38 changes: 27 additions & 11 deletions migration/v3-to-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Base: `origin/v3` (`2e471d9cec31889cd6548aa5423b64c2b85238be`)

Head: `origin/main` (`5a802043984727b0c5a291af39d1b9bbfa8d7b8b`)
Head: `HEAD` (`23ef385e7082e115d34e4e6f3cc6727e85b6b1ba`)

This file is generated from the API diff and `migration/annotations/*.yaml`.

Expand Down Expand Up @@ -7156,12 +7156,18 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc)

### `@effect/platform/HttpServer`

- `HttpServer.Address` -> `effect/unstable/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union.

- `HttpServer.HttpServer` -> `HttpServer.HttpServer`: The interface and tag became one Context.Service class; use its Service member for implementations.

- `HttpServer.ServeOptions` -> `none`: The unused respond option model was removed with no shared v4 counterpart.

- `HttpServer.TcpAddress` -> `effect/unstable/net/NetAddress#InetAddress`: Replaced by the shared resolved internet-address model; use address and port instead of hostname and port.

- `HttpServer.TypeId` -> `none`: The public TypeId was removed; HttpServer is now a Context.Service class.

- `HttpServer.UnixAddress` -> `effect/unstable/net/NetAddress#UnixPathAddress`: Replaced by the shared Unix filesystem-path address model.

- `HttpServer.addressWith` -> `HttpServer.HttpServer.use(({ address }) => effect(address))`: The accessor was removed; read the service and pass its Address to the callback.

- `HttpServer.layerContext` -> `HttpServer.layerServices`: Renamed; it provides the standard HTTP platform services.
Expand Down Expand Up @@ -7434,8 +7440,14 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc)

### `@effect/platform/SocketServer`

- `SocketServer.Address` -> `effect/unstable/net/NetAddress#SocketAddress`: Replaced by the shared concrete internet-or-Unix socket address union.

- `SocketServer.ErrorTypeId` -> `SocketServer.ErrorTypeId`: The API moved to effect/unstable/socket/SocketServer and retains this name.

- `SocketServer.TcpAddress` -> `effect/unstable/net/NetAddress#InetAddress`: Replaced by the shared resolved internet-address model; use address and port instead of hostname and port.

- `SocketServer.UnixAddress` -> `effect/unstable/net/NetAddress#UnixPathAddress`: Replaced by the shared Unix filesystem-path address model.

### `@effect/platform/Template`

- `Template.Interpolated.Context` -> `Template.Interpolated.Context`: The API moved to effect/unstable/http/Template; v4 interpolation types also account for Effect values.
Expand All @@ -7460,6 +7472,8 @@ effect/unstable/rpc/Utils (barrel: effect/unstable/rpc)

### `@effect/platform/Url`

- `Url.fromString`: TODO: needs guidance

- `Url.setUrlParams` -> `Url.setUrlParams`: Retained and widened to accept UrlParams.Input.

### `@effect/platform/UrlParams`
Expand Down Expand Up @@ -14875,7 +14889,7 @@ Schema.toFormatter(schema)

- `SchemaAST.ArbitraryAnnotationId` -> `Schema.Annotations.ToArbitrary`: Symbol annotation IDs were removed. Declarations use the toCodecArbitrary annotation; filters use arbitraryConstraint.

- `SchemaAST.BatchingAnnotation` -> `none`: Per-schema batching annotations were removed; control asynchronous parsing with ParseOptions.concurrency.
- `SchemaAST.BatchingAnnotation` -> `none`: Per-schema batching annotations were removed. Composite schemas parse children sequentially; use Effect combinators to coordinate independent parsing operations.

- `SchemaAST.BatchingAnnotationId` -> `none`: Symbol annotation IDs were removed and batching is no longer a schema annotation.

Expand All @@ -14891,9 +14905,9 @@ Schema.toFormatter(schema)

- `SchemaAST.ComposeTransformation` -> `SchemaAST.Encoding`: The marker transformation was replaced by explicit SchemaAST.Link encoding chains.

- `SchemaAST.ConcurrencyAnnotation` -> `SchemaAST.ParseOptions["concurrency"]`: Concurrency is now a parse option rather than its own annotation type.
- `SchemaAST.ConcurrencyAnnotation` -> `none`: Schema parsing concurrency was removed. Composite schemas parse children sequentially; use Effect concurrency combinators around independent parsing operations.

- `SchemaAST.ConcurrencyAnnotationId` -> `Schema.Annotations.Bottom["parseOptions"]`: Symbol annotation IDs were removed; put concurrency inside the parseOptions annotation.
- `SchemaAST.ConcurrencyAnnotationId` -> `none`: Schema parsing concurrency was removed. Composite schemas parse children sequentially; use Effect concurrency combinators around independent parsing operations.

- `SchemaAST.Declaration` -> `SchemaAST.Declaration`: The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model.

Expand Down Expand Up @@ -14969,9 +14983,9 @@ Schema.toFormatter(schema)

- `SchemaAST.ParseJsonSchemaId` -> `Schema.UnknownFromJsonString`: Use the built-in JSON string codec instead of checking the old schema ID.

- `SchemaAST.ParseOptions` -> `SchemaAST.ParseOptions`: The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model.
- `SchemaAST.ParseOptions` -> `SchemaAST.ParseOptions`: Pass parsing options at runtime. onExcessProperty supports ignore or error, not preserve; model extra values with an explicit Record or StructWithRest. The concurrency and propertyOrder options were removed. Output key order is unspecified, including in values passed to checks. Handle required presentation or serialization order explicitly outside the parser.

- `SchemaAST.ParseOptionsAnnotationId` -> `Schema.Annotations.Bottom["parseOptions"]`: Symbol annotation IDs were removed; use the parseOptions key.
- `SchemaAST.ParseOptionsAnnotationId` -> `none`: Parse options are no longer schema annotations. Pass options when creating or calling a decoder or encoder; there is no annotation-based override for nested schemas.

- `SchemaAST.PrettyAnnotationId` -> `Schema.overrideToFormatter`: The symbol annotation was removed; attach custom formatters with Schema.overrideToFormatter.

Expand Down Expand Up @@ -15015,13 +15029,13 @@ Schema.toFormatter(schema)

- `SchemaAST.TypeConstructorAnnotationId` -> `Schema.Annotations.Declaration["toCodec"]`: Symbol annotation IDs were removed; use declaration codec annotation keys.

- `SchemaAST.TypeLiteral` -> `SchemaAST.Objects`: The v4 SchemaAST redesign renamed this primitive, collection, or guard while preserving its role.
- `SchemaAST.TypeLiteral` -> `SchemaAST.Objects`: Use Objects(propertySignatures, indexSignatures, annotations?, checks?, encoding?, context?, encodingChecks?). Output key order is unspecified; there is no property-order option.

- `SchemaAST.TypeLiteralTransformation` -> `SchemaAST.Encoding`: Object transformations are encoding links; use Schema.encodeKeys for key mappings.

- `SchemaAST.UndefinedKeyword` -> `SchemaAST.Undefined`: The v4 SchemaAST redesign renamed this primitive, collection, or guard while preserving its role.

- `SchemaAST.Union` -> `SchemaAST.Union`: The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model.
- `SchemaAST.Union` -> `SchemaAST.Union`: Pass member ASTs and an optional options object: new SchemaAST.Union(types, { mode: 'oneOf' }). Read options.mode, defaulting to 'anyOf', instead of a direct mode field.

- `SchemaAST.UniqueSymbol` -> `SchemaAST.UniqueSymbol`: The name remains, but its constructor and fields changed in the v4 Base/check/context/encoding model.

Expand All @@ -15047,13 +15061,13 @@ Schema.toFormatter(schema)

- `SchemaAST.getAnnotation` -> `SchemaAST.resolveAt`: Resolve string-keyed annotations with resolveAt, or use resolveIdentifier, resolveTitle, and resolveDescription.

- `SchemaAST.getBatchingAnnotation` -> `none`: Batching annotations were removed; read ParseOptions.concurrency when controlling asynchronous parsing.
- `SchemaAST.getBatchingAnnotation` -> `none`: Batching annotations were removed. Composite schemas parse children sequentially; use Effect combinators to coordinate independent parsing operations.

- `SchemaAST.getBrandAnnotation` -> `SchemaAST.resolveAt("brands")`: Resolve the string-keyed brands annotation.

- `SchemaAST.getCompiler` -> `none`: The Match-based compiler was removed; traverse SchemaAST.AST directly or use the relevant Schema derivation API.

- `SchemaAST.getConcurrencyAnnotation` -> `SchemaAST.resolveAt("parseOptions")`: Resolve parseOptions and read concurrency from it.
- `SchemaAST.getConcurrencyAnnotation` -> `none`: Schema parsing concurrency and its annotations were removed. Use Effect concurrency combinators around independent parsing operations.

- `SchemaAST.getDecodingFallbackAnnotation` -> `none`: Fallbacks are encoding middleware in v4, not readable annotations; attach them with Schema.catchDecoding.

Expand All @@ -15079,7 +15093,7 @@ Schema.toFormatter(schema)

- `SchemaAST.getParseIssueTitleAnnotation` -> `none`: Issue-title callbacks were removed; use message or expected annotations and SchemaIssue formatters.

- `SchemaAST.getParseOptionsAnnotation` -> `SchemaAST.resolveAt("parseOptions")`: Resolve the string-keyed parseOptions annotation.
- `SchemaAST.getParseOptionsAnnotation` -> `none`: Parse options are no longer schema annotations. Pass options when creating or calling a decoder or encoder; there is no annotation-based override for nested schemas.

- `SchemaAST.getPropertySignatures` -> `SchemaAST.Objects.propertySignatures`: Narrow to Objects and read propertySignatures directly.

Expand Down Expand Up @@ -16060,6 +16074,8 @@ switch (strategy) {

- `TMap.removeAll` -> `TxHashMap.removeMany`: The bulk removal operation was renamed.

- `TMap.set`: TODO: needs guidance

- `TMap.setIfAbsent` -> `Effect.tx + TxHashMap.get/TxHashMap.set`: No direct helper remains; check and conditionally set under one outer transaction.

- `TMap.size` -> `TxHashMap.size`: Import TxHashMap from "effect/TxHashMap"; the operation keeps its name. V4 Tx operations return ordinary Effects; compose multiple operations under one outer Effect.tx to keep them atomic.
Expand Down
Loading