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.
Choose the operation explicitly
Section titled “Choose the operation explicitly”| 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.
Dialects and references
Section titled “Dialects and references”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”propertiesdescribes fields; it does not make them required. List mandatory keys inrequiredon the containing object.- Missing and
nullare different. To accept a presentnull, explicitly includenullin the schema’s allowed types. - Set
additionalProperties: falseon 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. defaultis an annotation unless a consumer explicitly applies it. Validation does not populate it by default.generateJSONSchemaDefaultsis a separate operation.- Use
minItems/maxItemsfor arrays, numeric limits for numbers, andminLength/maxLengthfor 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.
Validate before accepting data
Section titled “Validate before accepting data”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.
Zod conversion: supported subset
Section titled “Zod conversion: supported subset”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.
Extensions and database mapping
Section titled “Extensions and database mapping”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:
- A primary key is non-nullable.
- Explicit
x-db.nullabletakes precedence for other columns. - Otherwise a missing
requiredentry, or an allowednulltype, 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 and Generative UI boundaries
Section titled “Action and Generative UI boundaries”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.
Runtime and integration requirements
Section titled “Runtime and integration requirements”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.
References
Section titled “References”- JSON Schema object, required, and additional-property semantics
- AJV data modification options
- AJV standalone compilation
- Zod JSON Schema conversion limits
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.