Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions test/ANALYSIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
- Adds regression coverage for optional string fields to ensure wrapped string schemas (`z.string().optional()`) still accept string operators.
- Validates Date shorthand filters are preserved as `{ equals: Date }` instead of being dropped when non-plain object values are provided.
- Validates `orderBy` parsing supports Prisma-style array envelopes and rejects unsupported direction values.
- Validates empty `orderBy` envelopes (`{}` / `[{}]` / `[]`) are rejected with deterministic schema errors.
- Uses repo-defined `npm test` script (dist + jest) to satisfy source-change test gate.
1 change: 1 addition & 0 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ Jest tests for protocol schema helpers.
- Optional string field compatibility (`z.string().optional()` supports `contains`/`mode`)
- Date shorthand filter compatibility (`where: { createdAt: new Date(...) }` keeps `{ equals: Date }` semantics)
- `orderBy` compatibility (single object and Prisma-style array envelopes)
- `orderBy` non-empty enforcement (reject `{}` / `[{}]` / `[]` envelopes)
22 changes: 22 additions & 0 deletions test/queryInput.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,28 @@ describe('getQueryInput', () => {
).toThrow();
});

it('rejects empty orderBy envelopes', () => {
const schema = getQueryInput(model);

expect(() =>
schema.parse({
orderBy: {},
}),
).toThrow('orderBy object must include at least one field');

expect(() =>
schema.parse({
orderBy: [{}],
}),
).toThrow('orderBy object must include at least one field');

expect(() =>
schema.parse({
orderBy: [],
}),
).toThrow('orderBy array must include at least one object');
});

it('rejects negative pagination values', () => {
const schema = getQueryInput(model);

Expand Down
1 change: 1 addition & 0 deletions util/ANALYSIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
- Scoped string-only operators (`contains` / `startsWith` / `endsWith` / `mode`) to string fields only; this prevents invalid filter payloads from being accepted for numeric/date fields and shifts failures to schema-parse time.
- Fixed optional/nullable/default/effects wrapper handling when inferring field type so optional string fields still expose valid string operators (previously `z.string().optional()` was misclassified and rejected `contains`/`mode`).
- Added `orderBy` compatibility for both single-object and Prisma-style array envelopes, reducing query-shape rejection for multi-sort callers.
- Added guardrails so `orderBy` objects must include at least one key and `orderBy` arrays must include at least one object; empty envelopes now fail schema parse early.
- Added test coverage to lock pagination behavior and shorthand `where` conversion, including invalid pagination rejection and `orderBy` array support.
- Fixed shorthand filter coercion for non-plain object scalars (e.g. `Date`): only plain objects are treated as operator envelopes, so `where: { createdAt: new Date(...) }` now correctly normalizes to `{ createdAt: { equals: ... } }` instead of being stripped.
1 change: 1 addition & 0 deletions util/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ Shared schema helpers for Sigil protocol routers.
- String-only operators (`contains` / `startsWith` / `endsWith` / `mode`) are now only accepted on string fields to fail fast before invalid Prisma query construction on numeric/date fields.
- Optional/nullable/default-wrapped string fields are unwrapped before filter generation, so valid string operators continue to work on fields like `z.string().optional()`.
- `orderBy` accepts both a single object and Prisma-style arrays of objects (e.g. `[{ level: 'desc' }, { name: 'asc' }]`).
- Empty `orderBy` envelopes are rejected (`{}` / `[{}]` / `[]`) so invalid sort payloads fail fast before resolver/database execution.
18 changes: 16 additions & 2 deletions util/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,22 @@ export const getQueryInput = <S extends zod.ZodTypeAny>(

orderBy: zod
.union([
zod.record(zod.enum(["asc", "desc"])),
zod.array(zod.record(zod.enum(["asc", "desc"]))),
zod
.record(zod.enum(["asc", "desc"]))
.refine(
(value) => Object.keys(value).length > 0,
"orderBy object must include at least one field",
),
zod
.array(
zod
.record(zod.enum(["asc", "desc"]))
.refine(
(value) => Object.keys(value).length > 0,
"orderBy object must include at least one field",
),
)
.min(1, "orderBy array must include at least one object"),
])
.optional(),
include: zod.record(zod.boolean()).optional(),
Expand Down