Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

v3: Custom JSON types ignore field validation attributes and type-level @@validate

Aperta
#2,844 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
65/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
node.js, postgresql, typescript
Ambito
backend

Direzione di ricerca

The payload points at ZodSchemaFactory.makeTypeDefSchema in the @zenstackhq/orm schema factory: it calls makeScalarSchema(def.type) without the field's def.attributes and does not apply type-level typeDef.attributes through the custom-validation helper. Start by running the provided schema.zmodel and repro.ts to confirm the six invalid mutations are accepted. Then trace how scalar fields pass attributes into validation and how the validateInput option gates the helpers. Done means all six invalid creates and updates are rejected while the wrong-scalar-type and unknown-key controls still reject.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Description and expected behavior

With validateInput: true, direct ORM creates and updates accept custom JSON values that violate @length, @gte, and type-level @@validate rules. The schema generates successfully and contains those validation attributes.

Expected: mutation input should be rejected when it violates those declared rules, including rules inside custom JSON types. Each update below replaces the entire JSON object, so this does not depend on validating omitted fields from a partial object.

@@strict and basic scalar-type checks still reject invalid input; this is specifically missing enforcement of the validation attributes.

Minimal reproduction

Create a fresh ESM project (package.json containing {"private":true,"type":"module"}), then install:

pnpm add @zenstackhq/[email protected] @zenstackhq/[email protected] @zenstackhq/[email protected] @zenstackhq/[email protected] [email protected] [email protected] [email protected]

Set DATABASE_URL to an empty disposable PostgreSQL database. The script creates an Example table there. Save the following files, then run:

pnpm exec zen generate --schema schema.zmodel
pnpm exec tsx repro.ts
schema.zmodel
datasource db {
  provider = "postgresql"
}

type Bounds {
  caption String @length(1, 3)
  low Int @gte(0)
  high Int
  @@validate(low <= high)
  @@strict
}

model Example {
  id Int @id
  bounds Bounds @json
}
repro.ts
import { ZenStackClient } from '@zenstackhq/orm';
import { PostgresDialect } from '@zenstackhq/orm/dialects/postgres';
import { Pool } from 'pg';
import { schema } from './schema';

const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 1 });
const db = new ZenStackClient(schema, {
  dialect: new PostgresDialect({ pool }),
  validateInput: true,
});
try {
  await pool.query('CREATE TABLE "Example" (id integer PRIMARY KEY, bounds jsonb NOT NULL)');
  const invalid = [
    { caption: 'too long', low: 0, high: 1 },
    { caption: 'ok', low: -1, high: 1 },
    { caption: 'ok', low: 5, high: 1 },
  ];
  for (const [index, bounds] of invalid.entries()) {
    console.log('create', JSON.stringify(await db.example.create({ data: { id: index + 1, bounds } })));
  }
  for (const bounds of invalid) {
    console.log('update', JSON.stringify(await db.example.update({ where: { id: 1 }, data: { bounds } })));
  }
  for (const [label, bounds] of [
    ['wrong scalar type', { caption: 'ok', low: 'zero', high: 1 }],
    ['unknown key', { caption: 'ok', low: 0, high: 1, extra: true }],
  ] as const) {
    try {
      // These control cases intentionally bypass static structural checks.
      await db.example.create({ data: { id: 99, bounds: bounds as any } });
      console.log(label, 'accepted');
    } catch {
      console.log(label, 'rejected');
    }
  }
} finally {
  await pool.end();
}
Actual output
create {"id":1,"bounds":{"low":0,"high":1,"caption":"too long"}}
create {"id":2,"bounds":{"low":-1,"high":1,"caption":"ok"}}
create {"id":3,"bounds":{"low":5,"high":1,"caption":"ok"}}
update {"id":1,"bounds":{"low":0,"high":1,"caption":"too long"}}
update {"id":1,"bounds":{"low":-1,"high":1,"caption":"ok"}}
update {"id":1,"bounds":{"low":5,"high":1,"caption":"ok"}}
wrong scalar type rejected
unknown key rejected

All six printed creates/updates should reject:

  1. caption: "too long" violates @length(1, 3).
  2. low: -1 violates @gte(0).
  3. low: 5, high: 1 violates @@validate(low <= high).

The last two lines are controls showing that basic structural validation and @@strict remain active. The as any applies only to those deliberately malformed control cases; the six incorrectly accepted mutations need no cast.

Environment

  • ZenStack: 3.9.4, stock npm packages, no patches
  • PostgreSQL: 16.15
  • Node.js: 22.14.0
  • Package manager: pnpm 11.25.0
  • pg: 8.21.0; Zod: 4.5.4; tsx: 4.19.0
  • Direct ZenStackClient with validateInput: true; no plugins

Additional context

In 3.9.4, ZodSchemaFactory.makeTypeDefSchema calls makeScalarSchema(def.type) without the field's def.attributes, and does not apply typeDef.attributes through the custom-validation helper. This appears to explain the behavior. Passing the field annotations and applying type-level validation through the existing helpers, while respecting the validation option, seems like a targeted fix.

Lingua principale
TypeScript
Stelle
2.9k
Fork
157
Merge medio
11h 42m
PR unite (30g)
20

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di zenstackhq/zenstack

Tutte le issue di zenstackhq/zenstack

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.