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

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specBuildingBuildingPreview, CommentJun 1, 2026 6:52pm

Request Review

@github-actionsgithub-actionsBot 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 mainJun 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-actionsgithub-actionsBot mentioned this pull request Jun 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationprotocol:datasize/xlteststooling

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

@xuyushun441-sys@github-advanced-security@os-zhuang