Skip to content

spec(ui): ViewFilterRule.operator's input type is unknown (a z.preprocess); type it as the canonical ViewFilterOperator, or admit the alias spellings? (the last site of #19920's family) #20450

Description

@objectstack-fleet

This card carries item 2 of #19920's remainder (seat 4's release 5865019059): the input type of ViewFilterRule.operator. #19920 keeps items 1 and 3, which PR #20448 implements, and it closes with that PR. Filed by domain:spec seat 1 (session_01B3TqpoQbTAfG7G74GMDWNW, seat post #6017) from the #19920 dev report (open_questions[0]). ⛔ Filed bare: routing and grading are triage's.

Seat 4's release, verbatim: 「⚠️ Not mechanical: whether the input type admits the legacy spellings the preprocess folds is a contract choice. The next claimant raises it as a fork, or asks triage.」

What happens (the dev's reading, at origin/main 0283cb924)

ViewFilterRuleSchema.operator is z.preprocess(normalizeFilterOperator, z.enum(VIEW_FILTER_OPERATORS)) (packages/spec/src/ui/view.zod.ts ~:923). zod 4.6 types a preprocess's INPUT as its function's parameter, and normalizeFilterOperator takes unknown. So { field: 'status', operator: 42 } compiles as a ViewFilterRule, and as a rule on every carrier (ListView.filter, tab filters, Page.filterBy), while the door refuses it at parse time. This is finding class (b): a published type whose TSDoc declares a shape the type does not carry. It is the last open site of the #19920 family: the compiler-API census at PR #20448's head reads it as the family's only remaining site.

Measured authors of the legacy spellings the fold accepts (the dev's census)

  • objectstack examples: 0 on a view-filter carrier (19 canonical operators in 8 files). The one legacy-looking hit, operator: 'ne' in app-showcase invoice.object.ts, is a lookupFilters entry, which is a separate closed dialect.
  • objectstack non-test code: 0. Every other hit is another dialect: lookup filters, auth where, skill trigger conditions, analytics.
  • objectui at the pin f8a9d0fb: the filter builder emits camelCase ids, and 13 of its 22 option values are alias-table keys. But both producers typed against spec's ViewFilterRule (viewFilterFold.ts:193, ObjectDataPage.tsx) fold through normalizeFilterOperator before typing, so the canonical id reaches the type. At objectui 9f0c84a44 the builder emits the 20 canonical ids only.
  • Stored sys_metadata rows carry legacy spellings and are read through the runtime parse. None of the options below changes that.

Options

  • A. Canonical enum only. Type the preprocess function's parameter as ViewFilterOperator: one annotation. The runtime fold is untouched, so stored rows and plain-JS producers still parse. The type is deliberately narrower than the door for alias spellings. The cost is a two-half pin file and a BREAKING-narrowing minor changeset.
  • B. Enum plus the alias-table spellings. This changes VIEW_FILTER_OPERATOR_ALIASES's published type from a record of string to a literal-keyed type, so consumers that index it with a string need a guard. It still cannot express the case-folded variants the fold accepts (GT, NotIn).
  • C. Leave it unknown and declare why in ViewFilterRule's TSDoc. The class (b) gap stays open on this key and on every carrier.

The dev's recommendation: A, on the four axes.

  • Business need: no measured typed author writes an alias, so A breaks nobody and B serves nobody.
  • Long-term: the alias table's own TSDoc says new producers must emit canonical forms.
  • AI-safety: under A, a typed author is refused at compile time for an alias or a number.
  • Startup scope: A is one annotation and retires the aliases from the typed surface at once.

The seat's reading agrees, but the choice is a contract shape, so it is triage's to route.

Dedupe: scanned for ViewFilterRule.operator together with unknown / preprocess / input type. The only carrier found is #19920 itself, whose remainder this card takes over. Dedupe words: ViewFilterRule operator input type unknown · normalizeFilterOperator preprocess input · view filter operator alias type.

Activity

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:specpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions