Skip to main content

Rasd Forms — JSON Schemas

Machine-readable contracts for the two JSON documents Rasd Forms consumes. Prose specifications live in 04 · Form schema spec and 12 · Theming; the normative decisions in 00 · Design spine win over everything.

File$idValidates
rasd-form.schema.jsonhttps://schemas.rasd.dev/form/v1.jsonA Rasd Form Definition (RFD) v1: root, meta, settings (audit/encryption/theme), choiceLists, datasets, pages → elements, logic (calculated + triggers), ext. One $def per element type (TextElementRepeatElement, CustomElement for x:*), dispatched from $defs/Element by an if/then chain on type.
rasd-theme.schema.jsonhttps://schemas.rasd.dev/theme/v1.jsonA design-token theme; its #/$defs/ThemePartial is the shape of an RFD's settings.theme.overrides.

Both are JSON Schema draft 2020-12. Every property carries a description — the schema doubles as reference documentation and drives editor autocompletion when a document starts with "$schema": "https://schemas.rasd.dev/form/v1.json". Worked examples are in ../examples/: pdm-food-distribution.form.json and facility-monitoring-visit.form.json (Arabic + English, groups, repeats, dataset-driven cascading selects, REL logic, triggers, media capture, ext payloads).

What the schema does and does not check

The schema is the structural pass: shapes, enums, patterns, per-type props, XOR rules (list/choices, default.value/default.expr, choices/source), reserved names, prototype-pollution keys. It is deliberately stricter than the runtime: unknown properties are rejected here (additionalProperties: false) while @rasd/core ignores-and-preserves them with W_UNKNOWN_PROPERTY. Open extension points stay open: ext everywhere, props of x:* elements, and appearance.

Everything JSON Schema cannot express is the semantic pass of validateFormDefinition() (and rasd validate): name uniqueness and shadowing across repeat scopes, references (props.list, source.dataset, skipTo pages, trigger targets), REL parsing and dependency cycles, defaultLocale ∈ locales, min ≤ max, size limits. A document that passes ajv can still fail rasd validate; the reverse is a bug.

Validating

With @rasd/cli (runs both passes, prints codes + JSON pointers, exit code 1 on errors):

npx @rasd/cli validate docs/examples/pdm-food-distribution.form.json
npx @rasd/cli theme check my-theme.json

With the ajv CLI (structural pass only). The form schema uses two annotation keywords, x-rasd-expr and x-rasd-localized (on $defs/Expr and $defs/LocalizedString), so ajv's unknown-keyword check must be relaxed with --strict-schema=false; all other strict checks stay on:

npm i -D ajv-cli@5 ajv-formats@3
npx ajv validate --spec=draft2020 -c ajv-formats --strict-schema=false \
-s docs/schema/rasd-form.schema.json -d "docs/examples/*.form.json"
npx ajv validate --spec=draft2020 -c ajv-formats \
-s docs/schema/rasd-theme.schema.json -d docs/examples/theme-agency-blue.json

Programmatically (this is what CI runs; the schema compiles under strict: true):

import Ajv2020 from "ajv/dist/2020.js";
import addFormats from "ajv-formats";
import schema from "./rasd-form.schema.json" with { type: "json" };

const ajv = new Ajv2020({ strict: true, strictRequired: false, allErrors: true });
addFormats(ajv);
ajv.addVocabulary(["x-rasd-expr", "x-rasd-localized"]);
const validate = ajv.compile(schema);
if (!validate(formJson)) console.error(validate.errors);

strictRequired is off because ajv compiles if/then before properties and would flag every conditional required (e.g. publicKeyId when encryption.mode ≠ "none"); it is also off in ajv's default strict mode. Python jsonschema (Draft202012Validator) and the VS Code JSON language service load the file unchanged — they ignore unknown annotation keywords.

Source of truth and CI

In the monorepo the zod schemas in @rasd/core are the executable source; pnpm schema:build regenerates this file (z.toJSONSchema, target 2020-12, plus $id, descriptions and the two annotation keywords) and pnpm schema:check fails CI on any drift. pnpm schema:test validates every docs/examples/*.form.json and every fixture in @rasd/testing against both zod and ajv; the two must agree. Hand-editing this file is fine for a proposal, but the zod change must land in the same PR.

Versioning of $id

  • https://schemas.rasd.dev/form/v1.json always serves the latest 1.x; frozen copies are published at /form/v1.0.json, /form/v1.1.json, … the moment a minor ships. The same rule applies to /theme/v1.json.
  • A MINOR only adds: optional properties, element types, enum values, REL functions, codes. Documents written against 1.0 stay valid against 1.1; documents using 1.1 features fail against the frozen 1.0 file (that is what rasd: "1.1" and requires are for).
  • A MAJOR changes meaning or removes something and gets a new $id (/form/v2.json) plus a converter (convertDefinition). Deprecated properties are marked deprecated: true and keep working for the whole 1.x line and at least 12 months after announcement.
  • The document's rasd property ("1.0") is pinned to major 1 by this schema's pattern; $schema may point at the rolling URL, a frozen minor, or a repository-relative path.