ObjectStackObjectStack

Validation

Validation protocol schemas

ObjectStack Validation Protocol

This module defines the validation schema protocol for ObjectStack, providing a comprehensive

type-safe validation system similar to Salesforce's validation rules but with enhanced capabilities.

Overview

Validation rules are applied at the data layer to ensure data integrity and enforce business logic.

A validation rule is a **deterministic, synchronous, side-effect-free predicate over a single

record** — it must be decidable from the incoming write (and, on update, the prior record) with

no I/O. Everything advertised here runs on the write path (see

objectql/src/validation/rule-validator.ts); nothing is a silent no-op.

The system supports these validation types:

  1. Script Validation: Formula-based validation using a CEL predicate

  2. State Machine Validation: Control allowed state transitions

  3. Format Validation: Validate a field's value (email, URL, phone, JSON, regex)

  4. Cross-Field Validation: Validate relationships between multiple fields

  5. JSON Schema Validation: Validate a JSON field against a JSON Schema

  6. Conditional Validation: Apply a nested rule based on a CEL condition

Deliberately NOT validation rules

These were once declared here but never enforced. Because the contract above rules them out

(they need I/O or are client-side concerns), they were removed rather than left as silent

no-ops. Use the layer that already does each one correctly:

  • Uniqueness → a unique index (ObjectSchema.indexes, \{ fields, unique: true \},

with partial for a scoped/conditional constraint), or field-level unique: true. A

SELECT-then-INSERT "rule" is inherently racy (TOCTOU); a DB unique constraint is not.

  • Async / remote validation → a client-form concern (debounce/validatorUrl only mean

anything against keystrokes) and an SSRF/latency hazard on the server write path. Keep it in

the form layer, or enforce the underlying invariant with a unique index / lifecycle hook.

  • Custom handler → a beforeInsert / beforeUpdate lifecycle hook, the typed, supported

extension point for arbitrary validation code.

Salesforce Comparison

ObjectStack validation rules are inspired by Salesforce validation rules but enhanced:

  • Salesforce: Formula-based validation with Error Condition Formula

  • ObjectStack: Multiple validation types with composable rules

Example Salesforce validation rule:


Rule Name: Discount_Cannot_Exceed_40_Percent

Error Condition Formula: Discount_Percent__c > 0.40

Error Message: Discount cannot exceed 40%.

Equivalent ObjectStack rule:


\{

type: 'script',

name: 'discount_cannot_exceed_40_percent',

condition: 'discount_percent > 0.40',

message: 'Discount cannot exceed 40%',

severity: 'error'

\}

Source: packages/spec/src/data/validation.zod.ts

TypeScript Usage

import { ConditionalValidation, CrossFieldValidation, FormatValidation, JSONValidation, ScriptValidation, StateMachineValidation, ValidationRule } from '@objectstack/spec/data';
import type { ConditionalValidation, CrossFieldValidation, FormatValidation, JSONValidation, ScriptValidation, StateMachineValidation, ValidationRule } from '@objectstack/spec/data';

// Validate data
const result = ConditionalValidation.parse(data);

ConditionalValidation

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
whenstring | ObjectPredicate (CEL). e.g. Precord.type == 'enterprise'
thenObject | Object | Object | Object | Object | [#](./#)Validation rule to apply when condition is true
otherwiseObject | Object | Object | Object | Object | [#](./#)optionalValidation rule to apply when condition is false

CrossFieldValidation

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
conditionstring | ObjectPredicate (CEL) comparing fields. e.g. Precord.end_date > record.start_date
fieldsstring[]Fields involved in the validation

FormatValidation

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activeboolean
eventsEnum<'insert' | 'update' | 'delete'>[]Validation contexts
priorityintegerExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>
messagestringError message to display to the user
typestring
fieldstring
regexstringoptional
formatEnum<'email' | 'url' | 'phone' | 'json'>optional

JSONValidation

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activeboolean
eventsEnum<'insert' | 'update' | 'delete'>[]Validation contexts
priorityintegerExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>
messagestringError message to display to the user
typestring
fieldstringJSON field to validate
schemaRecord<string, any>JSON Schema object definition

ScriptValidation

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
conditionstring | ObjectPredicate (CEL). If TRUE, validation fails. e.g. Precord.amount < 0

StateMachineValidation

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activeboolean
eventsEnum<'insert' | 'update' | 'delete'>[]Validation contexts
priorityintegerExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>
messagestringError message to display to the user
typestring
fieldstringState field (e.g. status)
transitionsRecord<string, string[]>Map of { OldState: [AllowedNewStates] }

ValidationRule

Union Options

This schema accepts one of the following structures:

Option 1

Type: script

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
conditionstring | ObjectPredicate (CEL). If TRUE, validation fails. e.g. Precord.amount < 0

Option 2

Type: state_machine

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
fieldstringState field (e.g. status)
transitionsRecord<string, string[]>Map of { OldState: [AllowedNewStates] }

Option 3

Type: format

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
fieldstring
regexstringoptional
formatEnum<'email' | 'url' | 'phone' | 'json'>optional

Option 4

Type: cross_field

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
conditionstring | ObjectPredicate (CEL) comparing fields. e.g. Precord.end_date > record.start_date
fieldsstring[]Fields involved in the validation

Option 5

Type: json_schema

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
fieldstringJSON field to validate
schemaRecord<string, any>JSON Schema object definition

Option 6

Type: conditional

Properties

PropertyTypeRequiredDescription
namestringUnique rule name (snake_case)
labelstringoptionalHuman-readable label for the rule listing
descriptionstringoptionalAdministrative notes explaining the business reason
activebooleanoptional
eventsEnum<'insert' | 'update' | 'delete'>[]optionalValidation contexts
priorityintegeroptionalExecution priority (lower runs first, default: 100)
tagsstring[]optionalCategorization tags (e.g., "compliance", "billing")
severityEnum<'error' | 'warning' | 'info'>optional
messagestringError message to display to the user
typestring
whenstring | ObjectPredicate (CEL). e.g. Precord.type == 'enterprise'
then[#](./#)Validation rule to apply when condition is true
otherwise[#](./#)optionalValidation rule to apply when condition is false


On this page