Skip to content

Schema specifications and constraints

Data Engine exposes several consumers of schemas. They have different contracts: a schema accepted by one API is not automatically supported by every compiler, tool, or UI contract. This guide describes the current implementation; API signatures and individual options are defined by source JSDoc.

Operation What it establishes What it does not establish
createSchema Returns the supplied JSON Schema with TypeScript type information Runtime schema validation, cloning, freezing, or data validation
createAjvValidator Validates data against JSON Schema using AJV Authorization, data persistence, or complete enforcement of custom x-* extensions
generateZodSchema Produces a Zod object for the implemented conversion subset Equivalent validation for arbitrary JSON Schema
zodToJSONSchema Converts supported Zod shapes Lossless round trips or preservation of every refinement and transform
auditSchema Checks package metadata conventions such as $id, x-plugin, and x-db, according to its options JSON Schema meta-schema validation or validation of application data
generateSqlFromJSONSchema Generates PostgreSQL-oriented SQL text for a selected table SQL execution, a completed migration, or enforcement of every JSON Schema rule in the database

Use JSON Schema as the stored/exchanged contract and AJV for its validation. Zod can provide application-facing integration, but do not use successful Zod conversion as evidence that all JSON Schema constraints were preserved.

The AJV adapter defaults to draft-07 when $schema is absent. It recognizes the standard draft-07 and 2020-12 schema URIs, with HTTP or HTTPS and an optional trailing #. An unsupported URI throws. An explicit draft option conflicting with $schema also throws. Do not mix dialects within one contract or assume draft-04/2019-09 support through this adapter.

dereferenceSchema disables file and HTTP resolvers. Keep references inside the supplied document. A URL reference is not permission to download another schema. Dereferencing does not make a recursive schema acceptable to tools that require finite JSON; perform the receiving API’s checks as well.

For strict Content Security Policy or runtimes that restrict generated functions, evaluate AJV’s compilation requirements before deployment. AJV supports build-time standalone validators, but the engine’s createAjvValidator currently compiles schemas at runtime. Its runtime portability must be verified in the intended host.

Required fields, null, defaults, and extra keys

Section titled “Required fields, null, defaults, and extra keys”
  • properties describes fields; it does not make them required. List mandatory keys in required on the containing object.
  • Missing and null are different. To accept a present null, explicitly include null in the schema’s allowed types.
  • Set additionalProperties: false on each object where unknown keys must be rejected. Omitting it permits extra keys under ordinary JSON Schema semantics. With composed schemas, check the dialect’s rules before closing an object.
  • default is an annotation unless a consumer explicitly applies it. Validation does not populate it by default. generateJSONSchemaDefaults is a separate operation.
  • Use minItems/maxItems for arrays, numeric limits for numbers, and minLength/maxLength for strings. These are separate from the engine’s payload limits.

The general AJV adapter currently defaults to coerceTypes: false, allErrors: true, and strict: false. The default path does not turn "3" into 3. Explicit ajvOptions can override behavior: coercion, default insertion, and removal of additional properties can modify data. Normalize form strings separately and validate the result. Do not enable mutation on frozen tool payloads.

import { createSchema, createAjvValidator } from '@alneas/data-engine/schema';
const schema = createSchema({
$schema: 'https://json-schema.org/draft/2020-12/schema',
type: 'object',
properties: {
name: { type: 'string', minLength: 1 },
quantity: { type: 'integer', minimum: 1 },
tags: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 5 },
note: { type: ['string', 'null'] },
},
required: ['name', 'quantity', 'tags'],
additionalProperties: false,
});
const validate = createAjvValidator({
draft: '2020-12',
ajvOptions: { coerceTypes: false, useDefaults: false, removeAdditional: false },
});
const result = validate(schema, { name: 'Notebook', quantity: 2, tags: ['office'], note: null });
if (!result.valid) throw new Error(result.errors.join('; '));
// Both must fail: no coercion, and the array must contain at least one item.
console.assert(!validate(schema, { name: 'Notebook', quantity: '2', tags: ['office'] }).valid);
console.assert(!validate(schema, { name: 'Notebook', quantity: 2, tags: [] }).valid);

Malformed schemas, unresolved references, and unsupported dialects can throw during compilation. They are configuration failures, distinct from { valid: false } for invalid data. Compile trusted schemas during initialization, reuse the validator and the same schema object, and do not mutate a schema after compilation. Supply a new object when changing its contract. General validation is not a sandbox for arbitrary, attacker-controlled schemas or regular expressions; control schema registration and request sizes in the host application.

AJV remains the authoritative JSON Schema validator. Its synchronous API rejects $async schemas before evaluation, in both supported dialects. generateZodSchema requires an object root (properties may be omitted); it is a UI conversion helper, not a complete JSON Schema evaluator.

Keyword Runtime and generated-code behavior
properties / required Root/nested required keys, including keys absent from properties; additionalProperties still applies. Optional means missing, not null
null / type arrays Null-only, nullable and multiple scalar types supported
enum Root and nested values; combined with other implemented constraints, using JSON structural equality. The TypeScript enum value type currently exposes scalars only
additionalProperties false rejects extra fields even without properties; a schema validates extras; omitted/true preserves extras in parsed output
minimum / maximum / positive multipleOf Enforced, including zero bounds
minLength / maxLength / pattern Enforced on string fields, including zero bounds; JavaScript regular expressions and Zod length semantics apply
minItems / maxItems / single items schema Enforced for homogeneous arrays
exclusive bounds, composition, references, uniqueItems, tuples, contains Not implemented; validate with AJV
formats Selected UI formats only; date checks string type, not calendar validity; not equivalent to all AJV formats
x-zod Only numeric coerce is implemented here; it deliberately differs from non-coercing AJV

For multipleOf: 2, both validators reject { n: 3 }. For unsupported keywords, do not substitute converted Zod for AJV. Allowed unknown keys are preserved rather than stripped. A parsed result can differ if an explicitly enabled coercion runs. Finite-number and Unicode string-length/format behavior should be compared against the application’s own fixtures.

generateZodSchemaCode emits an import of @alneas/data-engine/schema/compiler plus a safely JSON-encoded schema and sanitized export identifier (non-identifier characters become underscores; leading digits get an underscore). It delegates to the same runtime converter, including its limits. Treat generated source as a module with that package dependency; it is not a standalone JSON Schema evaluator. Regression tests execute generated code with apostrophes, hyphenated keys, titles with spaces and slash patterns.

x-ui, x-db, x-plugin, x-codelist, x-rules, and x-zod are engine/application extensions, not standard validation keywords. Each consumer implements its own subset. The general AJV adapter’s strict: false allows unknown keywords to be ignored; it does not execute their business meaning. Strict tool validation can instead reject unknown extension keywords. Keep storage/DDL metadata separate from an AI tool’s input schema.

For DDL, select a single table with non-empty properties. Passing a bundle containing only $defs is rejected. generateSqlFromJSONSchema targets PostgreSQL; other exported generators have their own targets and limitations. Database nullability is resolved in this order:

  1. A primary key is non-nullable.
  2. Explicit x-db.nullable takes precedence for other columns.
  3. Otherwise a missing required entry, or an allowed null type, permits SQL NULL.

This compatibility mapping is distinct from JSON Schema’s missing/null distinction. JSON Schema constraints such as array bounds are not automatically translated into SQL CHECK constraints. Review generated SQL against the target database and validate application data independently.

x-db.type, x-db.default, and x-db.check can contribute SQL expressions. Accept schema authorship only from trusted maintainers; schema data supplied by a request must not become executable SQL. Schema validation, quoting identifiers, and generating migration text do not provide database authorization or a safe migration rollout by themselves.

Action tool input schemas must have type: 'object' at the root and be synchronous. Their AJV validator is strict, permits union types, and disables coercion, defaults, and key removal. defineActionTool requires the explicit parser, handler, and output projection described in its JSDoc. Validate/refine fields in the synchronous parser and authorize the operation in the host before side effects. A tool description does not grant permission.

Generative UI component dataSchema has a stricter subset: inline synchronous draft-07 object schemas, with at most 32 registered component types. The implementation rejects $schema, $id, $ref, $anchor, $dynamicRef, $dynamicAnchor, $recursiveRef, $recursiveAnchor, and $async anywhere in component data schemas. A general 2020-12 schema cannot be passed unchanged here. The generated response envelope uses version 1, a nonblank document ID, and at most 32 flat blocks with unique nonblank IDs; document/block IDs have a maximum length of 128.

Tool and Generative UI snapshot boundaries require finite JSON values: plain objects, dense arrays, strings, booleans, finite numbers, and null. They reject cycles, accessors, symbol keys, non-enumerable properties, sparse arrays, class instances, undefined, BigInt, functions, NaN, and infinity. Do not pass Date, File, Blob, or Buffer instances through these JSON boundaries; serialize an application-approved representation instead.

Each snapshot independently allows at most 10,000 visited values and depth 64 (root depth 0). Its serialized JSON string must have JavaScript .length at most 1,000,000: this counts UTF-16 code units, not UTF-8 bytes. These checks do not replace HTTP/body/stream limits before JSON is parsed. Snapshots are detached and recursively frozen; use the returned validated value and avoid mutating it.

Portable schema data does not make every adapter available in every host. LocalStorageAdapter requires a Node-compatible filesystem. Database SDK adapters require their separately supplied SDKs and credentials. SQL generation never establishes a database connection. Check which adapters are actually exported; a source file is not necessarily a supported import path.

S3/R2/OCI uploads accept strings, Blob, Uint8Array and ReadableStream without Buffer. Downloads are Uint8Array; decode with TextDecoder. Import LocalStorageAdapter explicitly from @alneas/universal-storage/server. Portable imports use /portable or the browser-resolved root. Keep long-lived cloud credentials in a trusted host and use short-lived presigned URLs for browsers.

Data validation does not establish user authorization, tenant isolation, database constraints, legal compliance, or trust in AI output. Establish those in the host application and test the full operation. TSA clients require a trusted verification backend; importing data models or generating an invoice does not certify a deployment’s compliance.

Content JSON/Markdown keys must not equal the configured internal index key. For JSON with the default indexFile, slug _index is reserved; custom indexFile names reserve the corresponding content slug. get/save/delete reject collisions before accessing content or changing the index. Rename a colliding document in a reviewed migration; do not overwrite its index. Instance serialization does not provide a cross-process transaction.

Declared/required __proto__ fields are rejected during Zod conversion because Zod skips that key internally; use AJV for such JSON schemas. Object schemas with refinements (root enum or additional required-presence checks) follow Zod 4’s .safeExtend() rules; ordinary declared required fields do not add unnecessary refinements. Content slug/indexFile paths must be nonempty relative named segments, without empty, dot/dot-dot segments or backslashes, to prevent index aliases across storage backends.

The generic validator is owned by data-engine and directly depends on AJV; no ai-core installation is required for schema validation. AI tool validators remain owned by ai-core and retain their stricter boundary policy.