Skip to content

[finding] Five filter operators ($gt/$gte/$lt/$lte/$between) reach NO published reference page — build-schemas.ts skips their whole schema over an unrepresentable z.date(), and the skip is silent #16431

Description

@huangyiirene

Filed by the domain:spec dev seat (session_01T6HeZvT9wdSJD1ZxJb5Eno) while implementing #15059 (PR #16432). Out of scope there by that card's own fence — #15059 is prose on .describe(), and the remedy here is a change to the generator or to a schema shape, which #15059 forbids. Unassigned, bare, for triage.

Measured on origin/main @ 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39

packages/spec/scripts/build-schemas.ts emits one JSON Schema per exported z.ZodType. When z.toJSONSchema() throws for a known-unrepresentable type it retries with io: 'input', and when that throws too it skips the export with a console.warn and continues. Four exports of packages/spec/src/data/filter.zod.ts take that path. From my own run of pnpm --filter @objectstack/spec build:

⊘ Data.ComparisonOperatorSchema: Date cannot be represented in JSON Schema (skipped)
⊘ Data.FieldOperatorsSchema: Date cannot be represented in JSON Schema (skipped)
⊘ Data.NormalizedFilterSchema: Date cannot be represented in JSON Schema (skipped)
⊘ Data.RangeOperatorSchema: Date cannot be represented in JSON Schema (skipped)

The Date is real and deliberate: orderingComparandSchema is z.union([z.number(), z.date(), z.string(), FieldReferenceSchema]), and rangeEndpointSchema is z.union([z.number(), z.date(), z.string()]). Nothing is wrong with the schemas; the generator simply cannot project them.

content/docs/references/** renders from packages/spec/json-schema/, so a skipped export has no section. Measured with lit controls on the same tree:

grep -c 'ComparisonOperator' content/docs/references/data/filter.mdx   -> 0
grep -c 'RangeOperator'      content/docs/references/data/filter.mdx   -> 0
grep -c 'FieldOperator'      content/docs/references/data/filter.mdx   -> 0
grep -c 'SpecialOperator'    content/docs/references/data/filter.mdx   -> 2   (control: the file IS live and the pattern CAN hit)

grep -rn 'ComparisonOperator|RangeOperator|FieldOperators' content/docs/  -> no hits in ANY tree
grep -rln 'SpecialOperator' content/docs/                                -> 2 files (control)

packages/spec/json-schema.manifest/data.json agrees from the other side: it lists eight filter.zod.ts schemas (EqualityOperator, FieldReference, FilterArray, FilterCondition, QueryFilter, SetOperator, SpecialOperator, StringOperator) and not these four.

What that costs

Five operators have no published reference row at all — not a blank Description cell, no row:

operator where it is declared prose that reaches no page
$gt $gte $lt $lte ComparisonOperatorSchema ~1050 characters of .describe() each — the whole #5685 comparand contract: which comparand forms are guaranteed, that ordering non-temporal text is permitted but NOT promised, and that null is not a comparand
$between RangeOperatorSchema ~1010 characters — the #6571 endpoint contract and the #7596 no-{ $field }-in-a-list rule

So the reference page teaches the equality, set, string and null/exists families and is silent on the entire ordering family — including the { "$gte": "2026-01-01" } shape the platform's own date-macro resolver produces, which is exactly what #5685 wrote that prose to correct.

The ## TypeScript Usage import line on the page is driven by the export surface rather than by the emitted schemas, so it does not name them either and nothing on the page hints anything is missing.

Why it is worth recording rather than repairing in passing

Relationship to #15059

#15059 fills the blank Description cells on filter.mdx — the cells that render because their schema is emitted. This card is about sections that do not render at all. The two do not overlap and the remedy sites are different files. PR #16432 also corrects one sub-claim of #15059's body in passing: that card says FieldOperatorsSchema's restated $null / $exists leave "the FieldOperator table" blank, and there is no FieldOperator table on the page, for the reason measured above. PR #16432 describes those members anyway, so the two copies cannot drift.

Refs: #15059 · PR #16432 · #5685 · #6571 · #2978 · #4725

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions