1/**
2 * Provides the shared registry used by Schema decoder implementations. A
3 * decoder installed with {@link set} is consumed transparently by the normal
4 * `SchemaParser` APIs, allowing runtime and ahead-of-time compilers to use the
5 * same cache without introducing a compiled Schema type or a second parser API.
6 *
7 * The cache associates each exact AST with an entry containing decoder
8 * operations, never parsing results. The interpreter uses the same registry
9 * with lazy `decodeEffect` and constructor fallback; JIT, AOT, and manual
10 * installations may supply `makeEffect` and optional synchronous fast paths.
11 *
12 * @stability unstable
13 * @since 4.0.0
14 */
15import type * as Effect from "../Effect.ts"
16import * as CompilerRegistry from "../internal/schema/compilerRegistry.ts"
17import * as InternalParser from "../internal/schema/parser.ts"
18import type * as SchemaAST from "../SchemaAST.ts"
19import type * as SchemaIssue from "../SchemaIssue.ts"
20
21/**
22 * The result returned by {@link Decode} or {@link Make} when the fast path fails.
23 *
24 * @stability unstable
25 * @category symbols
26 * @since 4.0.0
27 */
28export const invalid = CompilerRegistry.invalid
29
30/**
31 * The sentinel distinguishing an absent input from a present `undefined`.
32 * Decoders and constructors propagate it as a successful result when no value
33 * is produced. Parents omit optional fields or report missing required keys;
34 * public root adapters reject it rather than returning it to callers.
35 *
36 * @stability unstable
37 * @category symbols
38 * @since 4.0.0
39 */
40export const missing = InternalParser.missing
41
42/**
43 * A compiled boolean validator.
44 *
45 * **Details**
46 *
47 * This optional fast path avoids constructing output. Omit it when validation
48 * requires reconstructed values, such as a Struct check that must see the
49 * object after excess properties are removed. Type guards then use ordinary
50 * decoding, including `decode` and its diagnostic fallback when available.
51 * It must honor the supplied parse options; public `Schema.is` and
52 * `SchemaParser.is` use the defaults.
53 *
54 * @stability unstable
55 * @category models
56 * @since 4.0.0
57 */
58export interface Is {
59 (input: unknown, options: SchemaAST.ParseOptions): boolean
60}
61
62/**
63 * A compiled decoder that returns the decoded value without constructing
64 * diagnostic issues.
65 *
66 * **Details**
67 *
68 * This optional synchronous fast path lets valid inputs return their output
69 * without the detailed decoding pass. For decoding, the registry follows
70 * {@link invalid} with `decodeEffect` because the sentinel provides no error details
71 * and can also occur as a valid input value. Type guards without an `is` operation
72 * use this same fallback. Omit this operation
73 * when the fast path is unsupported or replay would be unsafe, including ASTs
74 * containing transformations or middleware.
75 *
76 * It must honor every supported `ParseOptions` value. Return {@link invalid}
77 * for invalid input, never for an unsupported optimization. The detailed decoder
78 * must also accept valid data that happens to equal this marker. Do not call
79 * the detailed decoder and discard its failure: decoding would run `decodeEffect`
80 * again after `invalid`. User checks may themselves construct issues.
81 *
82 * @stability unstable
83 * @category models
84 * @since 4.0.0
85 */
86export interface Decode {
87 (input: unknown, options: SchemaAST.ParseOptions): unknown | typeof invalid
88}
89
90/**
91 * A compiled constructor that returns the constructed value without detailed
92 * diagnostic issues.
93 *
94 * **Details**
95 *
96 * This optional synchronous fast path lets construction return directly when
97 * it succeeds. Return {@link invalid} to let `makeEffect` produce the normal
98 * detailed result. Implementations must be deterministic and free of side
99 * effects because a failed construction can be repeated by `makeEffect`.
100 *
101 * Omit this operation when construction can execute defaults, Class
102 * constructors, transformations, middleware, or other effects that cannot be
103 * replayed safely. The operation must honor the supplied parse options and
104 * propagate {@link missing} when no value is produced.
105 *
106 * @stability unstable
107 * @category models
108 * @since 4.0.0
109 */
110export interface Make {
111 (input: unknown, options: SchemaAST.ParseOptions): unknown | typeof invalid
112}
113
114/**
115 * A compiled decoder that returns detailed Schema issues on failure.
116 *
117 * **Details**
118 *
119 * This required operation implements complete decoding for its AST, including
120 * transformations, middleware, and asynchronous work when present. It makes
121 * every parser API usable without optional fast paths and provides diagnostics
122 * after `decode` returns `invalid`. The implementation can also be interpreted;
123 * invoking `decodeEffect` does not imply a switch from compiled to interpreted parsing.
124 *
125 * @stability unstable
126 * @category models
127 * @since 4.0.0
128 */
129export interface DecodeEffect {
130 (input: unknown, options: SchemaAST.ParseOptions): Effect.Effect<unknown, SchemaIssue.Issue, any>
131}
132
133/**
134 * A complete constructor that returns detailed Schema issues on failure.
135 *
136 * @stability unstable
137 * @category models
138 * @since 4.0.0
139 */
140export interface MakeEffect {
141 (input: unknown, options: SchemaAST.ParseOptions): Effect.Effect<unknown, SchemaIssue.Issue, any>
142}
143
144/**
145 * The operations installed for an AST in the shared Schema parser registry.
146 *
147 * **Details**
148 *
149 * `decodeEffect` is required for complete decoding and detailed failures. `decode`,
150 * `is`, and `make` are optional optimizations, not requirements for an AST to be usable.
151 * The interpreter supplies only `decodeEffect` in this same format.
152 * An optional `makeEffect` supplies complete node construction. Otherwise the
153 * registry prepares and caches the interpreted constructor, never the decoder,
154 * for that operation. Public makers resolve the schema's exact type-side AST.
155 *
156 * The registry wraps these operations in an internal entry.
157 * Decoding tries `decode` when present, returning its output on success or
158 * calling `decodeEffect` after `invalid`. Without `decode`, or for the {@link missing}
159 * sentinel, it calls `decodeEffect` directly. Type guards prefer `is`; otherwise
160 * they use ordinary decoding with the same fast-path/diagnostic fallback.
161 * A boolean `false` from `is` needs no diagnostic replay.
162 * Synchronous decoding and encoding share an adapter that returns successful
163 * `decode` output directly, without wrapping it in an intermediate Effect.
164 * Each operation is resolved lazily on first use, so unused fast paths need
165 * not be compiled.
166 * Synchronous construction tries `make` when present, returning its output on
167 * success or calling `makeEffect` after {@link invalid}. Compilers must omit
168 * `make` when replay could repeat observable construction work. Construction
169 * never uses `is` or `decode`. Field/element defaults belong to the parent
170 * occurrence, not to the root node or a Union member.
171 * Runtime options apply to construction too; Union candidate selection preserves
172 * the constructor's conservative handling of absent discriminants.
173 *
174 * @stability unstable
175 * @category models
176 * @since 4.0.0
177 */
178export interface CompiledDecoder {
179 readonly is?: Is | undefined
180 readonly decode?: Decode | undefined
181 readonly make?: Make | undefined
182 readonly decodeEffect: DecodeEffect
183 /**
184 * Constructs this node without replay, including Class construction and child
185 * defaults. Omit it to use the lazy interpreted constructor. This operation
186 * initializes independently from decoding and never applies its own root default.
187 * Propagate `missing` as a success when no value is produced; the parent handles
188 * optional omission or missing-key issues. A present `undefined` is not `missing`.
189 */
190 readonly makeEffect?: MakeEffect | undefined
191}
192
193/**
194 * Installs a compiled decoder for an exact AST in the shared Schema parser
195 * registry.
196 *
197 * **Details**
198 *
199 * A later call for the same AST replaces the previous entry. Parser functions
200 * that have already resolved and retained an earlier entry are not updated.
201 * This also applies when subsequent calls use different parse options.
202 * The decoder is trusted to implement the semantics of the supplied AST.
203 * Installation does not evaluate operation getters. Each operation, including
204 * an absent optional operation, is resolved once when first needed. Accessors
205 * retain the supplied decoder as their receiver. The supplied object is not
206 * mutated. JIT installation uses these same rules.
207 * Replacement includes construction: omitting `makeEffect` in the replacement
208 * selects interpreted detailed construction for new consumers, without merging
209 * the old operation into the new entry. An installed `make` can still handle
210 * synchronous successes. Already captured constructors keep their entry.
211 *
212 * @stability unstable
213 * @category registry
214 * @since 4.0.0
215 */
216export const set = (ast: SchemaAST.AST, decoder: CompiledDecoder): void => {
217 CompilerRegistry.set(ast, decoder)
218}