Skip to content

feat(objectql,spec): enforce all declared validation-rule types; trim the unenforceable three (#1475)#1485

Merged
xuyushun441-sys merged 1 commit into
mainfrom
fix/1475-enforce-validation-rules
Jun 1, 2026
Merged

feat(objectql,spec): enforce all declared validation-rule types; trim the unenforceable three (#1475)#1485
xuyushun441-sys merged 1 commit into
mainfrom
fix/1475-enforce-validation-rules

Conversation

@xuyushun441-sys
Copy link
Copy Markdown
Contributor

Summary

Closes #1475. The ObjectSchema.validations union declared 9 rule types but the write-path evaluator enforced only 3 (state_machine, cross_field, script); the other six were accepted by the schema yet silently did nothing — the "advertise a capability we don't deliver" anti-pattern. This closes the gap on both sides: implement the synchronous types, trim the ones that don't belong on a write path.

The governing principle: a validation rule is a deterministic, synchronous, side-effect-free predicate over a single record.

Implemented enforcement (@objectstack/objectql)

  • format — a field value against a regex and/or named format (email/url/phone/json). Runs only when the write touches the field and the value is non-empty (requiredness stays the field validator's job); a malformed regex fails open.
  • json_schema — a JSON field validated against a JSON Schema via ajv (compiled result memoised per schema object). Accepts a parsed object or a JSON string (unparseable string = invalid_json); an uncompilable schema fails open.
  • conditional — evaluates when, recurses into then/otherwise via a shared dispatch. The nested rule supplies the message; the outer conditional's severity decides blocking. needsPriorRecord now recurses into conditional branches.

Adds ajv as a dependency and three FieldValidationError codes (invalid_format, invalid_json, json_schema_violation).

Trimmed from the schema (@objectstack/spec)

unique, async, custom — each needs I/O or a handler model a write-path rule must not carry, so removed rather than left as silent no-ops, and redirected to the layer that already does it correctly:

  • uniqueness → a unique index (ObjectSchema.indexes, partial for scope) or field-level unique: true (a SELECT-then-INSERT rule is racy; a DB constraint isn't). Field-level unique: true is unaffected.
  • async/remote → the client form layer (its debounce/validatorUrl are keystroke concepts; SSRF/latency on the server write path).
  • custombeforeInsert/beforeUpdate lifecycle hooks (the existing typed extension point).

Showcase (demonstrated and verified)

examples/app-showcase's Account object gains a format (EIN regex), json_schema (support-config shape), and conditional (churn requires a reason) example, each exercised against the real evaluator in test/validation.test.ts.

Docs

ADR-0020 gains an addendum recording this as the completion of its D3 ("enforce the whole validations union").

Verification

  • @objectstack/objectql484 pass (incl. new format/json_schema/conditional suites)
  • @objectstack/spec6578 pass (validation parse tests updated + json_schema coverage added)
  • @objectstack/example-showcase17 pass (incl. new write-path verification)
  • typecheck clean: objectql, spec, runtime, showcase

🤖 Generated with Claude Code

… the unenforceable three (#1475)

The `validations` union advertised nine rule types but only three
(state_machine / cross_field / script) ran on the write path — the other
six were accepted by the schema yet silently no-op'd. Close the gap on
both sides.

objectql: the rule evaluator now also enforces `format` (regex / named
email·url·phone·json), `json_schema` (ajv-compiled, memoised), and
`conditional` (recursive when → then/otherwise). All three are
deterministic, synchronous, side-effect-free predicates over one record.
Adds ajv + three error codes; needsPriorRecord recurses into conditionals.

spec: removes the `unique`, `async`, and `custom` variants — each needs
I/O or a handler model a write-path rule must not carry. Redirected to the
layer that already does it correctly (unique index / client form / lifecycle
hook). Field-level `unique: true` is unaffected.

examples/app-showcase demonstrates and verifies each newly-enforced type.
Documented as an ADR-0020 addendum.

Closes #1475

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jun 1, 2026 6:52pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file protocol:data tests tooling size/xl labels Jun 1, 2026
@xuyushun441-sys xuyushun441-sys merged commit 7648242 into main Jun 1, 2026
9 of 12 checks passed
@xuyushun441-sys xuyushun441-sys deleted the fix/1475-enforce-validation-rules branch June 1, 2026 18:53
function matchesNamedFormat(format: FormatRule['format'], str: string): boolean {
switch (format) {
case 'email':
return EMAIL_RE.test(str);
xuyushun441-sys added a commit that referenced this pull request Jun 1, 2026
…ation (#1485 regression) (#1490)

#1485 trimmed the `unique`/`async`/`custom` validation-rule types from the
union, but sys_user still declared `email_unique` with type: 'unique', so
ObjectSchema.create threw a ZodError at load and platform-objects.test.ts (and
main CI) went red. The rule was redundant — sys_user already has a unique index
on email and better-auth enforces it on the managed user table — so it's
removed rather than migrated. No other object uses a trimmed type.

Verified: platform-objects.test.ts 52/52 pass.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot mentioned this pull request Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:data size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validation rules: 6 of 9 spec rule types are declared but not enforced at runtime

3 participants