import { i as SchemaObject, S as SchemaOrBoolean } from './types-Dzi0PpYX.cjs'; import { V as ValidationError, f as createError, g as createLeafError, e as createBranchError } from './errors-BahYESV_.cjs'; /** * Issues unique identifier names for generated source. Keeps a counter per * prefix so callers can ask for `n0`, `n1`, `n2` without colliding with other * generators sharing the same function. * * @public */ declare class Scope { private readonly counters; /** * Generate a fresh identifier with the given prefix. * * @param prefix - Desired prefix (e.g. `"i"`, `"tmp"`, `"sub"`). * @returns A unique name like `"i0"`, `"i1"`, ... * * @example * ```ts * const s = new Scope(); * s.name("i"); // "i0" * s.name("i"); // "i1" * ``` */ name(prefix: string): string; } /** * Fresh-identifier generator surface. The only member custom-keyword * authors call on `ctx.gen.scope`. * * @public */ interface NameGenerator { /** Mint a fresh identifier of the form `N`, monotonically increasing per prefix. */ name(prefix: string): string; } /** * The narrow code-emission surface a custom-keyword author sees via * {@link KeywordCompileContext.gen}. Defines the minimum API needed to * emit validator source without exposing the whole {@link CodeGen} * implementation. * * If you find yourself wanting a method that isn't here (e.g. `forIn`, * `forRange`), open an issue; the interface is intentionally small so * it's stable. * * @public */ interface CodeEmitter { /** Shared name generator. */ readonly scope: NameGenerator; /** Append a single line of source at the current indent level. */ line(code: string): this; /** Emit an `if (cond) { then } else { else }` block. */ if(cond: string, thenBody: (g: CodeEmitter) => void, elseBody?: (g: CodeEmitter) => void): this; /** Emit a `for (const name of expr) { ... }` loop. */ forOf(name: string, expr: string, body: (g: CodeEmitter) => void): this; /** Emit a `for (const name in expr) { ... }` loop with a `hasOwn` guard. */ forIn(name: string, expr: string, body: (g: CodeEmitter) => void): this; /** Emit a `for (let name = 0; name < limit; name += 1) { ... }` loop. */ forRange(name: string, limit: string, body: (g: CodeEmitter) => void): this; /** Emit a `const name = expr;` declaration. */ const(name: string, expr: string): this; /** Emit a `let name = expr;` declaration. */ let(name: string, expr: string): this; /** Open an indentation level without emitting a brace pair. */ indent(): this; /** Close an indentation level opened by {@link CodeEmitter.indent}. */ dedent(): this; } /** * String-builder for generated JavaScript source. Each call appends a line (or * opens/closes a block) with automatic indentation. * * Used internally by the compiler. Custom-keyword authors interact with * the narrower {@link CodeEmitter} interface via `ctx.gen`. * * @internal */ declare class CodeGen implements CodeEmitter { private readonly lines; private indentLevel; /** Shared name generator. Keyword authors can request fresh identifiers here. */ readonly scope: Scope; /** * Append a single line of source at the current indent level. * * @param code - The source fragment (no trailing newline, no leading indent). * @returns `this`, for chaining. * * @example * ```ts * gen.line(`const n = 42;`); * ``` */ line(code: string): this; /** * Append a blank line. * * @returns `this`, for chaining. */ blank(): this; /** * Emit an `if (cond) { then } else { else }` block. * * @param cond - Condition expression. * @param thenBody - Callback populating the `then` branch. * @param elseBody - Optional callback populating the `else` branch. * @returns `this`, for chaining. * * @example * ```ts * gen.if("x > 0", (g) => g.line("return true;")); * ``` */ if(cond: string, thenBody: (g: CodeGen) => void, elseBody?: (g: CodeGen) => void): this; /** * Emit a `for (let name = 0; name < limit; name += 1) { ... }` loop. * * @param nameVar - Loop variable name. * @param limit - Upper bound expression. * @param body - Callback populating the loop body. * @returns `this`, for chaining. */ forRange(nameVar: string, limit: string, body: (g: CodeGen) => void): this; /** * Emit a `for (const nameVar of expr) { ... }` loop. * * @param nameVar - Loop variable name. * @param expr - Iterable expression. * @param body - Callback populating the loop body. * @returns `this`, for chaining. */ forOf(nameVar: string, expr: string, body: (g: CodeGen) => void): this; /** * Emit a `for (const nameVar in expr) { ... }` loop. Also emits the standard * `hasOwn` guard to skip inherited properties. * * @param nameVar - Loop variable name. * @param expr - Object expression. * @param body - Callback populating the guarded loop body. * @returns `this`, for chaining. */ forIn(nameVar: string, expr: string, body: (g: CodeGen) => void): this; /** * Emit a `const name = expr;` declaration. * * @param name - Identifier to bind. * @param expr - Initializer expression. * @returns `this`, for chaining. */ const(name: string, expr: string): this; /** * Emit a `let name = expr;` declaration. * * @param name - Identifier to bind. * @param expr - Initializer expression. * @returns `this`, for chaining. */ let(name: string, expr: string): this; /** * Enter an indentation level without emitting a brace pair. Rarely * used; prefer {@link CodeGen.if} / {@link CodeGen.forRange} / etc. * * @returns `this`, for chaining. */ indent(): this; /** * Exit an indentation level opened by {@link CodeGen.indent}. * * @returns `this`, for chaining. */ dedent(): this; /** * Produce the generated source as a single string. * * @returns The accumulated source with `\n` separators. */ toString(): string; } /** * Quote an arbitrary string as a JavaScript string literal suitable for * embedding into generated source. * * @param value - The string to quote. * @returns A quoted literal (including surrounding `"`). * * @example * ```ts * quoteString('a"b\nc'); // '"a\\"b\\nc"' * ``` * * @public */ declare function quoteString(value: string): string; /** * Coerce a schema-supplied finite number into a JavaScript numeric * literal that is safe to interpolate directly into generated source. * Throws at compile time on non-numbers, NaN, and infinities. * * Use this for any keyword whose value lands raw in a generated JS * expression (e.g. `${data} > ${limit}`). Interpolating * `ctx.schema` directly is a codegen-injection vector when the * schema is attacker-supplied; this helper closes that path by * validating the value first and returning a known-safe `String(n)`. * * @param value - The schema-supplied value (untrusted). * @param keyword - The schema keyword being compiled. Surfaced in the * error message so callers know which schema position to fix. * @returns The value rendered as a numeric literal, e.g. `"5"` or `"-1.5"`. * @throws Error when `value` is not a finite number. * * @public */ declare function numberLiteral(value: unknown, keyword: string): string; /** * Like {@link numberLiteral}, but additionally requires a non-negative * integer. Used for `maxLength`, `minItems`, `maxProperties`, etc. * * @param value - The schema-supplied value (untrusted). * @param keyword - The schema keyword being compiled. * @returns The value rendered as a numeric literal. * @throws Error if `value` is not an integer >= 0. * * @public */ declare function nonNegativeIntegerLiteral(value: unknown, keyword: string): string; /** * Like {@link numberLiteral}, but additionally requires a strictly positive * value. Used for `multipleOf`, where zero would mean "every number is a * multiple of zero" (vacuous, and triggers a division by zero in the * generated check). * * @param value - The schema-supplied value (untrusted). * @param keyword - The schema keyword being compiled. * @returns The value rendered as a numeric literal. * @throws Error if `value` is not a finite number > 0. * * @public */ declare function positiveNumberLiteral(value: unknown, keyword: string): string; /** * Coerce a schema-supplied boolean into a JavaScript boolean literal * (`"true"` or `"false"`) safe to interpolate into generated source. * * @param value - The schema-supplied value (untrusted). * @param keyword - The schema keyword being compiled. * @returns `"true"` or `"false"`. * @throws Error if `value` is not a boolean. * * @public */ declare function booleanLiteral(value: unknown, keyword: string): string; /** * Emit a JavaScript expression that produces a new path array consisting of a * runtime base path plus some extra segments. * * @param baseExpr - JS expression that evaluates to the base path array. * @param segments - Static segments to append. Strings become quoted literals; * numbers embed as-is; expressions wrapped with * {@link rawExpr} embed verbatim. * @returns A JS expression like `[...path, "foo", i0]`. * * @example * ```ts * pathJoinExpr("path", ["foo", rawExpr("i0")]); // '[...path, "foo", i0]' * ``` * * @public */ declare function pathJoinExpr(baseExpr: string, segments: PathSegmentLike[]): string; /** * Wrapper that marks a string as a raw JS expression rather than a literal * path segment. Used by {@link pathJoinExpr}. * * @param expr - The raw JavaScript expression. * @returns A tagged object recognized by path helpers. * * @example * ```ts * rawExpr("i0"); // produces { raw: "i0" } * ``` * * @public */ declare function rawExpr(expr: string): RawExpression; /** * A raw JavaScript expression used in place of a literal path segment. * * @public */ interface RawExpression { raw: string; } /** * Something acceptable as a path segment during codegen: a literal string or * number, or a raw expression created with {@link rawExpr}. * * @public */ type PathSegmentLike = string | number | RawExpression; /** * Runtime inputs exposed to generated validator source. Keyword authors * reference entries by name; the compiler wires the map into the new * Function's closure. * * @public */ interface CompileRuntime { patterns: Map; formats: Map boolean>; } /** * Which budget semantics to apply when pushing an error expression * onto the errors accumulator. See {@link KeywordCompileContext.emitError}. * * - `"leaf"`: fresh leaf error, counts against `maxErrors`. * - `"lift"`: already-counted error being propagated (unconditional push). * * @public */ type ErrorKind = "leaf" | "lift"; /** * The output shape a compiled (sub)validator produces: * * - `"tree"`: returns a nested `ValidationError | null` (the default). * - `"flat"`: returns a de-nested `ValidationError[] | null`. * - `"predicate"`: returns a `boolean` and builds no errors. * * Usually a subschema compiles in its enclosing function's mode. The * composition keywords request `"predicate"` explicitly for branches * whose result is consumed only as a yes/no (the decision phase of * `anyOf` / `oneOf`, the `not` assertion, the `if` condition), so the * valid path never builds an error tree it then discards. See * {@link KeywordCompileContext.compileSubschema}. * * @public */ type CompileMode = "tree" | "flat" | "predicate"; /** * Options for {@link KeywordCompileContext.validateSubschema}. * * @public */ interface ValidateSubschemaOptions { /** * Extra path segment to push onto `path` for the duration of this * subschema's traversal (e.g. an array index or property name). * Pass a JS expression; literal strings must be quoted. */ segment?: string; } /** * Options for * {@link KeywordCompileContext.compileAndCallSubschema}. * * @public */ interface CompileAndCallOptions { /** JS expression for the data to pass to the sub-validator. */ data: string; /** * Emitted into the "sub passed" branch. Receives the per-branch * evaluated-keys var names when the enclosing scope tracks either; * `null` otherwise. Merge into the caller's `outProps` / `outItems` * here; annotations from failing branches are not merged per the * 2020-12 spec, so the helper only exposes them on the pass side. */ onPass: (gen: CodeEmitter, branchProps: string | null, branchItems: string | null) => void; /** * Emitted into the "sub failed" branch. `errVar` is the name of the * local const holding the sub-validator's returned error in tree * mode, or `null` in predicate mode (which has nothing to pass). * Keywords that short-circuit on failure in predicate mode emit * `return false;` via the `gen` here; tree-mode keywords typically * push `errVar` into their own errors array. */ onFail: (gen: CodeEmitter, errVar: string | null, branchProps: string | null, branchItems: string | null) => void; } /** * The narrow context passed to each keyword's `compile()` function. Keyword * authors see only these names: no reference to the compiler, the full * vocabulary, or the validator instance. * * @public */ interface KeywordCompileContext { /** Handle for emitting source into the current validator function. */ readonly gen: CodeEmitter; /** The keyword's own schema value (e.g. `"number"` for `type`). */ readonly schema: unknown; /** The whole surrounding schema object (so cross-keyword peeks are possible). */ readonly parentSchema: SchemaObject; /** JS expression referring to the data variable at this scope (e.g. `"data"`). */ readonly data: string; /** JS expression referring to the current path array (e.g. `"path"`). */ readonly path: string; /** JS expression referring to the error accumulator (e.g. `"errors"`). */ readonly errors: string; /** * Compile a nested subschema to its own function and return the * function's identifier. The returned name is callable with * `(data, path)` inside generated code. Use this when a keyword * needs direct access to the function name; most composition-style * keywords are better served by * {@link KeywordCompileContext.compileAndCallSubschema}, which also * hides the predicate-vs-tree call-signature split. * * `mode` overrides the output shape of the compiled function. It * defaults to the enclosing function's mode. Pass `"predicate"` to * compile a branch whose result is consumed only as a boolean (its * function takes `(data)` and returns `boolean`); pass the enclosing * mode (or omit) for a branch whose errors are reported. See * {@link CompileMode}. */ compileSubschema(schema: SchemaOrBoolean, mode?: CompileMode): string; /** * Compile a subschema and emit a call + pass/fail branch, abstracting * over the two call conventions: * * - Tree mode: `const ev = fn(data, path, bProps, bItems); if (ev === null) { onPass } else { onFail(ev) }` * - Predicate: `if (fn(data, bProps, bItems)) { onPass } else { onFail(null) }` * * When the enclosing scope tracks evaluated properties or items, the * helper allocates per-branch accumulator Sets and passes their names * to the callbacks so the caller can merge them into its own outputs * on the pass branch (the 2020-12 spec discards annotations from * failing branches). When no tracking is active, both callbacks see * `null` for the branch variables. * * The sub-validator is called once; what each branch does is * keyword-specific: composition keywords push the error into a * per-keyword errors array on fail, `not` emits a leaf on pass, * etc. The abstraction deliberately stops at the call shape. */ compileAndCallSubschema(schema: SchemaOrBoolean, options: CompileAndCallOptions): void; /** * Resolve a `$ref` (absolute URI or fragment) to a compiled function * name. Used by `$ref` and `$dynamicRef` keywords. */ resolveRef(ref: string): string; /** * `true` when `ref` is a recursion back-edge: its target schema is * still on the compile stack, so the emitted call closes a cycle. * The `$ref` / `$dynamicRef` keywords consult this to decide whether * to wrap the call in the {@link KeywordCompileContext.depthGated} * recursion-depth guard. Forward refs return `false`. */ isRecursiveRef(ref: string): boolean; /** JS expression for the Set tracking evaluated properties, or `null`. */ readonly evaluatedPropertiesVar: string | null; /** JS expression for the Set tracking evaluated items, or `null`. */ readonly evaluatedItemsVar: string | null; /** * `true` when the runtime error-budget short-circuit is active (a * finite `maxErrors` was configured and the schema does not track * evaluated keys; see the note on the compiler's `CompileState.gated`). * Keyword authors usually don't need to read this directly; prefer * {@link KeywordCompileContext.emitError} / * {@link KeywordCompileContext.emitBudgetBreak} which inspect it. */ readonly gated: boolean; /** * `true` when a finite `maxDepth` cap was configured. The `$ref` / * `$dynamicRef` keywords read this together with * {@link KeywordCompileContext.isRecursiveRef} to emit the * recursion-depth guard; other keywords don't need it. */ readonly depthGated: boolean; /** * `true` when predicate mode is active: the compiled validator * returns `boolean` and constructs no error tree. Most keywords * don't need to read this directly: `emitError`, * `errorStatement`, `leafErrorExpr`, and `validateSubschema` all * do the right thing automatically. Composition-style keywords * that inspect a sub-validator's return value for their own * control flow (`allOf`, `anyOf`, `oneOf`, `not`, `if/then/else`, * `$ref`, `contains`, `discriminator`, `dependentSchemas`) must * branch on this flag; sub-validators in predicate mode return * `boolean`, not `ValidationError | null`, and don't take a * `path` argument. */ readonly predicate: boolean; /** * `true` when flat-collection mode is active: the compiled validator * returns a flat `ValidationError[]` of leaves (no branch wrappers). * Like {@link KeywordCompileContext.predicate}, most keywords don't * read this: `emitError`/`errorStatement` append a sub-validator's * list automatically on a `"lift"`, and the inline-wrap step is * suppressed. The branch-*wrapping* composition keywords (`allOf`, * `anyOf`, `oneOf`) must branch on it: instead of collecting child * nodes and wrapping them, they append each failing branch's leaves * flat and emit a single childless marker leaf for the composition * keyword itself. Mutually exclusive with * {@link KeywordCompileContext.predicate}. */ readonly flat: boolean; /** * `true` when the compile unit uses `unevaluatedProperties` / * `unevaluatedItems` anywhere, so functions thread evaluated-key * out-params. Composition keywords read this to gate the two-phase * (predicate-decision) optimization: predicate sub-validators don't * produce evaluated keys, so when tracking is on, branches whose * annotations must merge (`anyOf` / `oneOf` matches, the `if` * condition) keep the eager error-mode path instead. (`not` never * contributes evaluated keys, so it ignores this.) */ readonly unevaluatedTracking: boolean; /** * Emit an error-push statement directly into the current code * generator. Pick the right `kind` based on where the error * expression came from: * * - `"leaf"`: freshly-minted leaf error, created in this call. * Counts against the `maxErrors` budget when one is configured; * short-circuits cleanly when the cap has been hit. * - `"lift"`: an already-counted error being propagated up the * tree (a sub-validator's return value) or a branch wrapper * around already-counted children (`createBranchError`). Always * unconditional, never touches the budget counter. * * Using the wrong kind silently miscounts errors against the * budget, so think about it each time. TypeScript enforces that you * supply one of the two names; the intent of the choice is on you. */ emitError(kind: ErrorKind, errExpr: string): void; /** * String form of {@link KeywordCompileContext.emitError}: returns * the statement instead of emitting it. Useful inside compound * source like a `switch` body, where the push appears inline in a * larger `gen.line(...)` call. */ errorStatement(kind: ErrorKind, errExpr: string): string; /** * Flat-mode helper: a statement that appends the flat * `ValidationError[]` produced by `srcExpr` onto the accumulator * variable `destVar` (via `deps.appendErrors`, null-safe on both * sides). Used by the branch-wrapping composition keywords to buffer * per-branch leaves (`anyOf` / `oneOf`) before deciding whether to * commit or discard them. Only meaningful when * {@link KeywordCompileContext.flat} is `true`. */ appendErrorsStatement(destVar: string, srcExpr: string): string; /** * Build a budget-guarded `break;` statement for use at the bottom of * hot loops (array items / property keys / applicator branches). * Returns `""` when uncapped so callers can emit it unconditionally. */ budgetBreakStatement(): string; /** * Emit the statement from * {@link KeywordCompileContext.budgetBreakStatement} directly into * the current code generator. Useful at the tail of loops so they * short-circuit once the error cap is hit. */ emitBudgetBreak(): void; /** * Emit validation for a subschema against `dataExpr`, writing any * errors into the current scope's accumulator. When `segment` is * provided, wraps the emission in a `path.push(seg) … path.pop()` * pair so the shared-mutable `path` array carries the extra segment * only for the duration of this subschema's traversal. * * When the subschema is simple enough (a boolean, or a single * validation keyword from a safe whitelist) the keyword's code is * inlined directly, avoiding the per-call function dispatch. For * anything more complex, it falls back to compiling the subschema * into a named function and emitting the usual call + lift. Either * way the path is reused, not re-allocated. * * This is the right helper for the common "descend into a * subschema and emit any errors" pattern, used by `properties`, * `items`, `additionalProperties`, etc. Composition keywords that * need the sub-validator's return value for their own logic * (`allOf`, `anyOf`, `oneOf`, …) should call * {@link KeywordCompileContext.compileSubschema} instead. */ validateSubschema(schema: SchemaOrBoolean, dataExpr: string, options?: ValidateSubschemaOptions): void; /** * Pending path segments to splice as trailing args into * `createLeafError` / `createBranchError`. Populated by the * subschema inliner when it flattens a segmented * `validateSubschema` call into the enclosing function body: * instead of pre-materializing `[...path, seg]` for the inner * keyword contexts (which the runtime then re-snapshots, doubling * allocation), we leave `path` unchanged and let leaf keywords * splice segments as extra args. * * Most keywords don't need to read this directly: prefer * {@link KeywordCompileContext.leafErrorExpr} / * {@link KeywordCompileContext.branchErrorExpr}, which both * already consume it. */ readonly pathSegments: readonly string[]; /** * JS expression producing the effective path at runtime, * equivalent to `ctx.path` when `pathSegments` is empty, and to * `[...path, seg1, seg2, …]` otherwise. Prefer the error helpers * for error construction; reach for this only when a keyword * needs to pass the runtime path to something other than * `createLeafError` / `createBranchError` (e.g. a user-supplied * custom-keyword callback). */ readonly effectivePathExpr: string; /** * Assemble a `deps.createLeafError(...)` call expression, splicing * any pending {@link KeywordCompileContext.pathSegments} plus the * caller's own `extraSegments` as trailing args. Up to two total * extras embed as explicit parameters (matching the runtime * signature); three or more fall back to eagerly materializing the * extended path at the call site (rare; pathologically nested * inlined subschemas). * * @param codeExpr - Pre-quoted JS expression for the error code * (e.g. `quoteString("type")`). * @param messageExpr - JS expression for the message (literal or * template string). * @param paramsExpr - JS expression for the `params` object, or * `"{}"`. * @param extraSegments - Additional segments this keyword wants to * append (e.g. a missing property name). Appended after any * already-pending `pathSegments`. */ leafErrorExpr(codeExpr: string, messageExpr: string, paramsExpr: string, extraSegments?: readonly string[]): string; /** * Assemble a `deps.createBranchError(...)` call expression. Same * trailing-segment rules as {@link KeywordCompileContext.leafErrorExpr}. */ branchErrorExpr(codeExpr: string, messageExpr: string, childrenExpr: string, paramsExpr?: string, extraSegments?: readonly string[]): string; /** * Emit a `const = ;` declaration at the top of the * generated module (outside every validator function). Returns the * minted identifier so callers can reference the hoisted value from * their validator body. * * Use this for schema-derived constants that would otherwise be * allocated on every validate call: Sets of known property names, * required-name arrays, enum candidates. The hoisted value must be * immutable from the validator's perspective (the validator reads it; * nothing in the generated code mutates it). * * The optional `prefix` is used to name the identifier for easier * debugging of generated source. Defaults to `"C"`. */ hoistConstant(expr: string, prefix?: string): string; /** * Compute a runtime value once per validator-function scope and share * it across keywords. Emits `const = ;` into the current * function body the first time a given `key` is seen, and returns the * same identifier for every later call with that `key` within the same * function. Use for values derived from the runtime `data` that more * than one keyword on the same schema needs: the object-shape guard * (`typeof data === "object" && …`) is the canonical case, shared by * `required` / `properties` / `additionalProperties` / etc. * * The sibling of {@link KeywordCompileContext.hoistConstant}: that one * lifts a compile-time-derived constant to module scope (runs once at * factory init); this one caches a per-call value inside the validator * body (runs once per `validate()`). Reach for `hoistConstant` when the * value depends only on the schema, `scopeLocal` when it depends on the * data. * * Call this at the keyword's top-level entry (before opening loops or * `if` blocks), so the emitted `const` dominates every sibling * keyword's code. The `expr` must be evaluable at that point (i.e. * rooted in the function's `data` parameter) and side-effect-free; it * is computed unconditionally, so a guard that would throw on the wrong * input type is not a valid `expr`. * * @param key - Stable cache key. Keywords that want to share a value * must pass the same key (e.g. `` `isObject:${ctx.data}` ``). * @param expr - The JS expression to bind. Must match for a given key. * @param prefix - Identifier prefix for readable generated source. * Defaults to `"L"`. */ scopeLocal(key: string, expr: string, prefix?: string): string; } /** * Definition of a single schema keyword that plugs into a {@link Vocabulary}. * * @public */ interface KeywordDefinition { /** The keyword name (e.g. `"type"`, `"properties"`). */ keyword: string; /** The vocabulary URI this keyword belongs to. */ vocabulary: string; /** Generate validation code for this keyword. */ compile: (ctx: KeywordCompileContext) => void; /** * Reserved for declarative compile-time ordering. Currently unused; * keyword execution order comes from the vocabulary's `keywords` * array order (with `unevaluatedProperties` / `unevaluatedItems` * pushed to the tail). Author your vocabulary's array in the order * keywords should run; do not rely on this field. */ dependsOn?: string[]; /** * Names of keywords whose semantics this keyword subsumes. The * dispatcher treats them as already-handled when this keyword is * present, so the strict-mode unknown-key check doesn't flag them * and the per-keyword inliner doesn't emit duplicate code. * * Use when a custom keyword semantically replaces a built-in pair: * `discriminator` declares `implements: ["oneOf", "anyOf"]` because * its dispatch logic supersedes both; `if`/`then`/`else` declares * `implements: ["then", "else"]` so the partner keys aren't dispatched * a second time on their own; `contains` declares * `implements: ["minContains", "maxContains"]` so the bounds-only * keys are folded into the `contains` codegen. */ implements?: string[]; /** * Reserved for declarative compile-time ordering. Currently unused; * see {@link KeywordDefinition.dependsOn}. Set the vocabulary's * `keywords` array order instead. */ before?: string; /** * Declares that this keyword contributes to evaluated-properties / * evaluated-items tracking, the bookkeeping `unevaluatedProperties` * and `unevaluatedItems` consume. Set the relevant sub-flag for any * keyword that "evaluates" object members or array positions * (`properties`, `patternProperties`, `items`, `contains`, …). A * missed flag silently breaks `unevaluated*` siblings, which will * then see members as unevaluated and reject valid data. */ evaluates?: { properties?: boolean; items?: boolean; }; /** * When `true`, this keyword descends into subschemas (`items`, * `properties`, `allOf`, `not`, …). The flag drives the subschema * inliner to take the function-call path for multi-keyword schemas * containing this keyword. Setting it wrong is a silent * mis-optimization: a missed flag costs correctness (an inlined * applicator can skip the per-function evaluated-keys state) and * speed (V8 can't monomorphize a huge inlined body). */ applicator?: boolean; /** * When `true`, declares this keyword to be pure annotation/metadata: * it emits no runtime validation code. Annotation keywords can coexist * with inlineable keywords without disqualifying the schema. Used by * the subschema inliner to decide which keys to skip when counting * "real" validation keywords. A keyword defining `annotation: true` * should also have an empty `compile`. */ annotation?: boolean; /** * Short explanation when this keyword is only partially supported: * the compiler accepts and dispatches it, but the emitted validation * doesn't fully match the spec. Surfaced via the compile-time strict * mode (see {@link CompileOptions.strict}) so users know they're * getting degraded semantics rather than a silent fallback. * * Example: `$dynamicRef` sets `partial` because the implementation * resolves statically against the anchor map rather than walking the * runtime dynamic scope. */ partial?: string; } /** * A collection of keywords under a single vocabulary URI. * * @public */ interface Vocabulary { /** Vocabulary URI (per JSON Schema spec). */ uri: string; /** * Ordered list of keyword definitions. Read-only: the compiler and the * introspection cache rely on a stable vocabulary keyword order, and an * `as const` keyword array satisfies this directly. */ keywords: readonly KeywordDefinition[]; } /** * Keyword-dispatcher rules that vary between dialects. Fields go here * when a dialect difference can't be expressed as a keyword override * (i.e. it operates above the vocabulary level). * * @public */ interface DialectRules { /** * OpenAPI 3.0 semantics: when a schema has `$ref`, every sibling * keyword is ignored. Default `false` (JSON Schema 2020-12 and * OpenAPI 3.1+ semantics, where siblings are honored). */ refSuppressesSiblings: boolean; } /** * A compile-time dialect: a vocabulary stack plus the dispatcher rules * that make it coherent. Every call to `compileSchema` picks exactly * one dialect. The built-in dialects are {@link jsonSchemaDialect}, * {@link openapi31Dialect}, and {@link oas30Dialect}. * * @public */ interface Dialect { /** Short identifier for debugging / introspection (e.g. `"oas3.0"`). */ readonly id: string; /** Vocabularies whose keywords are available during compile. */ readonly vocabularies: readonly Vocabulary[]; /** Keyword-dispatcher rules (currently just `refSuppressesSiblings`). */ readonly rules: DialectRules; } /** * Vocabulary URI for user-registered keywords. Not a published JSON * Schema vocabulary; just a local tag that keeps custom entries * alongside the built-in ones in the keyword registry. * * @public */ declare const customKeywordVocabulary = "https://oav.dev/vocab/custom-keywords"; /** * Failure detail a {@link CustomKeywordValidator} may return to customize * the emitted {@link import("@oav/core").ValidationError}. Omitted fields * take sensible defaults: `message` becomes * `"value failed custom keyword \"\""`; `params` defaults to `{}`. * * @public */ interface CustomKeywordFailure { message?: string; params?: Record; } /** * A user-supplied validator invoked whenever its associated keyword * appears in a schema. * * - Return `true` for a valid value. * - Return `false` to emit a generic failure error for that keyword. * - Return a {@link CustomKeywordFailure} object to customize the error * `message` and/or `params`. * * The `schemaValue` argument is the JSON value the keyword carries in * the schema. Since schemas are JSON, `schemaValue` is always * JSON-serialisable; callers may close over precomputed values (e.g. a * compiled regex) at registration time if per-validation work should be * avoided. * * @public */ type CustomKeywordValidator = (data: unknown, schemaValue: unknown, path: readonly (string | number)[]) => boolean | CustomKeywordFailure; /** * Build a {@link KeywordDefinition} for a user-registered keyword name. * The generated code calls into the per-validator * `deps.customKeywords` map, so a single compiled validator dispatches * all custom keywords through one uniform shim. * * @internal */ declare function createCustomKeywordDefinition(keyword: string): KeywordDefinition; /** * A URI-keyed registry of schemas. A single `Map` provides `add` / `get` / * `remove`; there is deliberately no separate `schemas` vs `refs` vs `cache` * distinction because that distinction always leaks. * * @public */ declare class SchemaRegistry { private readonly map; /** * Register a schema under the given URI. * * @param uri - Absolute or scheme-less URI to key the schema by. * @param schema - The schema (object or boolean) to store. * @throws Error if the URI is already registered. * * @example * ```ts * reg.add("https://example.com/Pet", { type: "object" }); * ``` */ add(uri: string, schema: SchemaOrBoolean): void; /** * Look up a registered schema. * * @param uri - Registered URI. * @returns The schema, or `undefined` if the URI is unknown. * * @example * ```ts * const pet = reg.get("https://example.com/Pet"); * ``` */ get(uri: string): SchemaOrBoolean | undefined; /** * Test whether a URI is registered. * * @param uri - URI to probe. * @returns `true` when a schema is registered under `uri`. */ has(uri: string): boolean; /** * Remove a registered schema. * * @param uri - Registered URI to remove. * @returns `true` if a schema was removed, `false` if the URI was absent. */ remove(uri: string): boolean; /** * The number of schemas currently registered. */ get size(): number; /** * Remove all schemas. */ clear(): void; /** * Iterate over `[uri, schema]` pairs currently in the registry. */ entries(): IterableIterator<[string, SchemaOrBoolean]>; } /** * A resolved schema graph: the root schema plus lookup tables for every * $id / $anchor discovered during traversal. This is the input to the * compiler. * * @public */ interface ResolvedGraph { /** The entry point schema. */ root: SchemaOrBoolean; /** Base URI of the root schema (may be empty for anonymous schemas). */ baseUri: string; /** Every `$id` discovered, mapped to the schema it labels (keyed by absolute URI). */ byId: Map; /** * Flat union of every `$anchor` discovered: last-writer-wins when two * anchors share a name across scopes. Prefer {@link anchorScopes} when * scope-local lookup matters; this map exists for compatibility. */ byAnchor: Map; /** * Flat union of every `$dynamicAnchor` discovered; see {@link byAnchor} * for caveats. */ byDynamicAnchor: Map; /** * Per-base-URI anchor maps. Keys are absolute base URIs established by a * declared `$id` (or the root base URI); values are `anchor → schema` * maps for the anchors declared within that scope. */ anchorScopes: Map>; /** Per-base-URI `$dynamicAnchor` maps; same shape as {@link anchorScopes}. */ dynamicAnchorScopes: Map>; /** Identity-keyed map from every schema object to its enclosing base URI. */ schemaBaseUri: WeakMap; /** External schemas by URI (from the registry passed to {@link resolve}). */ registry: SchemaRegistry; } /** * Options accepted by {@link resolve}. * * @public */ interface ResolveOptions { /** Base URI to associate with the root schema. Defaults to `""`. */ baseUri?: string; /** External schema registry. Additional `$id` / anchor entries are added to it. */ registry?: SchemaRegistry; } /** * Walk a JSON Schema 2020-12 document and collect its `$id` / `$anchor` / * `$dynamicAnchor` locations into lookup tables, scoped by the enclosing * `$id` base URI. Boolean schemas (`true` / `false`) pass through * unchanged. Any schemas pre-registered in the passed registry are walked * too, using their registry key as the starting base URI. * * @remarks * This function does not yet inline `$ref`; references are left in place * and resolved lazily at compile time. The schemas remain the original * values (not cloned). * * @param schema - Root schema. * @param options - Optional base URI / registry. * @returns A {@link ResolvedGraph}. * * @example * ```ts * const graph = resolve({ $defs: { Pet: { type: "object" } } }); * graph.byAnchor.size; // 0 * ``` * * @public */ declare function resolve(schema: SchemaOrBoolean, options?: ResolveOptions): ResolvedGraph; /** * A function capable of resolving a JSON Schema `$ref` string (absolute or * fragment) into the schema it names. * * @public */ interface RefResolver { /** * Resolve a `$ref` string to the target schema. * * @param ref - The `$ref` value, either a fragment (`#...`), a relative * URI, or an absolute URI (with optional `#fragment`). * @param fromBaseUri - Optional base URI of the schema containing the * `$ref`. Used to absolutize relative refs and to pick the right * scope for `#anchor` / `#/pointer` fragments under nested `$id`s. */ resolve(ref: string, fromBaseUri?: string): SchemaOrBoolean; } /** * Build a {@link RefResolver} that resolves references against a given * {@link ResolvedGraph}. * * @remarks * Supported forms: * - `#`: the root of the enclosing `$id` scope (or the graph root if * the `$ref` appears at the root). * - `#/a/b/c`: JSON Pointer into the enclosing scope's root schema. * - `#name`: lookup in the enclosing scope's anchor map; falls back to * the flat anchor map for cross-scope references. * - absolute URI: lookup in `byId` or the external registry. * - absolute URI + fragment: resolve the URI, then the fragment. * * @param graph - Output of {@link resolve}. * @returns A resolver ready to hand to the compiler. * * @example * ```ts * const graph = resolve({ $defs: { Pet: { type: "object" } } }); * const refs = createRefResolver(graph); * refs.resolve("#/$defs/Pet"); // → { type: "object" } * ``` * * @public */ declare function createRefResolver(graph: ResolvedGraph): RefResolver; /** * Find every distinct schema object reachable via `$dynamicAnchor` from an * ancestor chain, used when the compiler needs to decide which dynamic * anchor the current `$dynamicRef` should bind to. * * @remarks * This is the "static" part of $dynamicRef resolution; the full runtime * dynamic scope is simulated at compile time by walking each schema's * enclosing context. For schemas that don't use `$dynamicAnchor`, a * `$dynamicRef` behaves exactly like a `$ref`. * * @param schema - Schema to inspect. * @param graph - Resolved graph (for fallback anchor lookups). * @returns The `$dynamicAnchor` map reachable from this schema. * * @public */ declare function collectDynamicAnchors(schema: SchemaOrBoolean, graph: ResolvedGraph): Map; /** * Runtime helpers exposed to every generated validator through the `deps` * closure. Keyword authors invoke these from generated source. * * @public */ interface ValidatorDeps { createError: typeof createError; createLeafError: typeof createLeafError; createBranchError: typeof createBranchError; typeOf: (value: unknown) => string; deepEqual: (a: unknown, b: unknown) => boolean; wrapErrors: (code: string, path: readonly (string | number)[], errs: ValidationError[]) => ValidationError | null; /** * Flat-mode error merge. Appends every error in `src` onto `dest` and * returns `dest`; when `dest` is `null` it adopts `src` directly (no * copy) and when `src` is `null` it returns `dest` unchanged. The * leaves in `src` were already counted against the budget where they * were created, so this never touches `errorsRemaining` (it is the * flat-mode analogue of a tree-mode "lift"). Loop-based rather than * `dest.push(...src)` so a million-element `src` cannot overflow the * call stack. See {@link appendErrors}. */ appendErrors: (dest: ValidationError[] | null, src: ValidationError[] | null) => ValidationError[] | null; patterns: Map; /** * Compile a user-supplied regex. By default tries the `u` (Unicode) * flag first (JSON Schema 2020-12 recommends it) and falls back to * no-flag when the pattern trips strict `u`-mode rules (stray `\-`, * `\:`, `\/` etc., common in real-world OpenAPI specs). When a custom * {@link RegexCompiler} is passed to {@link createDeps}, this routes * through it instead, and the fallback logic doesn't apply (the * compiler is the authority on what's accepted). Results are memoized * in `patterns`. The default path throws `SyntaxError` only when the * pattern is malformed under both modes; a custom compiler may throw * whatever it likes. */ compilePattern: (pattern: string) => CompiledRegex; /** * Count the Unicode code points in `s` without allocating an * intermediate array. Used by `minLength` / `maxLength`, which the * JSON Schema 2020-12 spec requires to count code points (surrogate * pairs count as one). * * The obvious `[...s].length` expression builds a one-string-per-code-point * array just to read its length; on a 10 MB payload that allocates far * more than the `maxLength` check can refuse. This helper walks the * string's iterator and counts; O(1) memory. */ countCodePoints: (s: string) => number; /** * Length-bounded `maxLength` check. Returns `true` iff `s` has more * than `limit` code points, short-circuiting on `s.length` so valid * strings inside their bound skip the O(n) code-point walk and the * worst case walks at most `limit + 1` code points. See * {@link exceedsMaxCodePoints}. */ exceedsMaxCodePoints: (s: string, limit: number) => boolean; /** * Length-bounded `minLength` check. Returns `true` iff `s` has fewer * than `limit` code points, short-circuiting on `s.length`. See * {@link belowMinCodePoints}. */ belowMinCodePoints: (s: string, limit: number) => boolean; /** * Find the first duplicate in an array. Returns `{ a, b }` where * `arr[a]` and `arr[b]` are structurally equal (JSON deep equality) * and `a < b`, or `null` when every item is unique. Backs `uniqueItems`. * * Primitives use a `Map` (O(N) total); objects and * arrays fall back to pairwise `deepEqual` against the running list * of seen non-primitive items (O(k²) in the object-count tail, which * is unavoidable without canonicalisation). Mixed inputs get the * primitive fast path for every primitive and the object fallback * only for the object subset. */ findDuplicate: (arr: readonly unknown[]) => { a: number; b: number; } | null; formats: Map boolean>; refs: Map; /** User-registered keyword validators, keyed by keyword name. */ customKeywords: Map; /** * The configured maximum number of leaf errors to collect, or * `Number.POSITIVE_INFINITY` when uncapped. Baked in at compile time; * not mutated after construction. */ maxErrors: number; /** * Runtime counter, reset to `maxErrors` at the top of each top-level * `validate()` call. Every push decrements by one; when it reaches 0 * further pushes are skipped and {@link ValidatorDeps.truncated} is set. */ errorsRemaining: number; /** * Set to `true` by the runtime when at least one error was dropped * because the `maxErrors` budget had been exhausted. Cleared at the * top of each top-level `validate()` call. */ truncated: boolean; /** * The configured maximum recursion depth, or `Number.POSITIVE_INFINITY` * when uncapped. Baked in at compile time; not mutated after * construction. Compared against {@link ValidatorDeps.depth} at each * recursive `$ref` boundary. */ maxDepth: number; /** * Runtime recursion-depth counter, reset to `0` at the top of each * top-level `validate()` call. Incremented before descending through a * recursive (`$ref` back-edge) call and decremented after it returns, * so it tracks the current nesting depth rather than a cumulative * count. Only emitted when a finite {@link ValidatorDeps.maxDepth} was * configured. */ depth: number; } /** * Function signature of a compiled validator. * * @public */ type Validator = (data: unknown, path: (string | number)[]) => ValidationError | null; /** * The minimal interface required of a value returned by a * {@link RegexCompiler}. The runtime only ever calls `.test()` on * the result; nothing else is read. JavaScript's built-in `RegExp` * already satisfies this shape. * * @public */ interface CompiledRegex { test(s: string): boolean; } /** * Custom compiler for schema `pattern` keywords and the `format: * "regex"` assertion. Defaults to `new RegExp(pattern, "u")` (with a * non-`u` fallback for patterns that trip strict `u`-mode rules). * * Override to plug in `re2`, wrap with a complexity check, or reject * patterns that fail a safe-regex analysis. JavaScript's built-in * `RegExp` has no execution timeout, which makes catastrophic * patterns (e.g. `(a+)+$`) a denial-of-service vector against any * string the validator checks. * * Invocation cadence: * - For `pattern` keywords, the runtime memoizes by pattern string; * the compiler runs once per unique schema-authored pattern for * the lifetime of the validator (bounded by spec size). * - For `format: "regex"`, the runtime bypasses the cache; the * compiler runs per validate() call against the candidate string. * Caching there would retain runtime values indefinitely, which is * the opposite of what hardening callers want. * * @public * * @example * ```ts * import RE2 from "re2"; * * createValidator(spec, { * regexCompiler: (pattern) => new RE2(pattern), * }); * ``` */ type RegexCompiler = (pattern: string) => CompiledRegex; /** * Options bag accepted by {@link createDeps}. Prefer this form when * passing a {@link RegexCompiler}; the legacy `createDeps(maxErrors)` * positional form is preserved for back-compat with AOT-emitted * modules built before the option existed. * * @public */ interface CreateDepsOptions { /** Cap on leaf errors collected per `validate()` call. */ maxErrors?: number; /** Cap on recursion depth through `$ref` cycles per `validate()` call. */ maxDepth?: number; /** Custom compiler for `pattern` keywords and `format: "regex"`. */ regexCompiler?: RegexCompiler; } /** * The JSON-Schema-flavored typeof function: distinguishes `integer`, * `number`, `null`, `array`, `object`, etc. (everything that JSON Schema * 2020-12's `type` keyword recognizes). * * @param value - Any value. * @returns The JSON Schema type name. * * @example * ```ts * typeOf(null); // "null" * typeOf([]); // "array" * typeOf(1); // "integer" * typeOf(1.5); // "number" * ``` * * @public */ declare function typeOf(value: unknown): string; /** * Structural equality for JSON values: honors array ordering, object key * sets (not ordering), and NaN-as-not-equal. Used by `enum`, `const`, and * `uniqueItems`. * * Iterative, over an explicit work stack of pairs left to compare, so a * deeply nested payload cannot overflow the native call stack. A recursive * walk would throw `RangeError: Maximum call stack size exceeded` on the * same small-but-deep input the validator is meant to reject. * * @param a - First value. * @param b - Second value. * @returns `true` when both values are structurally equal. * * @example * ```ts * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true * deepEqual([1, 2], [2, 1]); // false * ``` * * @public */ declare function deepEqual(a: unknown, b: unknown): boolean; /** * Combine an error accumulator into a single ValidationError, collapsing the * trivial cases: empty → null, single → the one error, otherwise wrap. * * @param code - Wrapping error's code when multiple errors are present. * @param path - Path for the wrapping error. * @param errors - The accumulated errors. * @returns `null` when there are no errors, else the (possibly wrapped) error. * * @example * ```ts * wrapErrors("schema", [], []); // null * wrapErrors("schema", [], [onlyError]); // onlyError * wrapErrors("schema", [], [a, b]); // { code: "schema", children: [a, b], ... } * ``` * * @public */ declare function wrapErrors(code: string, path: readonly (string | number)[], errors: ValidationError[]): ValidationError | null; /** * Build a {@link ValidatorDeps} bundle with fresh mutable caches. * * Accepts either a positional `maxErrors` (legacy) or an options * bag with `maxErrors` and `regexCompiler`. The positional form is * kept for back-compat with AOT-emitted modules built before the * options bag existed. * * @returns A new deps object wired with the default runtime helpers * and a built-in `regex` format that shares the * {@link RegexCompiler} hook with the `pattern` keyword but * bypasses the {@link ValidatorDeps.patterns} cache so * runtime values aren't retained. * * @public */ declare function createDeps(maxErrors?: number): ValidatorDeps; declare function createDeps(options?: CreateDepsOptions): ValidatorDeps; export { type ResolveOptions as A, type ResolvedGraph as B, type CustomKeywordValidator as C, type Dialect as D, type ErrorKind as E, type ValidateSubschemaOptions as F, type Validator as G, createCustomKeywordDefinition as H, createRefResolver as I, customKeywordVocabulary as J, type KeywordDefinition as K, resolve as L, type NameGenerator as N, type PathSegmentLike as P, type RefResolver as R, SchemaRegistry as S, type ValidatorDeps as V, type RegexCompiler as a, type CompiledRegex as b, type CustomKeywordFailure as c, CodeGen as d, type CompileMode as e, type KeywordCompileContext as f, type CodeEmitter as g, type CreateDepsOptions as h, type RawExpression as i, Scope as j, booleanLiteral as k, collectDynamicAnchors as l, createDeps as m, deepEqual as n, nonNegativeIntegerLiteral as o, numberLiteral as p, pathJoinExpr as q, positiveNumberLiteral as r, quoteString as s, rawExpr as t, typeOf as u, type Vocabulary as v, wrapErrors as w, type CompileAndCallOptions as x, type CompileRuntime as y, type DialectRules as z };